This commit is contained in:
Richard Dern
2022-01-12 00:35:37 +01:00
commit 400e3d01f1
1363 changed files with 57778 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Storage;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
public function s3resource(Request $request, $path)
{
return response(Storage::get($path), 200, [
'Content-Type' => Storage::mimeType($path)
]);
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\Documents\StoreRequest;
use App\Models\Document;
use App\Models\Folder;
use App\Notifications\UnreadItemsChanged;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
use Storage;
class DocumentController extends Controller
{
public function __construct()
{
$this->authorizeResource(Document::class, 'document');
}
/**
* Store a newly created resource in storage.
*
* @param App\Http\Requests\Documents\StoreRequest $request
*
* @return \Illuminate\Http\Response
*/
public function store(StoreRequest $request)
{
$validated = $request->validated();
$user = $request->user();
$url = $validated['url'];
$folder = Folder::find($validated['folder_id']);
$document = Document::firstOrCreate(['url' => $url]);
$folder->documents()->save($document, [
'initial_url' => $url,
]);
return $folder->listDocuments($user);
}
/**
* Display the specified resource.
*
* @return \Illuminate\Http\Response
*/
public function show(Request $request, Document $document)
{
$user = $request->user();
$document->findDupplicatesFor($user);
$document->loadMissing('feeds')->loadCount(['feedItemStates' => function ($query) use ($user) {
$query->where('is_read', false)->where('user_id', $user->id);
}]);
if (Storage::exists($document->getStoragePath().'/meta.json')) {
$document->meta_data = \json_decode(Storage::get($document->getStoragePath().'/meta.json'));
}
if (Storage::exists($document->getStoragePath().'/response.json')) {
$document->response = \json_decode(Storage::get($document->getStoragePath().'/response.json'));
}
return $document;
}
/**
* Move document into specified folder.
*
* @param Folder $folder
*
* @return \Illuminate\Http\Response
*/
public function move(Request $request, Folder $sourceFolder, Folder $targetFolder)
{
$this->authorize('createBookmarkIn', $targetFolder);
$this->authorize('deleteBookmarkFrom', $sourceFolder);
$bookmarks = $sourceFolder->documents()->whereIn('documents.id', $request->input('documents'))->get();
foreach ($bookmarks as $bookmark) {
$sourceFolder->documents()->updateExistingPivot($bookmark->id, ['folder_id' => $targetFolder->id]);
}
$usersToNotify = $sourceFolder->group->activeUsers->merge($targetFolder->group->activeUsers);
Notification::send($usersToNotify, new UnreadItemsChanged([
'folders' => [
$sourceFolder->id,
$targetFolder->id,
],
]));
return $request->user()->countUnreadItems([
'folders' => [
$sourceFolder->id,
$targetFolder->id,
],
]);
}
/**
* Remove documents from specified folder.
*
* @return \Illuminate\Http\Response
*/
public function destroyBookmarks(Request $request, Folder $folder)
{
$this->authorize('deleteBookmarkFrom', $folder);
$user = $request->user();
$documents = $folder->documents()->whereIn('documents.id', $request->input('documents'))->get();
foreach ($documents as $document) {
$folder->documents()->detach($document);
}
Notification::send($folder->group->activeUsers()->get(), new UnreadItemsChanged(['folders' => [$folder]]));
return $folder->listDocuments($user);
}
/**
* Increment visits for specified document in specified folder.
*
* @return \Illuminate\Http\Response
*/
public function visit(Request $request, Document $document)
{
++$document->visits;
$document->save();
return $this->show($request, $document);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers;
use App\Models\Feed;
use App\Models\IgnoredFeed;
use Illuminate\Http\Request;
class FeedController extends Controller
{
/**
* Ignore specified feed.
*
* @return \Illuminate\Http\Response
*/
public function ignore(Request $request, Feed $feed)
{
$ignoredFeed = IgnoredFeed::where('user_id', $request->user()->id)->where('feed_id', $feed->id)->first();
if (!$ignoredFeed) {
$ignoredFeed = new IgnoredFeed();
$ignoredFeed->user()->associate($request->user());
$ignoredFeed->feed()->associate($feed);
$ignoredFeed->save();
}
}
/**
* Follow specified feed.
*
* @return \Illuminate\Http\Response
*/
public function follow(Request $request, Feed $feed)
{
$ignoredFeed = IgnoredFeed::where('user_id', $request->user()->id)->where('feed_id', $feed->id)->first();
if ($ignoredFeed) {
$ignoredFeed->delete();
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Http\Controllers;
use App\Models\Feed;
use App\Models\FeedItem;
use Illuminate\Http\Request;
class FeedItemController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$feedIds = $request->input('feeds', []);
if (empty($feedIds)) {
return [];
}
$user = $request->user();
$folder = $user->selectedFolder();
$queryBuilder = FeedItem::with('feeds:feeds.id,title', 'feeds.documents:documents.id')->inFeeds($feedIds);
$queryBuilder->select([
'feed_items.id',
'url',
'title',
'published_at',
'created_at',
'updated_at',
]);
if ($folder->type === 'unread_items') {
$queryBuilder->unreadFor($user);
}
return $queryBuilder->countStates($user)->orderBy('published_at', 'desc')->simplePaginate(15);
}
/**
* Display the specified resource.
*
* @return \Illuminate\Http\Response
*/
public function show(Request $request, FeedItem $feedItem)
{
$feedItem->loadCount(['feedItemStates' => function ($query) use ($request) {
$query->where('is_read', false)->where('user_id', $request->user()->id);
}]);
$feedItem->loadMissing('feeds');
return $feedItem;
}
/**
* Mark feed items as read.
*/
public function markAsRead(Request $request)
{
$user = $request->user();
if ($request->has('folders')) {
return $user->markFeedItemsReadInFolders($request->input('folders'));
}
if ($request->has('documents')) {
return $user->markFeedItemsReadInDocuments($request->input('documents'));
}
if ($request->has('feeds')) {
return $user->markFeedItemsReadInFeeds($request->input('feeds'));
}
if ($request->has('feed_items')) {
return $user->markFeedItemsRead($request->input('feed_items'));
}
}
}

View File

@@ -0,0 +1,253 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\Folders\SetPermissionsRequest;
use App\Http\Requests\Folders\StoreRequest;
use App\Http\Requests\Folders\UpdateRequest;
use App\Models\Folder;
use App\Models\Group;
use App\Models\User;
use Illuminate\Http\Request;
class FolderController extends Controller
{
public function __construct()
{
$this->authorizeResource(Folder::class, 'folder');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$user = $request->user();
return $user->getFlatTree();
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Folder\StoreRequest $request
*
* @return \Illuminate\Http\Response
*/
public function store(StoreRequest $request)
{
$validated = $request->validated();
$user = $request->user();
$parentFolder = Folder::find($validated['parent_id']);
$group = Group::find($validated['group_id']);
$user->createdFolders()->save(new Folder([
'title' => $validated['title'],
'parent_id' => $parentFolder->id,
'group_id' => $group->id,
]));
$user->setFolderExpandedState(true, $parentFolder);
return $user->getFlatTree($group);
}
/**
* Display the specified resource.
*
* @return \Illuminate\Http\Response
*/
public function show(Request $request, Folder $folder)
{
$user = $request->user();
$user->setSelectedFolder($folder);
return $folder->listDocuments($user);
}
/**
* Load every details of specified folder.
*
* @return \Illuminate\Http\Response
*/
public function details(Request $request, Folder $folder)
{
$user = $request->user();
if (!$user->can('view', $folder)) {
abort(404);
}
if ($folder->type === 'unread_items') {
$folder->feed_item_states_count = $folder->group->feedItemStatesCount;
} else {
$folder->user_permissions = $folder->getUserPermissions($user);
$folder->default_permissions = $folder->getDefaultPermissions();
$folder->loadCount(['feedItemStates' => function ($query) use ($user) {
$query->where('is_read', false)->where('user_id', $user->id);
}]);
$folder->group->loadCount('activeUsers');
}
return $folder;
}
/**
* Load per-user permissions for specified folder.
*
* @return \Illuminate\Http\Response
*/
public function perUserPermissions(Request $request, Folder $folder)
{
if (!$request->user()->can('setPermission', $folder)) {
abort(404);
}
$users = $folder->group->activeUsers()->whereNotIn('users.id', [$request->user()->id])
->whereHas('permissions', function ($query) use ($folder) {
$query->where('folder_id', $folder->id);
})
->with(['permissions'=> function ($query) use ($folder) {
$query->where('folder_id', $folder->id);
}])
->select(['users.id', 'users.name', 'users.email'])
->get();
return $users;
}
/**
* Load list of users with no expicit permissions for specified folder.
*
* @return \Illuminate\Http\Response
*/
public function usersWithoutPermissions(Request $request, Folder $folder)
{
if (!$request->user()->can('setPermission', $folder)) {
abort(404);
}
$users = $folder->group->activeUsers()->whereNotIn('users.id', [$request->user()->id])
->whereDoesntHave('permissions', function ($query) use ($folder) {
$query->where('folder_id', $folder->id);
})
->select(['users.id', 'users.name', 'users.email'])
->get();
return $users;
}
/**
* Update the specified resource in storage.
*
* @param App\Http\Requests\Folder\UpdateRequest $request
*
* @return \Illuminate\Http\Response
*/
public function update(UpdateRequest $request, Folder $folder)
{
$validated = $request->validated();
$user = $request->user();
if ($request->has('is_expanded')) {
$user->setFolderExpandedState($validated['is_expanded'], $folder);
}
$folder->title = $validated['title'];
$folder->parent_id = $validated['parent_id'];
if ($folder->isDirty()) {
$folder->save();
}
if (!empty($folder->parent_id)) {
$user->setFolderExpandedState(true, $folder->parent);
}
return $folder;
}
/**
* Remove the specified resource from storage.
*
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, Folder $folder)
{
$user = $request->user();
$user->setSelectedFolder(null, $folder->group);
$folder->delete();
return $user->getFlatTree();
}
/**
* Toggle expanded/collapsed a whole folder's branch.
*
* @return \Illuminate\Http\Response
*/
public function toggleBranch(Request $request, Folder $folder)
{
$user = $request->user();
$user->setFolderExpandedState(!$folder->is_expanded, $folder, $folder->group, true);
return $user->getFlatTree();
}
/**
* Set permissions for specified folder, optionally for specified user.
*
* @param App\Http\Requests\Folder\SetPermissionsRequest $request
*
* @return \Illuminate\Http\Response
*/
public function setPermission(SetPermissionsRequest $request, Folder $folder)
{
if (!$request->user()->can('setPermission', $folder)) {
abort(404);
}
$validated = $request->validated();
$ability = !empty($validated['ability']) ? $validated['ability'] : null;
$granted = !empty($validated['granted']) ? $validated['granted'] : false;
if (empty($validated['user_id'])) {
$folder->setDefaultPermission($ability, $granted);
return $this->details($request, $folder);
}
$user = $folder->group->activeUsers()->findOrFail($validated['user_id']);
$user->setFolderPermissions($folder, $ability, $granted);
return $this->perUserPermissions($request, $folder);
}
/**
* Remove permissions for specified user in specified folder.
*
* @param App\Http\Requests\Folder\SetPermissionsRequest $request
*
* @return \Illuminate\Http\Response
*/
public function removePermissions(Request $request, Folder $folder, User $user)
{
if (!$request->user()->can('setPermission', $folder)) {
abort(404);
}
$user->permissions()->where('folder_id', $folder->id)->delete();
return $this->perUserPermissions($request, $folder);
}
}

View File

@@ -0,0 +1,254 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\Groups\InviteUserRequest;
use App\Http\Requests\Groups\StoreRequest;
use App\Http\Requests\Groups\UpdateRequest;
use App\Models\Group;
use App\Models\User;
use App\Notifications\AsksToJoinGroup;
use App\Notifications\InvitedToJoinGroup;
use Illuminate\Http\Request;
use Notification;
class GroupController extends Controller
{
public function __construct()
{
$this->authorizeResource(Group::class, 'group');
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$user = $request->user();
$search = $request->input('search');
$query = Group::visible()
->whereNotIn('id', $user->groups->pluck('id'))
->with('creator:id,name')
->withCount('activeUsers');
if (!empty($search)) {
$query = $query->where('groups.name', 'like', '%'.$search.'%');
}
return $query
->orderBy('name')
->simplePaginate(25);
}
/**
* Display a listing of the resource (active groups).
*
* @return \Illuminate\Http\Response
*/
public function indexActive(Request $request)
{
$user = $request->user();
return $user->listActiveGroups();
}
/**
* Display a listing of the resource (my groups).
*
* @return \Illuminate\Http\Response
*/
public function indexMyGroups(Request $request)
{
$user = $request->user();
return $user->groups()->withCount('activeUsers', 'pendingUsers')
->whereNotIn('status', [
Group::$STATUS_REJECTED,
Group::$STATUS_LEFT,
])->orderBy('position')->orderBy('id')->get();
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreRequest $request
*
* @return \Illuminate\Http\Response
*/
public function store(StoreRequest $request)
{
$validated = $request->validated();
$user = $request->user();
$validated['user_id'] = $user->id;
$group = Group::create($validated);
$user->groups()->save($group, [
'status' => 'created',
]);
return $user->groups()->withCount('activeUsers')->orderBy('position')->get();
}
/**
* Display the specified resource.
*
* @return \Illuminate\Http\Response
*/
public function show(Request $request, Group $group)
{
$user = $request->user();
$user->setSelectedGroup($group);
return $user->getFlatTree($group);
}
/**
* Update the specified resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function update(UpdateRequest $request, Group $group)
{
$validated = $request->validated();
$group->name = $validated['name'];
$group->description = $validated['description'];
$group->invite_only = $validated['invite_only'];
$group->auto_accept_users = $validated['auto_accept_users'];
$group->save();
return $request->user()->groups()->withCount('activeUsers', 'pendingUsers')->find($group->id);
}
/**
* Remove the specified resource from storage.
*
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, Group $group)
{
$user = $request->user();
$group->delete();
return $user->groups()->withCount('activeUsers')->get();
}
/**
* Update my groups positions.
*
* @return \Illuminate\Http\Response
*/
public function updatePositions(Request $request)
{
if (!$request->has('positions')) {
abort(422);
}
$positions = $request->input('positions');
if (!is_array($positions)) {
abort(422);
}
$user = $request->user();
foreach ($positions as $groupId => $position) {
if (!is_numeric($groupId) || !is_numeric($position)) {
abort(422);
}
$group = $user->groups()->findOrFail($groupId);
$user->groups()->updateExistingPivot($group, ['position' => $position]);
}
return $user->groups()->withCount('activeUsers')->orderBy('position')->get();
}
/**
* Invite user to join specified group.
*
* @param \App\Requests\Groups\InviteUserRequest $request
*
* @return \Illuminate\Http\Response
*/
public function inviteUser(InviteUserRequest $request, Group $group)
{
$user = $request->user();
if (!$user->can('invite', $group)) {
abort(403);
}
$validated = $request->validated();
$invitedUser = User::where('email', $validated['email'])->first();
if ($invitedUser) {
$invitedUser->updateGroupStatus($group, Group::$STATUS_INVITED);
}
Notification::route('mail', $validated['email'])
->notify(new InvitedToJoinGroup($request->user(), $group));
return $request->user()->groups()->withCount('activeUsers', 'pendingUsers')->find($group->id);
}
public function acceptInvitation(Request $request, Group $group)
{
$user = $request->user();
$user->updateGroupStatus($group, Group::$STATUS_ACCEPTED);
if ($request->ajax()) {
return $user->groups()->withCount('activeUsers', 'pendingUsers')->find($group->id);
}
return redirect()->route('account.groups');
}
public function approveUser(Request $request, Group $group, User $user)
{
$creator = $request->user();
if (!$creator->can('approve', $group)) {
abort(403);
}
$user->updateGroupStatus($group, Group::$STATUS_ACCEPTED);
return redirect()->route('account.groups');
}
public function rejectInvitation(Request $request, Group $group)
{
$user = $request->user();
$user->updateGroupStatus($group, Group::$STATUS_REJECTED);
return $user->groups()->withCount('activeUsers', 'pendingUsers')->find($group->id);
}
public function leave(Request $request, Group $group)
{
$user = $request->user();
$user->groups()->detach($group);
}
public function join(Request $request, Group $group)
{
$user = $request->user();
if ($group->auto_accept_users) {
$user->updateGroupStatus($group, Group::$STATUS_ACCEPTED);
} else {
$user->updateGroupStatus($group, Group::$STATUS_JOINING);
Notification::route('mail', $group->creator->email)
->notify(new AsksToJoinGroup($request->user(), $group));
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreHighlightRequest;
use App\Models\Highlight;
use Illuminate\Http\Request;
class HighlightController extends Controller
{
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function store(StoreHighlightRequest $request)
{
$data = $request->validated();
$highlight = new Highlight();
$highlight->user_id = $request->user()->id;
$highlight->expression = $data['expression'];
$highlight->color = $data['color'];
$highlight->save();
return $request->user()->highlights()->get();
}
/**
* Update the specified resource in storage.
*
* @param \App\Models\Models\Highlight $highlight
*
* @return \Illuminate\Http\Response
*/
public function update(StoreHighlightRequest $request, Highlight $highlight)
{
if ($highlight->user_id !== $request->user()->id) {
abort(404);
}
$data = $request->validated();
$highlight->expression = $data['expression'];
$highlight->color = $data['color'];
$highlight->save();
return $request->user()->highlights()->get();
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Models\Hightlight $hightlight
*
* @return \Illuminate\Http\Response
*/
public function destroy(Request $request, Highlight $highlight)
{
if ($highlight->user_id !== $request->user()->id) {
abort(404);
}
$highlight->delete();
return $request->user()->highlights()->get();
}
/**
* Update my groups positions.
*
* @return \Illuminate\Http\Response
*/
public function updatePositions(Request $request)
{
if (!$request->has('positions')) {
abort(422);
}
$positions = $request->input('positions');
if (!is_array($positions)) {
abort(422);
}
$user = $request->user();
foreach ($positions as $highlightId => $position) {
if (!is_numeric($highlightId) || !is_numeric($position)) {
abort(422);
}
$highlight = $user->highlights()->findOrFail($highlightId);
$highlight->position = $positions[$highlightId];
$highlight->save();
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers;
use App\Services\Exporter;
use App\Services\Importer;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('home');
}
/**
* Show application's about page'.
*
* @return \Illuminate\Http\Response
*/
public function about()
{
return view('account.about');
}
/**
* Show user's account page.
*/
public function account()
{
return view('account.my_account');
}
/**
* Show user's password update page.
*/
public function password()
{
return view('account.password');
}
/**
* Manage user's highlights.
*/
public function highlights()
{
return view('account.highlights');
}
/**
* Show the import form.
*/
public function showImportForm()
{
return view('account.import');
}
/**
* Import a file.
*/
public function import(Request $request)
{
(new Importer())->fromRequest($request)->import();
return ['ok' => true];
}
/**
* Export user's data.
*/
public function export(Request $request)
{
$data = (new Exporter())->forUser($request->user())->export();
return response()->streamDownload(function () use ($data) {
echo json_encode($data);
}, sprintf('%s - Export.json', $request->user()->name), [
'Content-Type' => 'application/x-json',
]);
}
/**
* Manage groups.
*/
public function groups()
{
return view('account.groups');
}
}