diff --git a/app/Http/Controllers/NotificationsController.php b/app/Http/Controllers/NotificationsController.php new file mode 100644 index 00000000..f93dba87 --- /dev/null +++ b/app/Http/Controllers/NotificationsController.php @@ -0,0 +1,55 @@ +. + */ + +namespace Poniverse\Ponyfm\Http\Controllers; + +use App; +use DB; +use Poniverse\Ponyfm\Models\Email; +use Poniverse\Ponyfm\Models\EmailSubscription; + +// TODO: #25 - finish these endpoints and secure them properly + +class NotificationsController extends Controller { + public function getEmailClick($emailKey) { + App::abort(403, "This isn't implemented yet!"); + + $emailKey = decrypt($emailKey); + /** @var Email $email */ + $email = Email::findOrFail($emailKey); + + DB::transaction(function() use ($email) { + $email->emailClicks()->create(['ip_address' => \Request::ip()]); + $email->notification->is_read = true; + $email->notification->save(); + }); + + return redirect($email->getActivity()->url); + } + + public function getEmailUnsubscribe($subscriptionKey) { + App::abort(403, "This isn't implemented yet!"); + + $subscriptionId = decrypt($subscriptionKey); + $subscription = EmailSubscription::findOrFail($subscriptionId); + + return var_export($subscription); + } +} diff --git a/app/Jobs/SendNotifications.php b/app/Jobs/SendNotifications.php index 8758d568..d550f9d5 100644 --- a/app/Jobs/SendNotifications.php +++ b/app/Jobs/SendNotifications.php @@ -24,6 +24,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Poniverse\Ponyfm\Jobs\Job; use Poniverse\Ponyfm\Library\Notifications\Drivers\AbstractDriver; +use Poniverse\Ponyfm\Library\Notifications\Drivers\EmailDriver; use Poniverse\Ponyfm\Library\Notifications\Drivers\NativeDriver; use Poniverse\Ponyfm\Library\Notifications\Drivers\PonyfmDriver; use Poniverse\Ponyfm\Models\User; @@ -64,6 +65,9 @@ class SendNotifications extends Job implements ShouldQueue //NativeDriver::class ]; + // NOTE: PonyfmDriver MUST execute before any other drivers; it creates + // the Notification records that the other drivers depend on! + foreach ($drivers as $driver) { /** @var $driver AbstractDriver */ $driver = new $driver; diff --git a/app/Library/Notifications/Drivers/AbstractDriver.php b/app/Library/Notifications/Drivers/AbstractDriver.php index 88b3002d..7b75d15f 100644 --- a/app/Library/Notifications/Drivers/AbstractDriver.php +++ b/app/Library/Notifications/Drivers/AbstractDriver.php @@ -31,7 +31,16 @@ abstract class AbstractDriver implements NotificationHandler public function __construct() { - $this->recipientFinder = new RecipientFinder(get_class($this)); + $notificationDriverClass = get_class($this); + + switch ($notificationDriverClass) { + case EmailDriver::class: + case PonyfmDriver::class: + $this->recipientFinder = new RecipientFinder(get_class($this)); + break; + default: + throw new \Exception("Invalid notification driver!"); + } } /** diff --git a/app/Library/Notifications/Drivers/PonyfmDriver.php b/app/Library/Notifications/Drivers/PonyfmDriver.php index 521bcc82..b4d1eaad 100644 --- a/app/Library/Notifications/Drivers/PonyfmDriver.php +++ b/app/Library/Notifications/Drivers/PonyfmDriver.php @@ -20,11 +20,14 @@ namespace Poniverse\Ponyfm\Library\Notifications\Drivers; -use ArrayAccess; use Carbon\Carbon; +use Log; +use Mail; use Poniverse\Ponyfm\Contracts\Favouritable; +use Poniverse\Ponyfm\Mail\BaseNotification; use Poniverse\Ponyfm\Models\Activity; use Poniverse\Ponyfm\Models\Comment; +use Poniverse\Ponyfm\Models\Email; use Poniverse\Ponyfm\Models\Notification; use Poniverse\Ponyfm\Models\Playlist; use Poniverse\Ponyfm\Models\Track; @@ -35,21 +38,39 @@ class PonyfmDriver extends AbstractDriver /** * A helper method for bulk insertion of notification records. * - * @param int $activityId + * @param Activity $activity * @param User[] $recipients collection of {@link User} objects */ - private function insertNotifications(int $activityId, $recipients) + private function insertNotifications(Activity $activity, $recipients) { $notifications = []; foreach ($recipients as $recipient) { $notifications[] = [ - 'activity_id' => $activityId, + 'activity_id' => $activity->id, 'user_id' => $recipient->id ]; } Notification::insert($notifications); } + /** + * Sends out an email about the given activity to the given set of users. + * + * @param Activity $activity + * @param User[] $recipients collection of {@link User} objects + */ + private function sendEmails(Activity $activity, $recipients) { + foreach ($recipients as $recipient) { + /** @var Notification $notification */ + $notification = $activity->notifications->where('user_id', $recipient->id)->first(); + /** @var Email $email */ + $email = $notification->email()->create([]); + + Log::debug("Attempting to send an email about notification {$notification->id} to {$recipient->email}."); + Mail::to($recipient->email)->queue(BaseNotification::factory($activity, $email)); + } + } + /** * @inheritdoc */ @@ -63,7 +84,11 @@ class PonyfmDriver extends AbstractDriver 'resource_id' => $track->id, ]); - $this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args())); + $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); + if (NULL !== $recipientsQuery) { + $this->insertNotifications($activity, $recipientsQuery->get()); + $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_TRACK)->get()); + } } /** @@ -79,9 +104,16 @@ class PonyfmDriver extends AbstractDriver 'resource_id' => $playlist->id, ]); - $this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args())); + $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); + if (NULL !== $recipientsQuery) { + $this->insertNotifications($activity, $recipientsQuery->get()); + $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_PUBLISHED_PLAYLIST)->get()); + } } + /** + * @inheritdoc + */ public function newFollower(User $userBeingFollowed, User $follower) { $activity = Activity::create([ @@ -92,7 +124,11 @@ class PonyfmDriver extends AbstractDriver 'resource_id' => $userBeingFollowed->id, ]); - $this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args())); + $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); + if (NULL !== $recipientsQuery) { + $this->insertNotifications($activity, $recipientsQuery->get()); + $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_FOLLOWER)->get()); + } } /** @@ -108,7 +144,11 @@ class PonyfmDriver extends AbstractDriver 'resource_id' => $comment->id, ]); - $this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args())); + $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); + if (NULL !== $recipientsQuery) { + $this->insertNotifications($activity, $recipientsQuery->get()); + $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_NEW_COMMENT)->get()); + } } /** @@ -124,6 +164,10 @@ class PonyfmDriver extends AbstractDriver 'resource_id' => $entityBeingFavourited->id, ]); - $this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args())); + $recipientsQuery = $this->getRecipients(__FUNCTION__, func_get_args()); + if (NULL !== $recipientsQuery) { + $this->insertNotifications($activity, $recipientsQuery->get()); + $this->sendEmails($activity, $recipientsQuery->withEmailSubscriptionFor(Activity::TYPE_CONTENT_FAVOURITED)->get()); + } } } diff --git a/app/Library/Notifications/RecipientFinder.php b/app/Library/Notifications/RecipientFinder.php index d0710e4c..afacfbe6 100644 --- a/app/Library/Notifications/RecipientFinder.php +++ b/app/Library/Notifications/RecipientFinder.php @@ -20,12 +20,14 @@ namespace Poniverse\Ponyfm\Library\Notifications; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Foundation\Bus\DispatchesJobs; use Poniverse\Ponyfm\Contracts\Favouritable; use Poniverse\Ponyfm\Contracts\NotificationHandler; use Poniverse\Ponyfm\Jobs\SendNotifications; use Poniverse\Ponyfm\Library\Notifications\Drivers\NativeDriver; use Poniverse\Ponyfm\Library\Notifications\Drivers\PonyfmDriver; +use Poniverse\Ponyfm\Models\Activity; use Poniverse\Ponyfm\Models\Comment; use Poniverse\Ponyfm\Models\Playlist; use Poniverse\Ponyfm\Models\Subscription; @@ -37,7 +39,8 @@ use Poniverse\Ponyfm\Models\User; * @package Poniverse\Ponyfm\Library\Notifications * * 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 { @@ -63,7 +66,8 @@ class RecipientFinder implements NotificationHandler { switch ($this->notificationDriver) { case PonyfmDriver::class: - return $track->user->followers; + return $track->user->followers(); + case NativeDriver::class: $followerIds = []; $subIds = []; @@ -91,7 +95,8 @@ class RecipientFinder implements NotificationHandler { switch ($this->notificationDriver) { case PonyfmDriver::class: - return $playlist->user->followers; + return $playlist->user->followers(); + case NativeDriver::class: $followerIds = []; $subIds = []; @@ -119,7 +124,8 @@ class RecipientFinder implements NotificationHandler { switch ($this->notificationDriver) { case PonyfmDriver::class: - return [$userBeingFollowed]; + return $this->queryForUser($userBeingFollowed); + case NativeDriver::class: return Subscription::where('user_id', '=', $userBeingFollowed->id)->get(); default: @@ -136,8 +142,8 @@ class RecipientFinder implements NotificationHandler case PonyfmDriver::class: return $comment->user->id === $comment->resource->user->id - ? [] - : [$comment->resource->user]; + ? NULL + : $this->queryForUser($comment->resource->user); case NativeDriver::class: return Subscription::where('user_id', '=', $comment->resource->user->id)->get(); default: @@ -154,12 +160,23 @@ class RecipientFinder implements NotificationHandler case PonyfmDriver::class: return $favouriter->id === $entityBeingFavourited->user->id - ? [] - : [$entityBeingFavourited->user]; + ? NULL + : $this->queryForUser($entityBeingFavourited->user); case NativeDriver::class: return Subscription::where('user_id', '=', $entityBeingFavourited->user->id)->get(); default: 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); + } } diff --git a/app/Mail/BaseNotification.php b/app/Mail/BaseNotification.php new file mode 100644 index 00000000..c5a6cd16 --- /dev/null +++ b/app/Mail/BaseNotification.php @@ -0,0 +1,135 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +use Illuminate\Bus\Queueable; +use Illuminate\Mail\Mailable; +use Illuminate\Queue\SerializesModels; +use Poniverse\Ponyfm\Models\Activity; +use Poniverse\Ponyfm\Models\Email; + +abstract class BaseNotification extends Mailable { + use Queueable, SerializesModels; + + /** @var Email */ + protected $emailRecord; + + /** @var \Poniverse\Ponyfm\Models\Notification */ + protected $notificationRecord; + + /** @var \Poniverse\Ponyfm\Models\Activity */ + protected $activityRecord; + + /** @var \Poniverse\Ponyfm\Models\User */ + protected $initiatingUser; + + /** + * Create a new message instance. + * + * @param Email $email + */ + public function __construct(Email $email) { + $this->emailRecord = $email; + $this->notificationRecord = $email->notification; + $this->activityRecord = $email->notification->activity; + $this->initiatingUser = $email->notification->activity->initiatingUser; + } + + /** + * Factory method that instantiates the appropriate {@link BaseNotification} + * subclass for the given activity type and {@link Email} record. + * + * @param Activity $activity + * @param Email $email + * @return BaseNotification + */ + static public function factory(Activity $activity, Email $email): BaseNotification { + switch ($activity->activity_type) { + case Activity::TYPE_NEWS: + break; + case Activity::TYPE_PUBLISHED_TRACK: + return new NewTrack($email); + case Activity::TYPE_PUBLISHED_ALBUM: + break; + case Activity::TYPE_PUBLISHED_PLAYLIST: + return new NewPlaylist($email); + case Activity::TYPE_NEW_FOLLOWER: + return new NewFollower($email); + case Activity::TYPE_NEW_COMMENT: + return new NewComment($email); + case Activity::TYPE_CONTENT_FAVOURITED: + return new ContentFavourited($email); + default: + break; + } + throw new \InvalidArgumentException("Email notifications for activity type {$activity->activity_type} are not implemented!"); + } + + /** + * Build the message. + * + * @return $this + */ + abstract public function build(); + + /** + * Generates an unsubscribe URL unique to the user. + * + * @return string + */ + protected function generateUnsubscribeUrl() { + $subscriptionKey = encrypt($this->emailRecord->getSubscription()->id); + return route('email:unsubscribe', ['subscriptionKey' => $subscriptionKey]); + } + + /** + * Generates a trackable URL to the content item this email is about. + * + * @return string + */ + protected function generateNotificationUrl() { + $emailKey = encrypt($this->emailRecord->id); + return route('email:click', ['emailKey' => $emailKey]); + } + + /** + * Helper method to eliminate duplication between different types of + * notifications. Use it inside the build() method on this class's children. + * + * Note that data common to all notification types is merged into the + * template variable array. + * + * @param string $templateName + * @param string $subject + * @param array $extraVariables + * @return $this + */ + protected function renderEmail(string $templateName, string $subject, array $extraVariables) { + return $this + ->subject($subject) + ->view("emails.notifications.{$templateName}") + ->text("emails.notifications.{$templateName}_plaintext") + ->with(array_merge($extraVariables, [ + 'notificationUrl' => $this->generateNotificationUrl(), + 'unsubscribeUrl' => $this->generateUnsubscribeUrl() + ])); + } +} diff --git a/app/Mail/ContentFavourited.php b/app/Mail/ContentFavourited.php new file mode 100644 index 00000000..21e07641 --- /dev/null +++ b/app/Mail/ContentFavourited.php @@ -0,0 +1,40 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +class ContentFavourited extends BaseNotification +{ + /** + * @inheritdoc + */ + public function build() + { + $creatorName = $this->initiatingUser->display_name; + + return $this->renderEmail( + 'content-favourited', + $this->activityRecord->text, [ + 'creatorName' => $creatorName, + 'resourceType' => $this->activityRecord->getResourceTypeString(), + 'resourceTitle' => $this->activityRecord->resource->title, + ]); + } +} diff --git a/app/Mail/NewComment.php b/app/Mail/NewComment.php new file mode 100644 index 00000000..5295f4ee --- /dev/null +++ b/app/Mail/NewComment.php @@ -0,0 +1,53 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +use Poniverse\Ponyfm\Models\User; + +class NewComment extends BaseNotification +{ + /** + * @inheritdoc + */ + public function build() + { + $creatorName = $this->initiatingUser->display_name; + + // Profile comments get a different template and subject line from + // other types of comments. + if ($this->activityRecord->getResourceTypeString() === User::class) { + return $this->renderEmail( + 'new-comment-profile', + $this->activityRecord->text, [ + 'creatorName' => $creatorName, + ]); + } else { + return $this->renderEmail( + 'new-comment-content', + $this->activityRecord->text, [ + 'creatorName' => $creatorName, + 'resourceType' => $this->activityRecord->getResourceTypeString(), + 'resourceTitle' => $this->activityRecord->resource->resource->title, + ]); + } + + } +} diff --git a/app/Mail/NewFollower.php b/app/Mail/NewFollower.php new file mode 100644 index 00000000..c96660e3 --- /dev/null +++ b/app/Mail/NewFollower.php @@ -0,0 +1,39 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +class NewFollower extends BaseNotification +{ + /** + * @inheritdoc + */ + public function build() + { + $creatorName = $this->initiatingUser->display_name; + + return $this->renderEmail( + 'new-follower', + "{$creatorName} is now following you on Pony.fm!", + [ + 'creatorName' => $creatorName, + ]); + } +} diff --git a/app/Mail/NewPlaylist.php b/app/Mail/NewPlaylist.php new file mode 100644 index 00000000..c865a494 --- /dev/null +++ b/app/Mail/NewPlaylist.php @@ -0,0 +1,41 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +class NewPlaylist extends BaseNotification +{ + /** + * @inheritdoc + */ + public function build() + { + $creatorName = $this->initiatingUser->display_name; + $playlistTitle = $this->activityRecord->resource->title; + + return $this->renderEmail( + 'new-playlist', + "{$creatorName} created a playlist, \"{$playlistTitle}\"!", + [ + 'creatorName' => $creatorName, + 'playlistTitle' => $playlistTitle, + ]); + } +} diff --git a/app/Mail/NewTrack.php b/app/Mail/NewTrack.php new file mode 100644 index 00000000..46bf2b2a --- /dev/null +++ b/app/Mail/NewTrack.php @@ -0,0 +1,41 @@ +. + */ + +namespace Poniverse\Ponyfm\Mail; + +class NewTrack extends BaseNotification +{ + /** + * @inheritdoc + */ + public function build() + { + $creatorName = $this->initiatingUser->display_name; + $trackTitle = $this->activityRecord->resource->title; + + return $this->renderEmail( + 'new-track', + "{$creatorName} published \"{$trackTitle}\"!", + [ + 'creatorName' => $creatorName, + 'trackTitle' => $trackTitle, + ]); + } +} diff --git a/app/Models/Activity.php b/app/Models/Activity.php index a11c1f88..edc79039 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -66,6 +66,11 @@ class Activity extends Model 'resource_id' => 'integer', ]; + /** + * These constants are stored in the "activity_types" table for the purpose + * of referential data integrity. Any additions or changes to them MUST + * include a database migration to apply the changes to that table as well. + */ const TYPE_NEWS = 1; const TYPE_PUBLISHED_TRACK = 2; const TYPE_PUBLISHED_ALBUM = 3; @@ -206,6 +211,39 @@ class Activity extends Model } /** + * Returns a string representing the type of resource this activity is about + * for use in human-facing notification text. + * + * @return string + * @throws \Exception + */ + public function getResourceTypeString():string + { + switch($this->activity_type) { + case static::TYPE_NEW_COMMENT: + if ($this->isProfileComment()) { + return $this->resource->getResourceType(); + } else { + return $this->resource->resource->getResourceType(); + } + case static::TYPE_CONTENT_FAVOURITED: + return $this->resource->getResourceType(); + } + 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 + * as on-site notifications. + * * @return string human-readable Markdown string describing this notification * @throws \Exception */ @@ -226,17 +264,16 @@ class Activity extends Model return "{$this->initiatingUser->display_name} is now following you!"; case static::TYPE_NEW_COMMENT: - // Is this a profile comment? - if ($this->resource_type === User::class) { + if ($this->isProfileComment()) { 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 { - return "{$this->initiatingUser->display_name} left a comment on your {$this->resource->resource->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: - return "{$this->initiatingUser->display_name} favourited your {$this->resource->getResourceType()}, {$this->resource->title}!"; + return "{$this->initiatingUser->display_name} favourited your {$this->getResourceTypeString()}, \"{$this->resource->title}\"!"; default: throw new \Exception('This activity\'s activity type is unknown!'); diff --git a/app/Models/Comment.php b/app/Models/Comment.php index d16cfd65..b4665c8e 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -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() { DB::transaction(function () { diff --git a/app/Models/Email.php b/app/Models/Email.php new file mode 100644 index 00000000..f4b0991f --- /dev/null +++ b/app/Models/Email.php @@ -0,0 +1,69 @@ +. + */ + +namespace Poniverse\Ponyfm\Models; + +use Alsofronie\Uuid\UuidModelTrait; +use Illuminate\Database\Eloquent\Model; +use Poniverse\Ponyfm\Models\Notification; + +/** + * Poniverse\Ponyfm\Models\Email + * + * @property string $id + * @property integer $notification_id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property-read \Poniverse\Ponyfm\Models\Notification $notification + * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\EmailClick[] $emailClicks + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Email whereId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Email whereNotificationId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Email whereCreatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Email whereUpdatedAt($value) + * @mixin \Eloquent + */ +class Email extends Model +{ + use UuidModelTrait; + + public function notification() { + return $this->belongsTo(Notification::class, 'notification_id', 'id', 'notifications'); + } + + public function emailClicks() { + return $this->hasMany(EmailClick::class, 'email_id', 'id'); + } + + public function getActivity():Activity { + return $this->notification->activity; + } + + public function getUser():User { + return $this->notification->recipient; + } + + public function getSubscription():EmailSubscription { + return $this + ->getUser() + ->emailSubscriptions() + ->where('activity_type', $this->getActivity()->activity_type) + ->first(); + } +} diff --git a/app/Models/EmailClick.php b/app/Models/EmailClick.php new file mode 100644 index 00000000..acc81b8f --- /dev/null +++ b/app/Models/EmailClick.php @@ -0,0 +1,51 @@ +. + */ + +namespace Poniverse\Ponyfm\Models; + +use Alsofronie\Uuid\UuidModelTrait; +use Illuminate\Database\Eloquent\Model; + +/** + * Poniverse\Ponyfm\Models\EmailClick + * + * @property string $id + * @property string $email_id + * @property string $ip_address + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property-read \Poniverse\Ponyfm\Models\Email $email + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailClick whereId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailClick whereEmailId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailClick whereIpAddress($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailClick whereCreatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailClick whereUpdatedAt($value) + * @mixin \Eloquent + */ +class EmailClick extends Model +{ + use UuidModelTrait; + + protected $fillable = ['ip_address']; + + public function email() { + return $this->belongsTo(Email::class, 'email_id', 'id', 'emails'); + } +} diff --git a/app/Models/EmailSubscription.php b/app/Models/EmailSubscription.php new file mode 100644 index 00000000..97479bee --- /dev/null +++ b/app/Models/EmailSubscription.php @@ -0,0 +1,53 @@ +. + */ + +namespace Poniverse\Ponyfm\Models; + +use Alsofronie\Uuid\UuidModelTrait; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; +use Poniverse\Ponyfm\Models\User; + +/** + * Poniverse\Ponyfm\EmailSubscription + * + * @property string $id + * @property integer $user_id + * @property integer $activity_type + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property string $deleted_at + * @property-read \Poniverse\Ponyfm\Models\User $user + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereUserId($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereActivityType($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereCreatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereUpdatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\EmailSubscription whereDeletedAt($value) + * @mixin \Eloquent + */ +class EmailSubscription extends Model +{ + use UuidModelTrait, SoftDeletes; + + public function user() { + return $this->belongsTo(User::class, 'user_id', 'id', 'users'); + } +} diff --git a/app/Models/Notification.php b/app/Models/Notification.php index 92bfd0b0..ce0cf0df 100644 --- a/app/Models/Notification.php +++ b/app/Models/Notification.php @@ -31,6 +31,7 @@ use Illuminate\Database\Eloquent\Model; * @property integer $user_id * @property boolean $is_read * @property-read \Poniverse\Ponyfm\Models\Activity $activity + * @property-read \Poniverse\Ponyfm\Models\Email $email * @property-read \Poniverse\Ponyfm\Models\User $recipient * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Notification forUser($user) * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Notification whereId($value) @@ -60,6 +61,11 @@ class Notification extends Model return $this->belongsTo(User::class, 'user_id', 'id'); } + public function email() + { + return $this->hasOne(Email::class, 'notification_id', 'id'); + } + /** * This scope grabs eager-loaded notifications for the given user. * diff --git a/app/Models/User.php b/app/Models/User.php index 20c9b13d..53e50265 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -73,6 +73,8 @@ use Venturecraft\Revisionable\RevisionableTrait; * @property-read mixed $user * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $notificationActivities + * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Email[] $emails + * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\EmailSubscription[] $emailSubscriptions * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereId($value) * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereDisplayName($value) * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereUsername($value) @@ -91,6 +93,7 @@ use Venturecraft\Revisionable\RevisionableTrait; * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereRememberToken($value) * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereIsArchived($value) * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User whereDisabledAt($value) + * @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User withEmailSubscriptionFor($activityType) * @mixin \Eloquent */ class User extends Model implements AuthenticatableContract, CanResetPasswordContract, \Illuminate\Contracts\Auth\Access\Authorizable, Searchable, Commentable @@ -126,6 +129,19 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return !$query; } + /** + * Returns users with an email subscription to the given activity type. + * + * @param $query + * @param int $activityType one of the TYPE_* constants in the Activity class + * @return mixed + */ + public function scopeWithEmailSubscriptionFor($query, int $activityType) { + return $query->whereHas('emailSubscriptions', function ($query) use ($activityType) { + $query->where('activity_type', $activityType); + }); + } + /** * Takes the given string, slugifies it, and increments a counter if needed * to generate a unique slug version of it. @@ -239,6 +255,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return $this->hasManyThrough(Activity::class, Notification::class, 'user_id', 'notification_id', 'id'); } + public function emails() + { + return $this->hasManyThrough(Email::class, Notification::class, 'user_id', 'notification_id', 'id'); + } + + public function emailSubscriptions() + { + return $this->hasMany(EmailSubscription::class, 'user_id', 'id'); + } + public function getIsArchivedAttribute() { return (bool) $this->attributes['is_archived']; diff --git a/composer.json b/composer.json index 51a01398..e2eb09f0 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,9 @@ "predis/predis": "^1.0", "ksubileau/color-thief-php": "^1.3", "graham-campbell/exceptions": "^9.1", - "minishlink/web-push": "^1.0" + "minishlink/web-push": "^1.0", + "laravel/legacy-encrypter": "^1.0", + "alsofronie/eloquent-uuid": "^1.0" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index adf807e5..cdbab371 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,49 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "a348ccb0e75179e2fc78ef4aea58ffcd", + "hash": "d6931f271706348a79f32feeab1c9902", + "content-hash": "bf3d7332057e254a4b97b1916782e6bb", "packages": [ + { + "name": "alsofronie/eloquent-uuid", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/alsofronie/eloquent-uuid.git", + "reference": "565a8dd79f432b01c9f5d4dd7389329f107aa037" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/alsofronie/eloquent-uuid/zipball/565a8dd79f432b01c9f5d4dd7389329f107aa037", + "reference": "565a8dd79f432b01c9f5d4dd7389329f107aa037", + "shasum": "" + }, + "require": { + "webpatser/laravel-uuid": "2.*" + }, + "require-dev": { + "laravel/framework": "^5.1.0", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Alsofronie\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Sofronie", + "email": "alsofronie@gmail.com" + } + ], + "description": "A Laravel Eloquent Model trait for using UUID's as primary keys", + "time": "2016-10-22 20:21:20" + }, { "name": "barryvdh/laravel-debugbar", "version": "v2.3.0", @@ -58,20 +99,20 @@ "profiler", "webprofiler" ], - "time": "2016-09-15T14:05:56+00:00" + "time": "2016-09-15 14:05:56" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.2.2", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "105f14a50d0959a0e80004a15b3350fdf78f9623" + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/105f14a50d0959a0e80004a15b3350fdf78f9623", - "reference": "105f14a50d0959a0e80004a15b3350fdf78f9623", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", "shasum": "" }, "require": { @@ -124,7 +165,7 @@ "phpstorm", "sublime" ], - "time": "2016-11-15T08:21:23+00:00" + "time": "2016-07-04 11:52:48" }, { "name": "barryvdh/reflection-docblock", @@ -173,20 +214,20 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2016-06-13T19:28:20+00:00" + "time": "2016-06-13 19:28:20" }, { "name": "beberlei/assert", - "version": "v2.6.8", + "version": "v2.6.3", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "848c8f0bde97b48d1e159075e20a6667583f3978" + "reference": "51e9d654481fc00c8a376641c390ec4e35d8c1fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/848c8f0bde97b48d1e159075e20a6667583f3978", - "reference": "848c8f0bde97b48d1e159075e20a6667583f3978", + "url": "https://api.github.com/repos/beberlei/assert/zipball/51e9d654481fc00c8a376641c390ec4e35d8c1fc", + "reference": "51e9d654481fc00c8a376641c390ec4e35d8c1fc", "shasum": "" }, "require": { @@ -194,13 +235,17 @@ "php": ">=5.3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", "phpunit/phpunit": "@stable" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, "autoload": { - "psr-4": { - "Assert\\": "lib/Assert" + "psr-0": { + "Assert": "lib/" }, "files": [ "lib/Assert/functions.php" @@ -228,24 +273,24 @@ "assertion", "validation" ], - "time": "2016-12-05T11:33:17+00:00" + "time": "2016-07-28 19:35:30" }, { "name": "classpreloader/classpreloader", - "version": "3.1.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/ClassPreloader/ClassPreloader.git", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096" + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/bc7206aa892b5a33f4680421b69b191efd32b096", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096", + "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", "shasum": "" }, "require": { - "nikic/php-parser": "^1.0|^2.0|^3.0", + "nikic/php-parser": "^1.0|^2.0", "php": ">=5.5.9" }, "require-dev": { @@ -254,7 +299,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -282,7 +327,7 @@ "class", "preload" ], - "time": "2016-09-16T12:50:15+00:00" + "time": "2015-11-09 22:51:51" }, { "name": "codescale/ffmpeg-php", @@ -326,7 +371,7 @@ "ffmpeg", "video" ], - "time": "2013-05-05T09:10:04+00:00" + "time": "2013-05-05 09:10:04" }, { "name": "cviebrock/laravel-elasticsearch", @@ -375,7 +420,7 @@ "laravel", "search" ], - "time": "2016-06-27T15:11:36+00:00" + "time": "2016-06-27 15:11:36" }, { "name": "dnoegel/php-xdg-base-dir", @@ -408,39 +453,39 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", - "time": "2014-10-24T07:27:01+00:00" + "time": "2014-10-24 07:27:01" }, { "name": "doctrine/annotations", - "version": "v1.3.0", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5" + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", - "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "php": ">=5.3.2" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.6.1" + "phpunit/phpunit": "4.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -476,20 +521,20 @@ "docblock", "parser" ], - "time": "2016-10-24T11:45:47+00:00" + "time": "2015-08-31 12:32:49" }, { "name": "doctrine/cache", - "version": "v1.6.1", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6", "shasum": "" }, "require": { @@ -546,7 +591,7 @@ "cache", "caching" ], - "time": "2016-10-29T11:16:17+00:00" + "time": "2015-12-31 16:37:02" }, { "name": "doctrine/collections", @@ -612,20 +657,20 @@ "collections", "iterator" ], - "time": "2015-04-14T22:21:58+00:00" + "time": "2015-04-14 22:21:58" }, { "name": "doctrine/common", - "version": "v2.6.2", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3" + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/7bce00698899aa2c06fe7365c76e4d78ddb15fa3", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", "shasum": "" }, "require": { @@ -685,7 +730,7 @@ "persistence", "spl" ], - "time": "2016-11-30T16:50:46+00:00" + "time": "2015-12-25 13:18:31" }, { "name": "doctrine/dbal", @@ -756,7 +801,7 @@ "persistence", "queryobject" ], - "time": "2016-09-09T19:13:33+00:00" + "time": "2016-09-09 19:13:33" }, { "name": "doctrine/inflector", @@ -823,7 +868,7 @@ "singularize", "string" ], - "time": "2015-11-06T14:35:42+00:00" + "time": "2015-11-06 14:35:42" }, { "name": "doctrine/lexer", @@ -877,20 +922,20 @@ "lexer", "parser" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2014-09-09 13:34:57" }, { "name": "elasticsearch/elasticsearch", - "version": "v2.3.0", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "12a400656e4cf4c231d83cb56af3f50a27dcde93" + "reference": "7b34186a58730d0a8963741bd62fa5ab45658ada" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/12a400656e4cf4c231d83cb56af3f50a27dcde93", - "reference": "12a400656e4cf4c231d83cb56af3f50a27dcde93", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/7b34186a58730d0a8963741bd62fa5ab45658ada", + "reference": "7b34186a58730d0a8963741bd62fa5ab45658ada", "shasum": "" }, "require": { @@ -931,20 +976,20 @@ "elasticsearch", "search" ], - "time": "2016-11-30T17:15:05+00:00" + "time": "2016-07-14 14:13:40" }, { "name": "fgrosse/phpasn1", - "version": "1.5.2", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/fgrosse/PHPASN1.git", - "reference": "a18b162eca6aa70f8f15615f4ac8c852dffbc7dd" + "reference": "27181c72dcaeef9046925a4a2bf75a68b98f6dfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/a18b162eca6aa70f8f15615f4ac8c852dffbc7dd", - "reference": "a18b162eca6aa70f8f15615f4ac8c852dffbc7dd", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/27181c72dcaeef9046925a4a2bf75a68b98f6dfb", + "reference": "27181c72dcaeef9046925a4a2bf75a68b98f6dfb", "shasum": "" }, "require": { @@ -1000,7 +1045,7 @@ "x509", "x690" ], - "time": "2016-10-29T15:46:46+00:00" + "time": "2015-10-02 06:11:35" }, { "name": "graham-campbell/exceptions", @@ -1069,20 +1114,20 @@ "laravel", "whoops" ], - "time": "2016-08-19T08:58:30+00:00" + "time": "2016-08-19 08:58:30" }, { "name": "guzzlehttp/guzzle", - "version": "6.2.2", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/3f808fba627f2c5b69e2501217bf31af349c1427", + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427", "shasum": "" }, "require": { @@ -1131,20 +1176,20 @@ "rest", "web service" ], - "time": "2016-10-08T15:01:37+00:00" + "time": "2016-07-15 17:22:37" }, { "name": "guzzlehttp/promises", - "version": "1.3.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "2693c101803ca78b27972d84081d027fca790a1e" + "reference": "c10d860e2a9595f8883527fa0021c7da9e65f579" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/2693c101803ca78b27972d84081d027fca790a1e", - "reference": "2693c101803ca78b27972d84081d027fca790a1e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/c10d860e2a9595f8883527fa0021c7da9e65f579", + "reference": "c10d860e2a9595f8883527fa0021c7da9e65f579", "shasum": "" }, "require": { @@ -1182,7 +1227,7 @@ "keywords": [ "promise" ], - "time": "2016-11-18T17:47:58+00:00" + "time": "2016-05-18 16:56:05" }, { "name": "guzzlehttp/psr7", @@ -1240,7 +1285,7 @@ "stream", "uri" ], - "time": "2016-06-24T23:00:38+00:00" + "time": "2016-06-24 23:00:38" }, { "name": "guzzlehttp/ringphp", @@ -1291,7 +1336,7 @@ } ], "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "time": "2015-05-20T03:37:09+00:00" + "time": "2015-05-20 03:37:09" }, { "name": "guzzlehttp/streams", @@ -1341,20 +1386,20 @@ "Guzzle", "stream" ], - "time": "2014-10-12T19:18:40+00:00" + "time": "2014-10-12 19:18:40" }, { "name": "intouch/laravel-newrelic", - "version": "2.1.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/In-Touch/laravel-newrelic.git", - "reference": "cb0c69075d7202c99d6ae705b106d1ecb1649989" + "reference": "24a6c025c1169afb1863593bd7f2a9bb9de5b233" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/In-Touch/laravel-newrelic/zipball/cb0c69075d7202c99d6ae705b106d1ecb1649989", - "reference": "cb0c69075d7202c99d6ae705b106d1ecb1649989", + "url": "https://api.github.com/repos/In-Touch/laravel-newrelic/zipball/24a6c025c1169afb1863593bd7f2a9bb9de5b233", + "reference": "24a6c025c1169afb1863593bd7f2a9bb9de5b233", "shasum": "" }, "require": { @@ -1385,7 +1430,7 @@ "new relic", "newrelic" ], - "time": "2016-10-14T13:14:21+00:00" + "time": "2016-05-05 12:30:20" }, { "name": "intouch/newrelic", @@ -1426,7 +1471,7 @@ "new relic", "newrelic" ], - "time": "2014-08-18T12:08:45+00:00" + "time": "2014-08-18 12:08:45" }, { "name": "jakub-onderka/php-console-color", @@ -1469,7 +1514,7 @@ "homepage": "http://www.acci.cz" } ], - "time": "2014-04-08T15:00:19+00:00" + "time": "2014-04-08 15:00:19" }, { "name": "jakub-onderka/php-console-highlighter", @@ -1513,24 +1558,24 @@ "homepage": "http://www.acci.cz/" } ], - "time": "2015-04-20T18:58:01+00:00" + "time": "2015-04-20 18:58:01" }, { "name": "jeremeamia/SuperClosure", - "version": "2.3.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/jeremeamia/super_closure.git", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db" + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/443c3df3207f176a1b41576ee2a66968a507b3db", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db", + "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/29a88be2a4846d27c1613aed0c9071dfad7b5938", + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938", "shasum": "" }, "require": { - "nikic/php-parser": "^1.2|^2.0|^3.0", + "nikic/php-parser": "^1.2|^2.0", "php": ">=5.4", "symfony/polyfill-php56": "^1.0" }, @@ -1540,7 +1585,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -1571,7 +1616,7 @@ "serialize", "tokenizer" ], - "time": "2016-12-07T09:37:55+00:00" + "time": "2015-12-05 17:17:57" }, { "name": "kriswallsmith/buzz", @@ -1619,7 +1664,7 @@ "curl", "http client" ], - "time": "2015-06-25T17:26:56+00:00" + "time": "2015-06-25 17:26:56" }, { "name": "ksubileau/color-thief-php", @@ -1672,20 +1717,20 @@ "php", "thief" ], - "time": "2016-01-17T18:55:16+00:00" + "time": "2016-01-17 18:55:16" }, { "name": "laravel/framework", - "version": "v5.3.26", + "version": "v5.3.15", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "609bcffd2ce2006925f8304e022288b3da5d23a4" + "reference": "f034a02f38db77d4b0b3e89942394cae456a627f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/609bcffd2ce2006925f8304e022288b3da5d23a4", - "reference": "609bcffd2ce2006925f8304e022288b3da5d23a4", + "url": "https://api.github.com/repos/laravel/framework/zipball/f034a02f38db77d4b0b3e89942394cae456a627f", + "reference": "f034a02f38db77d4b0b3e89942394cae456a627f", "shasum": "" }, "require": { @@ -1767,7 +1812,7 @@ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", "symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).", "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).", - "symfony/psr-http-message-bridge": "Required to use psr7 bridging features (0.2.*)." + "symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)." }, "type": "library", "extra": { @@ -1800,25 +1845,72 @@ "framework", "laravel" ], - "time": "2016-11-30T14:58:38+00:00" + "time": "2016-09-29 22:13:17" }, { - "name": "league/flysystem", - "version": "1.0.32", + "name": "laravel/legacy-encrypter", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab" + "url": "https://github.com/laravel/legacy-encrypter.git", + "reference": "4047fc1e6a9346501ba48ba3f79509534875dea3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/1b5c4a0031697f46e779a9d1b309c2e1b24daeab", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab", + "url": "https://api.github.com/repos/laravel/legacy-encrypter/zipball/4047fc1e6a9346501ba48ba3f79509534875dea3", + "reference": "4047fc1e6a9346501ba48ba3f79509534875dea3", "shasum": "" }, "require": { + "ext-mbstring": "*", + "ext-openssl": "*", + "illuminate/contracts": "5.3.*", + "illuminate/support": "5.3.*", + "paragonie/random_compat": "~1.4|~2.0", "php": ">=5.5.9" }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\LegacyEncrypter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The legacy version of the Laravel mcrypt encrypter.", + "homepage": "http://laravel.com", + "time": "2016-08-03 21:22:03" + }, + { + "name": "league/flysystem", + "version": "1.0.27", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "50e2045ed70a7e75a5e30bc3662904f3b67af8a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/50e2045ed70a7e75a5e30bc3662904f3b67af8a9", + "reference": "50e2045ed70a7e75a5e30bc3662904f3b67af8a9", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, "conflict": { "league/flysystem-sftp": "<1.0.6" }, @@ -1883,7 +1975,7 @@ "sftp", "storage" ], - "time": "2016-10-19T20:38:46+00:00" + "time": "2016-08-10 08:55:11" }, { "name": "maximebf/debugbar", @@ -1944,26 +2036,26 @@ "debug", "debugbar" ], - "time": "2016-09-15T14:01:59+00:00" + "time": "2016-09-15 14:01:59" }, { "name": "mdanter/ecc", - "version": "v0.4.1", + "version": "v0.4.0", "source": { "type": "git", "url": "https://github.com/phpecc/phpecc.git", - "reference": "15b47485bc3f75014d1296526cfc94cbd5175329" + "reference": "d8e34259357260ff74a07fd7550cb43cf0fd8aa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpecc/phpecc/zipball/15b47485bc3f75014d1296526cfc94cbd5175329", - "reference": "15b47485bc3f75014d1296526cfc94cbd5175329", + "url": "https://api.github.com/repos/phpecc/phpecc/zipball/d8e34259357260ff74a07fd7550cb43cf0fd8aa9", + "reference": "d8e34259357260ff74a07fd7550cb43cf0fd8aa9", "shasum": "" }, "require": { "ext-gmp": "*", "fgrosse/phpasn1": "~1.5", - "paragonie/random_compat": "^1|^2", + "paragonie/random_compat": "1.4.1|2.0.2", "php": ">=5.6.0" }, "require-dev": { @@ -2010,20 +2102,20 @@ "phpecc", "secp256k1" ], - "time": "2016-11-22T09:30:44+00:00" + "time": "2016-06-26 18:37:24" }, { "name": "minishlink/web-push", - "version": "v1.3.4", + "version": "v1.1", "source": { "type": "git", "url": "https://github.com/web-push-libs/web-push-php.git", - "reference": "8651c6ea5a8b26db3fb58ad399a33b8ee59046b0" + "reference": "7f56de6a8bed8542d1ef5475fa1136f4ed50e6db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-push-libs/web-push-php/zipball/8651c6ea5a8b26db3fb58ad399a33b8ee59046b0", - "reference": "8651c6ea5a8b26db3fb58ad399a33b8ee59046b0", + "url": "https://api.github.com/repos/web-push-libs/web-push-php/zipball/7f56de6a8bed8542d1ef5475fa1136f4ed50e6db", + "reference": "7f56de6a8bed8542d1ef5475fa1136f4ed50e6db", "shasum": "" }, "require": { @@ -2032,7 +2124,6 @@ "mdanter/ecc": "^0.4.0", "php": ">=5.6", "spomky-labs/base64url": "^1.0", - "spomky-labs/jose": "^6.0", "spomky-labs/php-aes-gcm": "^1.0" }, "require-dev": { @@ -2056,7 +2147,7 @@ } ], "description": "Web Push library for PHP", - "homepage": "https://github.com/web-push-libs/web-push-php", + "homepage": "https://github.com/Minishlink/web-push", "keywords": [ "Push API", "WebPush", @@ -2064,20 +2155,20 @@ "push", "web" ], - "time": "2016-11-30T12:24:44+00:00" + "time": "2016-07-28 22:53:50" }, { "name": "monolog/monolog", - "version": "1.22.0", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", "shasum": "" }, "require": { @@ -2088,7 +2179,7 @@ "psr/log-implementation": "1.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^2.4.9", "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", "jakub-onderka/php-parallel-lint": "0.9", @@ -2142,7 +2233,7 @@ "logging", "psr-3" ], - "time": "2016-11-26T00:15:39+00:00" + "time": "2016-07-29 03:23:52" }, { "name": "mtdowling/cron-expression", @@ -2186,7 +2277,7 @@ "cron", "schedule" ], - "time": "2016-01-26T21:23:30+00:00" + "time": "2016-01-26 21:23:30" }, { "name": "nesbot/carbon", @@ -2233,7 +2324,7 @@ "datetime", "time" ], - "time": "2015-11-04T20:07:17+00:00" + "time": "2015-11-04 20:07:17" }, { "name": "nikic/php-parser", @@ -2284,20 +2375,20 @@ "parser", "php" ], - "time": "2016-09-16T12:04:44+00:00" + "time": "2016-09-16 12:04:44" }, { "name": "paragonie/random_compat", - "version": "v2.0.4", + "version": "v2.0.2", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf", "shasum": "" }, "require": { @@ -2332,7 +2423,7 @@ "pseudorandom", "random" ], - "time": "2016-11-07T23:38:38+00:00" + "time": "2016-04-03 06:00:07" }, { "name": "pda/pheanstalk", @@ -2382,7 +2473,7 @@ "keywords": [ "beanstalkd" ], - "time": "2015-08-07T21:42:41+00:00" + "time": "2015-08-07 21:42:41" }, { "name": "predis/predis", @@ -2432,53 +2523,7 @@ "predis", "redis" ], - "time": "2016-06-16T16:22:20+00:00" - }, - { - "name": "psr/cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "time": "2016-08-06T20:24:11+00:00" + "time": "2016-06-16 16:22:20" }, { "name": "psr/http-message", @@ -2528,20 +2573,20 @@ "request", "response" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-08-06 14:39:51" }, { "name": "psr/log", - "version": "1.0.2", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "reference": "5277094ed527a1c4477177d102fe4c53551953e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/log/zipball/5277094ed527a1c4477177d102fe4c53551953e0", + "reference": "5277094ed527a1c4477177d102fe4c53551953e0", "shasum": "" }, "require": { @@ -2575,7 +2620,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2016-09-19 16:02:08" }, { "name": "psy/psysh", @@ -2647,20 +2692,20 @@ "interactive", "shell" ], - "time": "2016-03-09T05:03:14+00:00" + "time": "2016-03-09 05:03:14" }, { "name": "ramsey/uuid", - "version": "3.5.2", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954" + "reference": "a6d15c8618ea3951fd54d34e326b68d3d0bc0786" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/5677cfe02397dd6b58c861870dfaa5d9007d3954", - "reference": "5677cfe02397dd6b58c861870dfaa5d9007d3954", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/a6d15c8618ea3951fd54d34e326b68d3d0bc0786", + "reference": "a6d15c8618ea3951fd54d34e326b68d3d0bc0786", "shasum": "" }, "require": { @@ -2673,13 +2718,11 @@ "require-dev": { "apigen/apigen": "^4.1", "codeception/aspect-mock": "1.0.0", - "doctrine/annotations": "~1.2.0", "goaop/framework": "1.0.0-alpha.2", "ircmaxell/random-lib": "^1.1", "jakub-onderka/php-parallel-lint": "^0.9.0", "mockery/mockery": "^0.9.4", "moontoast/math": "^1.1", - "php-mock/php-mock-phpunit": "^0.3|^1.1", "phpunit/phpunit": "^4.7|>=5.0 <5.4", "satooshi/php-coveralls": "^0.6.1", "squizlabs/php_codesniffer": "^2.3" @@ -2729,7 +2772,7 @@ "identifier", "uuid" ], - "time": "2016-11-22T19:21:44+00:00" + "time": "2016-08-02 18:39:32" }, { "name": "react/promise", @@ -2773,67 +2816,7 @@ } ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2016-05-03T17:50:52+00:00" - }, - { - "name": "spomky-labs/aes-key-wrap", - "version": "v3.0.2", - "source": { - "type": "git", - "url": "https://github.com/Spomky-Labs/aes-key-wrap.git", - "reference": "416bb3b26609332fe9be8d425de3a293f5386683" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/aes-key-wrap/zipball/416bb3b26609332fe9be8d425de3a293f5386683", - "reference": "416bb3b26609332fe9be8d425de3a293f5386683", - "shasum": "" - }, - "require": { - "beberlei/assert": "^2.0", - "lib-openssl": "*", - "php": ">=5.4", - "symfony/polyfill-mbstring": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.5|^5.0", - "satooshi/php-coveralls": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "AESKW\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky-Labs/aes-key-wrap/contributors" - } - ], - "description": "AES Kew Wrap for PHP.", - "homepage": "https://github.com/Spomky-Labs/aes-key-wrap", - "keywords": [ - "A128KW", - "A192KW", - "A256KW", - "RFC3394", - "RFC5649", - "aes", - "key", - "padding", - "wrap" - ], - "time": "2016-03-31T11:02:58+00:00" + "time": "2016-05-03 17:50:52" }, { "name": "spomky-labs/base64url", @@ -2885,106 +2868,24 @@ "safe", "url" ], - "time": "2016-01-21T19:50:30+00:00" - }, - { - "name": "spomky-labs/jose", - "version": "v6.1.2", - "source": { - "type": "git", - "url": "https://github.com/Spomky-Labs/jose.git", - "reference": "b3a41752e930196694c352a600708d4a83db32fc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/jose/zipball/b3a41752e930196694c352a600708d4a83db32fc", - "reference": "b3a41752e930196694c352a600708d4a83db32fc", - "shasum": "" - }, - "require": { - "beberlei/assert": "^2.4", - "fgrosse/phpasn1": "^1.5", - "lib-openssl": "*", - "mdanter/ecc": "0.4.*", - "php": "^5.6|^7.0", - "psr/cache": "^1.0", - "spomky-labs/aes-key-wrap": "^3.0", - "spomky-labs/base64url": "^1.0", - "spomky-labs/php-aes-gcm": "^1.2", - "symfony/polyfill-mbstring": "^1.1", - "symfony/polyfill-php70": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.0", - "satooshi/php-coveralls": "^1.0", - "symfony/cache": "^2.0|^3.0" - }, - "suggest": { - "ext-crypto": "Highly recommended when you use AES GCM based algorithms.", - "ext-curve25519": "For EdDSA with X25519 curves support.", - "ext-ed25519": "For EdDSA with Ed25519 curves support." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jose\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky" - }, - { - "name": "All contributors", - "homepage": "https://github.com/Spomky-Labs/jose/contributors" - } - ], - "description": "JSON Object Signing and Encryption library for PHP.", - "homepage": "https://github.com/Spomky-Labs/Jose", - "keywords": [ - "JOSE", - "JWE", - "JWK", - "JWKSet", - "JWS", - "Jot", - "RFC7515", - "RFC7516", - "RFC7517", - "RFC7518", - "RFC7519", - "RFC7520", - "jwa", - "jwt" - ], - "time": "2016-11-22T22:31:17+00:00" + "time": "2016-01-21 19:50:30" }, { "name": "spomky-labs/php-aes-gcm", - "version": "v1.2.0", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/php-aes-gcm.git", - "reference": "b655bef0d4f0fa2f36c11c5122e284951e81961c" + "reference": "7d415c6eeb5133804a38451d6ed93b5af76872ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/php-aes-gcm/zipball/b655bef0d4f0fa2f36c11c5122e284951e81961c", - "reference": "b655bef0d4f0fa2f36c11c5122e284951e81961c", + "url": "https://api.github.com/repos/Spomky-Labs/php-aes-gcm/zipball/7d415c6eeb5133804a38451d6ed93b5af76872ce", + "reference": "7d415c6eeb5133804a38451d6ed93b5af76872ce", "shasum": "" }, "require": { - "beberlei/assert": "^2.4", + "beberlei/assert": "^2.0", "lib-openssl": "*", "php": ">=5.4", "symfony/polyfill-mbstring": "^1.1" @@ -2993,13 +2894,10 @@ "phpunit/phpunit": "^4.5|^5.0", "satooshi/php-coveralls": "^1.0" }, - "suggest": { - "ext-crypto": "Highly recommended for better performance." - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -3024,25 +2922,23 @@ "description": "AES GCM (Galois Counter Mode) PHP implementation.", "homepage": "https://github.com/Spomky-Labs/php-aes-gcm", "keywords": [ - "AES-GCM", "Galois Counter Mode", - "aes", "gcm" ], - "time": "2016-11-22T21:11:11+00:00" + "time": "2016-04-28 13:58:02" }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.4", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "545ce9136690cea74f98f86fbb9c92dd9ab1a756" + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/545ce9136690cea74f98f86fbb9c92dd9ab1a756", - "reference": "545ce9136690cea74f98f86fbb9c92dd9ab1a756", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", "shasum": "" }, "require": { @@ -3082,20 +2978,20 @@ "mail", "mailer" ], - "time": "2016-11-24T01:01:23+00:00" + "time": "2016-07-08 11:51:25" }, { "name": "symfony/class-loader", - "version": "v3.2.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63" + "reference": "2d0ba77c46ecc96a6641009a98f72632216811ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/87cd4e69435d98de01d0162c5f9c0ac017075c63", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/2d0ba77c46ecc96a6641009a98f72632216811ba", + "reference": "2d0ba77c46ecc96a6641009a98f72632216811ba", "shasum": "" }, "require": { @@ -3111,7 +3007,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -3138,25 +3034,24 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2016-11-29T08:26:13+00:00" + "time": "2016-08-23 13:39:15" }, { "name": "symfony/console", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5be36e1f3ac7ecbe7e34fb641480ad8497b83aa6" + "reference": "8ea494c34f0f772c3954b5fbe00bffc5a435e563" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5be36e1f3ac7ecbe7e34fb641480ad8497b83aa6", - "reference": "5be36e1f3ac7ecbe7e34fb641480ad8497b83aa6", + "url": "https://api.github.com/repos/symfony/console/zipball/8ea494c34f0f772c3954b5fbe00bffc5a435e563", + "reference": "8ea494c34f0f772c3954b5fbe00bffc5a435e563", "shasum": "" }, "require": { "php": ">=5.5.9", - "symfony/debug": "~2.8|~3.0", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { @@ -3199,20 +3094,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-11-16T22:17:09+00:00" + "time": "2016-08-19 06:48:39" }, { "name": "symfony/debug", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "c058661c32f5b462722e36d120905940089cbd9a" + "reference": "34f6ac18c2974ca5fce68adf419ee7d15def6f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c058661c32f5b462722e36d120905940089cbd9a", - "reference": "c058661c32f5b462722e36d120905940089cbd9a", + "url": "https://api.github.com/repos/symfony/debug/zipball/34f6ac18c2974ca5fce68adf419ee7d15def6f11", + "reference": "34f6ac18c2974ca5fce68adf419ee7d15def6f11", "shasum": "" }, "require": { @@ -3256,20 +3151,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-11-15T12:55:20+00:00" + "time": "2016-08-23 13:39:15" }, { "name": "symfony/event-dispatcher", - "version": "v3.2.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283" + "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e8f47a327c2f0fd5aa04fa60af2b693006ed7283", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c0c00c80b3a69132c4e55c3e7db32b4a387615e5", + "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5", "shasum": "" }, "require": { @@ -3289,7 +3184,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -3316,20 +3211,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-10-13T06:29:04+00:00" + "time": "2016-07-19 10:45:57" }, { "name": "symfony/finder", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9925935bf7144f9e4d2b976905881b4face036fb" + "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9925935bf7144f9e4d2b976905881b4face036fb", - "reference": "9925935bf7144f9e4d2b976905881b4face036fb", + "url": "https://api.github.com/repos/symfony/finder/zipball/e568ef1784f447a0e54dcb6f6de30b9747b0f577", + "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577", "shasum": "" }, "require": { @@ -3365,20 +3260,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-11-03T08:04:31+00:00" + "time": "2016-08-26 12:04:02" }, { "name": "symfony/http-foundation", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "5a4c8099a1547fe451256e056180ad4624177017" + "reference": "63592e00fd90632b57ee50220a1ddb29b6bf3bb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5a4c8099a1547fe451256e056180ad4624177017", - "reference": "5a4c8099a1547fe451256e056180ad4624177017", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/63592e00fd90632b57ee50220a1ddb29b6bf3bb4", + "reference": "63592e00fd90632b57ee50220a1ddb29b6bf3bb4", "shasum": "" }, "require": { @@ -3418,20 +3313,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2016-11-16T22:17:09+00:00" + "time": "2016-08-22 12:11:19" }, { "name": "symfony/http-kernel", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "674ac403c7b3742c2a988a86e3baf9aca2c696a0" + "reference": "aeda215d6b01f119508c090d2a09ebb5b0bc61f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/674ac403c7b3742c2a988a86e3baf9aca2c696a0", - "reference": "674ac403c7b3742c2a988a86e3baf9aca2c696a0", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aeda215d6b01f119508c090d2a09ebb5b0bc61f3", + "reference": "aeda215d6b01f119508c090d2a09ebb5b0bc61f3", "shasum": "" }, "require": { @@ -3439,7 +3334,7 @@ "psr/log": "~1.0", "symfony/debug": "~2.8|~3.0", "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/http-foundation": "~2.8.13|~3.1.6|~3.2" + "symfony/http-foundation": "~2.8.8|~3.0.8|~3.1.2|~3.2" }, "conflict": { "symfony/config": "<2.8" @@ -3500,20 +3395,20 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2016-11-21T02:44:20+00:00" + "time": "2016-09-03 15:28:24" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + "reference": "dff51f72b0706335131b00a7f49606168c582594" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", "shasum": "" }, "require": { @@ -3525,7 +3420,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -3559,20 +3454,20 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-05-18 14:26:46" }, { "name": "symfony/polyfill-php56", - "version": "v1.3.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c" + "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/1dd42b9b89556f18092f3d1ada22cb05ac85383c", - "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/3edf57a8fbf9a927533344cef65ad7e1cf31030a", + "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a", "shasum": "" }, "require": { @@ -3582,7 +3477,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -3615,79 +3510,20 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" - }, - { - "name": "symfony/polyfill-php70", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", - "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "~1.0|~2.0", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-05-18 14:26:46" }, { "name": "symfony/polyfill-util", - "version": "v1.3.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-util.git", - "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb" + "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/746bce0fca664ac0a575e465f65c6643faddf7fb", - "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/ef830ce3d218e622b221d6bfad42c751d974bf99", + "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99", "shasum": "" }, "require": { @@ -3696,7 +3532,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -3726,20 +3562,20 @@ "polyfill", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-05-18 14:26:46" }, { "name": "symfony/process", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "66de154ae86b1a07001da9fbffd620206e4faf94" + "reference": "e64e93041c80e77197ace5ab9385dedb5a143697" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/66de154ae86b1a07001da9fbffd620206e4faf94", - "reference": "66de154ae86b1a07001da9fbffd620206e4faf94", + "url": "https://api.github.com/repos/symfony/process/zipball/e64e93041c80e77197ace5ab9385dedb5a143697", + "reference": "e64e93041c80e77197ace5ab9385dedb5a143697", "shasum": "" }, "require": { @@ -3775,11 +3611,11 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-09-29T14:13:09+00:00" + "time": "2016-08-16 14:58:24" }, { "name": "symfony/routing", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", @@ -3850,20 +3686,20 @@ "uri", "url" ], - "time": "2016-08-16T14:58:24+00:00" + "time": "2016-08-16 14:58:24" }, { "name": "symfony/translation", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "2f4b6114b75c506dd1ee7eb485b35facbcb2d873" + "reference": "a35edc277513c9bc0f063ca174c36b346f974528" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/2f4b6114b75c506dd1ee7eb485b35facbcb2d873", - "reference": "2f4b6114b75c506dd1ee7eb485b35facbcb2d873", + "url": "https://api.github.com/repos/symfony/translation/zipball/a35edc277513c9bc0f063ca174c36b346f974528", + "reference": "a35edc277513c9bc0f063ca174c36b346f974528", "shasum": "" }, "require": { @@ -3914,20 +3750,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2016-11-18T21:15:08+00:00" + "time": "2016-08-05 08:37:39" }, { "name": "symfony/var-dumper", - "version": "v3.1.7", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "0c2d613e890e33f4da810159ac97931068f5bd17" + "reference": "62ee73706c421654a4c840028954510277f7dfc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0c2d613e890e33f4da810159ac97931068f5bd17", - "reference": "0c2d613e890e33f4da810159ac97931068f5bd17", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/62ee73706c421654a4c840028954510277f7dfc8", + "reference": "62ee73706c421654a4c840028954510277f7dfc8", "shasum": "" }, "require": { @@ -3977,7 +3813,7 @@ "debug", "dump" ], - "time": "2016-11-03T08:04:31+00:00" + "time": "2016-08-31 09:05:42" }, { "name": "venturecraft/revisionable", @@ -4025,7 +3861,7 @@ "model", "revision" ], - "time": "2016-08-02T02:32:00+00:00" + "time": "2016-08-02 02:32:00" }, { "name": "vlucas/phpdotenv", @@ -4075,7 +3911,54 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2016-09-01 10:05:43" + }, + { + "name": "webpatser/laravel-uuid", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/webpatser/laravel-uuid.git", + "reference": "6ed2705775e3edf066b90e1292f76f157ec00507" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webpatser/laravel-uuid/zipball/6ed2705775e3edf066b90e1292f76f157ec00507", + "reference": "6ed2705775e3edf066b90e1292f76f157ec00507", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "fzaninotto/faker": "1.5.*", + "phpunit/phpunit": "4.7.*" + }, + "suggest": { + "paragonie/random_compat": "A random_bytes Php 5.x polyfill." + }, + "type": "library", + "autoload": { + "psr-0": { + "Webpatser\\Uuid": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Kempen", + "email": "christoph@downsized.nl" + } + ], + "description": "Class to generate a UUID according to the RFC 4122 standard. Support for version 1, 3, 4 and 5 UUID are built-in.", + "homepage": "https://github.com/webpatser/uuid", + "keywords": [ + "UUID RFC4122" + ], + "time": "2016-05-09 09:22:18" } ], "packages-dev": [ @@ -4131,29 +4014,29 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2015-06-14 21:17:01" }, { "name": "filp/whoops", - "version": "2.1.4", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "3f5cbd0e6a2fc17bc40b0238025ef1ff4c8c3efa" + "reference": "8828aaa2178e0a19325522e2a45282ff0a14649b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/3f5cbd0e6a2fc17bc40b0238025ef1ff4c8c3efa", - "reference": "3f5cbd0e6a2fc17bc40b0238025ef1ff4c8c3efa", + "url": "https://api.github.com/repos/filp/whoops/zipball/8828aaa2178e0a19325522e2a45282ff0a14649b", + "reference": "8828aaa2178e0a19325522e2a45282ff0a14649b", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0" + "php": ">=5.5.9" }, "require-dev": { "mockery/mockery": "0.9.*", "phpunit/phpunit": "^4.8 || ^5.0", - "symfony/var-dumper": "^2.6 || ^3.0" + "symfony/var-dumper": "~3.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -4182,7 +4065,7 @@ } ], "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", + "homepage": "https://github.com/filp/whoops", "keywords": [ "error", "exception", @@ -4191,7 +4074,7 @@ "whoops", "zf2" ], - "time": "2016-10-11T15:32:23+00:00" + "time": "2016-05-06 18:25:35" }, { "name": "fzaninotto/faker", @@ -4239,7 +4122,7 @@ "faker", "fixtures" ], - "time": "2016-04-29T12:21:54+00:00" + "time": "2016-04-29 12:21:54" }, { "name": "hamcrest/hamcrest-php", @@ -4284,20 +4167,20 @@ "keywords": [ "test" ], - "time": "2015-05-11T14:41:42+00:00" + "time": "2015-05-11 14:41:42" }, { "name": "mockery/mockery", - "version": "0.9.6", + "version": "0.9.5", "source": { "type": "git", "url": "https://github.com/padraic/mockery.git", - "reference": "65d4ca18e15cb02eeb1e5336f884e46b9b905be0" + "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/65d4ca18e15cb02eeb1e5336f884e46b9b905be0", - "reference": "65d4ca18e15cb02eeb1e5336f884e46b9b905be0", + "url": "https://api.github.com/repos/padraic/mockery/zipball/4db079511a283e5aba1b3c2fb19037c645e70fc2", + "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2", "shasum": "" }, "require": { @@ -4349,20 +4232,20 @@ "test double", "testing" ], - "time": "2016-09-30T12:09:40+00:00" + "time": "2016-05-22 21:52:33" }, { "name": "myclabs/deep-copy", - "version": "1.5.5", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108" + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f", "shasum": "" }, "require": { @@ -4391,7 +4274,7 @@ "object", "object graph" ], - "time": "2016-10-31T17:19:45+00:00" + "time": "2016-09-16 13:37:59" }, { "name": "phpdocumentor/reflection-common", @@ -4445,20 +4328,20 @@ "reflection", "static analysis" ], - "time": "2015-12-27T11:43:31+00:00" + "time": "2015-12-27 11:43:31" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", + "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", "shasum": "" }, "require": { @@ -4490,20 +4373,20 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30T07:12:33+00:00" + "time": "2016-06-10 09:48:41" }, { "name": "phpdocumentor/type-resolver", - "version": "0.2.1", + "version": "0.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", + "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", "shasum": "" }, "require": { @@ -4537,7 +4420,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2016-11-25T06:54:22+00:00" + "time": "2016-06-10 07:14:17" }, { "name": "phpspec/php-diff", @@ -4571,20 +4454,20 @@ } ], "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", - "time": "2013-11-01T13:02:21+00:00" + "time": "2013-11-01 13:02:21" }, { "name": "phpspec/phpspec", - "version": "2.5.5", + "version": "2.5.3", "source": { "type": "git", "url": "https://github.com/phpspec/phpspec.git", - "reference": "db395f435eb8e820448e8690de1a8db86d5dd8af" + "reference": "153ebd99d9b842e0c99a024c41f970d79db46475" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/phpspec/zipball/db395f435eb8e820448e8690de1a8db86d5dd8af", - "reference": "db395f435eb8e820448e8690de1a8db86d5dd8af", + "url": "https://api.github.com/repos/phpspec/phpspec/zipball/153ebd99d9b842e0c99a024c41f970d79db46475", + "reference": "153ebd99d9b842e0c99a024c41f970d79db46475", "shasum": "" }, "require": { @@ -4649,20 +4532,20 @@ "testing", "tests" ], - "time": "2016-12-04T21:03:31+00:00" + "time": "2016-09-26 20:28:11" }, { "name": "phpspec/prophecy", - "version": "v1.6.2", + "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", "shasum": "" }, "require": { @@ -4670,11 +4553,10 @@ "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/recursion-context": "^1.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpspec/phpspec": "^2.0" }, "type": "library", "extra": { @@ -4712,20 +4594,20 @@ "spy", "stub" ], - "time": "2016-11-21T14:58:47+00:00" + "time": "2016-06-07 08:13:47" }, { "name": "phpunit/php-code-coverage", - "version": "4.0.3", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929" + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/903fd6318d0a90b4770a009ff73e4a4e9c437929", - "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3", + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3", "shasum": "" }, "require": { @@ -4775,20 +4657,20 @@ "testing", "xunit" ], - "time": "2016-11-28T16:00:31+00:00" + "time": "2016-07-26 14:39:29" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", "shasum": "" }, "require": { @@ -4822,7 +4704,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2015-06-21 13:08:43" }, { "name": "phpunit/php-text-template", @@ -4863,7 +4745,7 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", @@ -4907,20 +4789,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2016-05-12 18:03:57" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", "shasum": "" }, "require": { @@ -4956,20 +4838,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2015-09-15 10:49:45" }, { "name": "phpunit/phpunit", - "version": "5.6.4", + "version": "5.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4ddb822f1de421b4cadb47570a525fd7d9359493" + "reference": "a57126dc681b08289fef6ac96a48e30656f84350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ddb822f1de421b4cadb47570a525fd7d9359493", - "reference": "4ddb822f1de421b4cadb47570a525fd7d9359493", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a57126dc681b08289fef6ac96a48e30656f84350", + "reference": "a57126dc681b08289fef6ac96a48e30656f84350", "shasum": "" }, "require": { @@ -4997,14 +4879,13 @@ "symfony/yaml": "~2.1|~3.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "sebastian/object-enumerator": "1.0.1", - "sebastian/recursion-context": "1.0.3 || 1.0.4" + "phpdocumentor/reflection-docblock": "3.0.2" }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-tidy": "*", "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, @@ -5014,7 +4895,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.6.x-dev" + "dev-master": "5.5.x-dev" } }, "autoload": { @@ -5040,27 +4921,27 @@ "testing", "xunit" ], - "time": "2016-11-18T09:50:51+00:00" + "time": "2016-09-21 14:40:13" }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.4.3", + "version": "3.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" + "reference": "546898a2c0c356ef2891b39dd7d07f5d82c8ed0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/546898a2c0c356ef2891b39dd7d07f5d82c8ed0a", + "reference": "546898a2c0c356ef2891b39dd7d07f5d82c8ed0a", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.6 || ^7.0", "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" + "sebastian/exporter": "^1.2" }, "conflict": { "phpunit/phpunit": "<5.4.0" @@ -5099,7 +4980,7 @@ "mock", "xunit" ], - "time": "2016-12-08T20:27:08+00:00" + "time": "2016-09-06 16:07:45" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -5144,26 +5025,26 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13T06:45:14+00:00" + "time": "2016-02-13 06:45:14" }, { "name": "sebastian/comparator", - "version": "1.2.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", + "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", "shasum": "" }, "require": { "php": ">=5.3.3", "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "sebastian/exporter": "~1.2" }, "require-dev": { "phpunit/phpunit": "~4.4" @@ -5208,7 +5089,7 @@ "compare", "equality" ], - "time": "2016-11-19T09:18:40+00:00" + "time": "2015-07-26 15:48:44" }, { "name": "sebastian/diff", @@ -5260,32 +5141,32 @@ "keywords": [ "diff" ], - "time": "2015-12-08T07:14:41+00:00" + "time": "2015-12-08 07:14:41" }, { "name": "sebastian/environment", - "version": "2.0.0", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^4.8 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -5310,7 +5191,7 @@ "environment", "hhvm" ], - "time": "2016-11-26T07:53:53+00:00" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", @@ -5377,7 +5258,7 @@ "export", "exporter" ], - "time": "2016-06-17T09:04:28+00:00" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -5428,7 +5309,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/object-enumerator", @@ -5474,7 +5355,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-01-28T13:25:10+00:00" + "time": "2016-01-28 13:25:10" }, { "name": "sebastian/recursion-context", @@ -5527,7 +5408,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-11-11T19:50:13+00:00" + "time": "2015-11-11 19:50:13" }, { "name": "sebastian/resource-operations", @@ -5569,20 +5450,20 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "time": "2015-07-28 20:34:47" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", "shasum": "" }, "require": { @@ -5612,20 +5493,20 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "time": "2016-02-04 12:56:52" }, { "name": "symfony/css-selector", - "version": "v3.2.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "e1241f275814827c411d922ba8e64cf2a00b2994" + "reference": "2851e1932d77ce727776154d659b232d061e816a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/e1241f275814827c411d922ba8e64cf2a00b2994", - "reference": "e1241f275814827c411d922ba8e64cf2a00b2994", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2851e1932d77ce727776154d659b232d061e816a", + "reference": "2851e1932d77ce727776154d659b232d061e816a", "shasum": "" }, "require": { @@ -5634,7 +5515,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -5665,20 +5546,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2016-11-03T08:11:03+00:00" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/dom-crawler", - "version": "v3.2.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "c6b6111f5aae7c58698cdc10220785627ac44a2c" + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c6b6111f5aae7c58698cdc10220785627ac44a2c", - "reference": "c6b6111f5aae7c58698cdc10220785627ac44a2c", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/bb7395e8b1db3654de82b9f35d019958276de4d7", + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7", "shasum": "" }, "require": { @@ -5694,7 +5575,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -5721,35 +5602,29 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-11-25T12:32:42+00:00" + "time": "2016-08-05 08:37:39" }, { "name": "symfony/yaml", - "version": "v3.2.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f2300ba8fbb002c028710b92e1906e7457410693" + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f2300ba8fbb002c028710b92e1906e7457410693", - "reference": "f2300ba8fbb002c028710b92e1906e7457410693", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f291ed25eb1435bddbe8a96caaef16469c2a092d", + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d", "shasum": "" }, "require": { "php": ">=5.5.9" }, - "require-dev": { - "symfony/console": "~2.8|~3.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -5776,24 +5651,24 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-11-18T21:17:59+00:00" + "time": "2016-09-02 02:12:52" }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bb2d123231c095735130cc8f6d31385a44c7b308", + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3|^7.0" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -5802,7 +5677,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -5826,7 +5701,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2016-08-09 15:02:57" } ], "aliases": [], diff --git a/config/mail.php b/config/mail.php index 94a1e2f0..c8283923 100644 --- a/config/mail.php +++ b/config/mail.php @@ -54,7 +54,7 @@ return [ | */ - 'from' => ['address' => null, 'name' => null], + 'from' => ['address' => 'hello@pony.fm', 'name' => 'Pony.fm'], /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2016_11_15_014829_create_email_notification_tables.php b/database/migrations/2016_11_15_014829_create_email_notification_tables.php new file mode 100644 index 00000000..0cd6c630 --- /dev/null +++ b/database/migrations/2016_11_15_014829_create_email_notification_tables.php @@ -0,0 +1,107 @@ +. + */ + +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +class CreateEmailNotificationTables extends Migration +{ + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + DB::transaction(function(){ + // This table is used to enforce referential data integrity + // for the polymorphic "activity" table. + Schema::create('activity_types', function (Blueprint $table) { + $table->unsignedTinyInteger('activity_type')->primary(); + $table->string('description'); + }); + + DB::table('activity_types')->insert([ + ['activity_type' => 1, 'description' => 'news'], + ['activity_type' => 2, 'description' => 'followee published a track'], + ['activity_type' => 3, 'description' => 'followee published an album'], + ['activity_type' => 4, 'description' => 'followee published a playlist'], + ['activity_type' => 5, 'description' => 'new follower'], + ['activity_type' => 6, 'description' => 'new comment'], + ['activity_type' => 7, 'description' => 'new favourite'], + ]); + + Schema::table('activities', function (Blueprint $table) { + $table->foreign('activity_type')->references('activity_type')->on('activity_types'); + }); + + + Schema::create('email_subscriptions', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('activity_type'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('activity_type')->references('activity_type')->on('activity_types'); + }); + + Schema::create('emails', function (Blueprint $table) { + $table->uuid('id')->primary(); + // Clicking the email link should mark the corresponding on-site notification as read. + $table->unsignedBigInteger('notification_id')->index(); + $table->timestamps(); + + $table->foreign('notification_id')->references('id')->on('notifications'); + }); + + Schema::create('email_clicks', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->uuid('email_id')->index(); + $table->ipAddress('ip_address'); + $table->timestamps(); + + $table->foreign('email_id')->references('id')->on('emails'); + }); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::transaction(function() { + Schema::drop('email_clicks'); + Schema::drop('emails'); + Schema::drop('email_subscriptions'); + + Schema::table('activities', function (Blueprint $table) { + $table->dropForeign('activities_activity_type_foreign'); + }); + + Schema::drop('activity_types'); + }); + } +} diff --git a/documentation/notifications.md b/documentation/notifications.md index 55000cdb..9c988634 100644 --- a/documentation/notifications.md +++ b/documentation/notifications.md @@ -17,12 +17,12 @@ The `Notification` facade is used to send notifications as follows: ```php use Notification; -// Something happens, like a newtrack getting published. +// Something happens, like a new track getting published. $track = new Track(); ... // The "something" is done happening! Time to send a notification. -Notification::publishedTrack($track); +Notification::publishedNewTrack($track); ``` This facade has a method for every notification type, drawn from the @@ -45,10 +45,18 @@ Adding new notification types - [`RecipientFinder`](../app/Library/Notifications/RecipientFinder.php) - [`PonyfmDriver`](../app/Library/Notifications/PonyfmDriver.php) -3. Call the new method on the `Notification` facade from wherever the +3. Create a migration to add the new notification type to the `activity_types` + table. Add a constant for it to the [`Activity`](../app/Models/Activity.php) + class. + +3. Ensure you create HTML and plaintext templates, as well as a subclass of + [`BaseNotification`](../app/Mail/BaseNotification.php) for the email version + of the notification. + +4. Call the new method on the `Notification` facade from wherever the new notification gets triggered. -4. Implement any necessary logic for the new notification type in the +5. Implement any necessary logic for the new notification type in the [`Activity`](../app/Models/Activity.php) model. @@ -92,3 +100,25 @@ There's one exception to the use of `NotificationHandler` - the data we store about an activity in the database to a notification's API representation had to go somewhere, and using the `NotificationHandler` interface here would have made this logic a lot more obtuse. + +### Data flow + +1. Some action that triggers a notification calls the `NotificationManager` + facade. + +2. An asynchronous job is kicked off that figures out how to send the + notification. + +3. An `Activity` record is created for the action. + +4. A `Notification` record is created for every user who is to receive a + notification about that activity. These records act as Pony.fm's on-site + notifications and cannot be disabled. + +5. Depending on subscription preferences, push and email notifications will be + sent out as well, each creating their own respective database records. These + are linked to a `Notification` record for unified read/unread tracking. + +6. A `Notification` record is marked read when it is viewed on-site or any other + notification type associated with it (like an email or push notification) is + clicked. diff --git a/resources/views/emails/notifications/_layout.blade.php b/resources/views/emails/notifications/_layout.blade.php new file mode 100644 index 00000000..132a1131 --- /dev/null +++ b/resources/views/emails/notifications/_layout.blade.php @@ -0,0 +1,13 @@ +@yield('content') + +
Unsubscribe from this kind of email
+ + +Poniverse+ {{ $creatorName }} favourited your {{ $resourceType }}, + {{ $resourceTitle }} + Yay! +
+@endsection diff --git a/resources/views/emails/notifications/content-favourited_plaintext.blade.php b/resources/views/emails/notifications/content-favourited_plaintext.blade.php new file mode 100644 index 00000000..d14dc51d --- /dev/null +++ b/resources/views/emails/notifications/content-favourited_plaintext.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +{{ $creatorName }} favourited your {{ $resourceType }}, "{{ $resourceTitle }}". Yay! + +Here's a link to the {{ $resourceType }}: +{{ $notificationUrl }} +@endsection diff --git a/resources/views/emails/notifications/new-comment-content.blade.php b/resources/views/emails/notifications/new-comment-content.blade.php new file mode 100644 index 00000000..796d8321 --- /dev/null +++ b/resources/views/emails/notifications/new-comment-content.blade.php @@ -0,0 +1,9 @@ +@extends('emails.notifications._layout') + +@section('content') ++ {{ $creatorName }} left a comment on your {{ $resourceType }}, + {{ $resourceTitle }}! + Visit it to read the comment and reply. +
+@endsection diff --git a/resources/views/emails/notifications/new-comment-content_plaintext.blade.php b/resources/views/emails/notifications/new-comment-content_plaintext.blade.php new file mode 100644 index 00000000..3ece5499 --- /dev/null +++ b/resources/views/emails/notifications/new-comment-content_plaintext.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +{{ $creatorName }} left a comment on your {{ $resourceType }}, "{{ $resourceTitle }}"! + +Visit the following link to read the comment and reply: +{{ $notificationUrl }} +@endsection diff --git a/resources/views/emails/notifications/new-comment-profile.blade.php b/resources/views/emails/notifications/new-comment-profile.blade.php new file mode 100644 index 00000000..4dcf3609 --- /dev/null +++ b/resources/views/emails/notifications/new-comment-profile.blade.php @@ -0,0 +1,9 @@ +@extends('emails.notifications._layout') + +@section('content') ++ {{ $creatorName }} left a comment on your Pony.fm profile! + Visit your profile to + read it and reply. +
+@endsection diff --git a/resources/views/emails/notifications/new-comment-profile_plaintext.blade.php b/resources/views/emails/notifications/new-comment-profile_plaintext.blade.php new file mode 100644 index 00000000..8bc4c58a --- /dev/null +++ b/resources/views/emails/notifications/new-comment-profile_plaintext.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +{{ $creatorName }} left a comment on your Pony.fm profile! + +Visit your profile with the following link to read it and reply: +{{ $notificationUrl }} +@endsection diff --git a/resources/views/emails/notifications/new-follower.blade.php b/resources/views/emails/notifications/new-follower.blade.php new file mode 100644 index 00000000..582fd04e --- /dev/null +++ b/resources/views/emails/notifications/new-follower.blade.php @@ -0,0 +1,9 @@ +@extends('emails.notifications._layout') + +@section('content') ++ Congrats! + {{ $creatorName }} + is now following you on Pony.fm! +
+@endsection diff --git a/resources/views/emails/notifications/new-follower_plaintext.blade.php b/resources/views/emails/notifications/new-follower_plaintext.blade.php new file mode 100644 index 00000000..88d02d59 --- /dev/null +++ b/resources/views/emails/notifications/new-follower_plaintext.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +Congrats! {{ $creatorName }} is now following you on Pony.fm! + +Here's a link to their profile: +{{ $notificationUrl }} +@endsection diff --git a/resources/views/emails/notifications/new-playlist.blade.php b/resources/views/emails/notifications/new-playlist.blade.php new file mode 100644 index 00000000..79d857f9 --- /dev/null +++ b/resources/views/emails/notifications/new-playlist.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout') + +@section('content') +{{ $creatorName }} created a new playlist on Pony.fm! Check it out:
+ + + +@endsection diff --git a/resources/views/emails/notifications/new-playlist_plaintext.blade.php b/resources/views/emails/notifications/new-playlist_plaintext.blade.php new file mode 100644 index 00000000..cab8d9d0 --- /dev/null +++ b/resources/views/emails/notifications/new-playlist_plaintext.blade.php @@ -0,0 +1,10 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +{{ $creatorName }} created a new playlist on Pony.fm! + +Title: {{ $playlistTitle }} + +Listen to it: +{{ $notificationUrl }} +@endsection diff --git a/resources/views/emails/notifications/new-track.blade.php b/resources/views/emails/notifications/new-track.blade.php new file mode 100644 index 00000000..f5a7d660 --- /dev/null +++ b/resources/views/emails/notifications/new-track.blade.php @@ -0,0 +1,8 @@ +@extends('emails.notifications._layout') + +@section('content') +{{ $creatorName }} published a new track on Pony.fm! Listen to it now:
+ + + +@endsection diff --git a/resources/views/emails/notifications/new-track_plaintext.blade.php b/resources/views/emails/notifications/new-track_plaintext.blade.php new file mode 100644 index 00000000..89f01e4e --- /dev/null +++ b/resources/views/emails/notifications/new-track_plaintext.blade.php @@ -0,0 +1,10 @@ +@extends('emails.notifications._layout_plaintext') + +@section('content') +{{ $creatorName }} published a new track on Pony.fm! + +Title: {{ $trackTitle }} + +Listen to it: +{{ $notificationUrl }} +@endsection diff --git a/routes/web.php b/routes/web.php index f4dcb7fb..5dd1b672 100644 --- a/routes/web.php +++ b/routes/web.php @@ -77,6 +77,11 @@ Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload'); Route::get('notifications', 'AccountController@getNotifications'); + +Route::get('notifications/email/unsubscribe/{subscriptionKey}', 'NotificationsController@getEmailUnsubscribe')->name('email:unsubscribe'); +Route::get('notifications/email/click/{emailKey}', 'NotificationsController@getEmailClick')->name('email:click'); + + Route::get('oembed', 'TracksController@getOembed'); Route::group(['prefix' => 'api/v1', 'middleware' => 'json-exceptions'], function () {