Browse Source

The UI Update

master
Richard Dern 4 months ago
parent
commit
cb0b7ec8a9
162 changed files with 7336 additions and 8518 deletions
  1. +2
    -13
      .env.example
  2. +0
    -6
      .gitmodules
  3. +12
    -5
      app/Actions/Fortify/UpdateUserProfileInformation.php
  4. +0
    -54
      app/Console/Commands/InstallTheme.php
  5. +0
    -17
      app/Facades/ThemeManager.php
  6. +2
    -0
      app/Http/Controllers/FeedItemController.php
  7. +37
    -4
      app/Http/Controllers/HighlightController.php
  8. +0
    -24
      app/Http/Controllers/HistoryEntryController.php
  9. +0
    -32
      app/Http/Controllers/HomeController.php
  10. +0
    -28
      app/Http/Controllers/ThemeController.php
  11. +1
    -13
      app/Models/Document.php
  12. +1
    -13
      app/Models/Feed.php
  13. +3
    -16
      app/Models/Folder.php
  14. +1
    -11
      app/Models/Group.php
  15. +0
    -91
      app/Models/HistoryEntry.php
  16. +1
    -11
      app/Models/Observers/BookmarkObserver.php
  17. +2
    -12
      app/Models/Observers/FolderObserver.php
  18. +0
    -5
      app/Models/Observers/GroupObserver.php
  19. +2
    -8
      app/Models/Observers/IgnoredFeedObserver.php
  20. +0
    -2
      app/Models/Observers/UserObserver.php
  21. +14
    -16
      app/Models/Traits/Feed/AnalysesFeed.php
  22. +0
    -95
      app/Models/Traits/HasHistory.php
  23. +1
    -12
      app/Models/User.php
  24. +3
    -59
      app/Providers/BladeServiceProvider.php
  25. +0
    -42
      app/Providers/HorizonServiceProvider.php
  26. +0
    -42
      app/Providers/ThemeManagerServiceProvider.php
  27. +0
    -193
      app/Services/ThemeManager.php
  28. +0
    -4
      composer.json
  29. +1049
    -823
      composer.lock
  30. +1
    -3
      config/app.php
  31. +0
    -195
      config/horizon.php
  32. +1
    -1
      config/routes.php
  33. +32
    -0
      database/migrations/2020_11_30_231123_change_theme_column_default.php
  34. +32
    -0
      database/migrations/2020_12_01_111943_add_position_to_highlights_table.php
  35. +3789
    -3473
      package-lock.json
  36. +16
    -19
      package.json
  37. +4
    -0
      public/css/app.css
  38. BIN
      public/fonts/Quicksand-Medium.ttf
  39. +67
    -0
      public/images/icons.svg
  40. +1
    -1
      public/js/app.js
  41. +1
    -14
      public/js/app.js.LICENSE.txt
  42. +1
    -1
      public/js/groups.js
  43. +1
    -7
      public/js/groups.js.LICENSE.txt
  44. +1
    -1
      public/js/highlights.js
  45. +0
    -12
      public/js/highlights.js.LICENSE.txt
  46. +4
    -2
      public/js/history.js
  47. +1
    -2
      public/js/import.js
  48. +0
    -2
      public/js/themes-browser.js
  49. +0
    -18
      public/js/themes-browser.js.LICENSE.txt
  50. +5
    -10
      public/mix-manifest.json
  51. +0
    -8
      public/vendor/horizon/app-dark.css
  52. +0
    -8
      public/vendor/horizon/app.css
  53. +0
    -2
      public/vendor/horizon/app.js
  54. BIN
      public/vendor/horizon/img/favicon.png
  55. +0
    -4
      public/vendor/horizon/img/horizon.svg
  56. +0
    -806
      public/vendor/horizon/img/sprite.svg
  57. +0
    -5
      public/vendor/horizon/mix-manifest.json
  58. +13
    -5
      resources/css/app.css
  59. +4
    -0
      resources/css/base/_index.css
  60. +0
    -57
      resources/css/base/a.css
  61. +35
    -40
      resources/css/base/article.css
  62. +0
    -25
      resources/css/base/base.css
  63. +13
    -0
      resources/css/base/details.css
  64. +0
    -48
      resources/css/base/dl.css
  65. +0
    -8
      resources/css/base/fonts.css
  66. +0
    -101
      resources/css/base/forms.css
  67. +8
    -0
      resources/css/base/forms/_index.css
  68. +53
    -0
      resources/css/base/forms/button.css
  69. +7
    -0
      resources/css/base/forms/form-group.css
  70. +3
    -0
      resources/css/base/forms/form.css
  71. +3
    -0
      resources/css/base/forms/input-group.css
  72. +24
    -0
      resources/css/base/forms/input.css
  73. +3
    -0
      resources/css/base/forms/label.css
  74. +5
    -0
      resources/css/base/forms/select.css
  75. +5
    -0
      resources/css/base/forms/textarea.css
  76. +24
    -0
      resources/css/base/scrollbar.css
  77. +0
    -23
      resources/css/base/scrollbars.css
  78. +6
    -0
      resources/css/components/_index.css
  79. +0
    -35
      resources/css/components/about.css
  80. +0
    -22
      resources/css/components/alerts.css
  81. +20
    -28
      resources/css/components/badge.css
  82. +0
    -64
      resources/css/components/components.css
  83. +19
    -0
      resources/css/components/folder.css
  84. +0
    -47
      resources/css/components/folders.css
  85. +26
    -0
      resources/css/components/group-status.css
  86. +0
    -87
      resources/css/components/groups-browser.css
  87. +3
    -0
      resources/css/components/highlight.css
  88. +0
    -39
      resources/css/components/history.css
  89. +0
    -83
      resources/css/components/layout.css
  90. +0
    -67
      resources/css/components/list-items.css
  91. +136
    -0
      resources/css/components/list.css
  92. +109
    -0
      resources/css/components/prose.css
  93. +0
    -63
      resources/css/components/themes-browser.css
  94. +1
    -0
      resources/css/utilities/_index.css
  95. +32
    -0
      resources/css/utilities/readable.css
  96. +70
    -37
      resources/js/app.js
  97. +9
    -9
      resources/js/components/DateTime.vue
  98. +1
    -1
      resources/js/components/Details/DefaultFolderPermissions.vue
  99. +151
    -97
      resources/js/components/Details/DetailsDocument.vue
  100. +74
    -41
      resources/js/components/Details/DetailsFeedItem.vue

+ 2
- 13
.env.example View File

@ -67,17 +67,6 @@ MAIL_FROM_ADDRESS=
#
APP_LOCALE=en
#
# The following settings can be changed without breaking any feature.
#
#
# Application theme.
# This is the theme applied by default, unless logged in user has chosen another
# one.
#
APP_THEME=cyca-dark
# ----| Settings that shouldn't be changed |------------------------------------
#
@ -130,11 +119,11 @@ BROADCAST_DRIVER=pusher
#
# Cache driver.
# Driver used for caching purposes.
# Recommended option is "redis", so a redis server must be running.
# Recommended option is "file".
# For more informations, please read Laravel's documentation:
# https://laravel.com/docs/cache
#
CACHE_DRIVER=redis
CACHE_DRIVER=file
#
# Queue driver.


+ 0
- 6
.gitmodules View File

@ -1,6 +0,0 @@
[submodule "resources/themes/cyca_theme_dark"]
path = resources/themes/cyca_theme_dark
url = git@github.com:RichardDern/cyca_theme_dark.git
[submodule "resources/themes/cyca_theme_light"]
path = resources/themes/cyca_theme_light
url = git@github.com:RichardDern/cyca_theme_light.git

+ 12
- 5
app/Actions/Fortify/UpdateUserProfileInformation.php View File

@ -32,6 +32,11 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
'lang' => [
'required',
Rule::in(array_keys(config('lang')))
],
'theme' => [
'nullable',
Rule::in(['light', 'dark', 'auto'])
]
])->validateWithBag('updateProfileInformation');
@ -40,9 +45,10 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'name' => $input['name'],
'email' => $input['email'],
'lang' => $input['lang']
'lang' => $input['lang'],
'theme' => $input['theme']
])->save();
}
}
@ -57,9 +63,10 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
protected function updateVerifiedUser($user, array $input)
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'lang' => $input['lang'],
'name' => $input['name'],
'email' => $input['email'],
'lang' => $input['lang'],
'theme' => $input['theme'],
'email_verified_at' => null,
])->save();


+ 0
- 54
app/Console/Commands/InstallTheme.php View File

@ -1,54 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Facades\ThemeManager;
class InstallTheme extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'theme:install { name : Name of the theme to install }';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Install a theme for Cyca';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$name = $this->argument('name');
ThemeManager::updateCache();
$this->comment("Please be careful when installing themes.");
$this->comment("It is highly recommended to check the repository files before installation.");
//ThemeManager::installFromName($name);
$this->info("Theme installed !");
return 0;
}
}

+ 0
- 17
app/Facades/ThemeManager.php View File

@ -1,17 +0,0 @@
<?php
namespace App\Facades;
use App\Services\ThemeManager as BaseClass;
use Illuminate\Support\Facades\Facade;
class ThemeManager extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{return BaseClass::class;}
}

+ 2
- 0
app/Http/Controllers/FeedItemController.php View File

@ -54,6 +54,8 @@ class FeedItemController extends Controller
$query->where('is_read', false)->where('user_id', $request->user()->id);
}]);
$feedItem->loadMissing('feeds');
return $feedItem;
}


+ 37
- 4
app/Http/Controllers/HighlightController.php View File

@ -18,10 +18,10 @@ class HighlightController extends Controller
{
$data = $request->validated();
$highlight = new Highlight();
$highlight->user_id = $request->user()->id;
$highlight = new Highlight();
$highlight->user_id = $request->user()->id;
$highlight->expression = $data['expression'];
$highlight->color = $data['color'];
$highlight->color = $data['color'];
$highlight->save();
return $request->user()->highlights()->get();
@ -43,7 +43,7 @@ class HighlightController extends Controller
$data = $request->validated();
$highlight->expression = $data['expression'];
$highlight->color = $data['color'];
$highlight->color = $data['color'];
$highlight->save();
return $request->user()->highlights()->get();
@ -64,4 +64,37 @@ class HighlightController extends Controller
$highlight->delete();
return $request->user()->highlights()->get();
}
/**
* Update my groups positions
*
* @param \Illuminate\Http\Request $request
* @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();
}
}
}

+ 0
- 24
app/Http/Controllers/HistoryEntryController.php View File

@ -1,24 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\HistoryEntry;
use Illuminate\Http\Request;
class HistoryEntryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$entries = $request->user()->userHistoryEntries()
->orderBy('created_at', 'desc')
->orderBy('id', 'desc')
->simplePaginate(25);
return $entries;
}
}

+ 0
- 32
app/Http/Controllers/HomeController.php View File

@ -2,7 +2,6 @@
namespace App\Http\Controllers;
use App\Facades\ThemeManager;
use App\Services\Exporter;
use App\Services\Importer;
use Illuminate\Http\Request;
@ -55,29 +54,6 @@ class HomeController extends Controller
return view('account.password');
}
/**
* Theme selection page
*/
public function theme()
{
$availableThemes = ThemeManager::listAvailableThemes();
return view('account.themes')->with(['availableThemes' => $availableThemes]);
}
/**
* Save theme to user's profile
*/
public function setTheme(Request $request)
{
if (!ThemeManager::themeExists($request->input('theme'))) {
abort(422);
}
$request->user()->theme = $request->input('theme');
$request->user()->save();
}
/**
* Manage user's highlights
*/
@ -118,14 +94,6 @@ class HomeController extends Controller
]);
}
/**
* Browse user's history
*/
public function history()
{
return view('account.history');
}
/**
* Manage groups
*/


+ 0
- 28
app/Http/Controllers/ThemeController.php View File

@ -1,28 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Folder;
use App\Models\Document;
use App\Models\Feed;
use App\Models\IgnoredFeed;
use App\Facades\ThemeManager;
class ThemeController extends Controller
{
/**
* List themes
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return ThemeManager::listAvailableThemes();
}
public function details(string $name)
{
return ThemeManager::getThemeDetails($name);
}
}

+ 1
- 13
app/Models/Document.php View File

@ -4,12 +4,11 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Traits\Document\AnalysesDocument;
use App\Models\Traits\HasHistory;
use App\Models\Traits\HasUrl;
class Document extends Model
{
use AnalysesDocument, HasHistory, HasUrl;
use AnalysesDocument, HasUrl;
# --------------------------------------------------------------------------
# ----| Properties |--------------------------------------------------------
@ -64,17 +63,6 @@ class Document extends Model
*/
private $storagePath = null;
/**
* Attributes used to display this model in history
*
* @var array
*/
protected $historyAttributes = [
'url',
'title',
'favicon'
];
# --------------------------------------------------------------------------
# ----| Attributes |--------------------------------------------------------
# --------------------------------------------------------------------------


+ 1
- 13
app/Models/Feed.php View File

@ -3,13 +3,12 @@
namespace App\Models;
use App\Models\Traits\Feed\AnalysesFeed;
use App\Models\Traits\HasHistory;
use Illuminate\Database\Eloquent\Model;
use App\Models\Traits\HasUrl;
class Feed extends Model
{
use AnalysesFeed, HasHistory, HasUrl;
use AnalysesFeed, HasUrl;
# --------------------------------------------------------------------------
# ----| Properties |--------------------------------------------------------
@ -58,17 +57,6 @@ class Feed extends Model
'checked_at',
];
/**
* Attributes used to display this model in history
*
* @var array
*/
protected $historyAttributes = [
'url',
'title',
'favicon',
];
# --------------------------------------------------------------------------
# ----| Attributes |--------------------------------------------------------
# --------------------------------------------------------------------------


+ 3
- 16
app/Models/Folder.php View File

@ -4,12 +4,11 @@ namespace App\Models;
use App\Models\Traits\Folder\BuildsTree;
use App\Models\Traits\Folder\CreatesDefaultFolders;
use App\Models\Traits\HasHistory;
use Illuminate\Database\Eloquent\Model;
class Folder extends Model
{
use BuildsTree, CreatesDefaultFolders, HasHistory;
use BuildsTree, CreatesDefaultFolders;
# --------------------------------------------------------------------------
# ----| Properties |--------------------------------------------------------
@ -41,18 +40,6 @@ class Folder extends Model
'is_expanded',
];
/**
* Attributes used to display this model in history
*
* @var array
*/
protected $historyAttributes = [
'title',
'icon',
'iconColor',
'type',
];
# --------------------------------------------------------------------------
# ----| Attributes |--------------------------------------------------------
# --------------------------------------------------------------------------
@ -281,13 +268,13 @@ class Folder extends Model
{
$unreadItemsFolder = $group->folders()->ofType('unread_items')->first();
$query = $group->folders()->with('documents:document_id');
$query = $group->folders()->with('documents:id');
if (!in_array($unreadItemsFolder->id, $folders)) {
$query = $query->whereIn('id', $folders);
}
return $query->get()->pluck('documents')->flatten()->pluck('document_id')->unique();
return $query->get()->pluck('documents')->flatten()->pluck('id')->unique();
}
/**


+ 1
- 11
app/Models/Group.php View File

@ -3,13 +3,12 @@
namespace App\Models;
use App\Models\FeedItemState;
use App\Models\Traits\HasHistory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
use HasFactory, HasHistory;
use HasFactory;
# --------------------------------------------------------------------------
# ----| Constants |---------------------------------------------------------
@ -78,15 +77,6 @@ class Group extends Model
'feed_item_states_count' => 'integer',
];
/**
* Attributes used to display this model in history
*
* @var array
*/
protected $historyAttributes = [
'name',
];
/**
* The accessors to append to the model's array form.
*


+ 0
- 91
app/Models/HistoryEntry.php View File

@ -1,91 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Str;
class HistoryEntry extends Model
{
use HasFactory;
# --------------------------------------------------------------------------
# ----| Properties |--------------------------------------------------------
# --------------------------------------------------------------------------
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'properties' => 'array',
];
/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = [
'date',
'time',
'text'
];
# --------------------------------------------------------------------------
# ----| Attributes |--------------------------------------------------------
# --------------------------------------------------------------------------
/**
* Return entry's date
*
* @return string
*/
public function getDateAttribute() {
return $this->created_at->format('Y-m-d');
}
/**
* Return entry's time
*
* @return string
*/
public function getTimeAttribute() {
return $this->created_at->format("H:i");
}
/**
* Return a descriptive representation of this entry
*
* @return string
*/
public function getTextAttribute() {
$view = \sprintf('partials.history.%s.%s', Str::lower(class_basename($this->morphable_type)), $this->event);
return (string) view($view, $this->properties);
}
# --------------------------------------------------------------------------
# ----| Relations |---------------------------------------------------------
# --------------------------------------------------------------------------
/**
* Related user
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Get the owning model.
*/
public function morphable()
{
return $this->morphTo();
}
}

+ 1
- 11
app/Models/Observers/BookmarkObserver.php View File

@ -17,12 +17,6 @@ class BookmarkObserver
public function created(Bookmark $bookmark)
{
Notification::send($bookmark->folder->group->activeUsers, new UnreadItemsChanged(['documents' => [$bookmark->document->id]]));
$bookmark->document->addHistoryEntry('bookmark_created', [
'user' => $bookmark->folder->user->toHistoryArray(),
'breadcrumbs' => $bookmark->folder->breadcrumbs,
'document' => $bookmark->document->toHistoryArray(),
], $bookmark->folder->user);
}
/**
@ -33,10 +27,6 @@ class BookmarkObserver
*/
public function deleting(Bookmark $bookmark)
{
$bookmark->document->addHistoryEntry('bookmark_deleted', [
'user' => $bookmark->folder->user->toHistoryArray(),
'breadcrumbs' => $bookmark->folder->breadcrumbs,
'document' => $bookmark->document->toHistoryArray(),
], $bookmark->folder->user);
}
}

+ 2
- 12
app/Models/Observers/FolderObserver.php View File

@ -16,12 +16,7 @@ class FolderObserver
*/
public function created(Folder $folder)
{
$folder->addHistoryEntry('folder_created', [
'user' => $folder->user->toHistoryArray(),
'breadcrumbs' => $folder->breadcrumbs,
], $folder->user);
//TODO: Add history entries for users in the same group
}
/**
@ -32,12 +27,7 @@ class FolderObserver
*/
public function deleting(Folder $folder)
{
$folder->addHistoryEntry('folder_deleted', [
'user' => $folder->user->toHistoryArray(),
'breadcrumbs' => $folder->breadcrumbs,
], $folder->user);
//TODO: Add history entries for users in the same group
}
/**


+ 0
- 5
app/Models/Observers/GroupObserver.php View File

@ -14,11 +14,6 @@ class GroupObserver
*/
public function created(Group $group)
{
$group->addHistoryEntry('group_created', [
'user' => $group->creator->toHistoryArray(),
'group' => $group->toHistoryArray(),
], $group->creator);
$group->createDefaultFolders();
}
}

+ 2
- 8
app/Models/Observers/IgnoredFeedObserver.php View File

@ -15,10 +15,7 @@ class IgnoredFeedObserver
*/
public function created(IgnoredFeed $ignoredFeed)
{
$ignoredFeed->feed->addHistoryEntry('feed_ignored', [
'user' => $ignoredFeed->user->toHistoryArray(),
'feed' => $ignoredFeed->feed->toHistoryArray()
], $ignoredFeed->user);
}
/**
@ -29,9 +26,6 @@ class IgnoredFeedObserver
*/
public function deleting(IgnoredFeed $ignoredFeed)
{
$ignoredFeed->feed->addHistoryEntry('feed_followed', [
'user' => $ignoredFeed->user->toHistoryArray(),
'feed' => $ignoredFeed->feed->toHistoryArray()
], $ignoredFeed->user);
}
}

+ 0
- 2
app/Models/Observers/UserObserver.php View File

@ -15,8 +15,6 @@ class UserObserver
*/
public function created(User $user)
{
$user->addHistoryEntry('user_created', ['user' => $user->toHistoryArray()], $user);
$group = $user->createOwnGroup();
$user->importInitialData($group);


+ 14
- 16
app/Models/Traits/Feed/AnalysesFeed.php View File

@ -91,25 +91,23 @@ trait AnalysesFeed
foreach ($items as $item) {
$feedItem = FeedItem::where('hash', $item->get_id(true))->first();
if ($feedItem) {
continue;
}
$feedItem = new FeedItem();
$feedItem->hash = $item->get_id(true);
$feedItem->title = $this->cleanupString($item->get_title(), true, true);
$feedItem->url = $item->get_permalink();
$feedItem->description = $this->formatText($item->get_description(true));
$feedItem->content = $this->formatText($item->get_content(true));
$feedItem->published_at = $item->get_gmdate();
if (!$feedItem) {
$feedItem = new FeedItem();
$feedItem->hash = $item->get_id(true);
$feedItem->title = $this->cleanupString($item->get_title(), true, true);
$feedItem->url = $item->get_permalink();
$feedItem->description = $this->formatText($item->get_description(true));
$feedItem->content = $this->formatText($item->get_content(true));
$feedItem->published_at = $item->get_gmdate();
if ($feedItem->published_at->addDays(config('cyca.maxOrphanAge.feeditems'))->lt(now())) {
continue;
}
if ($feedItem->published_at->addDays(config('cyca.maxOrphanAge.feeditems'))->lt(now())) {
continue;
$feedItem->save();
}
$feedItem->save();
if (!in_array($feedItem->id, $toSync)) {
$toSync[] = $feedItem->id;
}


+ 0
- 95
app/Models/Traits/HasHistory.php View File

@ -1,95 +0,0 @@
<?php
namespace App\Models\Traits;
use App\Models\HistoryEntry;
use App\Models\User;
use Str;
/**
* Classes using this trait can have a history
*/
trait HasHistory
{
# --------------------------------------------------------------------------
# ----| Relations |---------------------------------------------------------
# --------------------------------------------------------------------------
/**
* Associated history entries
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function historyEntries() {
return $this->morphMany(HistoryEntry::class, 'morphable');
}
# --------------------------------------------------------------------------
# ----| Methods |-----------------------------------------------------------
# --------------------------------------------------------------------------
/**
* Add a history entry for this model.
*
* @param string $event Event name
* @param null|array $properties Properties to pass to views
* @param integer|\App\Models\User|array You could provide an integer (which
* will be resolved to corresponding user) or an array of users.
*/
public function addHistoryEntry($event, $properties = null, $users) {
if(\is_numeric($users) || get_class($users) === User::class) {
$users = [$users];
}
foreach($users as $user) {
$this->addHistoryEntryFor($event, $properties, $user);
}
}
/**
* Add a history entry for this model and specified user (which can be null)
*
* @param string $event Event name
* @param null|array $properties Properties to pass to views
* @param integer|\App\Models\User|null
*/
private function addHistoryEntryFor($event, $properties = null, $user = null) {
$historyEntry = new HistoryEntry();
$historyEntry->event = $event;
// Associating user, if needed
if(!empty($user)) {
if(\is_numeric($user)) {
$user = User::find($user);
}
$historyEntry->user()->associate($user);
}
$historyEntry->morphable()->associate($this);
$historyEntry->properties = $properties;
$historyEntry->save();
}
/**
* Return an array representing this model, specific to history displaying
*
* @return array
*/
public function toHistoryArray() {
$data = [];
if(!empty($this->historyAttributes)) {
foreach($this->historyAttributes as $attribute) {
$data[$attribute] = $this->$attribute;
}
} else {
$data = $this->toArray();
}
return $data;
}
}

+ 1
- 12
app/Models/User.php View File

@ -2,7 +2,6 @@
namespace App\Models;
use App\Models\Traits\HasHistory;
use App\Models\Traits\User\HasFeeds;
use App\Models\Traits\User\HasFolders;
use App\Models\Traits\User\HasGroups;
@ -14,7 +13,7 @@ use Illuminate\Notifications\Notifiable;
class User extends Authenticatable implements MustVerifyEmail, HasLocalePreference
{
use Notifiable, HasHistory, HasGroups, HasFolders, HasFeeds;
use Notifiable, HasGroups, HasFolders, HasFeeds;
# --------------------------------------------------------------------------
# ----| Properties |--------------------------------------------------------
@ -47,16 +46,6 @@ class User extends Authenticatable implements MustVerifyEmail, HasLocalePreferen
'email_verified_at' => 'datetime',
];
/**
* Attributes used to display this model in history
*
* @var array
*/
protected $historyAttributes = [
'name',
'email',
];
# --------------------------------------------------------------------------
# ----| Attributes |--------------------------------------------------------
# --------------------------------------------------------------------------


+ 3
- 59
app/Providers/BladeServiceProvider.php View File

@ -2,7 +2,6 @@
namespace App\Providers;
use App\Facades\ThemeManager;
use Illuminate\Support\ServiceProvider;
class BladeServiceProvider extends ServiceProvider
@ -24,75 +23,20 @@ class BladeServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->registerThemeVariables();
$this->registerHighlights();
}
/**
* Register theme-specific variables into view
*/
protected function registerThemeVariables() {
view()->composer('*', function ($view) {
$theme = config('app.theme');
if (auth()->check()) {
$theme = auth()->user()->theme;
}
$cssRelPath = sprintf('themes/%s/theme.css', request()->input('theme', $theme));
if(!file_exists(public_path($cssRelPath))) {
ThemeManager::installTheme($theme);
}
view()->share('activeTheme', $theme);
view()->share('iconsFileUrl', $this->getIconsFile($theme));
view()->share('css', asset($cssRelPath));
});
}
/**
* Find path to icons file for specified theme, or inherited themes
*
* @param string $theme
* @return string
*/
protected function getIconsFile($theme = null) {
if(empty($theme)) {
return null;
}
$jsonPath = public_path(sprintf('themes/%s/theme.json', $theme));
if(!file_exists($jsonPath)) {
return null;
}
$json = json_decode(file_get_contents($jsonPath), true);
if(!empty($json['icons'])) {
$iconsPath = asset(sprintf('themes/%s/%s', $theme, $json['icons']));
return $iconsPath;
}
if(!empty($json['inherits'])) {
return $this->getIconsFile($json['inherits']);
}
return null;
}
/**
* Register user's highlights into view
*/
protected function registerHighlights() {
protected function registerHighlights()
{
view()->composer('*', function ($view) {
if (!auth()->check()) {
return;
}
$highlights = auth()->user()->highlights()->select(['id', 'expression', 'color'])->get();
$highlights = auth()->user()->highlights()->select(['id', 'expression', 'color', 'position'])->orderBy('position')->get();
view()->share('highlights', $highlights);
});


+ 0
- 42
app/Providers/HorizonServiceProvider.php View File

@ -1,42 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
// Horizon::night();
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
//
]);
});
}
}

+ 0
- 42
app/Providers/ThemeManagerServiceProvider.php View File

@ -1,42 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use App\Services\ThemeManager;
class ThemeManagerServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->singleton(ThemeManager::class, function($app) {
return new ThemeManager();
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [ThemeManager::class];
}
}

+ 0
- 193
app/Services/ThemeManager.php View File

@ -1,193 +0,0 @@
<?php
namespace App\Services;
use Cache;
use Http;
use Str;
/**
* Manage themes
*/
class ThemeManager
{
/**
* List of available themes
*/
private $allThemes = [];
/**
* Download themes database and cache it
*/
public function updateCache($force = false)
{
if ($force || !Cache::has('themes_database')) {
$dbUrl = config('services.themes.database_url');
$response = Http::get($dbUrl);
$response->throw();
$contents = $response->json();
Cache::forever('themes_database', $contents);
} else {
$contents = Cache::get('themes_database');
}
$this->allThemes = collect($contents);
}
/**
* Return a list of available themes, along with their meta data
*
* @return array
*/
public function listAvailableThemes()
{
$this->updateCache();
return $this->allThemes;
}
/**
* Return URL to download specified theme
*
* @param string $theme
* @return string
*/
public function getThemeUrl($theme)
{
$url = null;
if (empty($this->allThemes)) {
$this->updateCache();
}
if (array_key_exists($theme, $this->allThemes['official'])) {
$url = $this->allThemes['official'][$theme];
} else if (array_key_exists($theme, $this->allThemes['community'])) {
$url = $this->allThemes['community'][$theme];
}
return $url;
}
/**
* Return a boolean value indicating if specified theme exists
*
* @param string $theme
* @return boolean
*/
public function themeExists($theme)
{
if (empty($this->allThemes)) {
$this->updateCache();
}
return array_key_exists($theme, $this->allThemes['official']) || array_key_exists($theme, $this->allThemes['community']);
}
/**
* Return theme's details.
*
* @param string $theme
* @return array
*/
public function getThemeDetails($theme)
{
$meta = null;
$jsonPath = public_path(sprintf('themes/%s/theme.json', $theme));
$this->installTheme($theme);
$meta = json_decode(file_get_contents($jsonPath), true);
$meta['screenshot'] = sprintf('/themes/%s/%s', $theme, $meta['screenshot']);
return $meta;
}
/**
* Install specified theme locally
*
* @param string $theme
*/
public function installTheme($theme)
{
$theme = Str::slug($theme);
$url = $this->getThemeUrl($theme);
// Physical path to the directory
$targetDir = storage_path('app/themes/' . $theme);
$command = sprintf('git clone "%s" %s', $url, $targetDir);
if (is_dir($targetDir)) {
$command = sprintf('cd %s && git pull', $targetDir);
}
exec($command);
// Where does the dist files are ?
$distPath = $targetDir . '/dist';
// Path to theme's json file
$jsonPath = $distPath . '/theme.json';
if (!is_file($jsonPath)) {
$this->cleanup($targetDir);
return false;
}
$json = json_decode(file_get_contents($jsonPath), true);
if (empty($json) || empty($json['name'])) {
$this->cleanup($targetDir);
return false;
}
$themeName = Str::kebab($json['name']);
$target = public_path(sprintf('/themes/%s', $themeName));
$this->copyDir($distPath, $target);
if (!empty($json['inherits'])) {
$this->installTheme($json['inherits']);
}
}
/**
* Remove specified directory
*
* @param string $dir
*/
protected function cleanup($dir)
{
exec(sprintf("rf -rm %s", $dir));
}
/**
* Recursively copy one directory to another
*
* @param string $src
* @param string $dst
*/
protected function copyDir($src, $dst)
{
$dir = opendir($src);
@mkdir($dst, 0755, true);
while (false !== ($file = readdir($dir))) {
if (($file != '.') && ($file != '..')) {
if (is_dir($src . '/' . $file)) {
$this->copyDir($src . '/' . $file, $dst . '/' . $file);
} else {
copy($src . '/' . $file, $dst . '/' . $file);
}
}
}
closedir($dir);
}
}

+ 0
- 4
composer.json View File

@ -16,7 +16,6 @@
"guzzlehttp/guzzle": "^7.0.1",
"laravel/fortify": "^1.5",
"laravel/framework": "^8.0",
"laravel/horizon": "^5.2",
"laravel/tinker": "^2.0",
"league/uri": "^6.3",
"lordelph/icofileloader": "^2.0",
@ -67,9 +66,6 @@
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
],
"post-update-cmd": [
"@php artisan horizon:publish --ansi"
]
}
}

+ 1049
- 823
composer.lock
File diff suppressed because it is too large
View File


+ 1
- 3
config/app.php View File

@ -112,7 +112,7 @@ return [
|--------------------------------------------------------------------------
*/
'version' => '0.8.12',
'version' => '0.9.0',
/*
|--------------------------------------------------------------------------
@ -193,12 +193,10 @@ return [
App\Providers\AuthServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\ObserversServiceProvider::class,
App\Providers\LangServiceProvider::class,
App\Providers\BladeServiceProvider::class,
App\Providers\ThemeManagerServiceProvider::class,
],
/*


+ 0
- 195
config/horizon.php View File

@ -1,195 +0,0 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
'domain' => null,
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => 'horizon',
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
'trim' => [
'recent' => 60,
'pending' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon worker
| may consume before it is terminated and restarted. You should set
| this value according to the resources available to your server.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
'defaults' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['notifications', 'default'],
'balance' => 'auto',
'minProcesses' => 1,
'tries' => 1,
'nice' => 0,
],
],
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
],
];

+ 1
- 1
config/routes.php View File

@ -58,7 +58,7 @@ return [
'highlight.destroy',
'highlight.store',
'highlight.update',
'history_entry.index',
'highlight.update_positions',
'home',
'logout',
],


+ 32
- 0
database/migrations/2020_11_30_231123_change_theme_column_default.php View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangeThemeColumnDefault extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('theme')->default('auto')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
//
});
}
}

+ 32
- 0
database/migrations/2020_12_01_111943_add_position_to_highlights_table.php View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddPositionToHighlightsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('highlights', function (Blueprint $table) {