Showing
18 changed files
with
311 additions
and
30 deletions
app/Console/Commands/ResetViews.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace BookStack\Console\Commands; | ||
| 4 | + | ||
| 5 | +use Illuminate\Console\Command; | ||
| 6 | + | ||
| 7 | +class ResetViews extends Command | ||
| 8 | +{ | ||
| 9 | + /** | ||
| 10 | + * The name and signature of the console command. | ||
| 11 | + * | ||
| 12 | + * @var string | ||
| 13 | + */ | ||
| 14 | + protected $signature = 'views:reset'; | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * The console command description. | ||
| 18 | + * | ||
| 19 | + * @var string | ||
| 20 | + */ | ||
| 21 | + protected $description = 'Reset all view-counts for all entities.'; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * Create a new command instance. | ||
| 25 | + * | ||
| 26 | + */ | ||
| 27 | + public function __construct() | ||
| 28 | + { | ||
| 29 | + parent::__construct(); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * Execute the console command. | ||
| 34 | + * | ||
| 35 | + * @return mixed | ||
| 36 | + */ | ||
| 37 | + public function handle() | ||
| 38 | + { | ||
| 39 | + \Views::resetAll(); | ||
| 40 | + } | ||
| 41 | +} |
| ... | @@ -14,6 +14,7 @@ class Kernel extends ConsoleKernel | ... | @@ -14,6 +14,7 @@ class Kernel extends ConsoleKernel |
| 14 | */ | 14 | */ |
| 15 | protected $commands = [ | 15 | protected $commands = [ |
| 16 | \BookStack\Console\Commands\Inspire::class, | 16 | \BookStack\Console\Commands\Inspire::class, |
| 17 | + \BookStack\Console\Commands\ResetViews::class, | ||
| 17 | ]; | 18 | ]; |
| 18 | 19 | ||
| 19 | /** | 20 | /** | ... | ... |
| ... | @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; | ... | @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; |
| 6 | 6 | ||
| 7 | abstract class Entity extends Model | 7 | abstract class Entity extends Model |
| 8 | { | 8 | { |
| 9 | + | ||
| 9 | /** | 10 | /** |
| 10 | * Relation for the user that created this entity. | 11 | * Relation for the user that created this entity. |
| 11 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo | 12 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
| ... | @@ -36,7 +37,7 @@ abstract class Entity extends Model | ... | @@ -36,7 +37,7 @@ abstract class Entity extends Model |
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | - * Gets the activity for this entity. | 40 | + * Gets the activity objects for this entity. |
| 40 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany | 41 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany |
| 41 | */ | 42 | */ |
| 42 | public function activity() | 43 | public function activity() |
| ... | @@ -45,6 +46,24 @@ abstract class Entity extends Model | ... | @@ -45,6 +46,24 @@ abstract class Entity extends Model |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | /** | 48 | /** |
| 49 | + * Get View objects for this entity. | ||
| 50 | + * @return mixed | ||
| 51 | + */ | ||
| 52 | + public function views() | ||
| 53 | + { | ||
| 54 | + return $this->morphMany('BookStack\View', 'viewable'); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * Get just the views for the current user. | ||
| 59 | + * @return mixed | ||
| 60 | + */ | ||
| 61 | + public function userViews() | ||
| 62 | + { | ||
| 63 | + return $this->views()->where('user_id', '=', auth()->user()->id); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /** | ||
| 48 | * Allows checking of the exact class, Used to check entity type. | 67 | * Allows checking of the exact class, Used to check entity type. |
| 49 | * Cleaner method for is_a. | 68 | * Cleaner method for is_a. |
| 50 | * @param $type | 69 | * @param $type | ... | ... |
| ... | @@ -11,6 +11,7 @@ use BookStack\Http\Requests; | ... | @@ -11,6 +11,7 @@ use BookStack\Http\Requests; |
| 11 | use BookStack\Repos\BookRepo; | 11 | use BookStack\Repos\BookRepo; |
| 12 | use BookStack\Repos\ChapterRepo; | 12 | use BookStack\Repos\ChapterRepo; |
| 13 | use BookStack\Repos\PageRepo; | 13 | use BookStack\Repos\PageRepo; |
| 14 | +use Views; | ||
| 14 | 15 | ||
| 15 | class BookController extends Controller | 16 | class BookController extends Controller |
| 16 | { | 17 | { |
| ... | @@ -41,7 +42,8 @@ class BookController extends Controller | ... | @@ -41,7 +42,8 @@ class BookController extends Controller |
| 41 | public function index() | 42 | public function index() |
| 42 | { | 43 | { |
| 43 | $books = $this->bookRepo->getAllPaginated(10); | 44 | $books = $this->bookRepo->getAllPaginated(10); |
| 44 | - return view('books/index', ['books' => $books]); | 45 | + $recents = $this->bookRepo->getRecentlyViewed(10, 0); |
| 46 | + return view('books/index', ['books' => $books, 'recents' => $recents]); | ||
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | /** | 49 | /** |
| ... | @@ -86,6 +88,7 @@ class BookController extends Controller | ... | @@ -86,6 +88,7 @@ class BookController extends Controller |
| 86 | public function show($slug) | 88 | public function show($slug) |
| 87 | { | 89 | { |
| 88 | $book = $this->bookRepo->getBySlug($slug); | 90 | $book = $this->bookRepo->getBySlug($slug); |
| 91 | + Views::add($book); | ||
| 89 | return view('books/show', ['book' => $book, 'current' => $book]); | 92 | return view('books/show', ['book' => $book, 'current' => $book]); |
| 90 | } | 93 | } |
| 91 | 94 | ... | ... |
| ... | @@ -10,6 +10,7 @@ use BookStack\Http\Requests; | ... | @@ -10,6 +10,7 @@ use BookStack\Http\Requests; |
| 10 | use BookStack\Http\Controllers\Controller; | 10 | use BookStack\Http\Controllers\Controller; |
| 11 | use BookStack\Repos\BookRepo; | 11 | use BookStack\Repos\BookRepo; |
| 12 | use BookStack\Repos\ChapterRepo; | 12 | use BookStack\Repos\ChapterRepo; |
| 13 | +use Views; | ||
| 13 | 14 | ||
| 14 | class ChapterController extends Controller | 15 | class ChapterController extends Controller |
| 15 | { | 16 | { |
| ... | @@ -79,6 +80,7 @@ class ChapterController extends Controller | ... | @@ -79,6 +80,7 @@ class ChapterController extends Controller |
| 79 | { | 80 | { |
| 80 | $book = $this->bookRepo->getBySlug($bookSlug); | 81 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 81 | $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); | 82 | $chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id); |
| 83 | + Views::add($chapter); | ||
| 82 | return view('chapters/show', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); | 84 | return view('chapters/show', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]); |
| 83 | } | 85 | } |
| 84 | 86 | ... | ... |
| ... | @@ -2,13 +2,12 @@ | ... | @@ -2,13 +2,12 @@ |
| 2 | 2 | ||
| 3 | namespace BookStack\Http\Controllers; | 3 | namespace BookStack\Http\Controllers; |
| 4 | 4 | ||
| 5 | +use Activity; | ||
| 5 | use Illuminate\Http\Request; | 6 | use Illuminate\Http\Request; |
| 6 | 7 | ||
| 7 | use BookStack\Http\Requests; | 8 | use BookStack\Http\Requests; |
| 8 | -use BookStack\Http\Controllers\Controller; | ||
| 9 | use BookStack\Repos\BookRepo; | 9 | use BookStack\Repos\BookRepo; |
| 10 | -use BookStack\Services\ActivityService; | 10 | +use Views; |
| 11 | -use BookStack\Services\Facades\Activity; | ||
| 12 | 11 | ||
| 13 | class HomeController extends Controller | 12 | class HomeController extends Controller |
| 14 | { | 13 | { |
| ... | @@ -18,12 +17,10 @@ class HomeController extends Controller | ... | @@ -18,12 +17,10 @@ class HomeController extends Controller |
| 18 | 17 | ||
| 19 | /** | 18 | /** |
| 20 | * HomeController constructor. | 19 | * HomeController constructor. |
| 21 | - * @param ActivityService $activityService | ||
| 22 | * @param BookRepo $bookRepo | 20 | * @param BookRepo $bookRepo |
| 23 | */ | 21 | */ |
| 24 | - public function __construct(ActivityService $activityService, BookRepo $bookRepo) | 22 | + public function __construct(BookRepo $bookRepo) |
| 25 | { | 23 | { |
| 26 | - $this->activityService = $activityService; | ||
| 27 | $this->bookRepo = $bookRepo; | 24 | $this->bookRepo = $bookRepo; |
| 28 | parent::__construct(); | 25 | parent::__construct(); |
| 29 | } | 26 | } |
| ... | @@ -36,9 +33,9 @@ class HomeController extends Controller | ... | @@ -36,9 +33,9 @@ class HomeController extends Controller |
| 36 | */ | 33 | */ |
| 37 | public function index() | 34 | public function index() |
| 38 | { | 35 | { |
| 39 | - $books = $this->bookRepo->getAll(10); | 36 | + $activity = Activity::latest(); |
| 40 | - $activity = $this->activityService->latest(); | 37 | + $recentlyViewed = Views::getUserRecentlyViewed(10, 0); |
| 41 | - return view('home', ['books' => $books, 'activity' => $activity]); | 38 | + return view('home', ['activity' => $activity, 'recents' => $recentlyViewed]); |
| 42 | } | 39 | } |
| 43 | 40 | ||
| 44 | } | 41 | } | ... | ... |
| ... | @@ -10,6 +10,7 @@ use BookStack\Http\Requests; | ... | @@ -10,6 +10,7 @@ use BookStack\Http\Requests; |
| 10 | use BookStack\Repos\BookRepo; | 10 | use BookStack\Repos\BookRepo; |
| 11 | use BookStack\Repos\ChapterRepo; | 11 | use BookStack\Repos\ChapterRepo; |
| 12 | use BookStack\Repos\PageRepo; | 12 | use BookStack\Repos\PageRepo; |
| 13 | +use Views; | ||
| 13 | 14 | ||
| 14 | class PageController extends Controller | 15 | class PageController extends Controller |
| 15 | { | 16 | { |
| ... | @@ -86,6 +87,7 @@ class PageController extends Controller | ... | @@ -86,6 +87,7 @@ class PageController extends Controller |
| 86 | { | 87 | { |
| 87 | $book = $this->bookRepo->getBySlug($bookSlug); | 88 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 88 | $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | 89 | $page = $this->pageRepo->getBySlug($pageSlug, $book->id); |
| 90 | + Views::add($page); | ||
| 89 | return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page]); | 91 | return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page]); |
| 90 | } | 92 | } |
| 91 | 93 | ... | ... |
| ... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | ||
| 3 | namespace BookStack\Providers; | 3 | namespace BookStack\Providers; |
| 4 | 4 | ||
| 5 | +use BookStack\Services\ViewService; | ||
| 5 | use Illuminate\Support\ServiceProvider; | 6 | use Illuminate\Support\ServiceProvider; |
| 6 | use BookStack\Services\ActivityService; | 7 | use BookStack\Services\ActivityService; |
| 7 | use BookStack\Services\SettingService; | 8 | use BookStack\Services\SettingService; |
| ... | @@ -29,6 +30,10 @@ class CustomFacadeProvider extends ServiceProvider | ... | @@ -29,6 +30,10 @@ class CustomFacadeProvider extends ServiceProvider |
| 29 | return new ActivityService($this->app->make('BookStack\Activity')); | 30 | return new ActivityService($this->app->make('BookStack\Activity')); |
| 30 | }); | 31 | }); |
| 31 | 32 | ||
| 33 | + $this->app->bind('views', function() { | ||
| 34 | + return new ViewService($this->app->make('BookStack\View')); | ||
| 35 | + }); | ||
| 36 | + | ||
| 32 | $this->app->bind('setting', function() { | 37 | $this->app->bind('setting', function() { |
| 33 | return new SettingService( | 38 | return new SettingService( |
| 34 | $this->app->make('BookStack\Setting'), | 39 | $this->app->make('BookStack\Setting'), | ... | ... |
| 1 | <?php namespace BookStack\Repos; | 1 | <?php namespace BookStack\Repos; |
| 2 | 2 | ||
| 3 | +use BookStack\Activity; | ||
| 3 | use Illuminate\Support\Str; | 4 | use Illuminate\Support\Str; |
| 4 | use BookStack\Book; | 5 | use BookStack\Book; |
| 6 | +use Views; | ||
| 5 | 7 | ||
| 6 | class BookRepo | 8 | class BookRepo |
| 7 | { | 9 | { |
| ... | @@ -20,18 +22,28 @@ class BookRepo | ... | @@ -20,18 +22,28 @@ class BookRepo |
| 20 | $this->pageRepo = $pageRepo; | 22 | $this->pageRepo = $pageRepo; |
| 21 | } | 23 | } |
| 22 | 24 | ||
| 25 | + /** | ||
| 26 | + * Get the book that has the given id. | ||
| 27 | + * @param $id | ||
| 28 | + * @return mixed | ||
| 29 | + */ | ||
| 23 | public function getById($id) | 30 | public function getById($id) |
| 24 | { | 31 | { |
| 25 | return $this->book->findOrFail($id); | 32 | return $this->book->findOrFail($id); |
| 26 | } | 33 | } |
| 27 | 34 | ||
| 35 | + /** | ||
| 36 | + * Get all books, Limited by count. | ||
| 37 | + * @param int $count | ||
| 38 | + * @return mixed | ||
| 39 | + */ | ||
| 28 | public function getAll($count = 10) | 40 | public function getAll($count = 10) |
| 29 | { | 41 | { |
| 30 | return $this->book->orderBy('name', 'asc')->take($count)->get(); | 42 | return $this->book->orderBy('name', 'asc')->take($count)->get(); |
| 31 | } | 43 | } |
| 32 | 44 | ||
| 33 | /** | 45 | /** |
| 34 | - * Getas | 46 | + * Get all books paginated. |
| 35 | * @param int $count | 47 | * @param int $count |
| 36 | * @return mixed | 48 | * @return mixed |
| 37 | */ | 49 | */ |
| ... | @@ -40,6 +52,16 @@ class BookRepo | ... | @@ -40,6 +52,16 @@ class BookRepo |
| 40 | return $this->book->orderBy('name', 'asc')->paginate($count); | 52 | return $this->book->orderBy('name', 'asc')->paginate($count); |
| 41 | } | 53 | } |
| 42 | 54 | ||
| 55 | + public function getRecentlyViewed($count = 10, $page = 0) | ||
| 56 | + { | ||
| 57 | + return Views::getUserRecentlyViewed($count, $page, $this->book); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Get a book by slug | ||
| 62 | + * @param $slug | ||
| 63 | + * @return mixed | ||
| 64 | + */ | ||
| 43 | public function getBySlug($slug) | 65 | public function getBySlug($slug) |
| 44 | { | 66 | { |
| 45 | return $this->book->where('slug', '=', $slug)->first(); | 67 | return $this->book->where('slug', '=', $slug)->first(); |
| ... | @@ -65,11 +87,20 @@ class BookRepo | ... | @@ -65,11 +87,20 @@ class BookRepo |
| 65 | return $this->book->fill($input); | 87 | return $this->book->fill($input); |
| 66 | } | 88 | } |
| 67 | 89 | ||
| 90 | + /** | ||
| 91 | + * Count the amount of books that have a specific slug. | ||
| 92 | + * @param $slug | ||
| 93 | + * @return mixed | ||
| 94 | + */ | ||
| 68 | public function countBySlug($slug) | 95 | public function countBySlug($slug) |
| 69 | { | 96 | { |
| 70 | return $this->book->where('slug', '=', $slug)->count(); | 97 | return $this->book->where('slug', '=', $slug)->count(); |
| 71 | } | 98 | } |
| 72 | 99 | ||
| 100 | + /** | ||
| 101 | + * Destroy a book identified by the given slug. | ||
| 102 | + * @param $bookSlug | ||
| 103 | + */ | ||
| 73 | public function destroyBySlug($bookSlug) | 104 | public function destroyBySlug($bookSlug) |
| 74 | { | 105 | { |
| 75 | $book = $this->getBySlug($bookSlug); | 106 | $book = $this->getBySlug($bookSlug); |
| ... | @@ -84,12 +115,22 @@ class BookRepo | ... | @@ -84,12 +115,22 @@ class BookRepo |
| 84 | $book->delete(); | 115 | $book->delete(); |
| 85 | } | 116 | } |
| 86 | 117 | ||
| 118 | + /** | ||
| 119 | + * Get the next child element priority. | ||
| 120 | + * @param Book $book | ||
| 121 | + * @return int | ||
| 122 | + */ | ||
| 87 | public function getNewPriority($book) | 123 | public function getNewPriority($book) |
| 88 | { | 124 | { |
| 89 | $lastElem = $book->children()->pop(); | 125 | $lastElem = $book->children()->pop(); |
| 90 | return $lastElem ? $lastElem->priority + 1 : 0; | 126 | return $lastElem ? $lastElem->priority + 1 : 0; |
| 91 | } | 127 | } |
| 92 | 128 | ||
| 129 | + /** | ||
| 130 | + * @param string $slug | ||
| 131 | + * @param bool|false $currentId | ||
| 132 | + * @return bool | ||
| 133 | + */ | ||
| 93 | public function doesSlugExist($slug, $currentId = false) | 134 | public function doesSlugExist($slug, $currentId = false) |
| 94 | { | 135 | { |
| 95 | $query = $this->book->where('slug', '=', $slug); | 136 | $query = $this->book->where('slug', '=', $slug); |
| ... | @@ -99,6 +140,13 @@ class BookRepo | ... | @@ -99,6 +140,13 @@ class BookRepo |
| 99 | return $query->count() > 0; | 140 | return $query->count() > 0; |
| 100 | } | 141 | } |
| 101 | 142 | ||
| 143 | + /** | ||
| 144 | + * Provides a suitable slug for the given book name. | ||
| 145 | + * Ensures the returned slug is unique in the system. | ||
| 146 | + * @param string $name | ||
| 147 | + * @param bool|false $currentId | ||
| 148 | + * @return string | ||
| 149 | + */ | ||
| 102 | public function findSuitableSlug($name, $currentId = false) | 150 | public function findSuitableSlug($name, $currentId = false) |
| 103 | { | 151 | { |
| 104 | $originalSlug = Str::slug($name); | 152 | $originalSlug = Str::slug($name); |
| ... | @@ -111,6 +159,11 @@ class BookRepo | ... | @@ -111,6 +159,11 @@ class BookRepo |
| 111 | return $slug; | 159 | return $slug; |
| 112 | } | 160 | } |
| 113 | 161 | ||
| 162 | + /** | ||
| 163 | + * Get books by search term. | ||
| 164 | + * @param $term | ||
| 165 | + * @return mixed | ||
| 166 | + */ | ||
| 114 | public function getBySearch($term) | 167 | public function getBySearch($term) |
| 115 | { | 168 | { |
| 116 | $terms = explode(' ', preg_quote(trim($term))); | 169 | $terms = explode(' ', preg_quote(trim($term))); | ... | ... |
| ... | @@ -17,7 +17,7 @@ class ActivityService | ... | @@ -17,7 +17,7 @@ class ActivityService |
| 17 | public function __construct(Activity $activity) | 17 | public function __construct(Activity $activity) |
| 18 | { | 18 | { |
| 19 | $this->activity = $activity; | 19 | $this->activity = $activity; |
| 20 | - $this->user = Auth::user(); | 20 | + $this->user = auth()->user(); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | /** | 23 | /** | ... | ... |
app/Services/Facades/Views.php
0 → 100644
| 1 | +<?php namespace BookStack\Services\Facades; | ||
| 2 | + | ||
| 3 | +use Illuminate\Support\Facades\Facade; | ||
| 4 | + | ||
| 5 | +class Views extends Facade | ||
| 6 | +{ | ||
| 7 | + /** | ||
| 8 | + * Get the registered name of the component. | ||
| 9 | + * | ||
| 10 | + * @return string | ||
| 11 | + */ | ||
| 12 | + protected static function getFacadeAccessor() { return 'views'; } | ||
| 13 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
app/Services/ViewService.php
0 → 100644
| 1 | +<?php namespace BookStack\Services; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +use BookStack\Entity; | ||
| 5 | +use BookStack\View; | ||
| 6 | + | ||
| 7 | +class ViewService | ||
| 8 | +{ | ||
| 9 | + | ||
| 10 | + protected $view; | ||
| 11 | + protected $user; | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * ViewService constructor. | ||
| 15 | + * @param $view | ||
| 16 | + */ | ||
| 17 | + public function __construct(View $view) | ||
| 18 | + { | ||
| 19 | + $this->view = $view; | ||
| 20 | + $this->user = auth()->user(); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * Add a view to the given entity. | ||
| 25 | + * @param Entity $entity | ||
| 26 | + * @return int | ||
| 27 | + */ | ||
| 28 | + public function add(Entity $entity) | ||
| 29 | + { | ||
| 30 | + $view = $entity->views()->where('user_id', '=', $this->user->id)->first(); | ||
| 31 | + // Add view if model exists | ||
| 32 | + if ($view) { | ||
| 33 | + $view->increment('views'); | ||
| 34 | + return $view->views; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + // Otherwise create new view count | ||
| 38 | + $entity->views()->save($this->view->create([ | ||
| 39 | + 'user_id' => $this->user->id, | ||
| 40 | + 'views' => 1 | ||
| 41 | + ])); | ||
| 42 | + | ||
| 43 | + return 1; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * Get all recently viewed entities for the current user. | ||
| 48 | + * @param int $count | ||
| 49 | + * @param int $page | ||
| 50 | + * @param Entity|bool $filterModel | ||
| 51 | + * @return mixed | ||
| 52 | + */ | ||
| 53 | + public function getUserRecentlyViewed($count = 10, $page = 0, $filterModel = false) | ||
| 54 | + { | ||
| 55 | + $skipCount = $count * $page; | ||
| 56 | + $query = $this->view->where('user_id', '=', auth()->user()->id); | ||
| 57 | + | ||
| 58 | + if ($filterModel) $query->where('viewable_type', '=', get_class($filterModel)); | ||
| 59 | + | ||
| 60 | + $views = $query->with('viewable')->orderBy('updated_at', 'desc')->skip($skipCount)->take($count)->get(); | ||
| 61 | + $viewedEntities = $views->map(function ($item) { | ||
| 62 | + return $item->viewable()->getResults(); | ||
| 63 | + }); | ||
| 64 | + return $viewedEntities; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * Reset all view counts by deleting all views. | ||
| 70 | + */ | ||
| 71 | + public function resetAll() | ||
| 72 | + { | ||
| 73 | + $this->view->truncate(); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + | ||
| 77 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
app/View.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace BookStack; | ||
| 4 | + | ||
| 5 | +use Illuminate\Database\Eloquent\Model; | ||
| 6 | + | ||
| 7 | +class View extends Model | ||
| 8 | +{ | ||
| 9 | + | ||
| 10 | + protected $fillable = ['user_id', 'views']; | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * Get all owning viewable models. | ||
| 14 | + * @return \Illuminate\Database\Eloquent\Relations\MorphTo | ||
| 15 | + */ | ||
| 16 | + public function viewable() | ||
| 17 | + { | ||
| 18 | + return $this->morphTo(); | ||
| 19 | + } | ||
| 20 | +} |
| ... | @@ -214,6 +214,7 @@ return [ | ... | @@ -214,6 +214,7 @@ return [ |
| 214 | 214 | ||
| 215 | 'Activity' => BookStack\Services\Facades\Activity::class, | 215 | 'Activity' => BookStack\Services\Facades\Activity::class, |
| 216 | 'Setting' => BookStack\Services\Facades\Setting::class, | 216 | 'Setting' => BookStack\Services\Facades\Setting::class, |
| 217 | + 'Views' => BookStack\Services\Facades\Views::class, | ||
| 217 | 218 | ||
| 218 | ], | 219 | ], |
| 219 | 220 | ... | ... |
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +use Illuminate\Database\Schema\Blueprint; | ||
| 4 | +use Illuminate\Database\Migrations\Migration; | ||
| 5 | + | ||
| 6 | +class CreateViewsTable extends Migration | ||
| 7 | +{ | ||
| 8 | + /** | ||
| 9 | + * Run the migrations. | ||
| 10 | + * | ||
| 11 | + * @return void | ||
| 12 | + */ | ||
| 13 | + public function up() | ||
| 14 | + { | ||
| 15 | + Schema::create('views', function (Blueprint $table) { | ||
| 16 | + $table->increments('id'); | ||
| 17 | + $table->integer('user_id'); | ||
| 18 | + $table->integer('viewable_id'); | ||
| 19 | + $table->string('viewable_type'); | ||
| 20 | + $table->integer('views'); | ||
| 21 | + $table->timestamps(); | ||
| 22 | + }); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * Reverse the migrations. | ||
| 27 | + * | ||
| 28 | + * @return void | ||
| 29 | + */ | ||
| 30 | + public function down() | ||
| 31 | + { | ||
| 32 | + Schema::drop('views'); | ||
| 33 | + } | ||
| 34 | +} |
| ... | @@ -5,8 +5,8 @@ | ... | @@ -5,8 +5,8 @@ |
| 5 | <div class="faded-small"> | 5 | <div class="faded-small"> |
| 6 | <div class="container"> | 6 | <div class="container"> |
| 7 | <div class="row"> | 7 | <div class="row"> |
| 8 | - <div class="col-md-6"></div> | 8 | + <div class="col-xs-1"></div> |
| 9 | - <div class="col-md-6 faded"> | 9 | + <div class="col-xs-11 faded"> |
| 10 | <div class="action-buttons"> | 10 | <div class="action-buttons"> |
| 11 | @if($currentUser->can('book-create')) | 11 | @if($currentUser->can('book-create')) |
| 12 | <a href="/books/create" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a> | 12 | <a href="/books/create" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a> |
| ... | @@ -20,7 +20,7 @@ | ... | @@ -20,7 +20,7 @@ |
| 20 | 20 | ||
| 21 | <div class="container"> | 21 | <div class="container"> |
| 22 | <div class="row"> | 22 | <div class="row"> |
| 23 | - <div class="col-md-8"> | 23 | + <div class="col-sm-7"> |
| 24 | <h1>Books</h1> | 24 | <h1>Books</h1> |
| 25 | @if(count($books) > 0) | 25 | @if(count($books) > 0) |
| 26 | @foreach($books as $book) | 26 | @foreach($books as $book) |
| ... | @@ -33,7 +33,11 @@ | ... | @@ -33,7 +33,11 @@ |
| 33 | <a href="/books/create" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a> | 33 | <a href="/books/create" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a> |
| 34 | @endif | 34 | @endif |
| 35 | </div> | 35 | </div> |
| 36 | - <div class="col-md-4"></div> | 36 | + <div class="col-sm-4 col-sm-offset-1"> |
| 37 | + <div class="margin-top large"> </div> | ||
| 38 | + <h3>Recently Viewed</h3> | ||
| 39 | + @include('partials/entity-list', ['entities' => $recents]) | ||
| 40 | + </div> | ||
| 37 | </div> | 41 | </div> |
| 38 | </div> | 42 | </div> |
| 39 | 43 | ... | ... |
| ... | @@ -4,26 +4,18 @@ | ... | @@ -4,26 +4,18 @@ |
| 4 | 4 | ||
| 5 | <div class="container"> | 5 | <div class="container"> |
| 6 | <div class="row"> | 6 | <div class="row"> |
| 7 | + | ||
| 7 | <div class="col-md-7"> | 8 | <div class="col-md-7"> |
| 8 | - <h2>Books</h2> | 9 | + <h2>My Recently Viewed</h2> |
| 9 | - @if(count($books) > 0) | 10 | + @include('partials/entity-list', ['entities' => $recents]) |
| 10 | - @foreach($books as $book) | ||
| 11 | - @include('books/list-item', ['book' => $book]) | ||
| 12 | - <hr> | ||
| 13 | - @endforeach | ||
| 14 | - @if(count($books) === 10) | ||
| 15 | - <a href="/books">View all books »</a> | ||
| 16 | - @endif | ||
| 17 | - @else | ||
| 18 | - <p class="text-muted">No books have been created.</p> | ||
| 19 | - <a href="/books/create" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a> | ||
| 20 | - @endif | ||
| 21 | </div> | 11 | </div> |
| 12 | + | ||
| 22 | <div class="col-md-4 col-md-offset-1"> | 13 | <div class="col-md-4 col-md-offset-1"> |
| 23 | <div class="margin-top large"> </div> | 14 | <div class="margin-top large"> </div> |
| 24 | <h3>Recent Activity</h3> | 15 | <h3>Recent Activity</h3> |
| 25 | @include('partials/activity-list', ['activity' => $activity]) | 16 | @include('partials/activity-list', ['activity' => $activity]) |
| 26 | </div> | 17 | </div> |
| 18 | + | ||
| 27 | </div> | 19 | </div> |
| 28 | </div> | 20 | </div> |
| 29 | 21 | ... | ... |
| 1 | + | ||
| 2 | +@if(count($entities) > 0) | ||
| 3 | + @foreach($entities as $entity) | ||
| 4 | + @if($entity->isA('page')) | ||
| 5 | + @include('pages/list-item', ['page' => $entity]) | ||
| 6 | + @elseif($entity->isA('book')) | ||
| 7 | + @include('books/list-item', ['book' => $entity]) | ||
| 8 | + @elseif($entity->isA('chapter')) | ||
| 9 | + @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true]) | ||
| 10 | + @endif | ||
| 11 | + <hr> | ||
| 12 | + @endforeach | ||
| 13 | +@else | ||
| 14 | + <p class="text-muted"> | ||
| 15 | + No items available :( | ||
| 16 | + </p> | ||
| 17 | +@endif | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or sign in to post a comment