mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-18 19:13:17 +00:00
Merge pull request #5325 from pixelfed/staging
Add Admin Contact Responses
This commit is contained in:
commit
c46023456f
15 changed files with 610 additions and 89 deletions
|
@ -5,6 +5,7 @@
|
||||||
### Added
|
### Added
|
||||||
- Implement Admin Domain Blocks API (Mastodon API Compatible) [ThisIsMissEm](https://github.com/ThisIsMissEm) ([#5021](https://github.com/pixelfed/pixelfed/pull/5021))
|
- Implement Admin Domain Blocks API (Mastodon API Compatible) [ThisIsMissEm](https://github.com/ThisIsMissEm) ([#5021](https://github.com/pixelfed/pixelfed/pull/5021))
|
||||||
- Authorize Interaction support (for handling remote interactions) ([4ca7c6c3](https://github.com/pixelfed/pixelfed/commit/4ca7c6c3))
|
- Authorize Interaction support (for handling remote interactions) ([4ca7c6c3](https://github.com/pixelfed/pixelfed/commit/4ca7c6c3))
|
||||||
|
- Contact Form Admin Responses ([52cc6090](https://github.com/pixelfed/pixelfed/commit/52cc6090))
|
||||||
|
|
||||||
### Federation
|
### Federation
|
||||||
- Add ActiveSharedInboxService, for efficient sharedInbox caching ([1a6a3397](https://github.com/pixelfed/pixelfed/commit/1a6a3397))
|
- Add ActiveSharedInboxService, for efficient sharedInbox caching ([1a6a3397](https://github.com/pixelfed/pixelfed/commit/1a6a3397))
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
- Update layout, add og:logo ([4cc576e1](https://github.com/pixelfed/pixelfed/commit/4cc576e1))
|
- Update layout, add og:logo ([4cc576e1](https://github.com/pixelfed/pixelfed/commit/4cc576e1))
|
||||||
- Update ReblogService, fix cache sync issues ([3de8ceca](https://github.com/pixelfed/pixelfed/commit/3de8ceca))
|
- Update ReblogService, fix cache sync issues ([3de8ceca](https://github.com/pixelfed/pixelfed/commit/3de8ceca))
|
||||||
- Update config, allow Beagle discover service to be disabled ([de4ce3c8](https://github.com/pixelfed/pixelfed/commit/de4ce3c8))
|
- Update config, allow Beagle discover service to be disabled ([de4ce3c8](https://github.com/pixelfed/pixelfed/commit/de4ce3c8))
|
||||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
- Update ApiV1Dot1Controller, allow upto 5 similar push tokens ([7820b506](https://github.com/pixelfed/pixelfed/commit/7820b506))
|
||||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||||
|
|
||||||
## [v0.12.3 (2024-07-01)](https://github.com/pixelfed/pixelfed/compare/v0.12.2...v0.12.3)
|
## [v0.12.3 (2024-07-01)](https://github.com/pixelfed/pixelfed/compare/v0.12.2...v0.12.3)
|
||||||
|
|
|
@ -3,9 +3,14 @@
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Contact extends Model
|
class Contact extends Model
|
||||||
{
|
{
|
||||||
|
protected $casts = [
|
||||||
|
'responded_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
|
@ -15,4 +20,14 @@ class Contact extends Model
|
||||||
{
|
{
|
||||||
return url('/i/admin/messages/show/'.$this->id);
|
return url('/i/admin/messages/show/'.$this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function userResponseUrl()
|
||||||
|
{
|
||||||
|
return url('/i/contact-admin-response/'.$this->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessageId()
|
||||||
|
{
|
||||||
|
return $this->id.'-'.(string) Str::uuid().'@'.strtolower(config('pixelfed.domain.app', 'example.org'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use App\Http\Controllers\Admin\AdminReportController;
|
||||||
use App\Http\Controllers\Admin\AdminSettingsController;
|
use App\Http\Controllers\Admin\AdminSettingsController;
|
||||||
use App\Http\Controllers\Admin\AdminUserController;
|
use App\Http\Controllers\Admin\AdminUserController;
|
||||||
use App\Instance;
|
use App\Instance;
|
||||||
|
use App\Mail\AdminMessageResponse;
|
||||||
use App\Models\CustomEmoji;
|
use App\Models\CustomEmoji;
|
||||||
use App\Newsroom;
|
use App\Newsroom;
|
||||||
use App\OauthClient;
|
use App\OauthClient;
|
||||||
|
@ -29,6 +30,7 @@ use Cache;
|
||||||
use DB;
|
use DB;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Mail;
|
||||||
use Storage;
|
use Storage;
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
|
@ -221,18 +223,86 @@ class AdminController extends Controller
|
||||||
|
|
||||||
public function messagesHome(Request $request)
|
public function messagesHome(Request $request)
|
||||||
{
|
{
|
||||||
$messages = Contact::orderByDesc('id')->paginate(10);
|
$this->validate($request, [
|
||||||
|
'sort' => 'sometimes|string|in:all,open,closed',
|
||||||
|
]);
|
||||||
|
$sort = $request->input('sort', 'open');
|
||||||
|
|
||||||
return view('admin.messages.home', compact('messages'));
|
$messages = Contact::when($sort, function ($query, $sort) {
|
||||||
|
if ($sort === 'open') {
|
||||||
|
$query->whereNull('read_at');
|
||||||
|
}
|
||||||
|
if ($sort === 'closed') {
|
||||||
|
$query->whereNotNull('read_at');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->orderByDesc('id')
|
||||||
|
->paginate(10)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return view('admin.messages.home', compact('messages', 'sort'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function messagesShow(Request $request, $id)
|
public function messagesShow(Request $request, $id)
|
||||||
{
|
{
|
||||||
$message = Contact::findOrFail($id);
|
$message = Contact::findOrFail($id);
|
||||||
|
$user = User::whereNull('status')->find($message->user_id);
|
||||||
|
if(!$user) {
|
||||||
|
$message->read_at = now();
|
||||||
|
$message->save();
|
||||||
|
return redirect('/i/admin/messages/home')->with('status', 'Redirected from message sent from a deleted account');
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.messages.show', compact('message'));
|
return view('admin.messages.show', compact('message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function messagesReply(Request $request, $id)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'message' => 'required|string|min:1|max:500',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(config('mail.default') === 'log') {
|
||||||
|
return redirect('/i/admin/messages/home')->with('error', 'Mail driver not configured, please setup before you can sent email.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = Contact::whereNull('responded_at')->findOrFail($id);
|
||||||
|
$user = User::whereNull('status')->find($message->user_id);
|
||||||
|
if(!$user) {
|
||||||
|
$message->read_at = now();
|
||||||
|
$message->save();
|
||||||
|
return redirect('/i/admin/messages/home')->with('status', 'Redirected from message sent from a deleted account');
|
||||||
|
}
|
||||||
|
$message->response = $request->input('message');
|
||||||
|
$message->read_at = now();
|
||||||
|
$message->responded_at = now();
|
||||||
|
$message->save();
|
||||||
|
|
||||||
|
Mail::to($message->user->email)->send(new AdminMessageResponse($message));
|
||||||
|
|
||||||
|
return redirect('/i/admin/messages/home')->with('status', 'Sent response to '.$message->user->username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messagesReplyPreview(Request $request, $id)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'message' => 'required|string|min:1|max:500',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(config('mail.default') === 'log') {
|
||||||
|
return redirect('/i/admin/messages/home')->with('error', 'Mail driver not configured, please setup before you can sent email.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = Contact::whereNull('read_at')->findOrFail($id);
|
||||||
|
$user = User::whereNull('status')->find($message->user_id);
|
||||||
|
if(!$user) {
|
||||||
|
$message->read_at = now();
|
||||||
|
$message->save();
|
||||||
|
return redirect('/i/admin/messages/home')->with('error', 'Redirected from message sent from a deleted account');
|
||||||
|
}
|
||||||
|
return new AdminMessageResponse($message);
|
||||||
|
}
|
||||||
|
|
||||||
public function messagesMarkRead(Request $request)
|
public function messagesMarkRead(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
|
@ -240,12 +310,21 @@ class AdminController extends Controller
|
||||||
]);
|
]);
|
||||||
$id = $request->input('id');
|
$id = $request->input('id');
|
||||||
$message = Contact::findOrFail($id);
|
$message = Contact::findOrFail($id);
|
||||||
|
|
||||||
|
$user = User::whereNull('status')->find($message->user_id);
|
||||||
|
if(!$user) {
|
||||||
|
$message->read_at = now();
|
||||||
|
$message->save();
|
||||||
|
return redirect('/i/admin/messages/home')->with('error', 'Redirected from message sent from a deleted account');
|
||||||
|
}
|
||||||
if ($message->read_at) {
|
if ($message->read_at) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$message->read_at = now();
|
$message->read_at = now();
|
||||||
$message->save();
|
$message->save();
|
||||||
|
$request->session()->flash('status', 'Marked response from '.$message->user->username.' as read!');
|
||||||
|
|
||||||
|
return ['status' => 200];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newsroomHome(Request $request)
|
public function newsroomHome(Request $request)
|
||||||
|
@ -355,7 +434,7 @@ class AdminController extends Controller
|
||||||
if (Newsroom::whereSlug($slug)->exists()) {
|
if (Newsroom::whereSlug($slug)->exists()) {
|
||||||
$slug = $slug.'-'.str_random(4);
|
$slug = $slug.'-'.str_random(4);
|
||||||
}
|
}
|
||||||
$news = new Newsroom();
|
$news = new Newsroom;
|
||||||
$fields = [
|
$fields = [
|
||||||
'title' => 'string',
|
'title' => 'string',
|
||||||
'summary' => 'string',
|
'summary' => 'string',
|
||||||
|
|
|
@ -50,4 +50,15 @@ class ContactController extends Controller
|
||||||
|
|
||||||
return redirect()->back()->with('status', 'Success - Your message has been sent to admins.');
|
return redirect()->back()->with('status', 'Success - Your message has been sent to admins.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showAdminResponse(Request $request, $id)
|
||||||
|
{
|
||||||
|
abort_if(!$request->user(), 404);
|
||||||
|
$uid = $request->user()->id;
|
||||||
|
$contact = Contact::whereUserId($uid)
|
||||||
|
->whereNotNull('response')
|
||||||
|
->whereNotNull('responded_at')
|
||||||
|
->findOrFail($id);
|
||||||
|
return view('site.contact.admin-response', compact('contact'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
71
app/Mail/AdminMessageResponse.php
Normal file
71
app/Mail/AdminMessageResponse.php
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use App\Contact;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
|
use Illuminate\Mail\Mailables\Headers;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class AdminMessageResponse extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public Contact $contact,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message headers.
|
||||||
|
*/
|
||||||
|
public function headers(): Headers
|
||||||
|
{
|
||||||
|
$mid = $this->contact->getMessageId();
|
||||||
|
|
||||||
|
return new Headers(
|
||||||
|
messageId: $mid,
|
||||||
|
text: [
|
||||||
|
'X-Entity-Ref-ID' => $mid,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message envelope.
|
||||||
|
*/
|
||||||
|
public function envelope(): Envelope
|
||||||
|
{
|
||||||
|
return new Envelope(
|
||||||
|
subject: ucfirst(strtolower(config('pixelfed.domain.app'))).' Contact Form Response [Ticket #'.$this->contact->id.']',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message content definition.
|
||||||
|
*/
|
||||||
|
public function content(): Content
|
||||||
|
{
|
||||||
|
return new Content(
|
||||||
|
markdown: 'emails.contact.admin-response',
|
||||||
|
with: [
|
||||||
|
'url' => $this->contact->userResponseUrl(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attachments for the message.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||||
|
*/
|
||||||
|
public function attachments(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@
|
||||||
"pragmarx/google2fa": "^8.0",
|
"pragmarx/google2fa": "^8.0",
|
||||||
"predis/predis": "^2.0",
|
"predis/predis": "^2.0",
|
||||||
"pusher/pusher-php-server": "^7.2",
|
"pusher/pusher-php-server": "^7.2",
|
||||||
|
"resend/resend-php": "^0.13.0",
|
||||||
"spatie/laravel-backup": "^8.0.0",
|
"spatie/laravel-backup": "^8.0.0",
|
||||||
"spatie/laravel-image-optimizer": "^1.8.0",
|
"spatie/laravel-image-optimizer": "^1.8.0",
|
||||||
"stevebauman/purify": "^6.2.0",
|
"stevebauman/purify": "^6.2.0",
|
||||||
|
|
59
composer.lock
generated
59
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "fecc0efcc40880a422690feefedef584",
|
"content-hash": "0035325cb0240e92fc378e49f76447bd",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aws/aws-crt-php",
|
"name": "aws/aws-crt-php",
|
||||||
|
@ -6378,6 +6378,63 @@
|
||||||
],
|
],
|
||||||
"time": "2024-04-27T21:32:50+00:00"
|
"time": "2024-04-27T21:32:50+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "resend/resend-php",
|
||||||
|
"version": "v0.13.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/resend/resend-php.git",
|
||||||
|
"reference": "c74926e34472fe3e3e21f150f3e3ce56fcbf8298"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/resend/resend-php/zipball/c74926e34472fe3e3e21f150f3e3ce56fcbf8298",
|
||||||
|
"reference": "c74926e34472fe3e3e21f150f3e3ce56fcbf8298",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"php": "^8.1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.13",
|
||||||
|
"mockery/mockery": "^1.6",
|
||||||
|
"pestphp/pest": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Resend.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Resend\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Resend and contributors",
|
||||||
|
"homepage": "https://github.com/resend/resend-php/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Resend PHP library.",
|
||||||
|
"homepage": "https://resend.com/",
|
||||||
|
"keywords": [
|
||||||
|
"api",
|
||||||
|
"client",
|
||||||
|
"php",
|
||||||
|
"resend",
|
||||||
|
"sdk"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/resend/resend-php/issues",
|
||||||
|
"source": "https://github.com/resend/resend-php/tree/v0.13.0"
|
||||||
|
},
|
||||||
|
"time": "2024-08-15T03:27:29+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "spatie/db-dumper",
|
"name": "spatie/db-dumper",
|
||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
|
|
|
@ -37,8 +37,8 @@ return [
|
||||||
'smtp' => [
|
'smtp' => [
|
||||||
'transport' => 'smtp',
|
'transport' => 'smtp',
|
||||||
'url' => env('MAIL_URL'),
|
'url' => env('MAIL_URL'),
|
||||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||||
'port' => env('MAIL_PORT', 587),
|
'port' => env('MAIL_PORT', 2525),
|
||||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||||
'username' => env('MAIL_USERNAME'),
|
'username' => env('MAIL_USERNAME'),
|
||||||
'password' => env('MAIL_PASSWORD'),
|
'password' => env('MAIL_PASSWORD'),
|
||||||
|
@ -53,16 +53,21 @@ return [
|
||||||
|
|
||||||
'mailgun' => [
|
'mailgun' => [
|
||||||
'transport' => 'mailgun',
|
'transport' => 'mailgun',
|
||||||
// 'client' => [
|
'client' => [
|
||||||
// 'timeout' => 5,
|
'timeout' => 5,
|
||||||
// ],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'postmark' => [
|
'postmark' => [
|
||||||
'transport' => 'postmark',
|
'transport' => 'postmark',
|
||||||
// 'client' => [
|
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||||
// 'timeout' => 5,
|
'client' => [
|
||||||
// ],
|
'timeout' => 5,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'resend' => [
|
||||||
|
'transport' => 'resend',
|
||||||
],
|
],
|
||||||
|
|
||||||
'sendmail' => [
|
'sendmail' => [
|
||||||
|
@ -82,7 +87,6 @@ return [
|
||||||
'failover' => [
|
'failover' => [
|
||||||
'transport' => 'failover',
|
'transport' => 'failover',
|
||||||
'mailers' => [
|
'mailers' => [
|
||||||
'smtp',
|
|
||||||
'log',
|
'log',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
@ -38,4 +38,8 @@ return [
|
||||||
'expo' => [
|
'expo' => [
|
||||||
'access_token' => env('EXPO_ACCESS_TOKEN'),
|
'access_token' => env('EXPO_ACCESS_TOKEN'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'resend' => [
|
||||||
|
'key' => env('RESEND_KEY'),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,11 +1,51 @@
|
||||||
@extends('admin.partial.template-full')
|
@extends('admin.partial.template-full')
|
||||||
|
|
||||||
@section('section')
|
@section('section')
|
||||||
<div class="title">
|
|
||||||
<h3 class="font-weight-bold d-inline-block">Messages</h3>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="header bg-primary pb-3 mt-n4">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="header-body">
|
||||||
|
<div class="row align-items-center py-4">
|
||||||
|
<div class="col-lg-6 col-7">
|
||||||
|
<p class="display-1 text-white d-inline-block mb-0">Messages</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container mt-3">
|
||||||
|
|
||||||
<hr>
|
<div class="row justify-content-center">
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="col-12" id="flash">
|
||||||
|
<div class="alert alert-success">
|
||||||
|
{{ session('status') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if (session('error'))
|
||||||
|
<div class="col-12" id="flash">
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div class="col-12">
|
||||||
|
<ul class="nav nav-pills my-3">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{$sort=='all'?'active':''}}" href="?sort=all">All</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{$sort=='open'?'active':''}}" href="?sort=open">Open</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{$sort=='closed'?'active':''}}" href="?sort=closed">Closed</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead class="bg-light">
|
<thead class="bg-light">
|
||||||
|
@ -32,5 +72,30 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
{{$messages->links()}}
|
{{$messages->links()}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script type="text/javascript">
|
||||||
|
function checkAndRemoveElementOnLoad(selector, delay, action = 'hide') {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
if (element) {
|
||||||
|
if (action === 'hide') {
|
||||||
|
element.style.display = 'none';
|
||||||
|
} else if (action === 'remove') {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, delay * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndRemoveElementOnLoad('#flash', 5, 'remove');
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
|
@ -4,33 +4,96 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="font-weight-bold"># {{$message->id}}</div>
|
<div class="font-weight-bold"># {{$message->id}}</div>
|
||||||
<div class="font-weight-bold h3">Message</div>
|
<div class="font-weight-bold h3">Contact Form Message</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="mt-0">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
@if($message->responded_at)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="small text-muted">Admin Response Sent</div>
|
||||||
<div>
|
<div>
|
||||||
@if($message->read_at)
|
<span class="font-weight-bold" title="{{$message->responded_at}}" data-toggle="tooltip">
|
||||||
<span class="btn btn-outline-secondary btn-sm disabled" disabled>Read</span>
|
{{$message->responded_at->diffForHumans()}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="small text-muted">Status</div>
|
||||||
|
@if($message->read_at == null)
|
||||||
|
<div class="text-success font-weight-bold">Open</div>
|
||||||
@else
|
@else
|
||||||
<button type="button" class="btn btn-outline-primary btn-sm" id="markRead">Mark Read</button>
|
<div class="text-muted">Closed</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="small text-muted">Response Requested</div>
|
||||||
|
@if($message->response_requested == 1)
|
||||||
|
<div class="font-weight-bold">Yes</div>
|
||||||
|
@else
|
||||||
|
<div class="text-muted">No</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="small text-muted">Created</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-weight-bold" title="{{$message->created_at}}" data-toggle="tooltip">
|
||||||
|
{{$message->created_at->diffForHumans()}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($message->user && $message->user->last_active_at)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="small text-muted">User Last Active</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-weight-bold" title="{{$message->user->last_active_at}}" data-toggle="tooltip">
|
||||||
|
{{$message->user->last_active_at->diffForHumans()}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(!$message->read_at)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-block" id="markRead">Mark Read</button>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="col-12 col-md-8">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
<div class="col-12 col-md-3 text-md-right">
|
|
||||||
@if($message->response_requested)
|
|
||||||
<p class="text-dark font-weight-bold">Response Requested</p>
|
|
||||||
@endif
|
|
||||||
<p class="text-dark">Sent {{$message->created_at->diffForHumans()}}</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
|
|
||||||
<div class="card shadow-none border">
|
<div class="card shadow-none border">
|
||||||
<div class="card-header bg-white">
|
<div class="card-header bg-white">
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<img src="{{$message->user->profile->avatarUrl()}}" class="mr-3 rounded-circle" width="40px" height="40px">
|
<img
|
||||||
|
src="{{$message->user->profile->avatarUrl()}}"
|
||||||
|
class="mr-3 rounded-circle"
|
||||||
|
width="40px"
|
||||||
|
height="40px"
|
||||||
|
onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h5 class="my-0">@{{$message->user->username}}</h5>
|
<h5 class="my-0">@{{$message->user->username}}</h5>
|
||||||
<span class="text-muted">{{$message->user->email}}</span>
|
<span class="text-muted">{{$message->user->email}}</span>
|
||||||
|
@ -38,22 +101,64 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<p class="text-uppercase text-muted small mb-2">Message Body</p>
|
||||||
<p class="mb-0">{{$message->message}}</p>
|
<p class="mb-0">{{$message->message}}</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-3">
|
|
||||||
{{-- @if($message->responded_at == null)
|
|
||||||
<button class="btn btn-primary font-weight-bold btn-block">Send Response</button>
|
|
||||||
<hr>
|
<hr>
|
||||||
@endif
|
<p class="text-uppercase text-muted small mb-2">Admin Reply:</p>
|
||||||
<button class="btn btn-outline-danger font-weight-bold btn-block">Delete</button> --}}
|
|
||||||
|
@if($message->responded_at)
|
||||||
|
<p class="mb-0">{{$message->response}}</p>
|
||||||
|
@else
|
||||||
|
@if(config('mail.default') === 'log')
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<p class="mb-0">You need to configure your mail driver before you can send outgoing emails.</p>
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<form method="post" id="mform">
|
||||||
|
@csrf
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
name="message"
|
||||||
|
id="message"
|
||||||
|
rows="4"
|
||||||
|
style="resize: none;"
|
||||||
|
maxlength="500"
|
||||||
|
placeholder="Reply to @{{$message->user->username}} via email ..."></textarea>
|
||||||
|
@if ($errors->any())
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<p class="invalid-feedback mb-0" style="display:block;">
|
||||||
|
<strong>{{ $error }}</strong>
|
||||||
|
</p>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<button type="button" class="btn btn-primary font-weight-bold submit-btn">Send</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary font-weight-bold preview-btn">Preview</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="small text-muted font-weight-bold">
|
||||||
|
<span id="messageCount">0</span>/500
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
|
@if($message->responded_at == null)
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$('#markRead').on('click', function(e) {
|
$('#markRead').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -61,8 +166,34 @@
|
||||||
axios.post('/i/admin/messages/mark-read', {
|
axios.post('/i/admin/messages/mark-read', {
|
||||||
id: '{{$message->id}}',
|
id: '{{$message->id}}',
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
window.location.href = window.location.href;
|
window.location.href = '/i/admin/messages/home';
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const submitBtn = document.querySelector('.submit-btn');
|
||||||
|
submitBtn.addEventListener('click', () => {
|
||||||
|
const form = document.getElementById('mform');
|
||||||
|
form.action = '/i/admin/messages/show/{{$message->id}}';
|
||||||
|
form.submit()
|
||||||
|
});
|
||||||
|
|
||||||
|
const previewBtn = document.querySelector('.preview-btn');
|
||||||
|
previewBtn.addEventListener('click', () => {
|
||||||
|
const form = document.getElementById('mform');
|
||||||
|
form.action = '/i/admin/messages/preview/{{$message->id}}';
|
||||||
|
form.submit()
|
||||||
|
});
|
||||||
|
|
||||||
|
function countChars() {
|
||||||
|
const input = document.getElementById('message');
|
||||||
|
const counter = document.getElementById('messageCount');
|
||||||
|
|
||||||
|
input.addEventListener('input', function() {
|
||||||
|
counter.textContent = input.value.length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
countChars();
|
||||||
</script>
|
</script>
|
||||||
|
@endif
|
||||||
@endpush
|
@endpush
|
24
resources/views/emails/contact/admin-response.blade.php
Normal file
24
resources/views/emails/contact/admin-response.blade.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<x-mail::message>
|
||||||
|
Hello **@{{$contact->user->username}}**,
|
||||||
|
|
||||||
|
You contacted the admin team of {{config('pixelfed.domain.app')}} with the following inquiry:
|
||||||
|
|
||||||
|
<x-mail::panel>
|
||||||
|
<i>{{str_limit($contact->message, 80)}}</i>
|
||||||
|
</x-mail::panel>
|
||||||
|
|
||||||
|
<x-mail::button :url="$url" color="primary">
|
||||||
|
View Admin Response
|
||||||
|
</x-mail::button>
|
||||||
|
|
||||||
|
<small>
|
||||||
|
or copy and paste the following url: <a href="{{$url}}">{{$url}}</a>
|
||||||
|
</small>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<small>
|
||||||
|
Thanks,<br>
|
||||||
|
The {{ ucfirst(config('pixelfed.domain.app')) }} Admin Team
|
||||||
|
</small>
|
||||||
|
</x-mail::message>
|
55
resources/views/site/contact/admin-response.blade.php
Normal file
55
resources/views/site/contact/admin-response.blade.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
@extends('layouts.blank')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container pt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-lg-8">
|
||||||
|
<div class="card shadow-none border">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<a class="btn btn-link font-weight-bold" href="/i/web">Back to Pixelfed</a>
|
||||||
|
<h1 class="h4 mb-0">Contact Form Response</h1>
|
||||||
|
<p class="d-none d-md-block mb-0 text-muted">Ticket ID #{{$contact->id}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="media">
|
||||||
|
<img src="{{$contact->user->profile->avatarUrl()}}" class="mr-3 rounded-circle" width="40px" height="40px" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=2'">
|
||||||
|
<div class="media-body">
|
||||||
|
<h5 class="my-0">@{{$contact->user->username}}</h5>
|
||||||
|
<span class="text-muted">{{$contact->user->name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="my-2 font-weight-bold">You sent the following inquiry:</p>
|
||||||
|
<div class="card shadow-none border bg-light rounded mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
{{ $contact->message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="small text-muted">You sent this inquiry on {{$contact->created_at->format('M d, Y')}} at {{$contact->created_at->format('h:i:s a e')}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($contact->response)
|
||||||
|
<div class="list-group-item">
|
||||||
|
<p class="my-2 font-weight-bold">The admin(s) responded to your inquiry:</p>
|
||||||
|
<div class="card shadow-none border bg-light rounded mb-2">
|
||||||
|
<div class="card-body">
|
||||||
|
{{ $contact->response }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if($contact->responded_at)
|
||||||
|
<p class="small text-muted">The response was created on {{$contact->responded_at->format('M d, Y')}} at {{$contact->responded_at->format('h:i:s a e')}}</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item">
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="mb-0 small text-muted font-weight-bold">If you would like to respond, use the <a href="/site/contact">contact form</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -77,6 +77,8 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
|
||||||
Route::get('messages/home', 'AdminController@messagesHome')->name('admin.messages');
|
Route::get('messages/home', 'AdminController@messagesHome')->name('admin.messages');
|
||||||
Route::get('messages/show/{id}', 'AdminController@messagesShow');
|
Route::get('messages/show/{id}', 'AdminController@messagesShow');
|
||||||
Route::post('messages/mark-read', 'AdminController@messagesMarkRead');
|
Route::post('messages/mark-read', 'AdminController@messagesMarkRead');
|
||||||
|
Route::post('messages/show/{id}', 'AdminController@messagesReply');
|
||||||
|
Route::post('messages/preview/{id}', 'AdminController@messagesReplyPreview');
|
||||||
Route::redirect('site-news', '/i/admin/newsroom');
|
Route::redirect('site-news', '/i/admin/newsroom');
|
||||||
Route::get('newsroom', 'AdminController@newsroomHome')->name('admin.newsroom.home');
|
Route::get('newsroom', 'AdminController@newsroomHome')->name('admin.newsroom.home');
|
||||||
Route::get('newsroom/create', 'AdminController@newsroomCreate')->name('admin.newsroom.create');
|
Route::get('newsroom/create', 'AdminController@newsroomCreate')->name('admin.newsroom.create');
|
||||||
|
|
|
@ -125,7 +125,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
||||||
|
|
||||||
Route::get('warning', 'AccountInterstitialController@get');
|
Route::get('warning', 'AccountInterstitialController@get');
|
||||||
Route::post('warning', 'AccountInterstitialController@read');
|
Route::post('warning', 'AccountInterstitialController@read');
|
||||||
Route::get('my2020', 'SeasonalController@yearInReview');
|
|
||||||
|
Route::get('contact-admin-response/{id}', 'ContactController@showAdminResponse');
|
||||||
|
|
||||||
Route::get('web/my-portfolio', 'PortfolioController@myRedirect');
|
Route::get('web/my-portfolio', 'PortfolioController@myRedirect');
|
||||||
Route::get('web/hashtag/{tag}', 'SpaController@hashtagRedirect');
|
Route::get('web/hashtag/{tag}', 'SpaController@hashtagRedirect');
|
||||||
|
|
Loading…
Reference in a new issue