mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Add slack support to Firefly III
This commit is contained in:
parent
2945f5fcde
commit
45d7042405
@ -75,8 +75,9 @@ class HomeController extends Controller
|
|||||||
foreach (config('firefly.admin_notifications') as $item) {
|
foreach (config('firefly.admin_notifications') as $item) {
|
||||||
$notifications[$item] = FireflyConfig::get(sprintf('notification_%s', $item), true)->data;
|
$notifications[$item] = FireflyConfig::get(sprintf('notification_%s', $item), true)->data;
|
||||||
}
|
}
|
||||||
|
$slackUrl = FireflyConfig::get('slack_webhook_url', '')->data;
|
||||||
|
|
||||||
return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications'));
|
return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notifications(Request $request): RedirectResponse
|
public function notifications(Request $request): RedirectResponse
|
||||||
@ -86,7 +87,14 @@ class HomeController extends Controller
|
|||||||
if ($request->has(sprintf('notification_%s', $item))) {
|
if ($request->has(sprintf('notification_%s', $item))) {
|
||||||
$value = true;
|
$value = true;
|
||||||
}
|
}
|
||||||
FireflyConfig::set(sprintf('notification_%s',$item), $value);
|
FireflyConfig::set(sprintf('notification_%s', $item), $value);
|
||||||
|
}
|
||||||
|
$url = (string) $request->get('slackUrl');
|
||||||
|
if ('' === $url) {
|
||||||
|
FireflyConfig::delete('slack_webhook_url');
|
||||||
|
}
|
||||||
|
if (str_starts_with($url, 'https://hooks.slack.com/services/')) {
|
||||||
|
FireflyConfig::set('slack_webhook_url', $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
session()->flash('success', (string) trans('firefly.notification_settings_saved'));
|
session()->flash('success', (string) trans('firefly.notification_settings_saved'));
|
||||||
|
@ -101,6 +101,7 @@ class PreferencesController extends Controller
|
|||||||
$languages = config('firefly.languages');
|
$languages = config('firefly.languages');
|
||||||
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
|
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
|
||||||
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
|
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
|
||||||
|
$slackUrl = app('preferences')->get('slack_webhook_url', '')->data;
|
||||||
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
|
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
|
||||||
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
|
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
|
||||||
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
|
$fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr;
|
||||||
@ -139,6 +140,7 @@ class PreferencesController extends Controller
|
|||||||
'frontPageAccounts',
|
'frontPageAccounts',
|
||||||
'languages',
|
'languages',
|
||||||
'notifications',
|
'notifications',
|
||||||
|
'slackUrl',
|
||||||
'locales',
|
'locales',
|
||||||
'locale',
|
'locale',
|
||||||
'tjOptionalFields',
|
'tjOptionalFields',
|
||||||
@ -173,12 +175,12 @@ class PreferencesController extends Controller
|
|||||||
|
|
||||||
// extract notifications:
|
// extract notifications:
|
||||||
$all = $request->all();
|
$all = $request->all();
|
||||||
foreach(config('firefly.available_notifications') as $option) {
|
foreach (config('firefly.available_notifications') as $option) {
|
||||||
$key = sprintf('notification_%s', $option);
|
$key = sprintf('notification_%s', $option);
|
||||||
if(array_key_exists($key, $all)) {
|
if (array_key_exists($key, $all)) {
|
||||||
app('preferences')->set($key, true);
|
app('preferences')->set($key, true);
|
||||||
}
|
}
|
||||||
if(!array_key_exists($key, $all)) {
|
if (!array_key_exists($key, $all)) {
|
||||||
app('preferences')->set($key, false);
|
app('preferences')->set($key, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,6 +192,16 @@ class PreferencesController extends Controller
|
|||||||
session()->forget('end');
|
session()->forget('end');
|
||||||
session()->forget('range');
|
session()->forget('range');
|
||||||
|
|
||||||
|
|
||||||
|
// slack URL:
|
||||||
|
$url = (string) $request->get('slackUrl');
|
||||||
|
if(str_starts_with($url, 'https://hooks.slack.com/services/')){
|
||||||
|
app('preferences')->set('slack_webhook_url', $url);
|
||||||
|
}
|
||||||
|
if('' === $url) {
|
||||||
|
app('preferences')->delete('slack_webhook_url');
|
||||||
|
}
|
||||||
|
|
||||||
// custom fiscal year
|
// custom fiscal year
|
||||||
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
|
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
|
||||||
$string = strtotime((string) $request->get('fiscalYearStart'));
|
$string = strtotime((string) $request->get('fiscalYearStart'));
|
||||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Notifications\Admin;
|
|||||||
use FireflyIII\Mail\AdminTestMail;
|
use FireflyIII\Mail\AdminTestMail;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +55,7 @@ class TestNotification extends Notification
|
|||||||
*/
|
*/
|
||||||
public function via($notifiable)
|
public function via($notifiable)
|
||||||
{
|
{
|
||||||
return ['mail'];
|
return ['mail', 'slack'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +70,16 @@ class TestNotification extends Notification
|
|||||||
->markdown('emails.admin-test', ['email' => $this->address])
|
->markdown('emails.admin-test', ['email' => $this->address])
|
||||||
->subject((string) trans('email.admin_test_subject'));
|
->subject((string) trans('email.admin_test_subject'));
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get the Slack representation of the notification.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
return (new SlackMessage)->content((string)trans('email.admin_test_subject'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the array representation of the notification.
|
* Get the array representation of the notification.
|
||||||
|
@ -25,8 +25,12 @@ namespace FireflyIII\Notifications\Admin;
|
|||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserRegistration
|
||||||
|
*/
|
||||||
class UserRegistration extends Notification
|
class UserRegistration extends Notification
|
||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
@ -46,18 +50,18 @@ class UserRegistration extends Notification
|
|||||||
/**
|
/**
|
||||||
* Get the notification's delivery channels.
|
* Get the notification's delivery channels.
|
||||||
*
|
*
|
||||||
* @param mixed $notifiable
|
* @param mixed $notifiable
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function via($notifiable)
|
public function via($notifiable)
|
||||||
{
|
{
|
||||||
return ['mail'];
|
return ['mail', 'slack'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the mail representation of the notification.
|
* Get the mail representation of the notification.
|
||||||
*
|
*
|
||||||
* @param mixed $notifiable
|
* @param mixed $notifiable
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||||
*/
|
*/
|
||||||
public function toMail($notifiable)
|
public function toMail($notifiable)
|
||||||
@ -67,10 +71,21 @@ class UserRegistration extends Notification
|
|||||||
->subject((string) trans('email.registered_subject_admin'));
|
->subject((string) trans('email.registered_subject_admin'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Slack representation of the notification.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
return (new SlackMessage)->content((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'id' => $this->user->id]));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the array representation of the notification.
|
* Get the array representation of the notification.
|
||||||
*
|
*
|
||||||
* @param mixed $notifiable
|
* @param mixed $notifiable
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function toArray($notifiable)
|
public function toArray($notifiable)
|
||||||
|
@ -22,9 +22,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Notifications\Admin;
|
namespace FireflyIII\Notifications\Admin;
|
||||||
|
|
||||||
use FireflyIII\Models\Bill;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +35,7 @@ class VersionCheckResult extends Notification
|
|||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
private string $message;
|
private string $message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new notification instance.
|
* Create a new notification instance.
|
||||||
@ -55,7 +55,7 @@ class VersionCheckResult extends Notification
|
|||||||
*/
|
*/
|
||||||
public function via($notifiable)
|
public function via($notifiable)
|
||||||
{
|
{
|
||||||
return ['mail'];
|
return ['mail', 'slack'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +69,21 @@ class VersionCheckResult extends Notification
|
|||||||
|
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
->markdown('emails.new-version', ['message' => $this->message])
|
->markdown('emails.new-version', ['message' => $this->message])
|
||||||
->subject((string)trans('email.new_version_email_subject'));
|
->subject((string) trans('email.new_version_email_subject'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Slack representation of the notification.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
return (new SlackMessage)->content($this->message)
|
||||||
|
->attachment(function ($attachment) {
|
||||||
|
$attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Notifications\User;
|
|||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +59,7 @@ class BillReminder extends Notification
|
|||||||
*/
|
*/
|
||||||
public function via($notifiable)
|
public function via($notifiable)
|
||||||
{
|
{
|
||||||
return ['mail'];
|
return ['mail', 'slack'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,6 +80,28 @@ class BillReminder extends Notification
|
|||||||
->subject($subject);
|
->subject($subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Slack representation of the notification.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return SlackMessage
|
||||||
|
*/
|
||||||
|
public function toSlack($notifiable)
|
||||||
|
{
|
||||||
|
$message = (string) trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
|
||||||
|
if (0 === $this->diff) {
|
||||||
|
$message = (string) trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
|
||||||
|
}
|
||||||
|
$bill = $this->bill;
|
||||||
|
$url = route('bills.show', [$bill->id]);
|
||||||
|
return (new SlackMessage)
|
||||||
|
->warning()
|
||||||
|
->attachment(function ($attachment) use ($bill, $url) {
|
||||||
|
$attachment->title((string) trans('firefly.visit_bill', ['name' => $bill->name]), $url);
|
||||||
|
})
|
||||||
|
->content($message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the array representation of the notification.
|
* Get the array representation of the notification.
|
||||||
*
|
*
|
||||||
|
24
app/User.php
24
app/User.php
@ -48,6 +48,9 @@ use FireflyIII\Models\TransactionGroup;
|
|||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\UserGroup;
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
|
use FireflyIII\Notifications\Admin\TestNotification;
|
||||||
|
use FireflyIII\Notifications\Admin\UserRegistration;
|
||||||
|
use FireflyIII\Notifications\Admin\VersionCheckResult;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
@ -578,4 +581,25 @@ class User extends Authenticatable
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route notifications for the Slack channel.
|
||||||
|
*
|
||||||
|
* @param Notification $notification
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function routeNotificationForSlack(Notification $notification): string
|
||||||
|
{
|
||||||
|
// this check does not validate if the user is owner, Should be done by notification itself.
|
||||||
|
if ($notification instanceof TestNotification) {
|
||||||
|
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
|
||||||
|
}
|
||||||
|
if ($notification instanceof UserRegistration) {
|
||||||
|
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
|
||||||
|
}
|
||||||
|
if ($notification instanceof VersionCheckResult) {
|
||||||
|
return app('fireflyconfig')->get('slack_webhook_url', '')->data;
|
||||||
|
}
|
||||||
|
return app('preferences')->getForUser($this, 'slack_webhook_url', '')->data;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1051,6 +1051,10 @@ return [
|
|||||||
'pref_notification_user_login' => 'Alert when you login from a new location',
|
'pref_notification_user_login' => 'Alert when you login from a new location',
|
||||||
'pref_notifications' => 'Notifications',
|
'pref_notifications' => 'Notifications',
|
||||||
'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.',
|
'pref_notifications_help' => 'Indicate if these are notifications you would like to get. Some notifications may contain sensitive financial information.',
|
||||||
|
'slack_webhook_url' => 'Slack Webhook URL',
|
||||||
|
'slack_webhook_url_help' => 'If you want Firefly III to notify you using Slack, enter the webhook URL here. Otherwise leave the field blank. If you are an admin, you need to set this URL in the administration as well.',
|
||||||
|
'slack_url_label' => 'Slack "incoming webhook" URL',
|
||||||
|
|
||||||
// profile:
|
// profile:
|
||||||
'delete_stuff_header' => 'Delete data',
|
'delete_stuff_header' => 'Delete data',
|
||||||
'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.',
|
'permanent_delete_stuff' => 'Be careful with these buttons. Deleting stuff is permanent.',
|
||||||
@ -1349,6 +1353,7 @@ return [
|
|||||||
// bills:
|
// bills:
|
||||||
'not_expected_period' => 'Not expected this period',
|
'not_expected_period' => 'Not expected this period',
|
||||||
'not_or_not_yet' => 'Not (yet)',
|
'not_or_not_yet' => 'Not (yet)',
|
||||||
|
'visit_bill' => 'Visit bill ":name" at Firefly III',
|
||||||
'match_between_amounts' => 'Bill matches transactions between :low and :high.',
|
'match_between_amounts' => 'Bill matches transactions between :low and :high.',
|
||||||
'running_again_loss' => 'Previously linked transactions to this bill may lose their connection, if they (no longer) match the rule(s).',
|
'running_again_loss' => 'Previously linked transactions to this bill may lose their connection, if they (no longer) match the rule(s).',
|
||||||
'bill_related_rules' => 'Rules related to this bill',
|
'bill_related_rules' => 'Rules related to this bill',
|
||||||
@ -1978,7 +1983,7 @@ return [
|
|||||||
'delete_user' => 'Delete user :email',
|
'delete_user' => 'Delete user :email',
|
||||||
'user_deleted' => 'The user has been deleted',
|
'user_deleted' => 'The user has been deleted',
|
||||||
'send_test_email' => 'Send test email message',
|
'send_test_email' => 'Send test email message',
|
||||||
'send_test_email_text' => 'To see if your installation is capable of sending email, please press this button. You will not see an error here (if any), <strong>the log files will reflect any errors</strong>. You can press this button as many times as you like. There is no spam control. The message will be sent to <code>:email</code> and should arrive shortly.',
|
'send_test_email_text' => 'To see if your installation is capable of sending email or posting Slack messages, please press this button. You will not see an error here (if any), <strong>the log files will reflect any errors</strong>. You can press this button as many times as you like. There is no spam control. The message will be sent to <code>:email</code> and should arrive shortly.',
|
||||||
'send_message' => 'Send message',
|
'send_message' => 'Send message',
|
||||||
'send_test_triggered' => 'Test was triggered. Check your inbox and the log files.',
|
'send_test_triggered' => 'Test was triggered. Check your inbox and the log files.',
|
||||||
'give_admin_careful' => 'Users who are given admin rights can take away yours. Be careful.',
|
'give_admin_careful' => 'Users who are given admin rights can take away yours. Be careful.',
|
||||||
@ -1986,9 +1991,10 @@ return [
|
|||||||
'admin_maintanance_expl' => 'Some nifty buttons for Firefly III maintenance',
|
'admin_maintanance_expl' => 'Some nifty buttons for Firefly III maintenance',
|
||||||
'admin_maintenance_clear_cache' => 'Clear cache',
|
'admin_maintenance_clear_cache' => 'Clear cache',
|
||||||
'admin_notifications' => 'Admin notifications',
|
'admin_notifications' => 'Admin notifications',
|
||||||
'admin_notifications_expl' => 'The following notifications can be enabled or disabled by the administrator.',
|
'admin_notifications_expl' => 'The following notifications can be enabled or disabled by the administrator. If you want to get these messages over Slack as well, set the "incoming webhook" URL.',
|
||||||
'admin_notification_check_user_new_reg' => 'User gets post-registration welcome message',
|
'admin_notification_check_user_new_reg' => 'User gets post-registration welcome message',
|
||||||
'admin_notification_check_admin_new_reg' => 'Administrator(s) get new user registration notification',
|
'admin_notification_check_admin_new_reg' => 'Administrator(s) get new user registration notification',
|
||||||
|
'admin_notification_check_new_version' => 'A new version is available',
|
||||||
'save_notification_settings' => 'Save settings',
|
'save_notification_settings' => 'Save settings',
|
||||||
'notification_settings_saved' => 'The notification settings have been saved',
|
'notification_settings_saved' => 'The notification settings have been saved',
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{{ ExpandedForm.text('slackUrl', slackUrl, {'label' : 'slack_url_label'|_}) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<button type="submit" class="btn btn-success">
|
<button type="submit" class="btn btn-success">
|
||||||
|
@ -260,6 +260,13 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||||
|
<div class="preferences-box">
|
||||||
|
<h3>{{ 'slack_webhook_url'|_ }}</h3>
|
||||||
|
<p class="text-info">{{ 'slack_webhook_url_help'|_ }}</p>
|
||||||
|
{{ ExpandedForm.text('slackUrl',slackUrl,{'label' : 'slack_url_label'|_}) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user