mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-22 04:58:01 +01:00
Feature/notifications (#87)
* #25: Implemented enough of the notification system to start writing drivers. * #25: Implemented most of the Pony.fm notification driver's backend. * #25: Abstracted the logic for building lists of notification recipients. * #25: Implemented notification API endpoints for the SPA. * Front end setup for notifications * #25: Implemented notification API endpoints for the SPA.
This commit is contained in:
parent
7786950990
commit
0109244115
36 changed files with 1480 additions and 15 deletions
|
@ -25,6 +25,8 @@ For quick fixes, go ahead and submit a pull request!
|
||||||
For larger features, it's best to open an issue before sinking a ton of work
|
For larger features, it's best to open an issue before sinking a ton of work
|
||||||
into building them, to coordinate with Pony.fm's maintainers.
|
into building them, to coordinate with Pony.fm's maintainers.
|
||||||
|
|
||||||
|
Developer documentation is available in the [`documentation` directory](documentation).
|
||||||
|
|
||||||
**Protip:** Looking for a place to jump in and start coding? Try a
|
**Protip:** Looking for a place to jump in and start coding? Try a
|
||||||
[quickwin issue](https://github.com/Poniverse/Pony.fm/labels/quickwin%21) -
|
[quickwin issue](https://github.com/Poniverse/Pony.fm/labels/quickwin%21) -
|
||||||
these are smaller in scope and easier to tackle if you're unfamiliar with the codebase!
|
these are smaller in scope and easier to tackle if you're unfamiliar with the codebase!
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Commands;
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Notification;
|
||||||
use Poniverse\Ponyfm\Models\Album;
|
use Poniverse\Ponyfm\Models\Album;
|
||||||
use Poniverse\Ponyfm\Models\Comment;
|
use Poniverse\Ponyfm\Models\Comment;
|
||||||
use Poniverse\Ponyfm\Models\Playlist;
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
|
@ -115,6 +116,8 @@ class CreateCommentCommand extends CommandBase
|
||||||
|
|
||||||
$entity->comment_count = Comment::where($column, $this->_id)->count();
|
$entity->comment_count = Comment::where($column, $this->_id)->count();
|
||||||
$entity->save();
|
$entity->save();
|
||||||
|
|
||||||
|
Notification::newComment($comment);
|
||||||
|
|
||||||
return CommandResponse::succeed(Comment::mapPublic($comment));
|
return CommandResponse::succeed(Comment::mapPublic($comment));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Commands;
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Notification;
|
||||||
use Poniverse\Ponyfm\Models\Playlist;
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
@ -68,6 +69,8 @@ class CreatePlaylistCommand extends CommandBase
|
||||||
$playlist->is_public = $this->_input['is_public'] == 'true';
|
$playlist->is_public = $this->_input['is_public'] == 'true';
|
||||||
|
|
||||||
$playlist->save();
|
$playlist->save();
|
||||||
|
|
||||||
|
Notification::publishedNewPlaylist($playlist);
|
||||||
|
|
||||||
if ($this->_input['is_pinned'] == 'true') {
|
if ($this->_input['is_pinned'] == 'true') {
|
||||||
$playlist->pin(Auth::user()->id);
|
$playlist->pin(Auth::user()->id);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
namespace Poniverse\Ponyfm\Commands;
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
use Gate;
|
use Gate;
|
||||||
|
use Notification;
|
||||||
use Poniverse\Ponyfm\Models\Album;
|
use Poniverse\Ponyfm\Models\Album;
|
||||||
use Poniverse\Ponyfm\Models\Image;
|
use Poniverse\Ponyfm\Models\Image;
|
||||||
use Poniverse\Ponyfm\Models\Track;
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
@ -32,6 +33,10 @@ use DB;
|
||||||
class EditTrackCommand extends CommandBase
|
class EditTrackCommand extends CommandBase
|
||||||
{
|
{
|
||||||
private $_trackId;
|
private $_trackId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Track
|
||||||
|
*/
|
||||||
private $_track;
|
private $_track;
|
||||||
private $_input;
|
private $_input;
|
||||||
|
|
||||||
|
@ -132,6 +137,8 @@ class EditTrackCommand extends CommandBase
|
||||||
|
|
||||||
DB::table('tracks')->whereUserId($track->user_id)->update(['is_latest' => false]);
|
DB::table('tracks')->whereUserId($track->user_id)->update(['is_latest' => false]);
|
||||||
$track->is_latest = true;
|
$track->is_latest = true;
|
||||||
|
|
||||||
|
Notification::publishedNewTrack($track);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->_input['cover_id'])) {
|
if (isset($this->_input['cover_id'])) {
|
||||||
|
@ -174,12 +181,13 @@ class EditTrackCommand extends CommandBase
|
||||||
return CommandResponse::succeed(['real_cover_url' => $track->getCoverUrl(Image::NORMAL)]);
|
return CommandResponse::succeed(['real_cover_url' => $track->getCoverUrl(Image::NORMAL)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function removeTrackFromAlbum($track)
|
private function removeTrackFromAlbum(Track $track)
|
||||||
{
|
{
|
||||||
$album = $track->album;
|
$album = $track->album;
|
||||||
$index = 0;
|
$index = 0;
|
||||||
|
|
||||||
foreach ($album->tracks as $track) {
|
foreach ($album->tracks as $track) {
|
||||||
|
/** @var $track Track */
|
||||||
if ($track->id == $this->_trackId) {
|
if ($track->id == $this->_trackId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,14 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Commands;
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Notification;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
use Poniverse\Ponyfm\Models\Favourite;
|
use Poniverse\Ponyfm\Models\Favourite;
|
||||||
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
use Poniverse\Ponyfm\Models\ResourceUser;
|
use Poniverse\Ponyfm\Models\ResourceUser;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
|
||||||
class ToggleFavouriteCommand extends CommandBase
|
class ToggleFavouriteCommand extends CommandBase
|
||||||
{
|
{
|
||||||
|
@ -45,6 +49,20 @@ class ToggleFavouriteCommand extends CommandBase
|
||||||
|
|
||||||
return $user != null;
|
return $user != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getEntityBeingFavourited():Favouritable
|
||||||
|
{
|
||||||
|
switch ($this->_resourceType) {
|
||||||
|
case 'track':
|
||||||
|
return Track::find($this->_resourceId);
|
||||||
|
case 'album':
|
||||||
|
return Album::find($this->_resourceId);
|
||||||
|
case 'playlist':
|
||||||
|
return Playlist::find($this->_resourceId);
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException('Unknown resource type given!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
@ -65,6 +83,8 @@ class ToggleFavouriteCommand extends CommandBase
|
||||||
$fav->created_at = time();
|
$fav->created_at = time();
|
||||||
$fav->save();
|
$fav->save();
|
||||||
$isFavourited = true;
|
$isFavourited = true;
|
||||||
|
|
||||||
|
Notification::newFavourite($this->getEntityBeingFavourited(), $fav->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
|
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
|
||||||
|
|
|
@ -22,7 +22,8 @@ namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
use Poniverse\Ponyfm\Models\Follower;
|
use Poniverse\Ponyfm\Models\Follower;
|
||||||
use Poniverse\Ponyfm\Models\ResourceUser;
|
use Poniverse\Ponyfm\Models\ResourceUser;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Auth;
|
||||||
|
use Notification;
|
||||||
|
|
||||||
class ToggleFollowingCommand extends CommandBase
|
class ToggleFollowingCommand extends CommandBase
|
||||||
{
|
{
|
||||||
|
@ -64,6 +65,8 @@ class ToggleFollowingCommand extends CommandBase
|
||||||
$follow->created_at = time();
|
$follow->created_at = time();
|
||||||
$follow->save();
|
$follow->save();
|
||||||
$isFollowed = true;
|
$isFollowed = true;
|
||||||
|
|
||||||
|
Notification::newFollower($follow->artist, Auth::user());
|
||||||
}
|
}
|
||||||
|
|
||||||
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
|
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
|
||||||
|
|
38
app/Contracts/Commentable.php
Normal file
38
app/Contracts/Commentable.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Contracts;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is used for type safety when referring to entities that
|
||||||
|
* are capable of accepting comments.
|
||||||
|
*
|
||||||
|
* @package Poniverse\Ponyfm\Contracts
|
||||||
|
*/
|
||||||
|
interface Commentable extends GeneratesNotifications {
|
||||||
|
/**
|
||||||
|
* This method returns an Eloquent relation to the entity's comments.
|
||||||
|
*
|
||||||
|
* @return HasMany
|
||||||
|
*/
|
||||||
|
public function comments():HasMany;
|
||||||
|
}
|
38
app/Contracts/Favouritable.php
Normal file
38
app/Contracts/Favouritable.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Contracts;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is used for type safety when referring to entities that
|
||||||
|
* are capable of being favourited.
|
||||||
|
*
|
||||||
|
* @package Poniverse\Ponyfm\Contracts
|
||||||
|
*/
|
||||||
|
interface Favouritable extends GeneratesNotifications {
|
||||||
|
/**
|
||||||
|
* This method returns an Eloquent relation to the entity's favourites.
|
||||||
|
*
|
||||||
|
* @return HasMany
|
||||||
|
*/
|
||||||
|
public function favourites():HasMany;
|
||||||
|
}
|
42
app/Contracts/GeneratesNotifications.php
Normal file
42
app/Contracts/GeneratesNotifications.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Contracts;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is used for type safety when referring to entities that can be
|
||||||
|
* the "target resource" of a notification (ie. what the notification is about).
|
||||||
|
*
|
||||||
|
* @package Poniverse\Ponyfm\Contracts
|
||||||
|
*/
|
||||||
|
interface GeneratesNotifications {
|
||||||
|
/**
|
||||||
|
* Returns a human-friendly string (lowercase & singular) representing this
|
||||||
|
* type of resource.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getResourceType():string;
|
||||||
|
|
||||||
|
public function activities():MorphMany;
|
||||||
|
}
|
68
app/Contracts/NotificationHandler.php
Normal file
68
app/Contracts/NotificationHandler.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Contracts;
|
||||||
|
|
||||||
|
use Poniverse\Ponyfm\Models\Comment;
|
||||||
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface NotificationHandler
|
||||||
|
* @package Poniverse\Ponyfm\Contracts
|
||||||
|
*
|
||||||
|
* Each method in this interface represents a type of notification. To add a new
|
||||||
|
* type of notification, add a method for it to this interface and every class
|
||||||
|
* that implements it. Your IDE should be able to help with this.
|
||||||
|
*/
|
||||||
|
interface NotificationHandler {
|
||||||
|
/**
|
||||||
|
* @param Track $track
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function publishedNewTrack(Track $track);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Playlist $playlist
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function publishedNewPlaylist(Playlist $playlist);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $userBeingFollowed
|
||||||
|
* @param User $follower
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function newFollower(User $userBeingFollowed, User $follower);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Comment $comment
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function newComment(Comment $comment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Favouritable $entityBeingFavourited
|
||||||
|
* @param User $favouriter
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function newFavourite(Favouritable $entityBeingFavourited, User $favouriter);
|
||||||
|
}
|
28
app/Facades/Notification.php
Normal file
28
app/Facades/Notification.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Facades;
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
class Notification extends Facade {
|
||||||
|
protected static function getFacadeAccessor() {
|
||||||
|
return 'notification';
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,10 +20,10 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
||||||
use Poniverse\Ponyfm\Commands\SaveAccountSettingsCommand;
|
use Poniverse\Ponyfm\Commands\SaveAccountSettingsCommand;
|
||||||
use Poniverse\Ponyfm\Models\User;
|
use Poniverse\Ponyfm\Models\User;
|
||||||
use Cover;
|
|
||||||
use Gate;
|
use Gate;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Input;
|
use Illuminate\Support\Facades\Input;
|
||||||
|
|
63
app/Http/Controllers/Api/Web/NotificationsController.php
Normal file
63
app/Http/Controllers/Api/Web/NotificationsController.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Input;
|
||||||
|
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
||||||
|
use Poniverse\Ponyfm\Models\Notification;
|
||||||
|
|
||||||
|
class NotificationsController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the logged-in user's last 20 notifications.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getNotifications()
|
||||||
|
{
|
||||||
|
$notifications = Notification::forUser(Auth::user())
|
||||||
|
->take(20)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
|
||||||
|
return ['notifications' => $notifications->toArray()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This action returns the number of notifications that were updated.
|
||||||
|
* Any notifications that were specified that don't belong to the logged-in
|
||||||
|
* user are ignored.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function putMarkAsRead()
|
||||||
|
{
|
||||||
|
$notificationIds = Input::get('notification_ids');
|
||||||
|
$numberOfUpdatedRows = Auth::user()
|
||||||
|
->notifications()
|
||||||
|
->whereIn('id', $notificationIds)
|
||||||
|
->update(['is_read' => true]);
|
||||||
|
|
||||||
|
return ['notifications_updated' => $numberOfUpdatedRows];
|
||||||
|
}
|
||||||
|
}
|
|
@ -134,6 +134,8 @@ Route::group(['prefix' => 'api/web'], function() {
|
||||||
|
|
||||||
Route::group(['middleware' => 'auth'], function() {
|
Route::group(['middleware' => 'auth'], function() {
|
||||||
Route::get('/account/settings/{slug}', 'Api\Web\AccountController@getSettings');
|
Route::get('/account/settings/{slug}', 'Api\Web\AccountController@getSettings');
|
||||||
|
Route::get('/notifications', 'Api\Web\NotificationsController@getNotifications');
|
||||||
|
Route::put('/notifications/mark-as-read', 'Api\Web\NotificationsController@putMarkAsRead');
|
||||||
|
|
||||||
Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned');
|
Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned');
|
||||||
Route::get('/tracks/edit/{id}', 'Api\Web\TracksController@getEdit');
|
Route::get('/tracks/edit/{id}', 'Api\Web\TracksController@getEdit');
|
||||||
|
|
84
app/Jobs/SendNotifications.php
Normal file
84
app/Jobs/SendNotifications.php
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Poniverse\Ponyfm\Jobs\Job;
|
||||||
|
use Illuminate\Contracts\Bus\SelfHandling;
|
||||||
|
use Poniverse\Ponyfm\Library\Notifications\Drivers\AbstractDriver;
|
||||||
|
use Poniverse\Ponyfm\Library\Notifications\Drivers\PonyfmDriver;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
class SendNotifications extends Job implements SelfHandling, ShouldQueue
|
||||||
|
{
|
||||||
|
use InteractsWithQueue, SerializesModels;
|
||||||
|
|
||||||
|
protected $notificationType;
|
||||||
|
protected $notificationData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
* @param string $notificationType
|
||||||
|
* @param array $notificationData
|
||||||
|
*/
|
||||||
|
public function __construct(string $notificationType, array $notificationData)
|
||||||
|
{
|
||||||
|
$this->notificationType = $notificationType;
|
||||||
|
$this->notificationData = $notificationData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->beforeHandle();
|
||||||
|
|
||||||
|
// get list of users who are supposed to receive this notification
|
||||||
|
$recipients = [User::find(1)];
|
||||||
|
|
||||||
|
foreach ($recipients as $recipient) {
|
||||||
|
// get drivers that this notification should be delivered through
|
||||||
|
$drivers = $this->getDriversForNotification($recipient, $this->notificationType);
|
||||||
|
|
||||||
|
foreach ($drivers as $driver) {
|
||||||
|
/** @var $driver AbstractDriver */
|
||||||
|
call_user_func_array([$driver, $this->notificationType], $this->notificationData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the drivers with which the given user has subscribed to the given
|
||||||
|
* notification type.
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
* @param string $notificationType
|
||||||
|
* @return AbstractDriver[]
|
||||||
|
*/
|
||||||
|
private function getDriversForNotification(User $user, string $notificationType) {
|
||||||
|
return [new PonyfmDriver()];
|
||||||
|
}
|
||||||
|
}
|
47
app/Library/Notifications/Drivers/AbstractDriver.php
Normal file
47
app/Library/Notifications/Drivers/AbstractDriver.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Library\Notifications\Drivers;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Poniverse\Ponyfm\Contracts\NotificationHandler;
|
||||||
|
use Poniverse\Ponyfm\Library\Notifications\RecipientFinder;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
abstract class AbstractDriver implements NotificationHandler {
|
||||||
|
private $recipientFinder;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->recipientFinder = new RecipientFinder(get_class($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of users who are to receive the given notification type.
|
||||||
|
* This method is a wrapper around the {@link RecipientFinder} class, which
|
||||||
|
* does the actual processing for all the drivers.
|
||||||
|
*
|
||||||
|
* @param string $notificationType
|
||||||
|
* @param array $notificationData
|
||||||
|
* @return User[] collection of {@link User} objects
|
||||||
|
*/
|
||||||
|
protected function getRecipients(string $notificationType, array $notificationData) {
|
||||||
|
return call_user_func_array([$this->recipientFinder, $notificationType], $notificationData);
|
||||||
|
}
|
||||||
|
}
|
123
app/Library/Notifications/Drivers/PonyfmDriver.php
Normal file
123
app/Library/Notifications/Drivers/PonyfmDriver.php
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Library\Notifications\Drivers;
|
||||||
|
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
|
use Poniverse\Ponyfm\Models\Activity;
|
||||||
|
use Poniverse\Ponyfm\Models\Comment;
|
||||||
|
use Poniverse\Ponyfm\Models\Notification;
|
||||||
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
class PonyfmDriver extends AbstractDriver {
|
||||||
|
/**
|
||||||
|
* A helper method for bulk insertion of notification records.
|
||||||
|
*
|
||||||
|
* @param int $activityId
|
||||||
|
* @param User[] $recipients collection of {@link User} objects
|
||||||
|
*/
|
||||||
|
private function insertNotifications(int $activityId, $recipients) {
|
||||||
|
$notifications = [];
|
||||||
|
foreach ($recipients as $recipient) {
|
||||||
|
$notifications[] = [
|
||||||
|
'activity_id' => $activityId,
|
||||||
|
'user_id' => $recipient->id
|
||||||
|
];
|
||||||
|
}
|
||||||
|
Notification::insert($notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewTrack(Track $track) {
|
||||||
|
$activity = Activity::create([
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => $track->user_id,
|
||||||
|
'activity_type' => Activity::TYPE_PUBLISHED_TRACK,
|
||||||
|
'resource_type' => Track::class,
|
||||||
|
'resource_id' => $track->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewPlaylist(Playlist $playlist) {
|
||||||
|
$activity = Activity::create([
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => $playlist->user_id,
|
||||||
|
'activity_type' => Activity::TYPE_PUBLISHED_PLAYLIST,
|
||||||
|
'resource_type' => Playlist::class,
|
||||||
|
'resource_id' => $playlist->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newFollower(User $userBeingFollowed, User $follower) {
|
||||||
|
$activity = Activity::create([
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => $follower->id,
|
||||||
|
'activity_type' => Activity::TYPE_NEW_FOLLOWER,
|
||||||
|
'resource_type' => User::class,
|
||||||
|
'resource_id' => $userBeingFollowed->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newComment(Comment $comment) {
|
||||||
|
$activity = Activity::create([
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => $comment->user_id,
|
||||||
|
'activity_type' => Activity::TYPE_NEW_COMMENT,
|
||||||
|
'resource_type' => Comment::class,
|
||||||
|
'resource_id' => $comment->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newFavourite(Favouritable $entityBeingFavourited, User $favouriter) {
|
||||||
|
$activity = Activity::create([
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'user_id' => $favouriter->id,
|
||||||
|
'activity_type' => Activity::TYPE_CONTENT_FAVOURITED,
|
||||||
|
'resource_type' => get_class($entityBeingFavourited),
|
||||||
|
'resource_id' => $entityBeingFavourited->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->insertNotifications($activity->id, $this->getRecipients(__FUNCTION__, func_get_args()));
|
||||||
|
}
|
||||||
|
}
|
83
app/Library/Notifications/NotificationManager.php
Normal file
83
app/Library/Notifications/NotificationManager.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Library\Notifications;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
|
use Poniverse\Ponyfm\Contracts\NotificationHandler;
|
||||||
|
use Poniverse\Ponyfm\Jobs\SendNotifications;
|
||||||
|
use Poniverse\Ponyfm\Models\Comment;
|
||||||
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class NotificationManager
|
||||||
|
* @package Poniverse\Ponyfm\Library
|
||||||
|
*
|
||||||
|
* This class exists mostly to maintain type safety when sending notifications
|
||||||
|
* from around the Pony.fm codebase. There should be virtually zero logic here.
|
||||||
|
* All the heavy lifting happens asynchronously in the {@link SendNotifications}
|
||||||
|
* job and the notification drivers.
|
||||||
|
*/
|
||||||
|
class NotificationManager implements NotificationHandler {
|
||||||
|
use DispatchesJobs;
|
||||||
|
|
||||||
|
private function dispatchNotification(string $notificationType, array $notificationData) {
|
||||||
|
$this->dispatch( (new SendNotifications($notificationType, $notificationData))->onQueue('notifications') );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewTrack(Track $track) {
|
||||||
|
$this->dispatchNotification(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewPlaylist(Playlist $playlist) {
|
||||||
|
$this->dispatchNotification(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newFollower(User $userBeingFollowed, User $follower) {
|
||||||
|
$this->dispatchNotification(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newComment(Comment $comment) {
|
||||||
|
$this->dispatchNotification(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newFavourite(Favouritable $entityBeingFavourited, User $favouriter) {
|
||||||
|
$this->dispatchNotification(__FUNCTION__, func_get_args());
|
||||||
|
}
|
||||||
|
}
|
120
app/Library/Notifications/RecipientFinder.php
Normal file
120
app/Library/Notifications/RecipientFinder.php
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Library\Notifications;
|
||||||
|
|
||||||
|
|
||||||
|
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\PonyfmDriver;
|
||||||
|
use Poniverse\Ponyfm\Models\Comment;
|
||||||
|
use Poniverse\Ponyfm\Models\Playlist;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RecipientFinder
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
class RecipientFinder implements NotificationHandler {
|
||||||
|
/**
|
||||||
|
* @var string class name of a notification driver
|
||||||
|
*/
|
||||||
|
private $notificationDriver;
|
||||||
|
|
||||||
|
public function __construct(string $notificationDriver) {
|
||||||
|
$this->notificationDriver = $notificationDriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fail() {
|
||||||
|
throw new \InvalidArgumentException("Unknown notification driver given: {$this->notificationDriver}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewTrack(Track $track) {
|
||||||
|
switch ($this->notificationDriver) {
|
||||||
|
case PonyfmDriver::class:
|
||||||
|
return $track->user->followers;
|
||||||
|
default:
|
||||||
|
return $this->fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function publishedNewPlaylist(Playlist $playlist) {
|
||||||
|
switch ($this->notificationDriver) {
|
||||||
|
case PonyfmDriver::class:
|
||||||
|
return $playlist->user->followers;
|
||||||
|
default:
|
||||||
|
return $this->fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newFollower(User $userBeingFollowed, User $follower) {
|
||||||
|
switch ($this->notificationDriver) {
|
||||||
|
case PonyfmDriver::class:
|
||||||
|
return [$userBeingFollowed];
|
||||||
|
default:
|
||||||
|
return $this->fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newComment(Comment $comment) {
|
||||||
|
switch ($this->notificationDriver) {
|
||||||
|
case PonyfmDriver::class:
|
||||||
|
return
|
||||||
|
$comment->user->id === $comment->resource->user->id
|
||||||
|
? []
|
||||||
|
: [$comment->resource->user];
|
||||||
|
default:
|
||||||
|
return $this->fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function newFavourite(Favouritable $entityBeingFavourited, User $favouriter) {
|
||||||
|
switch ($this->notificationDriver) {
|
||||||
|
case PonyfmDriver::class:
|
||||||
|
return
|
||||||
|
$favouriter->id === $entityBeingFavourited->user->id
|
||||||
|
? []
|
||||||
|
: [$entityBeingFavourited->user];
|
||||||
|
default:
|
||||||
|
return $this->fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
205
app/Models/Activity.php
Normal file
205
app/Models/Activity.php
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poniverse\Ponyfm\Models\Activity
|
||||||
|
*
|
||||||
|
* @property integer $id
|
||||||
|
* @property \Carbon\Carbon $created_at
|
||||||
|
* @property integer $user_id
|
||||||
|
* @property boolean $activity_type
|
||||||
|
* @property boolean $resource_type
|
||||||
|
* @property integer $resource_id
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Notification[] $notifications
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\User $initiatingUser
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\Activity $resource
|
||||||
|
* @property-read mixed $url
|
||||||
|
* @property-read mixed $thumbnail_url
|
||||||
|
* @property-read mixed $text
|
||||||
|
*/
|
||||||
|
class Activity extends Model {
|
||||||
|
public $timestamps = false;
|
||||||
|
protected $dates = ['created_at'];
|
||||||
|
protected $fillable = ['created_at', 'user_id', 'activity_type', 'resource_type', 'resource_id'];
|
||||||
|
protected $appends = ['url', 'thumbnail_url', 'human_friendly_resource_type'];
|
||||||
|
protected $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'user_id' => 'integer',
|
||||||
|
'activity_type' => 'integer',
|
||||||
|
// resource_type has its own accessor and mutator
|
||||||
|
'resource_id' => 'integer',
|
||||||
|
];
|
||||||
|
|
||||||
|
const TYPE_NEWS = 1;
|
||||||
|
const TYPE_PUBLISHED_TRACK = 2;
|
||||||
|
const TYPE_PUBLISHED_ALBUM = 3;
|
||||||
|
const TYPE_PUBLISHED_PLAYLIST = 4;
|
||||||
|
const TYPE_NEW_FOLLOWER = 5;
|
||||||
|
const TYPE_NEW_COMMENT = 6;
|
||||||
|
const TYPE_CONTENT_FAVOURITED = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These "target" constants are an implementation detail of this model and
|
||||||
|
* should not be used directly in other classes. They're used to efficiently
|
||||||
|
* store the type of resource this notification is about in the database.
|
||||||
|
*
|
||||||
|
* The "resource_type" attribute is transformed into a class name at runtime
|
||||||
|
* so that the use of an integer in the database to represent this info
|
||||||
|
* remains an implementation detail of this model. Outside of this class,
|
||||||
|
* the resource_type attribute should be treated as a fully-qualified class
|
||||||
|
* name.
|
||||||
|
*/
|
||||||
|
const TARGET_USER = 1;
|
||||||
|
const TARGET_TRACK = 2;
|
||||||
|
const TARGET_ALBUM = 3;
|
||||||
|
const TARGET_PLAYLIST = 4;
|
||||||
|
const TARGET_COMMENT = 5;
|
||||||
|
|
||||||
|
public function initiatingUser() {
|
||||||
|
return $this->belongsTo(User::class, 'user_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifications() {
|
||||||
|
return $this->hasMany(Notification::class, 'activity_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notificationRecipients() {
|
||||||
|
return $this->hasManyThrough(User::class, Notification::class, 'activity_id', 'user_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resource() {
|
||||||
|
return $this->morphTo('resource', 'resource_type', 'resource_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUrlAttribute() {
|
||||||
|
return $this->resource->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResourceTypeAttribute($value) {
|
||||||
|
switch ($value) {
|
||||||
|
case static::TARGET_USER:
|
||||||
|
return User::class;
|
||||||
|
|
||||||
|
case static::TARGET_TRACK:
|
||||||
|
return Track::class;
|
||||||
|
|
||||||
|
case static::TARGET_ALBUM:
|
||||||
|
return Album::class;
|
||||||
|
|
||||||
|
case static::TARGET_PLAYLIST:
|
||||||
|
return Playlist::class;
|
||||||
|
|
||||||
|
case static::TARGET_COMMENT:
|
||||||
|
return Comment::class;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Null must be returned here for Eloquent's eager-loading
|
||||||
|
// of the polymorphic relation to work.
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setResourceTypeAttribute($value) {
|
||||||
|
switch ($value) {
|
||||||
|
case User::class:
|
||||||
|
$this->attributes['resource_type'] = static::TARGET_USER;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Track::class:
|
||||||
|
$this->attributes['resource_type'] = static::TARGET_TRACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Album::class:
|
||||||
|
$this->attributes['resource_type'] = static::TARGET_ALBUM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Playlist::class:
|
||||||
|
$this->attributes['resource_type'] = static::TARGET_PLAYLIST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Comment::class:
|
||||||
|
$this->attributes['resource_type'] = static::TARGET_COMMENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getThumbnailUrlAttribute()
|
||||||
|
{
|
||||||
|
switch ($this->resource_type) {
|
||||||
|
case User::class:
|
||||||
|
return $this->resource->getAvatarUrl(Image::THUMBNAIL);
|
||||||
|
|
||||||
|
case Track::class:
|
||||||
|
case Album::class:
|
||||||
|
case Playlist::class:
|
||||||
|
return $this->resource->getCoverUrl(Image::THUMBNAIL);
|
||||||
|
|
||||||
|
case Comment::class:
|
||||||
|
return $this->resource->user->getAvatarUrl(Image::THUMBNAIL);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('This activity\'s resource is of an unknown type!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string human-readable Markdown string describing this notification
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getTextAttribute()
|
||||||
|
{
|
||||||
|
switch ($this->activity_type) {
|
||||||
|
case static::TYPE_NEWS:
|
||||||
|
// not implemented yet
|
||||||
|
throw new \InvalidArgumentException('This type of activity has not been implemented yet!');
|
||||||
|
|
||||||
|
case static::TYPE_PUBLISHED_TRACK:
|
||||||
|
return "{$this->resource->user->display_name} published a new track, __{$this->resource->title}__!";
|
||||||
|
|
||||||
|
case static::TYPE_PUBLISHED_PLAYLIST:
|
||||||
|
return "{$this->resource->user->display_name} published a new playlist, __{$this->resource->title}__!";
|
||||||
|
|
||||||
|
case static::TYPE_NEW_FOLLOWER:
|
||||||
|
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) {
|
||||||
|
return "{$this->initiatingUser->display_name} left a comment on your profile!";
|
||||||
|
|
||||||
|
// Must be a content comment.
|
||||||
|
} else {
|
||||||
|
return "{$this->initiatingUser->display_name} left a comment on your {$this->resource->resource->getResourceType()}, __{$this->resource->resource->title}__!";
|
||||||
|
}
|
||||||
|
|
||||||
|
case static::TYPE_CONTENT_FAVOURITED:
|
||||||
|
return "{$this->initiatingUser->display_name} favourited your {$this->resource->type}, __{$this->resource->title}__!";
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('This activity\'s activity type is unknown!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,14 @@ namespace Poniverse\Ponyfm\Models;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Helpers;
|
use Helpers;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Gate;
|
use Gate;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Commentable;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
use Poniverse\Ponyfm\Contracts\Searchable;
|
use Poniverse\Ponyfm\Contracts\Searchable;
|
||||||
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
||||||
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
||||||
|
@ -60,8 +64,9 @@ use Venturecraft\Revisionable\RevisionableTrait;
|
||||||
* @property-read mixed $url
|
* @property-read mixed $url
|
||||||
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Album userDetails()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Album userDetails()
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities
|
||||||
*/
|
*/
|
||||||
class Album extends Model implements Searchable
|
class Album extends Model implements Searchable, Commentable, Favouritable
|
||||||
{
|
{
|
||||||
use SoftDeletes, SlugTrait, TrackCollection, RevisionableTrait, IndexedInElasticsearchTrait;
|
use SoftDeletes, SlugTrait, TrackCollection, RevisionableTrait, IndexedInElasticsearchTrait;
|
||||||
|
|
||||||
|
@ -101,7 +106,7 @@ class Album extends Model implements Searchable
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser');
|
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function favourites()
|
public function favourites():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Favourite');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Favourite');
|
||||||
}
|
}
|
||||||
|
@ -120,11 +125,15 @@ class Album extends Model implements Searchable
|
||||||
return $this->hasManyThrough(TrackFile::class, Track::class, 'album_id', 'track_id');
|
return $this->hasManyThrough(TrackFile::class, Track::class, 'album_id', 'track_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
public function comments():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activities():MorphMany {
|
||||||
|
return $this->morphMany(Activity::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public static function mapPublicAlbumShow(Album $album)
|
public static function mapPublicAlbumShow(Album $album)
|
||||||
{
|
{
|
||||||
$tracks = [];
|
$tracks = [];
|
||||||
|
@ -429,4 +438,14 @@ class Album extends Model implements Searchable
|
||||||
public function shouldBeIndexed():bool {
|
public function shouldBeIndexed():bool {
|
||||||
return $this->track_count > 0 && !$this->trashed();
|
return $this->track_count > 0 && !$this->trashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the corresponding resource type ID from the Activity class for
|
||||||
|
* this resource.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getResourceType():string {
|
||||||
|
return 'album';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@
|
||||||
namespace Poniverse\Ponyfm\Models;
|
namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Commentable;
|
||||||
|
use Poniverse\Ponyfm\Contracts\GeneratesNotifications;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poniverse\Ponyfm\Models\Comment
|
* Poniverse\Ponyfm\Models\Comment
|
||||||
|
@ -42,7 +45,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
* @property-read \Poniverse\Ponyfm\Models\Album $album
|
* @property-read \Poniverse\Ponyfm\Models\Album $album
|
||||||
* @property-read \Poniverse\Ponyfm\Models\Playlist $playlist
|
* @property-read \Poniverse\Ponyfm\Models\Playlist $playlist
|
||||||
* @property-read \Poniverse\Ponyfm\Models\User $profile
|
* @property-read \Poniverse\Ponyfm\Models\User $profile
|
||||||
* @property-read mixed $resource
|
* @property-read Commentable $resource
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities
|
||||||
|
* @property-read mixed $url
|
||||||
*/
|
*/
|
||||||
class Comment extends Model
|
class Comment extends Model
|
||||||
{
|
{
|
||||||
|
@ -78,6 +83,16 @@ class Comment extends Model
|
||||||
return $this->belongsTo('Poniverse\Ponyfm\Models\User', 'profile_id');
|
return $this->belongsTo('Poniverse\Ponyfm\Models\User', 'profile_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activities():MorphMany
|
||||||
|
{
|
||||||
|
return $this->morphMany(Activity::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUrlAttribute()
|
||||||
|
{
|
||||||
|
return $this->resource->url;
|
||||||
|
}
|
||||||
|
|
||||||
public static function mapPublic($comment)
|
public static function mapPublic($comment)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -97,7 +112,7 @@ class Comment extends Model
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResourceAttribute()
|
public function getResourceAttribute():Commentable
|
||||||
{
|
{
|
||||||
if ($this->track_id !== null) {
|
if ($this->track_id !== null) {
|
||||||
return $this->track;
|
return $this->track;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
namespace Poniverse\Ponyfm\Models;
|
namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poniverse\Ponyfm\Models\Follower
|
* Poniverse\Ponyfm\Models\Follower
|
||||||
|
@ -30,10 +31,21 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
* @property integer $artist_id
|
* @property integer $artist_id
|
||||||
* @property integer $playlist_id
|
* @property integer $playlist_id
|
||||||
* @property string $created_at
|
* @property string $created_at
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\User $follower
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\User $artist
|
||||||
*/
|
*/
|
||||||
class Follower extends Model
|
class Follower extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'followers';
|
protected $table = 'followers';
|
||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
|
|
||||||
|
public function follower():BelongsTo {
|
||||||
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function artist():BelongsTo {
|
||||||
|
return $this->belongsTo(User::class, 'artist_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
83
app/Models/Notification.php
Normal file
83
app/Models/Notification.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poniverse\Ponyfm\Models\Notification
|
||||||
|
*
|
||||||
|
* @property integer $id
|
||||||
|
* @property integer $activity_id
|
||||||
|
* @property integer $user_id
|
||||||
|
* @property boolean $is_read
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\Activity $activity
|
||||||
|
* @property-read \Poniverse\Ponyfm\Models\User $recipient
|
||||||
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Notification forUser($user)
|
||||||
|
*/
|
||||||
|
class Notification extends Model {
|
||||||
|
public $timestamps = false;
|
||||||
|
protected $fillable = ['activity_id', 'user_id'];
|
||||||
|
protected $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'activity_id' => 'integer',
|
||||||
|
'user_id' => 'integer',
|
||||||
|
'is_read' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function activity() {
|
||||||
|
return $this->belongsTo(Activity::class, 'activity_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recipient() {
|
||||||
|
return $this->belongsTo(User::class, 'user_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This scope grabs eager-loaded notifications for the given user.
|
||||||
|
*
|
||||||
|
* @param Builder $query
|
||||||
|
* @param User $user
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public function scopeForUser(Builder $query, User $user) {
|
||||||
|
return $query->with([
|
||||||
|
'activity',
|
||||||
|
'activity.initiatingUser',
|
||||||
|
'activity.resource',
|
||||||
|
'activity.resource.user',
|
||||||
|
])
|
||||||
|
->join('activities', 'notifications.activity_id', '=', 'activities.id')
|
||||||
|
->where('notifications.user_id', $user->id)
|
||||||
|
->orderBy('activities.created_at', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray() {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'date' => $this->activity->created_at->toAtomString(),
|
||||||
|
'thumbnail_url' => $this->activity->thumbnail_url,
|
||||||
|
'text' => $this->activity->text,
|
||||||
|
'url' => $this->activity->url
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,10 +22,14 @@ namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
use Helpers;
|
use Helpers;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Commentable;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
use Poniverse\Ponyfm\Contracts\Searchable;
|
use Poniverse\Ponyfm\Contracts\Searchable;
|
||||||
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
||||||
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
||||||
|
@ -59,8 +63,10 @@ use Venturecraft\Revisionable\RevisionableTrait;
|
||||||
* @property-read mixed $url
|
* @property-read mixed $url
|
||||||
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Playlist userDetails()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Playlist userDetails()
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Favourite[] $favourites
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities
|
||||||
*/
|
*/
|
||||||
class Playlist extends Model implements Searchable
|
class Playlist extends Model implements Searchable, Commentable, Favouritable
|
||||||
{
|
{
|
||||||
use SoftDeletes, SlugTrait, TrackCollection, RevisionableTrait, IndexedInElasticsearchTrait;
|
use SoftDeletes, SlugTrait, TrackCollection, RevisionableTrait, IndexedInElasticsearchTrait;
|
||||||
|
|
||||||
|
@ -212,7 +218,7 @@ class Playlist extends Model implements Searchable
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser');
|
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
public function comments():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
||||||
}
|
}
|
||||||
|
@ -221,12 +227,20 @@ class Playlist extends Model implements Searchable
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\PinnedPlaylist');
|
return $this->hasMany('Poniverse\Ponyfm\Models\PinnedPlaylist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function favourites():HasMany {
|
||||||
|
return $this->hasMany(Favourite::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('Poniverse\Ponyfm\Models\User');
|
return $this->belongsTo('Poniverse\Ponyfm\Models\User');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activities():MorphMany {
|
||||||
|
return $this->morphMany(Activity::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public function hasPinFor($userId)
|
public function hasPinFor($userId)
|
||||||
{
|
{
|
||||||
foreach ($this->pins as $pin) {
|
foreach ($this->pins as $pin) {
|
||||||
|
@ -324,4 +338,11 @@ class Playlist extends Model implements Searchable
|
||||||
$this->track_count > 0 &&
|
$this->track_count > 0 &&
|
||||||
!$this->trashed();
|
!$this->trashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getResourceType():string {
|
||||||
|
return 'playlist';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ use Cache;
|
||||||
use Config;
|
use Config;
|
||||||
use DB;
|
use DB;
|
||||||
use Gate;
|
use Gate;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Commentable;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Favouritable;
|
||||||
use Poniverse\Ponyfm\Contracts\Searchable;
|
use Poniverse\Ponyfm\Contracts\Searchable;
|
||||||
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
use Poniverse\Ponyfm\Exceptions\TrackFileNotFoundException;
|
||||||
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
||||||
|
@ -95,8 +99,10 @@ use Venturecraft\Revisionable\RevisionableTrait;
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track explicitFilter()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track explicitFilter()
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track withComments()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track withComments()
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track mlpma()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\Track mlpma()
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $notifications
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities
|
||||||
*/
|
*/
|
||||||
class Track extends Model implements Searchable
|
class Track extends Model implements Searchable, Commentable, Favouritable
|
||||||
{
|
{
|
||||||
use SoftDeletes, IndexedInElasticsearchTrait;
|
use SoftDeletes, IndexedInElasticsearchTrait;
|
||||||
|
|
||||||
|
@ -486,12 +492,12 @@ class Track extends Model implements Searchable
|
||||||
return $this->belongsTo('Poniverse\Ponyfm\Models\TrackType', 'track_type_id');
|
return $this->belongsTo('Poniverse\Ponyfm\Models\TrackType', 'track_type_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
public function comments():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Comment')->orderBy('created_at', 'desc');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function favourites()
|
public function favourites():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Favourite');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Favourite');
|
||||||
}
|
}
|
||||||
|
@ -526,6 +532,15 @@ class Track extends Model implements Searchable
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\TrackFile');
|
return $this->hasMany('Poniverse\Ponyfm\Models\TrackFile');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function notifications()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Activity::class, 'notification_type');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activities():MorphMany {
|
||||||
|
return $this->morphMany(Activity::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public function getYearAttribute()
|
public function getYearAttribute()
|
||||||
{
|
{
|
||||||
return date('Y', strtotime($this->getReleaseDate()));
|
return date('Y', strtotime($this->getReleaseDate()));
|
||||||
|
@ -863,4 +878,8 @@ class Track extends Model implements Searchable
|
||||||
'show_songs' => $this->showSongs->pluck('title')
|
'show_songs' => $this->showSongs->pluck('title')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getResourceType():string {
|
||||||
|
return 'track';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,12 @@ use Illuminate\Auth\Passwords\CanResetPassword;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Poniverse\Ponyfm\Contracts\Commentable;
|
||||||
use Poniverse\Ponyfm\Contracts\Searchable;
|
use Poniverse\Ponyfm\Contracts\Searchable;
|
||||||
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
use Poniverse\Ponyfm\Traits\IndexedInElasticsearchTrait;
|
||||||
use Venturecraft\Revisionable\RevisionableTrait;
|
use Venturecraft\Revisionable\RevisionableTrait;
|
||||||
|
@ -63,8 +66,12 @@ use Venturecraft\Revisionable\RevisionableTrait;
|
||||||
* @property-read mixed $message_url
|
* @property-read mixed $message_url
|
||||||
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Venturecraft\Revisionable\Revision[] $revisionHistory
|
||||||
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User userDetails()
|
* @method static \Illuminate\Database\Query\Builder|\Poniverse\Ponyfm\Models\User userDetails()
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Notification[] $notifications
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\User[] $followers
|
||||||
|
* @property-read mixed $user
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Activity[] $activities
|
||||||
*/
|
*/
|
||||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, \Illuminate\Contracts\Auth\Access\Authorizable, Searchable
|
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, \Illuminate\Contracts\Auth\Access\Authorizable, Searchable, Commentable
|
||||||
{
|
{
|
||||||
use Authenticatable, CanResetPassword, Authorizable, RevisionableTrait, IndexedInElasticsearchTrait;
|
use Authenticatable, CanResetPassword, Authorizable, RevisionableTrait, IndexedInElasticsearchTrait;
|
||||||
|
|
||||||
|
@ -133,13 +140,18 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser', 'artist_id');
|
return $this->hasMany('Poniverse\Ponyfm\Models\ResourceUser', 'artist_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function followers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(User::class, 'followers', 'artist_id', 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function roles()
|
public function roles()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Role::class, 'role_user');
|
return $this->belongsToMany(Role::class, 'role_user');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
public function comments():HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Comment', 'profile_id')->orderBy('created_at', 'desc');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Comment', 'profile_id')->orderBy('created_at', 'desc');
|
||||||
}
|
}
|
||||||
|
@ -148,6 +160,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
{
|
{
|
||||||
return $this->hasMany('Poniverse\Ponyfm\Models\Track', 'user_id');
|
return $this->hasMany('Poniverse\Ponyfm\Models\Track', 'user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function notifications()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Notification::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notificationActivities()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Activity::class, Notification::class, 'user_id', 'notification_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
public function getIsArchivedAttribute()
|
public function getIsArchivedAttribute()
|
||||||
{
|
{
|
||||||
|
@ -235,6 +257,21 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
return "remember_token";
|
return "remember_token";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUserAttribute():User {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getResourceType():string {
|
||||||
|
return 'profile';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activities():MorphMany {
|
||||||
|
return $this->morphMany(Activity::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this user has the given role.
|
* Returns true if this user has the given role.
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Providers;
|
namespace Poniverse\Ponyfm\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use PfmValidator;
|
use PfmValidator;
|
||||||
|
@ -58,5 +59,16 @@ class AppServiceProvider extends ServiceProvider
|
||||||
$app['config']->get('ponyfm.elasticsearch_index')
|
$app['config']->get('ponyfm.elasticsearch_index')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NOTE: Use integer keys exclusively for Pony.fm's morphMap to avoid
|
||||||
|
// any weirdness with merging array indices. $merge = false is
|
||||||
|
// set below so that no morphMap array merging happens!
|
||||||
|
Relation::morphMap([
|
||||||
|
Poniverse\Ponyfm\Models\Activity::TARGET_TRACK => Poniverse\Ponyfm\Models\Track::class,
|
||||||
|
Poniverse\Ponyfm\Models\Activity::TARGET_ALBUM => Poniverse\Ponyfm\Models\Album::class,
|
||||||
|
Poniverse\Ponyfm\Models\Activity::TARGET_PLAYLIST => Poniverse\Ponyfm\Models\Playlist::class,
|
||||||
|
Poniverse\Ponyfm\Models\Activity::TARGET_USER => Poniverse\Ponyfm\Models\User::class,
|
||||||
|
Poniverse\Ponyfm\Models\Activity::TARGET_COMMENT => Poniverse\Ponyfm\Models\Comment::class,
|
||||||
|
], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
50
app/Providers/NotificationServiceProvider.php
Normal file
50
app/Providers/NotificationServiceProvider.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Application;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Poniverse\Ponyfm\Library\Notifications\NotificationManager;
|
||||||
|
|
||||||
|
class NotificationServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap the application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->app->singleton('notification', function(Application $app){
|
||||||
|
return new NotificationManager();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -145,6 +145,7 @@ return [
|
||||||
Poniverse\Ponyfm\Providers\EventServiceProvider::class,
|
Poniverse\Ponyfm\Providers\EventServiceProvider::class,
|
||||||
Poniverse\Ponyfm\Providers\RouteServiceProvider::class,
|
Poniverse\Ponyfm\Providers\RouteServiceProvider::class,
|
||||||
Poniverse\Ponyfm\Providers\AuthServiceProvider::class,
|
Poniverse\Ponyfm\Providers\AuthServiceProvider::class,
|
||||||
|
Poniverse\Ponyfm\Providers\NotificationServiceProvider::class,
|
||||||
|
|
||||||
Intouch\LaravelNewrelic\NewrelicServiceProvider::class,
|
Intouch\LaravelNewrelic\NewrelicServiceProvider::class,
|
||||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||||
|
@ -201,6 +202,7 @@ return [
|
||||||
|
|
||||||
'Elasticsearch' => Cviebrock\LaravelElasticsearch\Facade::class,
|
'Elasticsearch' => Cviebrock\LaravelElasticsearch\Facade::class,
|
||||||
'Newrelic' => Intouch\LaravelNewrelic\Facades\Newrelic::class,
|
'Newrelic' => Intouch\LaravelNewrelic\Facades\Newrelic::class,
|
||||||
|
'Notification' => Poniverse\Ponyfm\Facades\Notification::class,
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Peter Deltchev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateNotificationsTables extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('activities', function(Blueprint $table){
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->dateTime('created_at')->index();
|
||||||
|
$table->unsignedInteger('user_id'); // initiator of the action
|
||||||
|
$table->unsignedTinyInteger('activity_type');
|
||||||
|
$table->unsignedTinyInteger('resource_type');
|
||||||
|
$table->unsignedInteger('resource_id'); // ID of the entity this activity is about
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('notifications', function(Blueprint $table){
|
||||||
|
// Notifications are a pivot table between activities and users.
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->unsignedBigInteger('activity_id')->index();
|
||||||
|
$table->unsignedInteger('user_id')->index(); // recipient of the notification
|
||||||
|
$table->boolean('is_read')->default(false)->index();
|
||||||
|
|
||||||
|
$table->foreign('activity_id')->references('id')->on('activities')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::drop('notifications');
|
||||||
|
Schema::drop('activities');
|
||||||
|
}
|
||||||
|
}
|
94
documentation/notifications.md
Normal file
94
documentation/notifications.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
Developing notifications for Pony.fm
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Pony.fm's notification system is designed around "drivers" for various
|
||||||
|
notification delivery methods. The types of notification one can receive
|
||||||
|
are defined in the
|
||||||
|
[`NotificationHandler`](app/Contracts/NotificationHandler.php)
|
||||||
|
interface, which is implemented by every class that needs to know about
|
||||||
|
the various notification types.
|
||||||
|
|
||||||
|
|
||||||
|
Sending a notification
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The `Notification` facade is used to send notifications as follows:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Notification;
|
||||||
|
|
||||||
|
// Something happens, like a newtrack getting published.
|
||||||
|
$track = new Track();
|
||||||
|
...
|
||||||
|
|
||||||
|
// The "something" is done happening! Time to send a notification.
|
||||||
|
Notification::publishedTrack($track);
|
||||||
|
```
|
||||||
|
|
||||||
|
This facade has a method for every notification type, drawn from the
|
||||||
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php) interface.
|
||||||
|
Each of these methods accepts the data needed to build a notification
|
||||||
|
message and a list of the notification's recipients.
|
||||||
|
|
||||||
|
|
||||||
|
Adding new notification types
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
1. Add a method for the new notification type to the
|
||||||
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
||||||
|
interface.
|
||||||
|
|
||||||
|
2. Implement the new methods in every class that implements the
|
||||||
|
interface. Use your IDE to find these. An inexhaustive list:
|
||||||
|
|
||||||
|
- [`NotificationManager`](../app/Library/Notifications/NotificationManager.php)
|
||||||
|
- [`RecipientFinder`](../app/Library/Notifications/RecipientFinder.php)
|
||||||
|
- [`PonyfmDriver`](../app/Library/Notifications/PonyfmDriver.php)
|
||||||
|
|
||||||
|
3. 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
|
||||||
|
[`Activity`](../app/Models/Activity.php) model.
|
||||||
|
|
||||||
|
|
||||||
|
Adding new notification drivers
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
1. Create a new class for the driver that implements the
|
||||||
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
||||||
|
interface.
|
||||||
|
|
||||||
|
2. Make each method from the above interface send the corresponding type
|
||||||
|
of notification to everyone who is to receive it via that driver.
|
||||||
|
Implement UI and API integrations as needed.
|
||||||
|
|
||||||
|
3. Modify the
|
||||||
|
[`RecipientFinder`](../app/Library/Notifications/RecipientFinder.php)
|
||||||
|
class to build recipient lists for the new driver.
|
||||||
|
|
||||||
|
|
||||||
|
Architectural notes
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The notification system is designed around two ideas: being as type-safe
|
||||||
|
as PHP allows it to be, and doing all the processing and sending of
|
||||||
|
notifications asynchronously.
|
||||||
|
|
||||||
|
To that end, the
|
||||||
|
[`NotificationManager`](../app/Library/Notifications/NotificationManager.php)
|
||||||
|
class is a thin wrapper around the `SendNotifications` job. The job
|
||||||
|
calls the notification drivers asynchronously to actually send the
|
||||||
|
notifications. This job should run on a dedicated queue in production.
|
||||||
|
|
||||||
|
The [`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
||||||
|
interface is key to maintaining type safety - it ensures that drivers
|
||||||
|
and `NotificationManager` all support every type of notification. All
|
||||||
|
classes that have logic specific to a notification type implement this
|
||||||
|
interface to ensure that all notification types are handled.
|
||||||
|
|
||||||
|
There's one exception to the use of `NotificationHandler` - the
|
||||||
|
[`Activity`](../app/Models/Activity.php) model. The logic for mapping 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.
|
0
public/templates/notifications/index.html
Normal file
0
public/templates/notifications/index.html
Normal file
|
@ -285,6 +285,11 @@ ponyfm.config [
|
||||||
url: '/'
|
url: '/'
|
||||||
templateUrl: '/templates/dashboard/index.html'
|
templateUrl: '/templates/dashboard/index.html'
|
||||||
controller: 'dashboard'
|
controller: 'dashboard'
|
||||||
|
|
||||||
|
state.state 'notifications',
|
||||||
|
url: '/notifications'
|
||||||
|
templateUrl: '/templates/notifications/index.html'
|
||||||
|
controller: 'notifications'
|
||||||
else
|
else
|
||||||
state.state 'home',
|
state.state 'home',
|
||||||
url: '/'
|
url: '/'
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Pony.fm - A community for pony fan music.
|
||||||
|
# Copyright (C) 2016 Josef Citrine
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module.exports = angular.module('ponyfm').controller "notifications", [
|
||||||
|
'$scope', 'notifications'
|
||||||
|
($scope, notifications) ->
|
||||||
|
notifications.getNotifications().done (result) ->
|
||||||
|
console.log result
|
||||||
|
]
|
30
resources/assets/scripts/app/services/notifications.coffee
Normal file
30
resources/assets/scripts/app/services/notifications.coffee
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Pony.fm - A community for pony fan music.
|
||||||
|
# Copyright (C) 2016 Josef Citrine
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module.exports = angular.module('ponyfm').factory('notifications', [
|
||||||
|
'$rootScope', '$http'
|
||||||
|
($rootScope, $http) ->
|
||||||
|
self =
|
||||||
|
getNotifications: () ->
|
||||||
|
def = new $.Deferred()
|
||||||
|
|
||||||
|
$http.get('/api/web/account/notifications').success (response) ->
|
||||||
|
def.resolve response
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
self
|
||||||
|
])
|
Loading…
Reference in a new issue