Refresh
This commit is contained in:
171
app/Models/Traits/User/HasFeeds.php
Executable file
171
app/Models/Traits/User/HasFeeds.php
Executable file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits\User;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\FeedItemState;
|
||||
use App\Models\Folder;
|
||||
use App\Models\Group;
|
||||
|
||||
trait HasFeeds
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Relations |--------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Associated feed item state.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function feedItemStates()
|
||||
{
|
||||
return $this->hasMany(FeedItemState::class);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Methods |----------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Mark feed items as read in specified folders, as an array of folder ids.
|
||||
*
|
||||
* @param array $folders
|
||||
*/
|
||||
public function markFeedItemsReadInFolders($folders, Group $group = null)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
$unreadItemsFolder = $group->folders()->ofType('unread_items')->first();
|
||||
|
||||
$query = $group->folders()->with('documents:documents.id');
|
||||
|
||||
if (!in_array($unreadItemsFolder->id, $folders)) {
|
||||
$query = $query->whereIn('folders.id', $folders);
|
||||
}
|
||||
|
||||
$folders = $query->get();
|
||||
$documentIds = $query->get()->pluck('documents')->flatten()->pluck('id')->unique();
|
||||
|
||||
$query = $this->feedItemStates()->unread()->whereIn('document_id', $documentIds);
|
||||
$feedItemIds = $query->pluck('feed_item_id')->unique();
|
||||
|
||||
$query->update(['is_read' => true]);
|
||||
|
||||
return $this->countUnreadItems([
|
||||
'folders' => $folders,
|
||||
'documents' => $documentIds,
|
||||
'updated_feed_items' => $feedItemIds,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark feed items as read in specified documents, as an array of document
|
||||
* ids.
|
||||
*
|
||||
* @param array $documents
|
||||
*/
|
||||
public function markFeedItemsReadInDocuments($documents)
|
||||
{
|
||||
$query = $this->feedItemStates()->unread()->whereIn('document_id', $documents);
|
||||
$feedItemIds = $query->pluck('feed_item_id')->unique();
|
||||
|
||||
$query->update(['is_read' => true]);
|
||||
|
||||
return $this->countUnreadItems([
|
||||
'documents' => $documents,
|
||||
'updated_feed_items' => $feedItemIds,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark feed items as read in specified feeds, as an array of feed ids.
|
||||
*
|
||||
* @param array $feeds
|
||||
*/
|
||||
public function markFeedItemsReadInFeeds($feeds)
|
||||
{
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark specified feed items as read, as an array of feed item ids.
|
||||
*
|
||||
* @param array $feedItems
|
||||
*/
|
||||
public function markFeedItemsRead($feedItems)
|
||||
{
|
||||
$query = $this->feedItemStates()->unread()->whereIn('feed_item_id', $feedItems);
|
||||
$feedItemIds = $query->pluck('feed_item_id')->unique();
|
||||
$documentIds = $query->pluck('document_id')->unique();
|
||||
|
||||
$query->update(['is_read' => true]);
|
||||
|
||||
return $this->countUnreadItems([
|
||||
'documents' => $documentIds,
|
||||
'updated_feed_items' => $feedItemIds,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate unread items counts for current user. The $for array allows to
|
||||
* be more specific by specifying ids for feed_items, documents or folder
|
||||
* to re-count for in particular.
|
||||
*
|
||||
* This method returns an array containing the id of each document and
|
||||
* folder along with corresponding unread items count, as well as a total
|
||||
* of unread items count for each group and folders of type "unread_items".
|
||||
*
|
||||
* @param array $for
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function countUnreadItems($for)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if (empty($for['documents'])) {
|
||||
if (!empty($for['folders'])) {
|
||||
$for['documents'] = Folder::listDocumentIds(collect($for['folders'])->pluck('id')->all(), $this->selectedGroup());
|
||||
}
|
||||
}
|
||||
|
||||
$countPerDocument = $this->feedItemStates()->unread()->whereIn('document_id', $for['documents'])->get()->countBy('document_id')->all();
|
||||
$countPerGroup = [];
|
||||
|
||||
if (!empty($for['documents'])) {
|
||||
foreach ($for['documents'] as $id) {
|
||||
if (!array_key_exists($id, $countPerDocument)) {
|
||||
$countPerDocument[$id] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($for['folders'])) {
|
||||
$folderIds = Document::with('folders')->find($for['documents'])->pluck('folders')->flatten()->pluck('id');
|
||||
|
||||
$for['folders'] = Folder::find($folderIds);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($for['folders'] as $folder) {
|
||||
$countPerFolder[$folder->id] = $this->feedItemStates()->unread()->whereIn('document_id', $folder->getDocumentIds())->count();
|
||||
}
|
||||
|
||||
foreach ($this->groups as $group) {
|
||||
$totalUnreadItems = $group->getUnreadFeedItemsCountFor($this);
|
||||
$unreadItemsFolderId = $group->folders()->ofType('unread_items')->first()->id;
|
||||
|
||||
$countPerFolder[$unreadItemsFolderId] = $totalUnreadItems;
|
||||
$countPerGroup[$group->id] = $totalUnreadItems;
|
||||
}
|
||||
|
||||
return [
|
||||
'documents' => $countPerDocument,
|
||||
'folders' => $countPerFolder,
|
||||
'groups' => $countPerGroup,
|
||||
'updated_feed_items' => !empty($for['updated_feed_items']) ? $for['updated_feed_items'] : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
244
app/Models/Traits/User/HasFolders.php
Executable file
244
app/Models/Traits/User/HasFolders.php
Executable file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits\User;
|
||||
|
||||
use App\Models\Folder;
|
||||
use App\Models\Group;
|
||||
use App\Models\Permission;
|
||||
|
||||
trait HasFolders
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Properties |-------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Currently selected folder in each group.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $selectedFolders = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Relations |--------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Folders owned (created) by this user.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function createdFolders()
|
||||
{
|
||||
return $this->hasMany(Folder::class);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Methods |----------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return user's folders as a flat tree.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getFlatTree(Group $group = null)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
return Folder::getFlatTreeFor($this, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current user's selected folder in specified group.
|
||||
*
|
||||
* @return null|\App\Models\Group
|
||||
*/
|
||||
public function selectedFolder(Group $group = null)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
if (empty($this->selectedFolders[$group->id])) {
|
||||
$this->selectedFolders[$group->id] = $this->fetchSelectedFolder($group);
|
||||
}
|
||||
|
||||
return $this->selectedFolders[$group->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember user's selected folder in specified (or current) group.
|
||||
*
|
||||
* @param \App\Models\Folder $folder
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function setSelectedFolder(Folder $folder = null, Group $group = null)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
$this->selectedFolders[$group->id] = $folder;
|
||||
|
||||
$this->storeSelectedFolder($group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return specified folder's expanded/collapsed state in specified group.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getFolderExpandedState(Folder $folder = null, Group $group = null)
|
||||
{
|
||||
if (empty($folder)) {
|
||||
$folder = $this->selectedFolder($group);
|
||||
}
|
||||
|
||||
$key = $this->folderExpandedStoreKey($folder, $group);
|
||||
|
||||
return (bool) cache($key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specified folder's expanded/collapsed state in specified group.
|
||||
*
|
||||
* @param bool $expanded
|
||||
* @param bool $recursive Apply new state recursively
|
||||
*/
|
||||
public function setFolderExpandedState($expanded, Folder $folder = null, Group $group = null, $recursive = false)
|
||||
{
|
||||
$key = $this->folderExpandedStoreKey($folder, $group);
|
||||
|
||||
cache()->forever($key, (bool) $expanded);
|
||||
|
||||
if ($recursive) {
|
||||
foreach ($folder->children as $subFolder) {
|
||||
$this->setFolderExpandedState($expanded, $subFolder, $group, $recursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure specified folder's ancestor are all expanded, so the folder is
|
||||
* visible.
|
||||
*
|
||||
* @param \App\Models\Folder $folder
|
||||
*/
|
||||
public function ensureAncestorsAreExpanded(Folder $folder = null)
|
||||
{
|
||||
if (empty($folder)) {
|
||||
$folder = $this->selectedFolder();
|
||||
}
|
||||
|
||||
while ($folder = $folder->parent) {
|
||||
$this->setFolderExpandedState(true, $folder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define this user permission for specified folder and ability.
|
||||
*
|
||||
* @param null|mixed $ability
|
||||
* @param mixed $grant
|
||||
*/
|
||||
public function setFolderPermissions(Folder $folder, $ability = null, $grant = false)
|
||||
{
|
||||
$permissions = $this->permissions()->where('folder_id', $folder->id)->first();
|
||||
|
||||
if (!$permissions) {
|
||||
$permissions = new Permission();
|
||||
|
||||
$permissions->user()->associate($this);
|
||||
$permissions->folder()->associate($folder);
|
||||
}
|
||||
|
||||
$defaultPermissions = $folder->getDefaultPermissions();
|
||||
|
||||
if ($ability !== null) {
|
||||
$permissions->{$ability} = $grant;
|
||||
} else {
|
||||
foreach ($defaultPermissions as $defaultAbility => $defaultGrant) {
|
||||
$permissions->{$defaultAbility} = $defaultGrant;
|
||||
}
|
||||
}
|
||||
|
||||
$permissions->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key to access reminded selected folder for this user and
|
||||
* specified group.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function selectedFolderStoreKey(Group $group)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
return sprintf('selectedFolder.%d.%d', $this->id, $group->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stored user's selected folder in specified group.
|
||||
*
|
||||
* @return \App\Models\Folder
|
||||
*/
|
||||
protected function fetchSelectedFolder(Group $group)
|
||||
{
|
||||
$key = $this->selectedFolderStoreKey($group);
|
||||
|
||||
if (cache()->has($key)) {
|
||||
$folder = $group->folders()->find(cache($key));
|
||||
|
||||
if (!empty($folder)) {
|
||||
return $folder;
|
||||
}
|
||||
}
|
||||
|
||||
return $group->folders()->ofType('root')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save user's selected folder in specified group.
|
||||
*/
|
||||
protected function storeSelectedFolder(Group $group)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
$key = $this->selectedFolderStoreKey($group);
|
||||
$folder = $this->selectedFolders[$group->id];
|
||||
|
||||
if (!empty($folder)) {
|
||||
cache()->forever($key, $folder->id);
|
||||
} else {
|
||||
cache()->forget($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key to get specified folder's expanded/collased state in
|
||||
* specified group.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function folderExpandedStoreKey(Folder $folder = null, Group $group = null)
|
||||
{
|
||||
if (empty($group)) {
|
||||
$group = $this->selectedGroup();
|
||||
}
|
||||
|
||||
if (empty($folder)) {
|
||||
$folder = $this->selectedFolder($group);
|
||||
}
|
||||
|
||||
return sprintf('folderExpandedState.%d.%d.%d', $this->id, $group->id, $folder->id);
|
||||
}
|
||||
}
|
||||
177
app/Models/Traits/User/HasGroups.php
Executable file
177
app/Models/Traits/User/HasGroups.php
Executable file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits\User;
|
||||
|
||||
use App\Models\Group;
|
||||
|
||||
trait HasGroups
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Properties |-------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Currently selected group.
|
||||
*
|
||||
* @var \App\Models\Group
|
||||
*/
|
||||
protected $selectedGroup;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Relations |--------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Groups created by this user.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function createdGroups()
|
||||
{
|
||||
return $this->hasMany(Group::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associated groups.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function groups()
|
||||
{
|
||||
return $this->belongsToMany(Group::class, 'user_groups')->withPivot(['status', 'position']);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ----| Methods |----------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create and return user's primary group.
|
||||
*
|
||||
* @return \App\Models\Group
|
||||
*/
|
||||
public function createOwnGroup()
|
||||
{
|
||||
$group = Group::create([
|
||||
'name' => $this->name,
|
||||
'invite_only' => true,
|
||||
'user_id' => $this->id,
|
||||
]);
|
||||
|
||||
$this->groups()->attach($group, [
|
||||
'status' => Group::$STATUS_OWN,
|
||||
]);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current user's selected group.
|
||||
*
|
||||
* @return \App\Models\Group
|
||||
*/
|
||||
public function selectedGroup()
|
||||
{
|
||||
if ($this->selectedGroup === null) {
|
||||
$this->selectedGroup = $this->fetchSelectedGroup();
|
||||
}
|
||||
|
||||
return $this->selectedGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember user's selected group.
|
||||
*/
|
||||
public function setSelectedGroup(Group $group)
|
||||
{
|
||||
$this->selectedGroup = $group;
|
||||
|
||||
$this->storeSelectedGroup();
|
||||
|
||||
return $this->getFlatTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of groups user is active in.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function listActiveGroups()
|
||||
{
|
||||
$userId = $this->id;
|
||||
|
||||
return $this->groups()
|
||||
->select([
|
||||
'groups.id',
|
||||
'groups.name',
|
||||
])->active()
|
||||
->orderBy('position')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group status for current user. If group is not associated with
|
||||
* user, association will be made. It won't change user group if it is
|
||||
* marked as being owned or created by current user, unless $force is true.
|
||||
*
|
||||
* @param string $newStatus
|
||||
* @param mixed $force
|
||||
*/
|
||||
public function updateGroupStatus(Group $group, $newStatus, $force = false)
|
||||
{
|
||||
$userGroup = $this->groups()->find($group->id);
|
||||
|
||||
if ($userGroup) {
|
||||
if (in_array($userGroup->pivot->status, [Group::$STATUS_OWN, Group::$STATUS_CREATED]) && !$force) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->groups()->updateExistingPivot($group->id, [
|
||||
'status' => $newStatus,
|
||||
]);
|
||||
} else {
|
||||
$this->groups()->save($group, [
|
||||
'status' => $newStatus,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key to access reminded selected group for this user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function selectedGroupStoreKey()
|
||||
{
|
||||
return sprintf('selectedGroup.%d', $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stored user's selected group.
|
||||
*
|
||||
* @return \App\Models\Group
|
||||
*/
|
||||
protected function fetchSelectedGroup()
|
||||
{
|
||||
$key = $this->selectedGroupStoreKey();
|
||||
|
||||
if (cache()->has($key)) {
|
||||
$group = $this->groups()->active()->find(cache($key));
|
||||
|
||||
if (!empty($group)) {
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->groups()->own()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save user's selected group.
|
||||
*/
|
||||
protected function storeSelectedGroup()
|
||||
{
|
||||
$key = $this->selectedGroupStoreKey();
|
||||
|
||||
cache()->forever($key, $this->selectedGroup->id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user