#25: Implemented all basic notification types.

- blocks off the unfinished URL endpoints for email notifications
This commit is contained in:
Peter Deltchev 2016-12-23 06:31:45 -08:00
parent 78b22cdfd0
commit ff5d8220ef
8 changed files with 85 additions and 43 deletions

View file

@ -20,12 +20,17 @@
namespace Poniverse\Ponyfm\Http\Controllers; namespace Poniverse\Ponyfm\Http\Controllers;
use App;
use DB; use DB;
use Poniverse\Ponyfm\Models\Email; use Poniverse\Ponyfm\Models\Email;
use Poniverse\Ponyfm\Models\EmailSubscription; use Poniverse\Ponyfm\Models\EmailSubscription;
// TODO: #25 - finish these endpoints and secure them properly
class NotificationsController extends Controller { class NotificationsController extends Controller {
public function getEmailClick($emailKey) { public function getEmailClick($emailKey) {
App::abort(403, "This isn't implemented yet!");
$emailKey = decrypt($emailKey); $emailKey = decrypt($emailKey);
/** @var Email $email */ /** @var Email $email */
$email = Email::findOrFail($emailKey); $email = Email::findOrFail($emailKey);
@ -40,6 +45,8 @@ class NotificationsController extends Controller {
} }
public function getEmailUnsubscribe($subscriptionKey) { public function getEmailUnsubscribe($subscriptionKey) {
App::abort(403, "This isn't implemented yet!");
$subscriptionId = decrypt($subscriptionKey); $subscriptionId = decrypt($subscriptionKey);
$subscription = EmailSubscription::findOrFail($subscriptionId); $subscription = EmailSubscription::findOrFail($subscriptionId);

View file

@ -25,11 +25,6 @@ use Log;
use Mail; use Mail;
use Poniverse\Ponyfm\Contracts\Favouritable; use Poniverse\Ponyfm\Contracts\Favouritable;
use Poniverse\Ponyfm\Mail\BaseNotification; use Poniverse\Ponyfm\Mail\BaseNotification;
use Poniverse\Ponyfm\Mail\ContentFavourited;
use Poniverse\Ponyfm\Mail\NewComment;
use Poniverse\Ponyfm\Mail\NewFollower;
use Poniverse\Ponyfm\Mail\NewPlaylist;
use Poniverse\Ponyfm\Mail\NewTrack;
use Poniverse\Ponyfm\Models\Activity; use Poniverse\Ponyfm\Models\Activity;
use Poniverse\Ponyfm\Models\Comment; use Poniverse\Ponyfm\Models\Comment;
use Poniverse\Ponyfm\Models\Email; use Poniverse\Ponyfm\Models\Email;
@ -66,7 +61,9 @@ class PonyfmDriver extends AbstractDriver
*/ */
private function sendEmails(Activity $activity, $recipients) { private function sendEmails(Activity $activity, $recipients) {
foreach ($recipients as $recipient) { foreach ($recipients as $recipient) {
/** @var Notification $notification */
$notification = $activity->notifications->where('user_id', $recipient->id)->first(); $notification = $activity->notifications->where('user_id', $recipient->id)->first();
/** @var Email $email */
$email = $notification->email()->create([]); $email = $notification->email()->create([]);
Log::debug("Attempting to send an email about notification {$notification->id} to {$recipient->email}."); Log::debug("Attempting to send an email about notification {$notification->id} to {$recipient->email}.");
@ -88,9 +85,11 @@ class PonyfmDriver extends AbstractDriver
]); ]);
$recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args());
if (NULL !== $recipientsQuery) {
$this->insertNotifications($activity, $recipientsQuery->get()); $this->insertNotifications($activity, $recipientsQuery->get());
$this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_TRACK)->get()); $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_TRACK)->get());
} }
}
/** /**
* @inheritdoc * @inheritdoc
@ -106,10 +105,15 @@ class PonyfmDriver extends AbstractDriver
]); ]);
$recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args());
if (NULL !== $recipientsQuery) {
$this->insertNotifications($activity, $recipientsQuery->get()); $this->insertNotifications($activity, $recipientsQuery->get());
$this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_PLAYLIST)->get()); $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_PLAYLIST)->get());
} }
}
/**
* @inheritdoc
*/
public function newFollower(User $userBeingFollowed, User $follower) public function newFollower(User $userBeingFollowed, User $follower)
{ {
$activity = Activity::create([ $activity = Activity::create([
@ -121,9 +125,11 @@ class PonyfmDriver extends AbstractDriver
]); ]);
$recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args());
if (NULL !== $recipientsQuery) {
$this->insertNotifications($activity, $recipientsQuery->get()); $this->insertNotifications($activity, $recipientsQuery->get());
$this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_FOLLOWER)->get()); $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_FOLLOWER)->get());
} }
}
/** /**
* @inheritdoc * @inheritdoc
@ -139,9 +145,11 @@ class PonyfmDriver extends AbstractDriver
]); ]);
$recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args());
if (NULL !== $recipientsQuery) {
$this->insertNotifications($activity, $recipientsQuery->get()); $this->insertNotifications($activity, $recipientsQuery->get());
$this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_COMMENT)->get()); $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_COMMENT)->get());
} }
}
/** /**
* @inheritdoc * @inheritdoc
@ -157,7 +165,9 @@ class PonyfmDriver extends AbstractDriver
]); ]);
$recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args());
if (NULL !== $recipientsQuery) {
$this->insertNotifications($activity, $recipientsQuery->get()); $this->insertNotifications($activity, $recipientsQuery->get());
$this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_CONTENT_FAVOURITED)->get()); $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_CONTENT_FAVOURITED)->get());
} }
}
} }

View file

@ -20,11 +20,11 @@
namespace Poniverse\Ponyfm\Library\Notifications; namespace Poniverse\Ponyfm\Library\Notifications;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Bus\DispatchesJobs;
use Poniverse\Ponyfm\Contracts\Favouritable; use Poniverse\Ponyfm\Contracts\Favouritable;
use Poniverse\Ponyfm\Contracts\NotificationHandler; use Poniverse\Ponyfm\Contracts\NotificationHandler;
use Poniverse\Ponyfm\Jobs\SendNotifications; use Poniverse\Ponyfm\Jobs\SendNotifications;
use Poniverse\Ponyfm\Library\Notifications\Drivers\EmailDriver;
use Poniverse\Ponyfm\Library\Notifications\Drivers\NativeDriver; use Poniverse\Ponyfm\Library\Notifications\Drivers\NativeDriver;
use Poniverse\Ponyfm\Library\Notifications\Drivers\PonyfmDriver; use Poniverse\Ponyfm\Library\Notifications\Drivers\PonyfmDriver;
use Poniverse\Ponyfm\Models\Activity; use Poniverse\Ponyfm\Models\Activity;
@ -39,7 +39,8 @@ use Poniverse\Ponyfm\Models\User;
* @package Poniverse\Ponyfm\Library\Notifications * @package Poniverse\Ponyfm\Library\Notifications
* *
* This class returns a list of users who are to receive a particular notification. * This class returns a list of users who are to receive a particular notification.
* It is instantiated on a per-driver basis. * It is instantiated on a per-driver basis. Its methods return Eloquent query
* objects for the PonyfmDriver.
*/ */
class RecipientFinder implements NotificationHandler class RecipientFinder implements NotificationHandler
{ {
@ -67,11 +68,6 @@ class RecipientFinder implements NotificationHandler
case PonyfmDriver::class: case PonyfmDriver::class:
return $track->user->followers(); return $track->user->followers();
case EmailDriver::class:
return $track->user->followers()->whereHas('emailSubscriptions', function($query) {
$query->where('activity_type', Activity::TYPE_PUBLISHED_TRACK);
})->get();
case NativeDriver::class: case NativeDriver::class:
$followerIds = []; $followerIds = [];
$subIds = []; $subIds = [];
@ -99,7 +95,8 @@ class RecipientFinder implements NotificationHandler
{ {
switch ($this->notificationDriver) { switch ($this->notificationDriver) {
case PonyfmDriver::class: case PonyfmDriver::class:
return $playlist->user->followers; return $playlist->user->followers();
case NativeDriver::class: case NativeDriver::class:
$followerIds = []; $followerIds = [];
$subIds = []; $subIds = [];
@ -127,8 +124,8 @@ class RecipientFinder implements NotificationHandler
{ {
switch ($this->notificationDriver) { switch ($this->notificationDriver) {
case PonyfmDriver::class: case PonyfmDriver::class:
case EmailDriver::class: return $this->queryForUser($userBeingFollowed);
return [$userBeingFollowed];
case NativeDriver::class: case NativeDriver::class:
return Subscription::where('user_id', '=', $userBeingFollowed->id)->get(); return Subscription::where('user_id', '=', $userBeingFollowed->id)->get();
default: default:
@ -145,8 +142,8 @@ class RecipientFinder implements NotificationHandler
case PonyfmDriver::class: case PonyfmDriver::class:
return return
$comment->user->id === $comment->resource->user->id $comment->user->id === $comment->resource->user->id
? [] ? NULL
: [$comment->resource->user]; : $this->queryForUser($comment->resource->user);
case NativeDriver::class: case NativeDriver::class:
return Subscription::where('user_id', '=', $comment->resource->user->id)->get(); return Subscription::where('user_id', '=', $comment->resource->user->id)->get();
default: default:
@ -163,12 +160,23 @@ class RecipientFinder implements NotificationHandler
case PonyfmDriver::class: case PonyfmDriver::class:
return return
$favouriter->id === $entityBeingFavourited->user->id $favouriter->id === $entityBeingFavourited->user->id
? [] ? NULL
: [$entityBeingFavourited->user]; : $this->queryForUser($entityBeingFavourited->user);
case NativeDriver::class: case NativeDriver::class:
return Subscription::where('user_id', '=', $entityBeingFavourited->user->id)->get(); return Subscription::where('user_id', '=', $entityBeingFavourited->user->id)->get();
default: default:
return $this->fail(); return $this->fail();
} }
} }
/**
* Helper function that returns an Eloquent query instance that will return
* a specific user when executed.
*
* @param User $user
* @return \Eloquent|Builder
*/
private function queryForUser(User $user):Builder {
return User::where('id', '=', $user->id);
}
} }

View file

@ -33,8 +33,8 @@ class ContentFavourited extends BaseNotification
'content-favourited', 'content-favourited',
$this->activityRecord->text, [ $this->activityRecord->text, [
'creatorName' => $creatorName, 'creatorName' => $creatorName,
'resourceType' => $this->activityRecord->getResourceType(), 'resourceType' => $this->activityRecord->getResourceTypeString(),
'resourceTitle' => $this->activityRecord->resource->resource->title, 'resourceTitle' => $this->activityRecord->resource->title,
]); ]);
} }
} }

View file

@ -33,7 +33,7 @@ class NewComment extends BaseNotification
// Profile comments get a different template and subject line from // Profile comments get a different template and subject line from
// other types of comments. // other types of comments.
if ($this->activityRecord->getResourceType() === User::class) { if ($this->activityRecord->getResourceTypeString() === User::class) {
return $this->renderEmail( return $this->renderEmail(
'new-comment-profile', 'new-comment-profile',
$this->activityRecord->text, [ $this->activityRecord->text, [
@ -44,7 +44,7 @@ class NewComment extends BaseNotification
'new-comment-content', 'new-comment-content',
$this->activityRecord->text, [ $this->activityRecord->text, [
'creatorName' => $creatorName, 'creatorName' => $creatorName,
'resourceType' => $this->activityRecord->getResourceType(), 'resourceType' => $this->activityRecord->getResourceTypeString(),
'resourceTitle' => $this->activityRecord->resource->resource->title, 'resourceTitle' => $this->activityRecord->resource->resource->title,
]); ]);
} }

View file

@ -217,11 +217,11 @@ class Activity extends Model
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
public function getResourceType():string public function getResourceTypeString():string
{ {
switch($this->activity_type) { switch($this->activity_type) {
case static::TYPE_NEW_COMMENT: case static::TYPE_NEW_COMMENT:
if ($this->resource_type === User::class) { if ($this->isProfileComment()) {
return $this->resource->getResourceType(); return $this->resource->getResourceType();
} else { } else {
return $this->resource->resource->getResourceType(); return $this->resource->resource->getResourceType();
@ -232,6 +232,14 @@ class Activity extends Model
throw new \Exception("Unknown activity type {$this->activity_type} - cannot determine resource type."); throw new \Exception("Unknown activity type {$this->activity_type} - cannot determine resource type.");
} }
/**
* @return bool
*/
public function isProfileComment():bool {
return static::TYPE_NEW_COMMENT === $this->activity_type &&
User::class === $this->resource->getResourceClass();
}
/** /**
* The string this method generates is used for email subject lines as well * The string this method generates is used for email subject lines as well
* as on-site notifications. * as on-site notifications.
@ -256,17 +264,16 @@ class Activity extends Model
return "{$this->initiatingUser->display_name} is now following you!"; return "{$this->initiatingUser->display_name} is now following you!";
case static::TYPE_NEW_COMMENT: case static::TYPE_NEW_COMMENT:
// Is this a profile comment? if ($this->isProfileComment()) {
if ($this->resource_type === User::class) {
return "{$this->initiatingUser->display_name} left a comment on your profile!"; return "{$this->initiatingUser->display_name} left a comment on your profile!";
// Must be a content comment. // If it's not a profile comment, it must be a content comment.
} else { } else {
return "{$this->initiatingUser->display_name} left a comment on your {$this->getResourceType()}, {$this->resource->resource->title}!"; return "{$this->initiatingUser->display_name} left a comment on your {$this->getResourceTypeString()}, \"{$this->resource->resource->title}\"!";
} }
case static::TYPE_CONTENT_FAVOURITED: case static::TYPE_CONTENT_FAVOURITED:
return "{$this->initiatingUser->display_name} favourited your {$this->getResourceType()}, {$this->resource->title}!"; return "{$this->initiatingUser->display_name} favourited your {$this->getResourceTypeString()}, \"{$this->resource->title}\"!";
default: default:
throw new \Exception('This activity\'s activity type is unknown!'); throw new \Exception('This activity\'s activity type is unknown!');

View file

@ -144,6 +144,15 @@ class Comment extends Model
} }
} }
/**
* Returns the class name of the object that this is a comment on.
*
* @return string
*/
public function getResourceClass():string {
return get_class($this->resource);
}
public function delete() public function delete()
{ {
DB::transaction(function () { DB::transaction(function () {

View file

@ -77,6 +77,7 @@ Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload');
Route::get('notifications', 'AccountController@getNotifications'); Route::get('notifications', 'AccountController@getNotifications');
Route::get('notifications/email/unsubscribe/{subscriptionKey}', 'NotificationsController@getEmailUnsubscribe')->name('email:unsubscribe'); Route::get('notifications/email/unsubscribe/{subscriptionKey}', 'NotificationsController@getEmailUnsubscribe')->name('email:unsubscribe');
Route::get('notifications/email/click/{emailKey}', 'NotificationsController@getEmailClick')->name('email:click'); Route::get('notifications/email/click/{emailKey}', 'NotificationsController@getEmailClick')->name('email:click');