Showing
61 changed files
with
1120 additions
and
379 deletions
| ... | @@ -98,7 +98,7 @@ abstract class Entity extends Model | ... | @@ -98,7 +98,7 @@ abstract class Entity extends Model |
| 98 | * @param string[] array $wheres | 98 | * @param string[] array $wheres |
| 99 | * @return mixed | 99 | * @return mixed |
| 100 | */ | 100 | */ |
| 101 | - public static function fullTextSearch($fieldsToSearch, $terms, $wheres = []) | 101 | + public static function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = []) |
| 102 | { | 102 | { |
| 103 | $termString = ''; | 103 | $termString = ''; |
| 104 | foreach ($terms as $term) { | 104 | foreach ($terms as $term) { |
| ... | @@ -107,7 +107,7 @@ abstract class Entity extends Model | ... | @@ -107,7 +107,7 @@ abstract class Entity extends Model |
| 107 | $fields = implode(',', $fieldsToSearch); | 107 | $fields = implode(',', $fieldsToSearch); |
| 108 | $termStringEscaped = \DB::connection()->getPdo()->quote($termString); | 108 | $termStringEscaped = \DB::connection()->getPdo()->quote($termString); |
| 109 | $search = static::addSelect(\DB::raw('*, MATCH(name) AGAINST('.$termStringEscaped.' IN BOOLEAN MODE) AS title_relevance')); | 109 | $search = static::addSelect(\DB::raw('*, MATCH(name) AGAINST('.$termStringEscaped.' IN BOOLEAN MODE) AS title_relevance')); |
| 110 | - $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termStringEscaped]); | 110 | + $search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]); |
| 111 | 111 | ||
| 112 | // Add additional where terms | 112 | // Add additional where terms |
| 113 | foreach ($wheres as $whereTerm) { | 113 | foreach ($wheres as $whereTerm) { |
| ... | @@ -115,10 +115,13 @@ abstract class Entity extends Model | ... | @@ -115,10 +115,13 @@ abstract class Entity extends Model |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | // Load in relations | 117 | // Load in relations |
| 118 | - if (!static::isA('book')) $search = $search->with('book'); | 118 | + if (static::isA('page')) { |
| 119 | - if (static::isA('page')) $search = $search->with('chapter'); | 119 | + $search = $search->with('book', 'chapter', 'createdBy', 'updatedBy'); |
| 120 | + } else if (static::isA('chapter')) { | ||
| 121 | + $search = $search->with('book'); | ||
| 122 | + } | ||
| 120 | 123 | ||
| 121 | - return $search->orderBy('title_relevance', 'desc')->get(); | 124 | + return $search->orderBy('title_relevance', 'desc'); |
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | /** | 127 | /** | ... | ... |
| ... | @@ -157,7 +157,7 @@ class BookController extends Controller | ... | @@ -157,7 +157,7 @@ class BookController extends Controller |
| 157 | $this->checkPermission('book-update'); | 157 | $this->checkPermission('book-update'); |
| 158 | $book = $this->bookRepo->getBySlug($bookSlug); | 158 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 159 | $bookChildren = $this->bookRepo->getChildren($book); | 159 | $bookChildren = $this->bookRepo->getChildren($book); |
| 160 | - $books = $this->bookRepo->getAll(); | 160 | + $books = $this->bookRepo->getAll(false); |
| 161 | $this->setPageTitle('Sort Book ' . $book->getShortName()); | 161 | $this->setPageTitle('Sort Book ' . $book->getShortName()); |
| 162 | return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]); | 162 | return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]); |
| 163 | } | 163 | } | ... | ... |
| ... | @@ -3,25 +3,21 @@ | ... | @@ -3,25 +3,21 @@ |
| 3 | namespace BookStack\Http\Controllers; | 3 | namespace BookStack\Http\Controllers; |
| 4 | 4 | ||
| 5 | use Activity; | 5 | use Activity; |
| 6 | -use Illuminate\Http\Request; | 6 | +use BookStack\Repos\EntityRepo; |
| 7 | - | ||
| 8 | use BookStack\Http\Requests; | 7 | use BookStack\Http\Requests; |
| 9 | -use BookStack\Repos\BookRepo; | ||
| 10 | use Views; | 8 | use Views; |
| 11 | 9 | ||
| 12 | class HomeController extends Controller | 10 | class HomeController extends Controller |
| 13 | { | 11 | { |
| 14 | - | 12 | + protected $entityRepo; |
| 15 | - protected $activityService; | ||
| 16 | - protected $bookRepo; | ||
| 17 | 13 | ||
| 18 | /** | 14 | /** |
| 19 | * HomeController constructor. | 15 | * HomeController constructor. |
| 20 | - * @param BookRepo $bookRepo | 16 | + * @param EntityRepo $entityRepo |
| 21 | */ | 17 | */ |
| 22 | - public function __construct(BookRepo $bookRepo) | 18 | + public function __construct(EntityRepo $entityRepo) |
| 23 | { | 19 | { |
| 24 | - $this->bookRepo = $bookRepo; | 20 | + $this->entityRepo = $entityRepo; |
| 25 | parent::__construct(); | 21 | parent::__construct(); |
| 26 | } | 22 | } |
| 27 | 23 | ||
| ... | @@ -33,9 +29,16 @@ class HomeController extends Controller | ... | @@ -33,9 +29,16 @@ class HomeController extends Controller |
| 33 | */ | 29 | */ |
| 34 | public function index() | 30 | public function index() |
| 35 | { | 31 | { |
| 36 | - $activity = Activity::latest(); | 32 | + $activity = Activity::latest(10); |
| 37 | - $recents = $this->signedIn ? Views::getUserRecentlyViewed(10, 0) : $this->bookRepo->getLatest(10); | 33 | + $recents = $this->signedIn ? Views::getUserRecentlyViewed(12, 0) : $this->entityRepo->getRecentlyCreatedBooks(10); |
| 38 | - return view('home', ['activity' => $activity, 'recents' => $recents]); | 34 | + $recentlyCreatedPages = $this->entityRepo->getRecentlyCreatedPages(5); |
| 35 | + $recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdatedPages(5); | ||
| 36 | + return view('home', [ | ||
| 37 | + 'activity' => $activity, | ||
| 38 | + 'recents' => $recents, | ||
| 39 | + 'recentlyCreatedPages' => $recentlyCreatedPages, | ||
| 40 | + 'recentlyUpdatedPages' => $recentlyUpdatedPages | ||
| 41 | + ]); | ||
| 39 | } | 42 | } |
| 40 | 43 | ||
| 41 | } | 44 | } | ... | ... |
| ... | @@ -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 Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
| 14 | use Views; | 15 | use Views; |
| 15 | 16 | ||
| 16 | class PageController extends Controller | 17 | class PageController extends Controller |
| ... | @@ -81,6 +82,8 @@ class PageController extends Controller | ... | @@ -81,6 +82,8 @@ class PageController extends Controller |
| 81 | 82 | ||
| 82 | /** | 83 | /** |
| 83 | * Display the specified page. | 84 | * Display the specified page. |
| 85 | + * If the page is not found via the slug the | ||
| 86 | + * revisions are searched for a match. | ||
| 84 | * | 87 | * |
| 85 | * @param $bookSlug | 88 | * @param $bookSlug |
| 86 | * @param $pageSlug | 89 | * @param $pageSlug |
| ... | @@ -89,7 +92,15 @@ class PageController extends Controller | ... | @@ -89,7 +92,15 @@ class PageController extends Controller |
| 89 | public function show($bookSlug, $pageSlug) | 92 | public function show($bookSlug, $pageSlug) |
| 90 | { | 93 | { |
| 91 | $book = $this->bookRepo->getBySlug($bookSlug); | 94 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 92 | - $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | 95 | + |
| 96 | + try { | ||
| 97 | + $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | ||
| 98 | + } catch (NotFoundHttpException $e) { | ||
| 99 | + $page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug); | ||
| 100 | + if ($page === null) abort(404); | ||
| 101 | + return redirect($page->getUrl()); | ||
| 102 | + } | ||
| 103 | + | ||
| 93 | $sidebarTree = $this->bookRepo->getChildren($book); | 104 | $sidebarTree = $this->bookRepo->getChildren($book); |
| 94 | Views::add($page); | 105 | Views::add($page); |
| 95 | $this->setPageTitle($page->getShortName()); | 106 | $this->setPageTitle($page->getShortName()); |
| ... | @@ -278,4 +289,30 @@ class PageController extends Controller | ... | @@ -278,4 +289,30 @@ class PageController extends Controller |
| 278 | ]); | 289 | ]); |
| 279 | } | 290 | } |
| 280 | 291 | ||
| 292 | + /** | ||
| 293 | + * Show a listing of recently created pages | ||
| 294 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 295 | + */ | ||
| 296 | + public function showRecentlyCreated() | ||
| 297 | + { | ||
| 298 | + $pages = $this->pageRepo->getRecentlyCreatedPaginated(20); | ||
| 299 | + return view('pages/detailed-listing', [ | ||
| 300 | + 'title' => 'Recently Created Pages', | ||
| 301 | + 'pages' => $pages | ||
| 302 | + ]); | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + /** | ||
| 306 | + * Show a listing of recently created pages | ||
| 307 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 308 | + */ | ||
| 309 | + public function showRecentlyUpdated() | ||
| 310 | + { | ||
| 311 | + $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20); | ||
| 312 | + return view('pages/detailed-listing', [ | ||
| 313 | + 'title' => 'Recently Updated Pages', | ||
| 314 | + 'pages' => $pages | ||
| 315 | + ]); | ||
| 316 | + } | ||
| 317 | + | ||
| 281 | } | 318 | } | ... | ... |
| ... | @@ -42,11 +42,77 @@ class SearchController extends Controller | ... | @@ -42,11 +42,77 @@ class SearchController extends Controller |
| 42 | return redirect()->back(); | 42 | return redirect()->back(); |
| 43 | } | 43 | } |
| 44 | $searchTerm = $request->get('term'); | 44 | $searchTerm = $request->get('term'); |
| 45 | - $pages = $this->pageRepo->getBySearch($searchTerm); | 45 | + $paginationAppends = $request->only('term'); |
| 46 | - $books = $this->bookRepo->getBySearch($searchTerm); | 46 | + $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends); |
| 47 | - $chapters = $this->chapterRepo->getBySearch($searchTerm); | 47 | + $books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends); |
| 48 | + $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends); | ||
| 48 | $this->setPageTitle('Search For ' . $searchTerm); | 49 | $this->setPageTitle('Search For ' . $searchTerm); |
| 49 | - return view('search/all', ['pages' => $pages, 'books' => $books, 'chapters' => $chapters, 'searchTerm' => $searchTerm]); | 50 | + return view('search/all', [ |
| 51 | + 'pages' => $pages, | ||
| 52 | + 'books' => $books, | ||
| 53 | + 'chapters' => $chapters, | ||
| 54 | + 'searchTerm' => $searchTerm | ||
| 55 | + ]); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * Search only the pages in the system. | ||
| 60 | + * @param Request $request | ||
| 61 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||
| 62 | + */ | ||
| 63 | + public function searchPages(Request $request) | ||
| 64 | + { | ||
| 65 | + if (!$request->has('term')) return redirect()->back(); | ||
| 66 | + | ||
| 67 | + $searchTerm = $request->get('term'); | ||
| 68 | + $paginationAppends = $request->only('term'); | ||
| 69 | + $pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends); | ||
| 70 | + $this->setPageTitle('Page Search For ' . $searchTerm); | ||
| 71 | + return view('search/entity-search-list', [ | ||
| 72 | + 'entities' => $pages, | ||
| 73 | + 'title' => 'Page Search Results', | ||
| 74 | + 'searchTerm' => $searchTerm | ||
| 75 | + ]); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * Search only the chapters in the system. | ||
| 80 | + * @param Request $request | ||
| 81 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||
| 82 | + */ | ||
| 83 | + public function searchChapters(Request $request) | ||
| 84 | + { | ||
| 85 | + if (!$request->has('term')) return redirect()->back(); | ||
| 86 | + | ||
| 87 | + $searchTerm = $request->get('term'); | ||
| 88 | + $paginationAppends = $request->only('term'); | ||
| 89 | + $chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends); | ||
| 90 | + $this->setPageTitle('Chapter Search For ' . $searchTerm); | ||
| 91 | + return view('search/entity-search-list', [ | ||
| 92 | + 'entities' => $chapters, | ||
| 93 | + 'title' => 'Chapter Search Results', | ||
| 94 | + 'searchTerm' => $searchTerm | ||
| 95 | + ]); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + * Search only the books in the system. | ||
| 100 | + * @param Request $request | ||
| 101 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||
| 102 | + */ | ||
| 103 | + public function searchBooks(Request $request) | ||
| 104 | + { | ||
| 105 | + if (!$request->has('term')) return redirect()->back(); | ||
| 106 | + | ||
| 107 | + $searchTerm = $request->get('term'); | ||
| 108 | + $paginationAppends = $request->only('term'); | ||
| 109 | + $books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends); | ||
| 110 | + $this->setPageTitle('Book Search For ' . $searchTerm); | ||
| 111 | + return view('search/entity-search-list', [ | ||
| 112 | + 'entities' => $books, | ||
| 113 | + 'title' => 'Book Search Results', | ||
| 114 | + 'searchTerm' => $searchTerm | ||
| 115 | + ]); | ||
| 50 | } | 116 | } |
| 51 | 117 | ||
| 52 | /** | 118 | /** | ... | ... |
| ... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | ||
| 3 | namespace BookStack\Http\Controllers; | 3 | namespace BookStack\Http\Controllers; |
| 4 | 4 | ||
| 5 | +use BookStack\Activity; | ||
| 5 | use Illuminate\Http\Request; | 6 | use Illuminate\Http\Request; |
| 6 | 7 | ||
| 7 | use Illuminate\Http\Response; | 8 | use Illuminate\Http\Response; |
| ... | @@ -92,10 +93,9 @@ class UserController extends Controller | ... | @@ -92,10 +93,9 @@ class UserController extends Controller |
| 92 | $user->save(); | 93 | $user->save(); |
| 93 | } | 94 | } |
| 94 | 95 | ||
| 95 | - return redirect('/users'); | 96 | + return redirect('/settings/users'); |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | - | ||
| 99 | /** | 99 | /** |
| 100 | * Show the form for editing the specified user. | 100 | * Show the form for editing the specified user. |
| 101 | * @param int $id | 101 | * @param int $id |
| ... | @@ -159,7 +159,7 @@ class UserController extends Controller | ... | @@ -159,7 +159,7 @@ class UserController extends Controller |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | $user->save(); | 161 | $user->save(); |
| 162 | - return redirect('/users'); | 162 | + return redirect('/settings/users'); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | /** | 165 | /** |
| ... | @@ -197,6 +197,25 @@ class UserController extends Controller | ... | @@ -197,6 +197,25 @@ class UserController extends Controller |
| 197 | } | 197 | } |
| 198 | $this->userRepo->destroy($user); | 198 | $this->userRepo->destroy($user); |
| 199 | 199 | ||
| 200 | - return redirect('/users'); | 200 | + return redirect('/settings/users'); |
| 201 | + } | ||
| 202 | + | ||
| 203 | + /** | ||
| 204 | + * Show the user profile page | ||
| 205 | + * @param $id | ||
| 206 | + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||
| 207 | + */ | ||
| 208 | + public function showProfilePage($id) | ||
| 209 | + { | ||
| 210 | + $user = $this->userRepo->getById($id); | ||
| 211 | + $userActivity = $this->userRepo->getActivity($user); | ||
| 212 | + $recentlyCreated = $this->userRepo->getRecentlyCreated($user, 5, 0); | ||
| 213 | + $assetCounts = $this->userRepo->getAssetCounts($user); | ||
| 214 | + return view('users/profile', [ | ||
| 215 | + 'user' => $user, | ||
| 216 | + 'activity' => $userActivity, | ||
| 217 | + 'recentlyCreated' => $recentlyCreated, | ||
| 218 | + 'assetCounts' => $assetCounts | ||
| 219 | + ]); | ||
| 201 | } | 220 | } |
| 202 | } | 221 | } | ... | ... |
| ... | @@ -3,6 +3,11 @@ | ... | @@ -3,6 +3,11 @@ |
| 3 | // Authenticated routes... | 3 | // Authenticated routes... |
| 4 | Route::group(['middleware' => 'auth'], function () { | 4 | Route::group(['middleware' => 'auth'], function () { |
| 5 | 5 | ||
| 6 | + Route::group(['prefix' => 'pages'], function() { | ||
| 7 | + Route::get('/recently-created', 'PageController@showRecentlyCreated'); | ||
| 8 | + Route::get('/recently-updated', 'PageController@showRecentlyUpdated'); | ||
| 9 | + }); | ||
| 10 | + | ||
| 6 | Route::group(['prefix' => 'books'], function () { | 11 | Route::group(['prefix' => 'books'], function () { |
| 7 | 12 | ||
| 8 | // Books | 13 | // Books |
| ... | @@ -47,14 +52,8 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -47,14 +52,8 @@ Route::group(['middleware' => 'auth'], function () { |
| 47 | 52 | ||
| 48 | }); | 53 | }); |
| 49 | 54 | ||
| 50 | - // Users | 55 | + // User Profile routes |
| 51 | - Route::get('/users', 'UserController@index'); | 56 | + Route::get('/user/{userId}', 'UserController@showProfilePage'); |
| 52 | - Route::get('/users/create', 'UserController@create'); | ||
| 53 | - Route::get('/users/{id}/delete', 'UserController@delete'); | ||
| 54 | - Route::post('/users/create', 'UserController@store'); | ||
| 55 | - Route::get('/users/{id}', 'UserController@edit'); | ||
| 56 | - Route::put('/users/{id}', 'UserController@update'); | ||
| 57 | - Route::delete('/users/{id}', 'UserController@destroy'); | ||
| 58 | 57 | ||
| 59 | // Image routes | 58 | // Image routes |
| 60 | Route::group(['prefix' => 'images'], function() { | 59 | Route::group(['prefix' => 'images'], function() { |
| ... | @@ -75,6 +74,9 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -75,6 +74,9 @@ Route::group(['middleware' => 'auth'], function () { |
| 75 | 74 | ||
| 76 | // Search | 75 | // Search |
| 77 | Route::get('/search/all', 'SearchController@searchAll'); | 76 | Route::get('/search/all', 'SearchController@searchAll'); |
| 77 | + Route::get('/search/pages', 'SearchController@searchPages'); | ||
| 78 | + Route::get('/search/books', 'SearchController@searchBooks'); | ||
| 79 | + Route::get('/search/chapters', 'SearchController@searchChapters'); | ||
| 78 | Route::get('/search/book/{bookId}', 'SearchController@searchBook'); | 80 | Route::get('/search/book/{bookId}', 'SearchController@searchBook'); |
| 79 | 81 | ||
| 80 | // Other Pages | 82 | // Other Pages |
| ... | @@ -82,8 +84,18 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -82,8 +84,18 @@ Route::group(['middleware' => 'auth'], function () { |
| 82 | Route::get('/home', 'HomeController@index'); | 84 | Route::get('/home', 'HomeController@index'); |
| 83 | 85 | ||
| 84 | // Settings | 86 | // Settings |
| 85 | - Route::get('/settings', 'SettingController@index'); | 87 | + Route::group(['prefix' => 'settings'], function() { |
| 86 | - Route::post('/settings', 'SettingController@update'); | 88 | + Route::get('/', 'SettingController@index'); |
| 89 | + Route::post('/', 'SettingController@update'); | ||
| 90 | + // Users | ||
| 91 | + Route::get('/users', 'UserController@index'); | ||
| 92 | + Route::get('/users/create', 'UserController@create'); | ||
| 93 | + Route::get('/users/{id}/delete', 'UserController@delete'); | ||
| 94 | + Route::post('/users/create', 'UserController@store'); | ||
| 95 | + Route::get('/users/{id}', 'UserController@edit'); | ||
| 96 | + Route::put('/users/{id}', 'UserController@update'); | ||
| 97 | + Route::delete('/users/{id}', 'UserController@destroy'); | ||
| 98 | + }); | ||
| 87 | 99 | ||
| 88 | }); | 100 | }); |
| 89 | 101 | ... | ... |
| ... | @@ -45,7 +45,8 @@ class Page extends Entity | ... | @@ -45,7 +45,8 @@ class Page extends Entity |
| 45 | 45 | ||
| 46 | public function getExcerpt($length = 100) | 46 | public function getExcerpt($length = 100) |
| 47 | { | 47 | { |
| 48 | - return strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text; | 48 | + $text = strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text; |
| 49 | + return mb_convert_encoding($text, 'UTF-8'); | ||
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | } | 52 | } | ... | ... |
| ... | @@ -14,8 +14,8 @@ class BookRepo | ... | @@ -14,8 +14,8 @@ class BookRepo |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * BookRepo constructor. | 16 | * BookRepo constructor. |
| 17 | - * @param Book $book | 17 | + * @param Book $book |
| 18 | - * @param PageRepo $pageRepo | 18 | + * @param PageRepo $pageRepo |
| 19 | * @param ChapterRepo $chapterRepo | 19 | * @param ChapterRepo $chapterRepo |
| 20 | */ | 20 | */ |
| 21 | public function __construct(Book $book, PageRepo $pageRepo, ChapterRepo $chapterRepo) | 21 | public function __construct(Book $book, PageRepo $pageRepo, ChapterRepo $chapterRepo) |
| ... | @@ -42,7 +42,9 @@ class BookRepo | ... | @@ -42,7 +42,9 @@ class BookRepo |
| 42 | */ | 42 | */ |
| 43 | public function getAll($count = 10) | 43 | public function getAll($count = 10) |
| 44 | { | 44 | { |
| 45 | - return $this->book->orderBy('name', 'asc')->take($count)->get(); | 45 | + $bookQuery = $this->book->orderBy('name', 'asc'); |
| 46 | + if (!$count) return $bookQuery->get(); | ||
| 47 | + return $bookQuery->take($count)->get(); | ||
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | /** | 50 | /** |
| ... | @@ -159,7 +161,7 @@ class BookRepo | ... | @@ -159,7 +161,7 @@ class BookRepo |
| 159 | } | 161 | } |
| 160 | 162 | ||
| 161 | /** | 163 | /** |
| 162 | - * @param string $slug | 164 | + * @param string $slug |
| 163 | * @param bool|false $currentId | 165 | * @param bool|false $currentId |
| 164 | * @return bool | 166 | * @return bool |
| 165 | */ | 167 | */ |
| ... | @@ -175,7 +177,7 @@ class BookRepo | ... | @@ -175,7 +177,7 @@ class BookRepo |
| 175 | /** | 177 | /** |
| 176 | * Provides a suitable slug for the given book name. | 178 | * Provides a suitable slug for the given book name. |
| 177 | * Ensures the returned slug is unique in the system. | 179 | * Ensures the returned slug is unique in the system. |
| 178 | - * @param string $name | 180 | + * @param string $name |
| 179 | * @param bool|false $currentId | 181 | * @param bool|false $currentId |
| 180 | * @return string | 182 | * @return string |
| 181 | */ | 183 | */ |
| ... | @@ -218,12 +220,15 @@ class BookRepo | ... | @@ -218,12 +220,15 @@ class BookRepo |
| 218 | /** | 220 | /** |
| 219 | * Get books by search term. | 221 | * Get books by search term. |
| 220 | * @param $term | 222 | * @param $term |
| 223 | + * @param int $count | ||
| 224 | + * @param array $paginationAppends | ||
| 221 | * @return mixed | 225 | * @return mixed |
| 222 | */ | 226 | */ |
| 223 | - public function getBySearch($term) | 227 | + public function getBySearch($term, $count = 20, $paginationAppends = []) |
| 224 | { | 228 | { |
| 225 | $terms = explode(' ', $term); | 229 | $terms = explode(' ', $term); |
| 226 | - $books = $this->book->fullTextSearch(['name', 'description'], $terms); | 230 | + $books = $this->book->fullTextSearchQuery(['name', 'description'], $terms) |
| 231 | + ->paginate($count)->appends($paginationAppends); | ||
| 227 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); | 232 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); |
| 228 | foreach ($books as $book) { | 233 | foreach ($books as $book) { |
| 229 | //highlight | 234 | //highlight | ... | ... |
| ... | @@ -125,12 +125,15 @@ class ChapterRepo | ... | @@ -125,12 +125,15 @@ class ChapterRepo |
| 125 | * Get chapters by the given search term. | 125 | * Get chapters by the given search term. |
| 126 | * @param $term | 126 | * @param $term |
| 127 | * @param array $whereTerms | 127 | * @param array $whereTerms |
| 128 | + * @param int $count | ||
| 129 | + * @param array $paginationAppends | ||
| 128 | * @return mixed | 130 | * @return mixed |
| 129 | */ | 131 | */ |
| 130 | - public function getBySearch($term, $whereTerms = []) | 132 | + public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = []) |
| 131 | { | 133 | { |
| 132 | $terms = explode(' ', $term); | 134 | $terms = explode(' ', $term); |
| 133 | - $chapters = $this->chapter->fullTextSearch(['name', 'description'], $terms, $whereTerms); | 135 | + $chapters = $this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms) |
| 136 | + ->paginate($count)->appends($paginationAppends); | ||
| 134 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); | 137 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); |
| 135 | foreach ($chapters as $chapter) { | 138 | foreach ($chapters as $chapter) { |
| 136 | //highlight | 139 | //highlight | ... | ... |
app/Repos/EntityRepo.php
0 → 100644
| 1 | +<?php namespace BookStack\Repos; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +use BookStack\Book; | ||
| 5 | +use BookStack\Chapter; | ||
| 6 | +use BookStack\Page; | ||
| 7 | + | ||
| 8 | +class EntityRepo | ||
| 9 | +{ | ||
| 10 | + | ||
| 11 | + public $book; | ||
| 12 | + public $chapter; | ||
| 13 | + public $page; | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * EntityService constructor. | ||
| 17 | + * @param $book | ||
| 18 | + * @param $chapter | ||
| 19 | + * @param $page | ||
| 20 | + */ | ||
| 21 | + public function __construct(Book $book, Chapter $chapter, Page $page) | ||
| 22 | + { | ||
| 23 | + $this->book = $book; | ||
| 24 | + $this->chapter = $chapter; | ||
| 25 | + $this->page = $page; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * Get the latest books added to the system. | ||
| 30 | + * @param $count | ||
| 31 | + * @param $page | ||
| 32 | + */ | ||
| 33 | + public function getRecentlyCreatedBooks($count = 20, $page = 0) | ||
| 34 | + { | ||
| 35 | + return $this->book->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get(); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * Get the most recently updated books. | ||
| 40 | + * @param $count | ||
| 41 | + * @param int $page | ||
| 42 | + * @return mixed | ||
| 43 | + */ | ||
| 44 | + public function getRecentlyUpdatedBooks($count = 20, $page = 0) | ||
| 45 | + { | ||
| 46 | + return $this->book->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get(); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * Get the latest pages added to the system. | ||
| 51 | + * @param $count | ||
| 52 | + * @param $page | ||
| 53 | + */ | ||
| 54 | + public function getRecentlyCreatedPages($count = 20, $page = 0) | ||
| 55 | + { | ||
| 56 | + return $this->page->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get(); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * Get the most recently updated pages. | ||
| 61 | + * @param $count | ||
| 62 | + * @param int $page | ||
| 63 | + * @return mixed | ||
| 64 | + */ | ||
| 65 | + public function getRecentlyUpdatedPages($count = 20, $page = 0) | ||
| 66 | + { | ||
| 67 | + return $this->page->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get(); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Log; | ... | @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Log; |
| 10 | use Illuminate\Support\Str; | 10 | use Illuminate\Support\Str; |
| 11 | use BookStack\Page; | 11 | use BookStack\Page; |
| 12 | use BookStack\PageRevision; | 12 | use BookStack\PageRevision; |
| 13 | +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
| 13 | 14 | ||
| 14 | class PageRepo | 15 | class PageRepo |
| 15 | { | 16 | { |
| ... | @@ -65,11 +66,28 @@ class PageRepo | ... | @@ -65,11 +66,28 @@ class PageRepo |
| 65 | public function getBySlug($slug, $bookId) | 66 | public function getBySlug($slug, $bookId) |
| 66 | { | 67 | { |
| 67 | $page = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first(); | 68 | $page = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first(); |
| 68 | - if ($page === null) abort(404); | 69 | + if ($page === null) throw new NotFoundHttpException('Page not found'); |
| 69 | return $page; | 70 | return $page; |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | /** | 73 | /** |
| 74 | + * Search through page revisions and retrieve | ||
| 75 | + * the last page in the current book that | ||
| 76 | + * has a slug equal to the one given. | ||
| 77 | + * @param $pageSlug | ||
| 78 | + * @param $bookSlug | ||
| 79 | + * @return null | Page | ||
| 80 | + */ | ||
| 81 | + public function findPageUsingOldSlug($pageSlug, $bookSlug) | ||
| 82 | + { | ||
| 83 | + $revision = $this->pageRevision->where('slug', '=', $pageSlug) | ||
| 84 | + ->where('book_slug', '=', $bookSlug)->orderBy('created_at', 'desc') | ||
| 85 | + ->with('page')->first(); | ||
| 86 | + return $revision !== null ? $revision->page : null; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * Get a new Page instance from the given input. | ||
| 73 | * @param $input | 91 | * @param $input |
| 74 | * @return Page | 92 | * @return Page |
| 75 | */ | 93 | */ |
| ... | @@ -125,21 +143,20 @@ class PageRepo | ... | @@ -125,21 +143,20 @@ class PageRepo |
| 125 | if($htmlText == '') return $htmlText; | 143 | if($htmlText == '') return $htmlText; |
| 126 | libxml_use_internal_errors(true); | 144 | libxml_use_internal_errors(true); |
| 127 | $doc = new \DOMDocument(); | 145 | $doc = new \DOMDocument(); |
| 128 | - $doc->loadHTML($htmlText); | 146 | + $doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8')); |
| 129 | 147 | ||
| 130 | $container = $doc->documentElement; | 148 | $container = $doc->documentElement; |
| 131 | $body = $container->childNodes->item(0); | 149 | $body = $container->childNodes->item(0); |
| 132 | $childNodes = $body->childNodes; | 150 | $childNodes = $body->childNodes; |
| 133 | 151 | ||
| 134 | // Ensure no duplicate ids are used | 152 | // Ensure no duplicate ids are used |
| 135 | - $lastId = false; | ||
| 136 | $idArray = []; | 153 | $idArray = []; |
| 137 | 154 | ||
| 138 | foreach ($childNodes as $index => $childNode) { | 155 | foreach ($childNodes as $index => $childNode) { |
| 139 | /** @var \DOMElement $childNode */ | 156 | /** @var \DOMElement $childNode */ |
| 140 | if (get_class($childNode) !== 'DOMElement') continue; | 157 | if (get_class($childNode) !== 'DOMElement') continue; |
| 141 | 158 | ||
| 142 | - // Overwrite id if not a bookstack custom id | 159 | + // Overwrite id if not a BookStack custom id |
| 143 | if ($childNode->hasAttribute('id')) { | 160 | if ($childNode->hasAttribute('id')) { |
| 144 | $id = $childNode->getAttribute('id'); | 161 | $id = $childNode->getAttribute('id'); |
| 145 | if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) { | 162 | if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) { |
| ... | @@ -149,13 +166,18 @@ class PageRepo | ... | @@ -149,13 +166,18 @@ class PageRepo |
| 149 | } | 166 | } |
| 150 | 167 | ||
| 151 | // Create an unique id for the element | 168 | // Create an unique id for the element |
| 152 | - do { | 169 | + // Uses the content as a basis to ensure output is the same every time |
| 153 | - $id = 'bkmrk-' . substr(uniqid(), -5); | 170 | + // the same content is passed through. |
| 154 | - } while ($id == $lastId); | 171 | + $contentId = 'bkmrk-' . substr(strtolower(preg_replace('/\s+/', '-', trim($childNode->nodeValue))), 0, 20); |
| 155 | - $lastId = $id; | 172 | + $newId = urlencode($contentId); |
| 173 | + $loopIndex = 0; | ||
| 174 | + while (in_array($newId, $idArray)) { | ||
| 175 | + $newId = urlencode($contentId . '-' . $loopIndex); | ||
| 176 | + $loopIndex++; | ||
| 177 | + } | ||
| 156 | 178 | ||
| 157 | - $childNode->setAttribute('id', $id); | 179 | + $childNode->setAttribute('id', $newId); |
| 158 | - $idArray[] = $id; | 180 | + $idArray[] = $newId; |
| 159 | } | 181 | } |
| 160 | 182 | ||
| 161 | // Generate inner html as a string | 183 | // Generate inner html as a string |
| ... | @@ -171,14 +193,17 @@ class PageRepo | ... | @@ -171,14 +193,17 @@ class PageRepo |
| 171 | /** | 193 | /** |
| 172 | * Gets pages by a search term. | 194 | * Gets pages by a search term. |
| 173 | * Highlights page content for showing in results. | 195 | * Highlights page content for showing in results. |
| 174 | - * @param string $term | 196 | + * @param string $term |
| 175 | * @param array $whereTerms | 197 | * @param array $whereTerms |
| 198 | + * @param int $count | ||
| 199 | + * @param array $paginationAppends | ||
| 176 | * @return mixed | 200 | * @return mixed |
| 177 | */ | 201 | */ |
| 178 | - public function getBySearch($term, $whereTerms = []) | 202 | + public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = []) |
| 179 | { | 203 | { |
| 180 | $terms = explode(' ', $term); | 204 | $terms = explode(' ', $term); |
| 181 | - $pages = $this->page->fullTextSearch(['name', 'text'], $terms, $whereTerms); | 205 | + $pages = $this->page->fullTextSearchQuery(['name', 'text'], $terms, $whereTerms) |
| 206 | + ->paginate($count)->appends($paginationAppends); | ||
| 182 | 207 | ||
| 183 | // Add highlights to page text. | 208 | // Add highlights to page text. |
| 184 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); | 209 | $words = join('|', explode(' ', preg_quote(trim($term), '/'))); |
| ... | @@ -238,9 +263,13 @@ class PageRepo | ... | @@ -238,9 +263,13 @@ class PageRepo |
| 238 | $this->saveRevision($page); | 263 | $this->saveRevision($page); |
| 239 | } | 264 | } |
| 240 | 265 | ||
| 266 | + // Prevent slug being updated if no name change | ||
| 267 | + if ($page->name !== $input['name']) { | ||
| 268 | + $page->slug = $this->findSuitableSlug($input['name'], $book_id, $page->id); | ||
| 269 | + } | ||
| 270 | + | ||
| 241 | // Update with new details | 271 | // Update with new details |
| 242 | $page->fill($input); | 272 | $page->fill($input); |
| 243 | - $page->slug = $this->findSuitableSlug($page->name, $book_id, $page->id); | ||
| 244 | $page->html = $this->formatHtml($input['html']); | 273 | $page->html = $this->formatHtml($input['html']); |
| 245 | $page->text = strip_tags($page->html); | 274 | $page->text = strip_tags($page->html); |
| 246 | $page->updated_by = auth()->user()->id; | 275 | $page->updated_by = auth()->user()->id; |
| ... | @@ -276,6 +305,8 @@ class PageRepo | ... | @@ -276,6 +305,8 @@ class PageRepo |
| 276 | { | 305 | { |
| 277 | $revision = $this->pageRevision->fill($page->toArray()); | 306 | $revision = $this->pageRevision->fill($page->toArray()); |
| 278 | $revision->page_id = $page->id; | 307 | $revision->page_id = $page->id; |
| 308 | + $revision->slug = $page->slug; | ||
| 309 | + $revision->book_slug = $page->book->slug; | ||
| 279 | $revision->created_by = auth()->user()->id; | 310 | $revision->created_by = auth()->user()->id; |
| 280 | $revision->created_at = $page->updated_at; | 311 | $revision->created_at = $page->updated_at; |
| 281 | $revision->save(); | 312 | $revision->save(); |
| ... | @@ -358,5 +389,22 @@ class PageRepo | ... | @@ -358,5 +389,22 @@ class PageRepo |
| 358 | $page->delete(); | 389 | $page->delete(); |
| 359 | } | 390 | } |
| 360 | 391 | ||
| 392 | + /** | ||
| 393 | + * Get the latest pages added to the system. | ||
| 394 | + * @param $count | ||
| 395 | + */ | ||
| 396 | + public function getRecentlyCreatedPaginated($count = 20) | ||
| 397 | + { | ||
| 398 | + return $this->page->orderBy('created_at', 'desc')->paginate($count); | ||
| 399 | + } | ||
| 400 | + | ||
| 401 | + /** | ||
| 402 | + * Get the latest pages added to the system. | ||
| 403 | + * @param $count | ||
| 404 | + */ | ||
| 405 | + public function getRecentlyUpdatedPaginated($count = 20) | ||
| 406 | + { | ||
| 407 | + return $this->page->orderBy('updated_at', 'desc')->paginate($count); | ||
| 408 | + } | ||
| 361 | 409 | ||
| 362 | -} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 410 | +} | ... | ... |
| 1 | <?php namespace BookStack\Repos; | 1 | <?php namespace BookStack\Repos; |
| 2 | 2 | ||
| 3 | - | ||
| 4 | use BookStack\Role; | 3 | use BookStack\Role; |
| 5 | use BookStack\User; | 4 | use BookStack\User; |
| 6 | use Setting; | 5 | use Setting; |
| ... | @@ -10,15 +9,19 @@ class UserRepo | ... | @@ -10,15 +9,19 @@ class UserRepo |
| 10 | 9 | ||
| 11 | protected $user; | 10 | protected $user; |
| 12 | protected $role; | 11 | protected $role; |
| 12 | + protected $entityRepo; | ||
| 13 | 13 | ||
| 14 | /** | 14 | /** |
| 15 | * UserRepo constructor. | 15 | * UserRepo constructor. |
| 16 | - * @param $user | 16 | + * @param User $user |
| 17 | + * @param Role $role | ||
| 18 | + * @param EntityRepo $entityRepo | ||
| 17 | */ | 19 | */ |
| 18 | - public function __construct(User $user, Role $role) | 20 | + public function __construct(User $user, Role $role, EntityRepo $entityRepo) |
| 19 | { | 21 | { |
| 20 | $this->user = $user; | 22 | $this->user = $user; |
| 21 | $this->role = $role; | 23 | $this->role = $role; |
| 24 | + $this->entityRepo = $entityRepo; | ||
| 22 | } | 25 | } |
| 23 | 26 | ||
| 24 | /** | 27 | /** |
| ... | @@ -112,4 +115,49 @@ class UserRepo | ... | @@ -112,4 +115,49 @@ class UserRepo |
| 112 | $user->socialAccounts()->delete(); | 115 | $user->socialAccounts()->delete(); |
| 113 | $user->delete(); | 116 | $user->delete(); |
| 114 | } | 117 | } |
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * Get the latest activity for a user. | ||
| 121 | + * @param User $user | ||
| 122 | + * @param int $count | ||
| 123 | + * @param int $page | ||
| 124 | + * @return array | ||
| 125 | + */ | ||
| 126 | + public function getActivity(User $user, $count = 20, $page = 0) | ||
| 127 | + { | ||
| 128 | + return \Activity::userActivity($user, $count, $page); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + /** | ||
| 132 | + * Get the recently created content for this given user. | ||
| 133 | + * @param User $user | ||
| 134 | + * @param int $count | ||
| 135 | + * @return mixed | ||
| 136 | + */ | ||
| 137 | + public function getRecentlyCreated(User $user, $count = 20) | ||
| 138 | + { | ||
| 139 | + return [ | ||
| 140 | + 'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->orderBy('created_at', 'desc') | ||
| 141 | + ->take($count)->get(), | ||
| 142 | + 'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->orderBy('created_at', 'desc') | ||
| 143 | + ->take($count)->get(), | ||
| 144 | + 'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->orderBy('created_at', 'desc') | ||
| 145 | + ->take($count)->get() | ||
| 146 | + ]; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + /** | ||
| 150 | + * Get asset created counts for the give user. | ||
| 151 | + * @param User $user | ||
| 152 | + * @return array | ||
| 153 | + */ | ||
| 154 | + public function getAssetCounts(User $user) | ||
| 155 | + { | ||
| 156 | + return [ | ||
| 157 | + 'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->count(), | ||
| 158 | + 'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->count(), | ||
| 159 | + 'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->count(), | ||
| 160 | + ]; | ||
| 161 | + } | ||
| 162 | + | ||
| 115 | } | 163 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -29,18 +29,19 @@ class ActivityService | ... | @@ -29,18 +29,19 @@ class ActivityService |
| 29 | */ | 29 | */ |
| 30 | public function add(Entity $entity, $activityKey, $bookId = 0, $extra = false) | 30 | public function add(Entity $entity, $activityKey, $bookId = 0, $extra = false) |
| 31 | { | 31 | { |
| 32 | - $this->activity->user_id = $this->user->id; | 32 | + $activity = $this->activity->newInstance(); |
| 33 | - $this->activity->book_id = $bookId; | 33 | + $activity->user_id = $this->user->id; |
| 34 | - $this->activity->key = strtolower($activityKey); | 34 | + $activity->book_id = $bookId; |
| 35 | + $activity->key = strtolower($activityKey); | ||
| 35 | if ($extra !== false) { | 36 | if ($extra !== false) { |
| 36 | - $this->activity->extra = $extra; | 37 | + $activity->extra = $extra; |
| 37 | } | 38 | } |
| 38 | - $entity->activity()->save($this->activity); | 39 | + $entity->activity()->save($activity); |
| 39 | $this->setNotification($activityKey); | 40 | $this->setNotification($activityKey); |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | /** | 43 | /** |
| 43 | - * Adds a activity history with a message & without binding to a entitiy. | 44 | + * Adds a activity history with a message & without binding to a entity. |
| 44 | * @param $activityKey | 45 | * @param $activityKey |
| 45 | * @param int $bookId | 46 | * @param int $bookId |
| 46 | * @param bool|false $extra | 47 | * @param bool|false $extra |
| ... | @@ -91,14 +92,14 @@ class ActivityService | ... | @@ -91,14 +92,14 @@ class ActivityService |
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | /** | 94 | /** |
| 94 | - * Gets the latest activity for an entitiy, Filtering out similar | 95 | + * Gets the latest activity for an entity, Filtering out similar |
| 95 | * items to prevent a message activity list. | 96 | * items to prevent a message activity list. |
| 96 | * @param Entity $entity | 97 | * @param Entity $entity |
| 97 | * @param int $count | 98 | * @param int $count |
| 98 | * @param int $page | 99 | * @param int $page |
| 99 | * @return array | 100 | * @return array |
| 100 | */ | 101 | */ |
| 101 | - function entityActivity($entity, $count = 20, $page = 0) | 102 | + public function entityActivity($entity, $count = 20, $page = 0) |
| 102 | { | 103 | { |
| 103 | $activity = $entity->hasMany('BookStack\Activity')->orderBy('created_at', 'desc') | 104 | $activity = $entity->hasMany('BookStack\Activity')->orderBy('created_at', 'desc') |
| 104 | ->skip($count * $page)->take($count)->get(); | 105 | ->skip($count * $page)->take($count)->get(); |
| ... | @@ -107,15 +108,30 @@ class ActivityService | ... | @@ -107,15 +108,30 @@ class ActivityService |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | /** | 110 | /** |
| 111 | + * Get latest activity for a user, Filtering out similar | ||
| 112 | + * items. | ||
| 113 | + * @param $user | ||
| 114 | + * @param int $count | ||
| 115 | + * @param int $page | ||
| 116 | + * @return array | ||
| 117 | + */ | ||
| 118 | + public function userActivity($user, $count = 20, $page = 0) | ||
| 119 | + { | ||
| 120 | + $activity = $this->activity->where('user_id', '=', $user->id) | ||
| 121 | + ->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get(); | ||
| 122 | + return $this->filterSimilar($activity); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + /** | ||
| 110 | * Filters out similar activity. | 126 | * Filters out similar activity. |
| 111 | - * @param Activity[] $activity | 127 | + * @param Activity[] $activities |
| 112 | * @return array | 128 | * @return array |
| 113 | */ | 129 | */ |
| 114 | - protected function filterSimilar($activity) | 130 | + protected function filterSimilar($activities) |
| 115 | { | 131 | { |
| 116 | $newActivity = []; | 132 | $newActivity = []; |
| 117 | $previousItem = false; | 133 | $previousItem = false; |
| 118 | - foreach ($activity as $activityItem) { | 134 | + foreach ($activities as $activityItem) { |
| 119 | if ($previousItem === false) { | 135 | if ($previousItem === false) { |
| 120 | $previousItem = $activityItem; | 136 | $previousItem = $activityItem; |
| 121 | $newActivity[] = $activityItem; | 137 | $newActivity[] = $activityItem; | ... | ... |
| ... | @@ -164,6 +164,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon | ... | @@ -164,6 +164,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon |
| 164 | */ | 164 | */ |
| 165 | public function getEditUrl() | 165 | public function getEditUrl() |
| 166 | { | 166 | { |
| 167 | - return '/users/' . $this->id; | 167 | + return '/settings/users/' . $this->id; |
| 168 | } | 168 | } |
| 169 | } | 169 | } | ... | ... |
| ... | @@ -9,16 +9,16 @@ | ... | @@ -9,16 +9,16 @@ |
| 9 | "packages": [ | 9 | "packages": [ |
| 10 | { | 10 | { |
| 11 | "name": "aws/aws-sdk-php", | 11 | "name": "aws/aws-sdk-php", |
| 12 | - "version": "3.14.2", | 12 | + "version": "3.15.1", |
| 13 | "source": { | 13 | "source": { |
| 14 | "type": "git", | 14 | "type": "git", |
| 15 | "url": "https://github.com/aws/aws-sdk-php.git", | 15 | "url": "https://github.com/aws/aws-sdk-php.git", |
| 16 | - "reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2" | 16 | + "reference": "5e6078913293576de969703481994b77c380ca30" |
| 17 | }, | 17 | }, |
| 18 | "dist": { | 18 | "dist": { |
| 19 | "type": "zip", | 19 | "type": "zip", |
| 20 | - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2", | 20 | + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5e6078913293576de969703481994b77c380ca30", |
| 21 | - "reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2", | 21 | + "reference": "5e6078913293576de969703481994b77c380ca30", |
| 22 | "shasum": "" | 22 | "shasum": "" |
| 23 | }, | 23 | }, |
| 24 | "require": { | 24 | "require": { |
| ... | @@ -40,7 +40,8 @@ | ... | @@ -40,7 +40,8 @@ |
| 40 | "ext-simplexml": "*", | 40 | "ext-simplexml": "*", |
| 41 | "ext-spl": "*", | 41 | "ext-spl": "*", |
| 42 | "nette/neon": "^2.3", | 42 | "nette/neon": "^2.3", |
| 43 | - "phpunit/phpunit": "~4.0|~5.0" | 43 | + "phpunit/phpunit": "~4.0|~5.0", |
| 44 | + "psr/cache": "^1.0" | ||
| 44 | }, | 45 | }, |
| 45 | "suggest": { | 46 | "suggest": { |
| 46 | "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", | 47 | "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", |
| ... | @@ -84,7 +85,7 @@ | ... | @@ -84,7 +85,7 @@ |
| 84 | "s3", | 85 | "s3", |
| 85 | "sdk" | 86 | "sdk" |
| 86 | ], | 87 | ], |
| 87 | - "time": "2016-01-28 21:33:18" | 88 | + "time": "2016-02-11 23:23:31" |
| 88 | }, | 89 | }, |
| 89 | { | 90 | { |
| 90 | "name": "barryvdh/laravel-debugbar", | 91 | "name": "barryvdh/laravel-debugbar", |
| ... | @@ -918,16 +919,16 @@ | ... | @@ -918,16 +919,16 @@ |
| 918 | }, | 919 | }, |
| 919 | { | 920 | { |
| 920 | "name": "laravel/framework", | 921 | "name": "laravel/framework", |
| 921 | - "version": "v5.2.12", | 922 | + "version": "v5.2.16", |
| 922 | "source": { | 923 | "source": { |
| 923 | "type": "git", | 924 | "type": "git", |
| 924 | "url": "https://github.com/laravel/framework.git", | 925 | "url": "https://github.com/laravel/framework.git", |
| 925 | - "reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7" | 926 | + "reference": "39e89553c124dce266da03ee3c0260bdd62f1848" |
| 926 | }, | 927 | }, |
| 927 | "dist": { | 928 | "dist": { |
| 928 | "type": "zip", | 929 | "type": "zip", |
| 929 | - "url": "https://api.github.com/repos/laravel/framework/zipball/6b6255ad7bfbdb721b8d00b09d52b146c5d363d7", | 930 | + "url": "https://api.github.com/repos/laravel/framework/zipball/39e89553c124dce266da03ee3c0260bdd62f1848", |
| 930 | - "reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7", | 931 | + "reference": "39e89553c124dce266da03ee3c0260bdd62f1848", |
| 931 | "shasum": "" | 932 | "shasum": "" |
| 932 | }, | 933 | }, |
| 933 | "require": { | 934 | "require": { |
| ... | @@ -1042,7 +1043,7 @@ | ... | @@ -1042,7 +1043,7 @@ |
| 1042 | "framework", | 1043 | "framework", |
| 1043 | "laravel" | 1044 | "laravel" |
| 1044 | ], | 1045 | ], |
| 1045 | - "time": "2016-01-26 04:15:37" | 1046 | + "time": "2016-02-15 17:46:58" |
| 1046 | }, | 1047 | }, |
| 1047 | { | 1048 | { |
| 1048 | "name": "laravel/socialite", | 1049 | "name": "laravel/socialite", |
| ... | @@ -1629,16 +1630,16 @@ | ... | @@ -1629,16 +1630,16 @@ |
| 1629 | }, | 1630 | }, |
| 1630 | { | 1631 | { |
| 1631 | "name": "paragonie/random_compat", | 1632 | "name": "paragonie/random_compat", |
| 1632 | - "version": "1.1.6", | 1633 | + "version": "v1.2.0", |
| 1633 | "source": { | 1634 | "source": { |
| 1634 | "type": "git", | 1635 | "type": "git", |
| 1635 | "url": "https://github.com/paragonie/random_compat.git", | 1636 | "url": "https://github.com/paragonie/random_compat.git", |
| 1636 | - "reference": "e6f80ab77885151908d0ec743689ca700886e8b0" | 1637 | + "reference": "b0e69d10852716b2ccbdff69c75c477637220790" |
| 1637 | }, | 1638 | }, |
| 1638 | "dist": { | 1639 | "dist": { |
| 1639 | "type": "zip", | 1640 | "type": "zip", |
| 1640 | - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/e6f80ab77885151908d0ec743689ca700886e8b0", | 1641 | + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/b0e69d10852716b2ccbdff69c75c477637220790", |
| 1641 | - "reference": "e6f80ab77885151908d0ec743689ca700886e8b0", | 1642 | + "reference": "b0e69d10852716b2ccbdff69c75c477637220790", |
| 1642 | "shasum": "" | 1643 | "shasum": "" |
| 1643 | }, | 1644 | }, |
| 1644 | "require": { | 1645 | "require": { |
| ... | @@ -1673,7 +1674,7 @@ | ... | @@ -1673,7 +1674,7 @@ |
| 1673 | "pseudorandom", | 1674 | "pseudorandom", |
| 1674 | "random" | 1675 | "random" |
| 1675 | ], | 1676 | ], |
| 1676 | - "time": "2016-01-29 16:19:52" | 1677 | + "time": "2016-02-06 03:52:05" |
| 1677 | }, | 1678 | }, |
| 1678 | { | 1679 | { |
| 1679 | "name": "phenx/php-font-lib", | 1680 | "name": "phenx/php-font-lib", |
| ... | @@ -2024,16 +2025,16 @@ | ... | @@ -2024,16 +2025,16 @@ |
| 2024 | }, | 2025 | }, |
| 2025 | { | 2026 | { |
| 2026 | "name": "symfony/console", | 2027 | "name": "symfony/console", |
| 2027 | - "version": "v3.0.1", | 2028 | + "version": "v3.0.2", |
| 2028 | "source": { | 2029 | "source": { |
| 2029 | "type": "git", | 2030 | "type": "git", |
| 2030 | "url": "https://github.com/symfony/console.git", | 2031 | "url": "https://github.com/symfony/console.git", |
| 2031 | - "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3" | 2032 | + "reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0" |
| 2032 | }, | 2033 | }, |
| 2033 | "dist": { | 2034 | "dist": { |
| 2034 | "type": "zip", | 2035 | "type": "zip", |
| 2035 | - "url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3", | 2036 | + "url": "https://api.github.com/repos/symfony/console/zipball/5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0", |
| 2036 | - "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3", | 2037 | + "reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0", |
| 2037 | "shasum": "" | 2038 | "shasum": "" |
| 2038 | }, | 2039 | }, |
| 2039 | "require": { | 2040 | "require": { |
| ... | @@ -2080,20 +2081,20 @@ | ... | @@ -2080,20 +2081,20 @@ |
| 2080 | ], | 2081 | ], |
| 2081 | "description": "Symfony Console Component", | 2082 | "description": "Symfony Console Component", |
| 2082 | "homepage": "https://symfony.com", | 2083 | "homepage": "https://symfony.com", |
| 2083 | - "time": "2015-12-22 10:39:06" | 2084 | + "time": "2016-02-02 13:44:19" |
| 2084 | }, | 2085 | }, |
| 2085 | { | 2086 | { |
| 2086 | "name": "symfony/debug", | 2087 | "name": "symfony/debug", |
| 2087 | - "version": "v3.0.1", | 2088 | + "version": "v3.0.2", |
| 2088 | "source": { | 2089 | "source": { |
| 2089 | "type": "git", | 2090 | "type": "git", |
| 2090 | "url": "https://github.com/symfony/debug.git", | 2091 | "url": "https://github.com/symfony/debug.git", |
| 2091 | - "reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a" | 2092 | + "reference": "29606049ced1ec715475f88d1bbe587252a3476e" |
| 2092 | }, | 2093 | }, |
| 2093 | "dist": { | 2094 | "dist": { |
| 2094 | "type": "zip", | 2095 | "type": "zip", |
| 2095 | - "url": "https://api.github.com/repos/symfony/debug/zipball/73612266ac709769effdbfc0762e5b07cfd2ac2a", | 2096 | + "url": "https://api.github.com/repos/symfony/debug/zipball/29606049ced1ec715475f88d1bbe587252a3476e", |
| 2096 | - "reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a", | 2097 | + "reference": "29606049ced1ec715475f88d1bbe587252a3476e", |
| 2097 | "shasum": "" | 2098 | "shasum": "" |
| 2098 | }, | 2099 | }, |
| 2099 | "require": { | 2100 | "require": { |
| ... | @@ -2137,20 +2138,20 @@ | ... | @@ -2137,20 +2138,20 @@ |
| 2137 | ], | 2138 | ], |
| 2138 | "description": "Symfony Debug Component", | 2139 | "description": "Symfony Debug Component", |
| 2139 | "homepage": "https://symfony.com", | 2140 | "homepage": "https://symfony.com", |
| 2140 | - "time": "2015-12-26 13:39:53" | 2141 | + "time": "2016-01-27 05:14:46" |
| 2141 | }, | 2142 | }, |
| 2142 | { | 2143 | { |
| 2143 | "name": "symfony/event-dispatcher", | 2144 | "name": "symfony/event-dispatcher", |
| 2144 | - "version": "v3.0.1", | 2145 | + "version": "v3.0.2", |
| 2145 | "source": { | 2146 | "source": { |
| 2146 | "type": "git", | 2147 | "type": "git", |
| 2147 | "url": "https://github.com/symfony/event-dispatcher.git", | 2148 | "url": "https://github.com/symfony/event-dispatcher.git", |
| 2148 | - "reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf" | 2149 | + "reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa" |
| 2149 | }, | 2150 | }, |
| 2150 | "dist": { | 2151 | "dist": { |
| 2151 | "type": "zip", | 2152 | "type": "zip", |
| 2152 | - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d36355e026905fa5229e1ed7b4e9eda2e67adfcf", | 2153 | + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa", |
| 2153 | - "reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf", | 2154 | + "reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa", |
| 2154 | "shasum": "" | 2155 | "shasum": "" |
| 2155 | }, | 2156 | }, |
| 2156 | "require": { | 2157 | "require": { |
| ... | @@ -2197,20 +2198,20 @@ | ... | @@ -2197,20 +2198,20 @@ |
| 2197 | ], | 2198 | ], |
| 2198 | "description": "Symfony EventDispatcher Component", | 2199 | "description": "Symfony EventDispatcher Component", |
| 2199 | "homepage": "https://symfony.com", | 2200 | "homepage": "https://symfony.com", |
| 2200 | - "time": "2015-10-30 23:35:59" | 2201 | + "time": "2016-01-27 05:14:46" |
| 2201 | }, | 2202 | }, |
| 2202 | { | 2203 | { |
| 2203 | "name": "symfony/finder", | 2204 | "name": "symfony/finder", |
| 2204 | - "version": "v3.0.1", | 2205 | + "version": "v3.0.2", |
| 2205 | "source": { | 2206 | "source": { |
| 2206 | "type": "git", | 2207 | "type": "git", |
| 2207 | "url": "https://github.com/symfony/finder.git", | 2208 | "url": "https://github.com/symfony/finder.git", |
| 2208 | - "reference": "8617895eb798b6bdb338321ce19453dc113e5675" | 2209 | + "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723" |
| 2209 | }, | 2210 | }, |
| 2210 | "dist": { | 2211 | "dist": { |
| 2211 | "type": "zip", | 2212 | "type": "zip", |
| 2212 | - "url": "https://api.github.com/repos/symfony/finder/zipball/8617895eb798b6bdb338321ce19453dc113e5675", | 2213 | + "url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723", |
| 2213 | - "reference": "8617895eb798b6bdb338321ce19453dc113e5675", | 2214 | + "reference": "623bda0abd9aa29e529c8e9c08b3b84171914723", |
| 2214 | "shasum": "" | 2215 | "shasum": "" |
| 2215 | }, | 2216 | }, |
| 2216 | "require": { | 2217 | "require": { |
| ... | @@ -2246,20 +2247,20 @@ | ... | @@ -2246,20 +2247,20 @@ |
| 2246 | ], | 2247 | ], |
| 2247 | "description": "Symfony Finder Component", | 2248 | "description": "Symfony Finder Component", |
| 2248 | "homepage": "https://symfony.com", | 2249 | "homepage": "https://symfony.com", |
| 2249 | - "time": "2015-12-05 11:13:14" | 2250 | + "time": "2016-01-27 05:14:46" |
| 2250 | }, | 2251 | }, |
| 2251 | { | 2252 | { |
| 2252 | "name": "symfony/http-foundation", | 2253 | "name": "symfony/http-foundation", |
| 2253 | - "version": "v3.0.1", | 2254 | + "version": "v3.0.2", |
| 2254 | "source": { | 2255 | "source": { |
| 2255 | "type": "git", | 2256 | "type": "git", |
| 2256 | "url": "https://github.com/symfony/http-foundation.git", | 2257 | "url": "https://github.com/symfony/http-foundation.git", |
| 2257 | - "reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5" | 2258 | + "reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d" |
| 2258 | }, | 2259 | }, |
| 2259 | "dist": { | 2260 | "dist": { |
| 2260 | "type": "zip", | 2261 | "type": "zip", |
| 2261 | - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5", | 2262 | + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9344a87ceedfc50354a39653e54257ee9aa6a77d", |
| 2262 | - "reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5", | 2263 | + "reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d", |
| 2263 | "shasum": "" | 2264 | "shasum": "" |
| 2264 | }, | 2265 | }, |
| 2265 | "require": { | 2266 | "require": { |
| ... | @@ -2298,20 +2299,20 @@ | ... | @@ -2298,20 +2299,20 @@ |
| 2298 | ], | 2299 | ], |
| 2299 | "description": "Symfony HttpFoundation Component", | 2300 | "description": "Symfony HttpFoundation Component", |
| 2300 | "homepage": "https://symfony.com", | 2301 | "homepage": "https://symfony.com", |
| 2301 | - "time": "2015-12-18 15:43:53" | 2302 | + "time": "2016-02-02 13:44:19" |
| 2302 | }, | 2303 | }, |
| 2303 | { | 2304 | { |
| 2304 | "name": "symfony/http-kernel", | 2305 | "name": "symfony/http-kernel", |
| 2305 | - "version": "v3.0.1", | 2306 | + "version": "v3.0.2", |
| 2306 | "source": { | 2307 | "source": { |
| 2307 | "type": "git", | 2308 | "type": "git", |
| 2308 | "url": "https://github.com/symfony/http-kernel.git", | 2309 | "url": "https://github.com/symfony/http-kernel.git", |
| 2309 | - "reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db" | 2310 | + "reference": "cec02604450481ac26710ca4249cc61b57b23942" |
| 2310 | }, | 2311 | }, |
| 2311 | "dist": { | 2312 | "dist": { |
| 2312 | "type": "zip", | 2313 | "type": "zip", |
| 2313 | - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f7933e9f19e26e7baba7ec04735b466fedd3a6db", | 2314 | + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/cec02604450481ac26710ca4249cc61b57b23942", |
| 2314 | - "reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db", | 2315 | + "reference": "cec02604450481ac26710ca4249cc61b57b23942", |
| 2315 | "shasum": "" | 2316 | "shasum": "" |
| 2316 | }, | 2317 | }, |
| 2317 | "require": { | 2318 | "require": { |
| ... | @@ -2380,7 +2381,7 @@ | ... | @@ -2380,7 +2381,7 @@ |
| 2380 | ], | 2381 | ], |
| 2381 | "description": "Symfony HttpKernel Component", | 2382 | "description": "Symfony HttpKernel Component", |
| 2382 | "homepage": "https://symfony.com", | 2383 | "homepage": "https://symfony.com", |
| 2383 | - "time": "2015-12-26 16:46:13" | 2384 | + "time": "2016-02-03 12:38:44" |
| 2384 | }, | 2385 | }, |
| 2385 | { | 2386 | { |
| 2386 | "name": "symfony/polyfill-mbstring", | 2387 | "name": "symfony/polyfill-mbstring", |
| ... | @@ -2551,16 +2552,16 @@ | ... | @@ -2551,16 +2552,16 @@ |
| 2551 | }, | 2552 | }, |
| 2552 | { | 2553 | { |
| 2553 | "name": "symfony/process", | 2554 | "name": "symfony/process", |
| 2554 | - "version": "v3.0.1", | 2555 | + "version": "v3.0.2", |
| 2555 | "source": { | 2556 | "source": { |
| 2556 | "type": "git", | 2557 | "type": "git", |
| 2557 | "url": "https://github.com/symfony/process.git", | 2558 | "url": "https://github.com/symfony/process.git", |
| 2558 | - "reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3" | 2559 | + "reference": "dfecef47506179db2501430e732adbf3793099c8" |
| 2559 | }, | 2560 | }, |
| 2560 | "dist": { | 2561 | "dist": { |
| 2561 | "type": "zip", | 2562 | "type": "zip", |
| 2562 | - "url": "https://api.github.com/repos/symfony/process/zipball/f4794f1d00f0746621be3020ffbd8c5e0b217ee3", | 2563 | + "url": "https://api.github.com/repos/symfony/process/zipball/dfecef47506179db2501430e732adbf3793099c8", |
| 2563 | - "reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3", | 2564 | + "reference": "dfecef47506179db2501430e732adbf3793099c8", |
| 2564 | "shasum": "" | 2565 | "shasum": "" |
| 2565 | }, | 2566 | }, |
| 2566 | "require": { | 2567 | "require": { |
| ... | @@ -2596,20 +2597,20 @@ | ... | @@ -2596,20 +2597,20 @@ |
| 2596 | ], | 2597 | ], |
| 2597 | "description": "Symfony Process Component", | 2598 | "description": "Symfony Process Component", |
| 2598 | "homepage": "https://symfony.com", | 2599 | "homepage": "https://symfony.com", |
| 2599 | - "time": "2015-12-23 11:04:02" | 2600 | + "time": "2016-02-02 13:44:19" |
| 2600 | }, | 2601 | }, |
| 2601 | { | 2602 | { |
| 2602 | "name": "symfony/routing", | 2603 | "name": "symfony/routing", |
| 2603 | - "version": "v3.0.1", | 2604 | + "version": "v3.0.2", |
| 2604 | "source": { | 2605 | "source": { |
| 2605 | "type": "git", | 2606 | "type": "git", |
| 2606 | "url": "https://github.com/symfony/routing.git", | 2607 | "url": "https://github.com/symfony/routing.git", |
| 2607 | - "reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59" | 2608 | + "reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166" |
| 2608 | }, | 2609 | }, |
| 2609 | "dist": { | 2610 | "dist": { |
| 2610 | "type": "zip", | 2611 | "type": "zip", |
| 2611 | - "url": "https://api.github.com/repos/symfony/routing/zipball/3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59", | 2612 | + "url": "https://api.github.com/repos/symfony/routing/zipball/4686baa55a835e1c1ede9b86ba02415c8c8d6166", |
| 2612 | - "reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59", | 2613 | + "reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166", |
| 2613 | "shasum": "" | 2614 | "shasum": "" |
| 2614 | }, | 2615 | }, |
| 2615 | "require": { | 2616 | "require": { |
| ... | @@ -2670,20 +2671,20 @@ | ... | @@ -2670,20 +2671,20 @@ |
| 2670 | "uri", | 2671 | "uri", |
| 2671 | "url" | 2672 | "url" |
| 2672 | ], | 2673 | ], |
| 2673 | - "time": "2015-12-23 08:00:11" | 2674 | + "time": "2016-01-27 05:14:46" |
| 2674 | }, | 2675 | }, |
| 2675 | { | 2676 | { |
| 2676 | "name": "symfony/translation", | 2677 | "name": "symfony/translation", |
| 2677 | - "version": "v3.0.1", | 2678 | + "version": "v3.0.2", |
| 2678 | "source": { | 2679 | "source": { |
| 2679 | "type": "git", | 2680 | "type": "git", |
| 2680 | "url": "https://github.com/symfony/translation.git", | 2681 | "url": "https://github.com/symfony/translation.git", |
| 2681 | - "reference": "dff0867826a7068d673801b7522f8e2634016ef9" | 2682 | + "reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91" |
| 2682 | }, | 2683 | }, |
| 2683 | "dist": { | 2684 | "dist": { |
| 2684 | "type": "zip", | 2685 | "type": "zip", |
| 2685 | - "url": "https://api.github.com/repos/symfony/translation/zipball/dff0867826a7068d673801b7522f8e2634016ef9", | 2686 | + "url": "https://api.github.com/repos/symfony/translation/zipball/2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91", |
| 2686 | - "reference": "dff0867826a7068d673801b7522f8e2634016ef9", | 2687 | + "reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91", |
| 2687 | "shasum": "" | 2688 | "shasum": "" |
| 2688 | }, | 2689 | }, |
| 2689 | "require": { | 2690 | "require": { |
| ... | @@ -2734,20 +2735,20 @@ | ... | @@ -2734,20 +2735,20 @@ |
| 2734 | ], | 2735 | ], |
| 2735 | "description": "Symfony Translation Component", | 2736 | "description": "Symfony Translation Component", |
| 2736 | "homepage": "https://symfony.com", | 2737 | "homepage": "https://symfony.com", |
| 2737 | - "time": "2015-12-05 17:45:07" | 2738 | + "time": "2016-02-02 13:44:19" |
| 2738 | }, | 2739 | }, |
| 2739 | { | 2740 | { |
| 2740 | "name": "symfony/var-dumper", | 2741 | "name": "symfony/var-dumper", |
| 2741 | - "version": "v3.0.1", | 2742 | + "version": "v3.0.2", |
| 2742 | "source": { | 2743 | "source": { |
| 2743 | "type": "git", | 2744 | "type": "git", |
| 2744 | "url": "https://github.com/symfony/var-dumper.git", | 2745 | "url": "https://github.com/symfony/var-dumper.git", |
| 2745 | - "reference": "87db8700deb12ba2b65e858f656a1f885530bcb0" | 2746 | + "reference": "24bb94807eff00db49374c37ebf56a0304e8aef3" |
| 2746 | }, | 2747 | }, |
| 2747 | "dist": { | 2748 | "dist": { |
| 2748 | "type": "zip", | 2749 | "type": "zip", |
| 2749 | - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/87db8700deb12ba2b65e858f656a1f885530bcb0", | 2750 | + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/24bb94807eff00db49374c37ebf56a0304e8aef3", |
| 2750 | - "reference": "87db8700deb12ba2b65e858f656a1f885530bcb0", | 2751 | + "reference": "24bb94807eff00db49374c37ebf56a0304e8aef3", |
| 2751 | "shasum": "" | 2752 | "shasum": "" |
| 2752 | }, | 2753 | }, |
| 2753 | "require": { | 2754 | "require": { |
| ... | @@ -2797,7 +2798,7 @@ | ... | @@ -2797,7 +2798,7 @@ |
| 2797 | "debug", | 2798 | "debug", |
| 2798 | "dump" | 2799 | "dump" |
| 2799 | ], | 2800 | ], |
| 2800 | - "time": "2015-12-05 11:13:14" | 2801 | + "time": "2016-01-07 13:38:51" |
| 2801 | }, | 2802 | }, |
| 2802 | { | 2803 | { |
| 2803 | "name": "vlucas/phpdotenv", | 2804 | "name": "vlucas/phpdotenv", |
| ... | @@ -3182,22 +3183,24 @@ | ... | @@ -3182,22 +3183,24 @@ |
| 3182 | }, | 3183 | }, |
| 3183 | { | 3184 | { |
| 3184 | "name": "phpspec/prophecy", | 3185 | "name": "phpspec/prophecy", |
| 3185 | - "version": "v1.5.0", | 3186 | + "version": "v1.6.0", |
| 3186 | "source": { | 3187 | "source": { |
| 3187 | "type": "git", | 3188 | "type": "git", |
| 3188 | "url": "https://github.com/phpspec/prophecy.git", | 3189 | "url": "https://github.com/phpspec/prophecy.git", |
| 3189 | - "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" | 3190 | + "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" |
| 3190 | }, | 3191 | }, |
| 3191 | "dist": { | 3192 | "dist": { |
| 3192 | "type": "zip", | 3193 | "type": "zip", |
| 3193 | - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", | 3194 | + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", |
| 3194 | - "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", | 3195 | + "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", |
| 3195 | "shasum": "" | 3196 | "shasum": "" |
| 3196 | }, | 3197 | }, |
| 3197 | "require": { | 3198 | "require": { |
| 3198 | "doctrine/instantiator": "^1.0.2", | 3199 | "doctrine/instantiator": "^1.0.2", |
| 3200 | + "php": "^5.3|^7.0", | ||
| 3199 | "phpdocumentor/reflection-docblock": "~2.0", | 3201 | "phpdocumentor/reflection-docblock": "~2.0", |
| 3200 | - "sebastian/comparator": "~1.1" | 3202 | + "sebastian/comparator": "~1.1", |
| 3203 | + "sebastian/recursion-context": "~1.0" | ||
| 3201 | }, | 3204 | }, |
| 3202 | "require-dev": { | 3205 | "require-dev": { |
| 3203 | "phpspec/phpspec": "~2.0" | 3206 | "phpspec/phpspec": "~2.0" |
| ... | @@ -3205,7 +3208,7 @@ | ... | @@ -3205,7 +3208,7 @@ |
| 3205 | "type": "library", | 3208 | "type": "library", |
| 3206 | "extra": { | 3209 | "extra": { |
| 3207 | "branch-alias": { | 3210 | "branch-alias": { |
| 3208 | - "dev-master": "1.4.x-dev" | 3211 | + "dev-master": "1.5.x-dev" |
| 3209 | } | 3212 | } |
| 3210 | }, | 3213 | }, |
| 3211 | "autoload": { | 3214 | "autoload": { |
| ... | @@ -3238,7 +3241,7 @@ | ... | @@ -3238,7 +3241,7 @@ |
| 3238 | "spy", | 3241 | "spy", |
| 3239 | "stub" | 3242 | "stub" |
| 3240 | ], | 3243 | ], |
| 3241 | - "time": "2015-08-13 10:07:40" | 3244 | + "time": "2016-02-15 07:46:21" |
| 3242 | }, | 3245 | }, |
| 3243 | { | 3246 | { |
| 3244 | "name": "phpunit/php-code-coverage", | 3247 | "name": "phpunit/php-code-coverage", |
| ... | @@ -3482,16 +3485,16 @@ | ... | @@ -3482,16 +3485,16 @@ |
| 3482 | }, | 3485 | }, |
| 3483 | { | 3486 | { |
| 3484 | "name": "phpunit/phpunit", | 3487 | "name": "phpunit/phpunit", |
| 3485 | - "version": "4.8.21", | 3488 | + "version": "4.8.23", |
| 3486 | "source": { | 3489 | "source": { |
| 3487 | "type": "git", | 3490 | "type": "git", |
| 3488 | "url": "https://github.com/sebastianbergmann/phpunit.git", | 3491 | "url": "https://github.com/sebastianbergmann/phpunit.git", |
| 3489 | - "reference": "ea76b17bced0500a28098626b84eda12dbcf119c" | 3492 | + "reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483" |
| 3490 | }, | 3493 | }, |
| 3491 | "dist": { | 3494 | "dist": { |
| 3492 | "type": "zip", | 3495 | "type": "zip", |
| 3493 | - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c", | 3496 | + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e351261f9cd33daf205a131a1ba61c6d33bd483", |
| 3494 | - "reference": "ea76b17bced0500a28098626b84eda12dbcf119c", | 3497 | + "reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483", |
| 3495 | "shasum": "" | 3498 | "shasum": "" |
| 3496 | }, | 3499 | }, |
| 3497 | "require": { | 3500 | "require": { |
| ... | @@ -3550,7 +3553,7 @@ | ... | @@ -3550,7 +3553,7 @@ |
| 3550 | "testing", | 3553 | "testing", |
| 3551 | "xunit" | 3554 | "xunit" |
| 3552 | ], | 3555 | ], |
| 3553 | - "time": "2015-12-12 07:45:58" | 3556 | + "time": "2016-02-11 14:56:33" |
| 3554 | }, | 3557 | }, |
| 3555 | { | 3558 | { |
| 3556 | "name": "phpunit/phpunit-mock-objects", | 3559 | "name": "phpunit/phpunit-mock-objects", |
| ... | @@ -3981,16 +3984,16 @@ | ... | @@ -3981,16 +3984,16 @@ |
| 3981 | }, | 3984 | }, |
| 3982 | { | 3985 | { |
| 3983 | "name": "symfony/css-selector", | 3986 | "name": "symfony/css-selector", |
| 3984 | - "version": "v3.0.1", | 3987 | + "version": "v3.0.2", |
| 3985 | "source": { | 3988 | "source": { |
| 3986 | "type": "git", | 3989 | "type": "git", |
| 3987 | "url": "https://github.com/symfony/css-selector.git", | 3990 | "url": "https://github.com/symfony/css-selector.git", |
| 3988 | - "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3" | 3991 | + "reference": "6605602690578496091ac20ec7a5cbd160d4dff4" |
| 3989 | }, | 3992 | }, |
| 3990 | "dist": { | 3993 | "dist": { |
| 3991 | "type": "zip", | 3994 | "type": "zip", |
| 3992 | - "url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3", | 3995 | + "url": "https://api.github.com/repos/symfony/css-selector/zipball/6605602690578496091ac20ec7a5cbd160d4dff4", |
| 3993 | - "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3", | 3996 | + "reference": "6605602690578496091ac20ec7a5cbd160d4dff4", |
| 3994 | "shasum": "" | 3997 | "shasum": "" |
| 3995 | }, | 3998 | }, |
| 3996 | "require": { | 3999 | "require": { |
| ... | @@ -4030,20 +4033,20 @@ | ... | @@ -4030,20 +4033,20 @@ |
| 4030 | ], | 4033 | ], |
| 4031 | "description": "Symfony CssSelector Component", | 4034 | "description": "Symfony CssSelector Component", |
| 4032 | "homepage": "https://symfony.com", | 4035 | "homepage": "https://symfony.com", |
| 4033 | - "time": "2015-12-05 17:45:07" | 4036 | + "time": "2016-01-27 05:14:46" |
| 4034 | }, | 4037 | }, |
| 4035 | { | 4038 | { |
| 4036 | "name": "symfony/dom-crawler", | 4039 | "name": "symfony/dom-crawler", |
| 4037 | - "version": "v3.0.1", | 4040 | + "version": "v3.0.2", |
| 4038 | "source": { | 4041 | "source": { |
| 4039 | "type": "git", | 4042 | "type": "git", |
| 4040 | "url": "https://github.com/symfony/dom-crawler.git", | 4043 | "url": "https://github.com/symfony/dom-crawler.git", |
| 4041 | - "reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d" | 4044 | + "reference": "b693a9650aa004576b593ff2e91ae749dc90123d" |
| 4042 | }, | 4045 | }, |
| 4043 | "dist": { | 4046 | "dist": { |
| 4044 | "type": "zip", | 4047 | "type": "zip", |
| 4045 | - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d", | 4048 | + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b693a9650aa004576b593ff2e91ae749dc90123d", |
| 4046 | - "reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d", | 4049 | + "reference": "b693a9650aa004576b593ff2e91ae749dc90123d", |
| 4047 | "shasum": "" | 4050 | "shasum": "" |
| 4048 | }, | 4051 | }, |
| 4049 | "require": { | 4052 | "require": { |
| ... | @@ -4086,20 +4089,20 @@ | ... | @@ -4086,20 +4089,20 @@ |
| 4086 | ], | 4089 | ], |
| 4087 | "description": "Symfony DomCrawler Component", | 4090 | "description": "Symfony DomCrawler Component", |
| 4088 | "homepage": "https://symfony.com", | 4091 | "homepage": "https://symfony.com", |
| 4089 | - "time": "2015-12-26 13:42:31" | 4092 | + "time": "2016-01-25 09:56:57" |
| 4090 | }, | 4093 | }, |
| 4091 | { | 4094 | { |
| 4092 | "name": "symfony/yaml", | 4095 | "name": "symfony/yaml", |
| 4093 | - "version": "v3.0.1", | 4096 | + "version": "v3.0.2", |
| 4094 | "source": { | 4097 | "source": { |
| 4095 | "type": "git", | 4098 | "type": "git", |
| 4096 | "url": "https://github.com/symfony/yaml.git", | 4099 | "url": "https://github.com/symfony/yaml.git", |
| 4097 | - "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691" | 4100 | + "reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a" |
| 4098 | }, | 4101 | }, |
| 4099 | "dist": { | 4102 | "dist": { |
| 4100 | "type": "zip", | 4103 | "type": "zip", |
| 4101 | - "url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691", | 4104 | + "url": "https://api.github.com/repos/symfony/yaml/zipball/3cf0709d7fe936e97bee9e954382e449003f1d9a", |
| 4102 | - "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691", | 4105 | + "reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a", |
| 4103 | "shasum": "" | 4106 | "shasum": "" |
| 4104 | }, | 4107 | }, |
| 4105 | "require": { | 4108 | "require": { |
| ... | @@ -4135,7 +4138,7 @@ | ... | @@ -4135,7 +4138,7 @@ |
| 4135 | ], | 4138 | ], |
| 4136 | "description": "Symfony Yaml Component", | 4139 | "description": "Symfony Yaml Component", |
| 4137 | "homepage": "https://symfony.com", | 4140 | "homepage": "https://symfony.com", |
| 4138 | - "time": "2015-12-26 13:39:53" | 4141 | + "time": "2016-02-02 13:44:19" |
| 4139 | } | 4142 | } |
| 4140 | ], | 4143 | ], |
| 4141 | "aliases": [], | 4144 | "aliases": [], | ... | ... |
| ... | @@ -18,7 +18,7 @@ class CreateUsersTable extends Migration | ... | @@ -18,7 +18,7 @@ class CreateUsersTable extends Migration |
| 18 | $table->string('email')->unique(); | 18 | $table->string('email')->unique(); |
| 19 | $table->string('password', 60); | 19 | $table->string('password', 60); |
| 20 | $table->rememberToken(); | 20 | $table->rememberToken(); |
| 21 | - $table->timestamps(); | 21 | + $table->nullableTimestamps(); |
| 22 | }); | 22 | }); |
| 23 | 23 | ||
| 24 | \BookStack\User::forceCreate([ | 24 | \BookStack\User::forceCreate([ | ... | ... |
| ... | @@ -17,7 +17,7 @@ class CreateBooksTable extends Migration | ... | @@ -17,7 +17,7 @@ class CreateBooksTable extends Migration |
| 17 | $table->string('name'); | 17 | $table->string('name'); |
| 18 | $table->string('slug')->indexed(); | 18 | $table->string('slug')->indexed(); |
| 19 | $table->text('description'); | 19 | $table->text('description'); |
| 20 | - $table->timestamps(); | 20 | + $table->nullableTimestamps(); |
| 21 | }); | 21 | }); |
| 22 | } | 22 | } |
| 23 | 23 | ... | ... |
| ... | @@ -21,7 +21,7 @@ class CreatePagesTable extends Migration | ... | @@ -21,7 +21,7 @@ class CreatePagesTable extends Migration |
| 21 | $table->longText('html'); | 21 | $table->longText('html'); |
| 22 | $table->longText('text'); | 22 | $table->longText('text'); |
| 23 | $table->integer('priority'); | 23 | $table->integer('priority'); |
| 24 | - $table->timestamps(); | 24 | + $table->nullableTimestamps(); |
| 25 | }); | 25 | }); |
| 26 | } | 26 | } |
| 27 | 27 | ... | ... |
| ... | @@ -16,7 +16,7 @@ class CreateImagesTable extends Migration | ... | @@ -16,7 +16,7 @@ class CreateImagesTable extends Migration |
| 16 | $table->increments('id'); | 16 | $table->increments('id'); |
| 17 | $table->string('name'); | 17 | $table->string('name'); |
| 18 | $table->string('url'); | 18 | $table->string('url'); |
| 19 | - $table->timestamps(); | 19 | + $table->nullableTimestamps(); |
| 20 | }); | 20 | }); |
| 21 | } | 21 | } |
| 22 | 22 | ... | ... |
| ... | @@ -19,7 +19,7 @@ class CreateChaptersTable extends Migration | ... | @@ -19,7 +19,7 @@ class CreateChaptersTable extends Migration |
| 19 | $table->text('name'); | 19 | $table->text('name'); |
| 20 | $table->text('description'); | 20 | $table->text('description'); |
| 21 | $table->integer('priority'); | 21 | $table->integer('priority'); |
| 22 | - $table->timestamps(); | 22 | + $table->nullableTimestamps(); |
| 23 | }); | 23 | }); |
| 24 | } | 24 | } |
| 25 | 25 | ... | ... |
| ... | @@ -19,7 +19,7 @@ class CreatePageRevisionsTable extends Migration | ... | @@ -19,7 +19,7 @@ class CreatePageRevisionsTable extends Migration |
| 19 | $table->longText('html'); | 19 | $table->longText('html'); |
| 20 | $table->longText('text'); | 20 | $table->longText('text'); |
| 21 | $table->integer('created_by'); | 21 | $table->integer('created_by'); |
| 22 | - $table->timestamps(); | 22 | + $table->nullableTimestamps(); |
| 23 | }); | 23 | }); |
| 24 | } | 24 | } |
| 25 | 25 | ... | ... |
| ... | @@ -20,7 +20,7 @@ class CreateActivitiesTable extends Migration | ... | @@ -20,7 +20,7 @@ class CreateActivitiesTable extends Migration |
| 20 | $table->integer('user_id'); | 20 | $table->integer('user_id'); |
| 21 | $table->integer('entity_id'); | 21 | $table->integer('entity_id'); |
| 22 | $table->string('entity_type'); | 22 | $table->string('entity_type'); |
| 23 | - $table->timestamps(); | 23 | + $table->nullableTimestamps(); |
| 24 | }); | 24 | }); |
| 25 | } | 25 | } |
| 26 | 26 | ... | ... |
| ... | @@ -28,7 +28,7 @@ class AddRolesAndPermissions extends Migration | ... | @@ -28,7 +28,7 @@ class AddRolesAndPermissions extends Migration |
| 28 | $table->string('name')->unique(); | 28 | $table->string('name')->unique(); |
| 29 | $table->string('display_name')->nullable(); | 29 | $table->string('display_name')->nullable(); |
| 30 | $table->string('description')->nullable(); | 30 | $table->string('description')->nullable(); |
| 31 | - $table->timestamps(); | 31 | + $table->nullableTimestamps(); |
| 32 | }); | 32 | }); |
| 33 | 33 | ||
| 34 | // Create table for associating roles to users (Many-to-Many) | 34 | // Create table for associating roles to users (Many-to-Many) |
| ... | @@ -50,7 +50,7 @@ class AddRolesAndPermissions extends Migration | ... | @@ -50,7 +50,7 @@ class AddRolesAndPermissions extends Migration |
| 50 | $table->string('name')->unique(); | 50 | $table->string('name')->unique(); |
| 51 | $table->string('display_name')->nullable(); | 51 | $table->string('display_name')->nullable(); |
| 52 | $table->string('description')->nullable(); | 52 | $table->string('description')->nullable(); |
| 53 | - $table->timestamps(); | 53 | + $table->nullableTimestamps(); |
| 54 | }); | 54 | }); |
| 55 | 55 | ||
| 56 | // Create table for associating permissions to roles (Many-to-Many) | 56 | // Create table for associating permissions to roles (Many-to-Many) | ... | ... |
| ... | @@ -15,7 +15,7 @@ class CreateSettingsTable extends Migration | ... | @@ -15,7 +15,7 @@ class CreateSettingsTable extends Migration |
| 15 | Schema::create('settings', function (Blueprint $table) { | 15 | Schema::create('settings', function (Blueprint $table) { |
| 16 | $table->string('setting_key')->primary()->indexed(); | 16 | $table->string('setting_key')->primary()->indexed(); |
| 17 | $table->text('value'); | 17 | $table->text('value'); |
| 18 | - $table->timestamps(); | 18 | + $table->nullableTimestamps(); |
| 19 | }); | 19 | }); |
| 20 | } | 20 | } |
| 21 | 21 | ... | ... |
| ... | @@ -18,7 +18,7 @@ class CreateSocialAccountsTable extends Migration | ... | @@ -18,7 +18,7 @@ class CreateSocialAccountsTable extends Migration |
| 18 | $table->string('driver')->index(); | 18 | $table->string('driver')->index(); |
| 19 | $table->string('driver_id'); | 19 | $table->string('driver_id'); |
| 20 | $table->string('avatar'); | 20 | $table->string('avatar'); |
| 21 | - $table->timestamps(); | 21 | + $table->nullableTimestamps(); |
| 22 | }); | 22 | }); |
| 23 | } | 23 | } |
| 24 | 24 | ... | ... |
| ... | @@ -20,7 +20,7 @@ class AddEmailConfirmationTable extends Migration | ... | @@ -20,7 +20,7 @@ class AddEmailConfirmationTable extends Migration |
| 20 | $table->increments('id'); | 20 | $table->increments('id'); |
| 21 | $table->integer('user_id')->index(); | 21 | $table->integer('user_id')->index(); |
| 22 | $table->string('token')->index(); | 22 | $table->string('token')->index(); |
| 23 | - $table->timestamps(); | 23 | + $table->nullableTimestamps(); |
| 24 | }); | 24 | }); |
| 25 | } | 25 | } |
| 26 | 26 | ... | ... |
| ... | @@ -18,7 +18,7 @@ class CreateViewsTable extends Migration | ... | @@ -18,7 +18,7 @@ class CreateViewsTable extends Migration |
| 18 | $table->integer('viewable_id'); | 18 | $table->integer('viewable_id'); |
| 19 | $table->string('viewable_type'); | 19 | $table->string('viewable_type'); |
| 20 | $table->integer('views'); | 20 | $table->integer('views'); |
| 21 | - $table->timestamps(); | 21 | + $table->nullableTimestamps(); |
| 22 | }); | 22 | }); |
| 23 | } | 23 | } |
| 24 | 24 | ... | ... |
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +use Illuminate\Database\Schema\Blueprint; | ||
| 4 | +use Illuminate\Database\Migrations\Migration; | ||
| 5 | + | ||
| 6 | +class AddSlugToRevisions extends Migration | ||
| 7 | +{ | ||
| 8 | + /** | ||
| 9 | + * Run the migrations. | ||
| 10 | + * | ||
| 11 | + * @return void | ||
| 12 | + */ | ||
| 13 | + public function up() | ||
| 14 | + { | ||
| 15 | + Schema::table('page_revisions', function (Blueprint $table) { | ||
| 16 | + $table->string('slug'); | ||
| 17 | + $table->index('slug'); | ||
| 18 | + $table->string('book_slug'); | ||
| 19 | + $table->index('book_slug'); | ||
| 20 | + }); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * Reverse the migrations. | ||
| 25 | + * | ||
| 26 | + * @return void | ||
| 27 | + */ | ||
| 28 | + public function down() | ||
| 29 | + { | ||
| 30 | + Schema::table('page_revisions', function (Blueprint $table) { | ||
| 31 | + $table->dropColumn('slug'); | ||
| 32 | + $table->dropColumn('book_slug'); | ||
| 33 | + }); | ||
| 34 | + } | ||
| 35 | +} |
| ... | @@ -25,8 +25,13 @@ | ... | @@ -25,8 +25,13 @@ |
| 25 | <env name="SESSION_DRIVER" value="array"/> | 25 | <env name="SESSION_DRIVER" value="array"/> |
| 26 | <env name="QUEUE_DRIVER" value="sync"/> | 26 | <env name="QUEUE_DRIVER" value="sync"/> |
| 27 | <env name="DB_CONNECTION" value="mysql_testing"/> | 27 | <env name="DB_CONNECTION" value="mysql_testing"/> |
| 28 | - <env name="MAIL_PRETEND" value="true"/> | 28 | + <env name="MAIL_DRIVER" value="log"/> |
| 29 | <env name="AUTH_METHOD" value="standard"/> | 29 | <env name="AUTH_METHOD" value="standard"/> |
| 30 | <env name="DISABLE_EXTERNAL_SERVICES" value="false"/> | 30 | <env name="DISABLE_EXTERNAL_SERVICES" value="false"/> |
| 31 | + <env name="LDAP_VERSION" value="3"/> | ||
| 32 | + <env name="GITHUB_APP_ID" value="aaaaaaaaaaaaaa"/> | ||
| 33 | + <env name="GITHUB_APP_SECRET" value="aaaaaaaaaaaaaa"/> | ||
| 34 | + <env name="GOOGLE_APP_ID" value="aaaaaaaaaaaaaa"/> | ||
| 35 | + <env name="GOOGLE_APP_SECRET" value="aaaaaaaaaaaaaa"/> | ||
| 31 | </php> | 36 | </php> |
| 32 | </phpunit> | 37 | </phpunit> | ... | ... |
| ... | @@ -106,6 +106,12 @@ $(function () { | ... | @@ -106,6 +106,12 @@ $(function () { |
| 106 | } | 106 | } |
| 107 | }); | 107 | }); |
| 108 | 108 | ||
| 109 | + // Common jQuery actions | ||
| 110 | + $('[data-action="expand-entity-list-details"]').click(function() { | ||
| 111 | + $('.entity-list.compact').find('p').slideToggle(240); | ||
| 112 | + }); | ||
| 113 | + | ||
| 114 | + | ||
| 109 | }); | 115 | }); |
| 110 | 116 | ||
| 111 | 117 | ... | ... |
| ... | @@ -139,54 +139,6 @@ form.search-box { | ... | @@ -139,54 +139,6 @@ form.search-box { |
| 139 | height: 43px; | 139 | height: 43px; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | -.dropdown-container { | ||
| 143 | - display: inline-block; | ||
| 144 | - vertical-align: top; | ||
| 145 | - position: relative; | ||
| 146 | -} | ||
| 147 | - | ||
| 148 | -.dropdown-container ul { | ||
| 149 | - display: none; | ||
| 150 | - position: absolute; | ||
| 151 | - z-index: 999; | ||
| 152 | - top: 0; | ||
| 153 | - list-style: none; | ||
| 154 | - right: 0; | ||
| 155 | - margin: $-m 0; | ||
| 156 | - background-color: #FFFFFF; | ||
| 157 | - box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1); | ||
| 158 | - border-radius: 1px; | ||
| 159 | - border: 1px solid #EEE; | ||
| 160 | - min-width: 180px; | ||
| 161 | - padding: $-xs 0; | ||
| 162 | - color: #555; | ||
| 163 | - text-align: left !important; | ||
| 164 | - &.wide { | ||
| 165 | - min-width: 220px; | ||
| 166 | - } | ||
| 167 | - .text-muted { | ||
| 168 | - color: #999; | ||
| 169 | - } | ||
| 170 | - a { | ||
| 171 | - display: block; | ||
| 172 | - padding: $-xs $-m; | ||
| 173 | - color: #555; | ||
| 174 | - &:hover { | ||
| 175 | - text-decoration: none; | ||
| 176 | - background-color: #EEE; | ||
| 177 | - } | ||
| 178 | - i { | ||
| 179 | - margin-right: $-m; | ||
| 180 | - padding-right: 0; | ||
| 181 | - display: inline; | ||
| 182 | - width: 22px; | ||
| 183 | - } | ||
| 184 | - } | ||
| 185 | - li.border-bottom { | ||
| 186 | - border-bottom: 1px solid #DDD; | ||
| 187 | - } | ||
| 188 | -} | ||
| 189 | - | ||
| 190 | .breadcrumbs span.sep { | 142 | .breadcrumbs span.sep { |
| 191 | color: #aaa; | 143 | color: #aaa; |
| 192 | padding: 0 $-xs; | 144 | padding: 0 $-xs; | ... | ... |
| ... | @@ -283,4 +283,87 @@ ul.pagination { | ... | @@ -283,4 +283,87 @@ ul.pagination { |
| 283 | a { | 283 | a { |
| 284 | color: $primary; | 284 | color: $primary; |
| 285 | } | 285 | } |
| 286 | -} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 286 | +} | ||
| 287 | + | ||
| 288 | +.entity-list { | ||
| 289 | + >div { | ||
| 290 | + padding: $-m 0; | ||
| 291 | + } | ||
| 292 | + h3 { | ||
| 293 | + margin: 0; | ||
| 294 | + } | ||
| 295 | + p { | ||
| 296 | + margin: $-xs 0 0 0; | ||
| 297 | + } | ||
| 298 | + hr { | ||
| 299 | + margin: 0; | ||
| 300 | + } | ||
| 301 | + .text-small.text-muted { | ||
| 302 | + color: #AAA; | ||
| 303 | + font-size: 0.75em; | ||
| 304 | + margin-top: $-xs; | ||
| 305 | + } | ||
| 306 | +} | ||
| 307 | +.entity-list.compact { | ||
| 308 | + font-size: 0.6em; | ||
| 309 | + h3, a { | ||
| 310 | + line-height: 1.2; | ||
| 311 | + } | ||
| 312 | + p { | ||
| 313 | + display: none; | ||
| 314 | + font-size: $fs-m * 0.8; | ||
| 315 | + padding-top: $-xs; | ||
| 316 | + margin: 0; | ||
| 317 | + } | ||
| 318 | + hr { | ||
| 319 | + margin: 0; | ||
| 320 | + } | ||
| 321 | +} | ||
| 322 | + | ||
| 323 | +.dropdown-container { | ||
| 324 | + display: inline-block; | ||
| 325 | + vertical-align: top; | ||
| 326 | + position: relative; | ||
| 327 | +} | ||
| 328 | + | ||
| 329 | +.dropdown-container ul { | ||
| 330 | + display: none; | ||
| 331 | + position: absolute; | ||
| 332 | + z-index: 999; | ||
| 333 | + top: 0; | ||
| 334 | + list-style: none; | ||
| 335 | + right: 0; | ||
| 336 | + margin: $-m 0; | ||
| 337 | + background-color: #FFFFFF; | ||
| 338 | + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1); | ||
| 339 | + border-radius: 1px; | ||
| 340 | + border: 1px solid #EEE; | ||
| 341 | + min-width: 180px; | ||
| 342 | + padding: $-xs 0; | ||
| 343 | + color: #555; | ||
| 344 | + text-align: left !important; | ||
| 345 | + &.wide { | ||
| 346 | + min-width: 220px; | ||
| 347 | + } | ||
| 348 | + .text-muted { | ||
| 349 | + color: #999; | ||
| 350 | + } | ||
| 351 | + a { | ||
| 352 | + display: block; | ||
| 353 | + padding: $-xs $-m; | ||
| 354 | + color: #555; | ||
| 355 | + &:hover { | ||
| 356 | + text-decoration: none; | ||
| 357 | + background-color: #EEE; | ||
| 358 | + } | ||
| 359 | + i { | ||
| 360 | + margin-right: $-m; | ||
| 361 | + padding-right: 0; | ||
| 362 | + display: inline; | ||
| 363 | + width: 22px; | ||
| 364 | + } | ||
| 365 | + } | ||
| 366 | + li.border-bottom { | ||
| 367 | + border-bottom: 1px solid #DDD; | ||
| 368 | + } | ||
| 369 | +} | ... | ... |
| ... | @@ -100,7 +100,7 @@ | ... | @@ -100,7 +100,7 @@ |
| 100 | background-color: #FFF; | 100 | background-color: #FFF; |
| 101 | border: 1px solid #DDD; | 101 | border: 1px solid #DDD; |
| 102 | color: #666; | 102 | color: #666; |
| 103 | - width: 180px; | 103 | + width: 172px; |
| 104 | z-index: 40; | 104 | z-index: 40; |
| 105 | } | 105 | } |
| 106 | input, button { | 106 | input, button { | ... | ... |
| ... | @@ -115,7 +115,8 @@ pre { | ... | @@ -115,7 +115,8 @@ pre { |
| 115 | box-shadow: 0 1px 2px 0px rgba(10, 10, 10, 0.06); | 115 | box-shadow: 0 1px 2px 0px rgba(10, 10, 10, 0.06); |
| 116 | border: 1px solid rgba(221, 221, 221, 0.66); | 116 | border: 1px solid rgba(221, 221, 221, 0.66); |
| 117 | background-color: #fdf6e3; | 117 | background-color: #fdf6e3; |
| 118 | - padding: 0.5em; | 118 | + padding: $-s; |
| 119 | + overflow-x: scroll; | ||
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | blockquote { | 122 | blockquote { |
| ... | @@ -251,6 +252,18 @@ ol { | ... | @@ -251,6 +252,18 @@ ol { |
| 251 | text-align: right; | 252 | text-align: right; |
| 252 | } | 253 | } |
| 253 | 254 | ||
| 255 | +.text-bigger { | ||
| 256 | + font-size: 1.1em; | ||
| 257 | +} | ||
| 258 | + | ||
| 259 | +.text-large { | ||
| 260 | + font-size: 1.6666em; | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +.no-color { | ||
| 264 | + color: inherit; | ||
| 265 | +} | ||
| 266 | + | ||
| 254 | /** | 267 | /** |
| 255 | * Grouping | 268 | * Grouping |
| 256 | */ | 269 | */ | ... | ... |
| ... | @@ -47,6 +47,13 @@ body.dragging, body.dragging * { | ... | @@ -47,6 +47,13 @@ body.dragging, body.dragging * { |
| 47 | width: 80px; | 47 | width: 80px; |
| 48 | height: 80px; | 48 | height: 80px; |
| 49 | } | 49 | } |
| 50 | + &.huge { | ||
| 51 | + width: 120px; | ||
| 52 | + height: 120px; | ||
| 53 | + } | ||
| 54 | + &.square { | ||
| 55 | + border-radius: 3px; | ||
| 56 | + } | ||
| 50 | } | 57 | } |
| 51 | 58 | ||
| 52 | // System wide notifications | 59 | // System wide notifications | ... | ... |
| ... | @@ -58,10 +58,13 @@ | ... | @@ -58,10 +58,13 @@ |
| 58 | </span> | 58 | </span> |
| 59 | <ul> | 59 | <ul> |
| 60 | <li> | 60 | <li> |
| 61 | - <a href="/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-lg"></i>Edit Profile</a> | 61 | + <a href="/user/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a> |
| 62 | </li> | 62 | </li> |
| 63 | <li> | 63 | <li> |
| 64 | - <a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a> | 64 | + <a href="/settings/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-fw zmdi-hc-lg"></i>Edit Profile</a> |
| 65 | + </li> | ||
| 66 | + <li> | ||
| 67 | + <a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a> | ||
| 65 | </li> | 68 | </li> |
| 66 | </ul> | 69 | </ul> |
| 67 | </div> | 70 | </div> | ... | ... |
| ... | @@ -10,7 +10,7 @@ | ... | @@ -10,7 +10,7 @@ |
| 10 | <p class="text-muted">{{ $chapter->getExcerpt() }}</p> | 10 | <p class="text-muted">{{ $chapter->getExcerpt() }}</p> |
| 11 | @endif | 11 | @endif |
| 12 | 12 | ||
| 13 | - @if(count($chapter->pages) > 0 && !isset($hidePages)) | 13 | + @if(!isset($hidePages) && count($chapter->pages) > 0) |
| 14 | <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ count($chapter->pages) }} Pages</span></p> | 14 | <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ count($chapter->pages) }} Pages</span></p> |
| 15 | <div class="inset-list"> | 15 | <div class="inset-list"> |
| 16 | @foreach($chapter->pages as $page) | 16 | @foreach($chapter->pages as $page) | ... | ... |
| ... | @@ -2,20 +2,44 @@ | ... | @@ -2,20 +2,44 @@ |
| 2 | 2 | ||
| 3 | @section('content') | 3 | @section('content') |
| 4 | 4 | ||
| 5 | + <div class="faded-small toolbar"> | ||
| 6 | + <div class="container"> | ||
| 7 | + <div class="row"> | ||
| 8 | + <div class="col-sm-4 faded"> | ||
| 9 | + <div class="action-buttons text-left"> | ||
| 10 | + <a data-action="expand-entity-list-details" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>Toggle Details</a> | ||
| 11 | + </div> | ||
| 12 | + </div> | ||
| 13 | + <div class="col-sm-8 faded"> | ||
| 14 | + <div class="action-buttons"> | ||
| 15 | + | ||
| 16 | + </div> | ||
| 17 | + </div> | ||
| 18 | + </div> | ||
| 19 | + </div> | ||
| 20 | + </div> | ||
| 21 | + | ||
| 5 | <div class="container" ng-non-bindable> | 22 | <div class="container" ng-non-bindable> |
| 6 | <div class="row"> | 23 | <div class="row"> |
| 7 | 24 | ||
| 8 | - <div class="col-md-7"> | 25 | + <div class="col-sm-4"> |
| 9 | @if($signedIn) | 26 | @if($signedIn) |
| 10 | - <h2>My Recently Viewed</h2> | 27 | + <h3>My Recently Viewed</h3> |
| 11 | @else | 28 | @else |
| 12 | - <h2>Recent Books</h2> | 29 | + <h3>Recent Books</h3> |
| 13 | @endif | 30 | @endif |
| 14 | - @include('partials/entity-list', ['entities' => $recents]) | 31 | + @include('partials/entity-list', ['entities' => $recents, 'style' => 'compact']) |
| 32 | + </div> | ||
| 33 | + | ||
| 34 | + <div class="col-sm-4"> | ||
| 35 | + <h3><a class="no-color" href="/pages/recently-created">Recently Created Pages</a></h3> | ||
| 36 | + @include('partials/entity-list', ['entities' => $recentlyCreatedPages, 'style' => 'compact']) | ||
| 37 | + | ||
| 38 | + <h3><a class="no-color" href="/pages/recently-updated">Recently Updated Pages</a></h3> | ||
| 39 | + @include('partials/entity-list', ['entities' => $recentlyCreatedPages, 'style' => 'compact']) | ||
| 15 | </div> | 40 | </div> |
| 16 | 41 | ||
| 17 | - <div class="col-md-4 col-md-offset-1"> | 42 | + <div class="col-sm-4" id="recent-activity"> |
| 18 | - <div class="margin-top large"> </div> | ||
| 19 | <h3>Recent Activity</h3> | 43 | <h3>Recent Activity</h3> |
| 20 | @include('partials/activity-list', ['activity' => $activity]) | 44 | @include('partials/activity-list', ['activity' => $activity]) |
| 21 | </div> | 45 | </div> | ... | ... |
| 1 | +@extends('base') | ||
| 2 | + | ||
| 3 | +@section('content') | ||
| 4 | + | ||
| 5 | + <div class="container"> | ||
| 6 | + <div class="row"> | ||
| 7 | + | ||
| 8 | + <div class="col-sm-7"> | ||
| 9 | + <h1>{{ $title }}</h1> | ||
| 10 | + @include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed']) | ||
| 11 | + {!! $pages->links() !!} | ||
| 12 | + </div> | ||
| 13 | + | ||
| 14 | + <div class="col-sm-4 col-sm-offset-1"></div> | ||
| 15 | + | ||
| 16 | + </div> | ||
| 17 | + </div> | ||
| 18 | +@stop | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -3,18 +3,29 @@ | ... | @@ -3,18 +3,29 @@ |
| 3 | <a href="{{ $page->getUrl() }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ $page->name }}</a> | 3 | <a href="{{ $page->getUrl() }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ $page->name }}</a> |
| 4 | </h3> | 4 | </h3> |
| 5 | 5 | ||
| 6 | - @if(isset($showMeta) && $showMeta) | ||
| 7 | - <div class="meta"> | ||
| 8 | - <span class="text-book"><i class="zmdi zmdi-book"></i> {{ $page->book->name }}</span> | ||
| 9 | - @if($page->chapter) | ||
| 10 | - <span class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i> {{ $page->chapter->name }}</span> | ||
| 11 | - @endif | ||
| 12 | - </div> | ||
| 13 | - @endif | ||
| 14 | - | ||
| 15 | @if(isset($page->searchSnippet)) | 6 | @if(isset($page->searchSnippet)) |
| 16 | <p class="text-muted">{!! $page->searchSnippet !!}</p> | 7 | <p class="text-muted">{!! $page->searchSnippet !!}</p> |
| 17 | @else | 8 | @else |
| 18 | <p class="text-muted">{{ $page->getExcerpt() }}</p> | 9 | <p class="text-muted">{{ $page->getExcerpt() }}</p> |
| 19 | @endif | 10 | @endif |
| 11 | + | ||
| 12 | + @if(isset($style) && $style === 'detailed') | ||
| 13 | + <div class="row meta text-muted text-small"> | ||
| 14 | + <div class="col-md-4"> | ||
| 15 | + Created {{$page->created_at->diffForHumans()}} @if($page->createdBy)by {{$page->createdBy->name}}@endif <br> | ||
| 16 | + Last updated {{ $page->updated_at->diffForHumans() }} @if($page->updatedBy)by {{$page->updatedBy->name}} @endif | ||
| 17 | + </div> | ||
| 18 | + <div class="col-md-8"> | ||
| 19 | + <a class="text-book" href="{{ $page->book->getUrl() }}"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName(30) }}</a> | ||
| 20 | + <br> | ||
| 21 | + @if($page->chapter) | ||
| 22 | + <a class="text-chapter" href="{{ $page->chapter->getUrl() }}"><i class="zmdi zmdi-collection-bookmark"></i>{{ $page->chapter->getShortName(30) }}</a> | ||
| 23 | + @else | ||
| 24 | + <i class="zmdi zmdi-collection-bookmark"></i> Page is not in a chapter | ||
| 25 | + @endif | ||
| 26 | + </div> | ||
| 27 | + </div> | ||
| 28 | + @endif | ||
| 29 | + | ||
| 30 | + | ||
| 20 | </div> | 31 | </div> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -9,7 +9,7 @@ | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | ||
| 10 | <div class="right" ng-non-bindable> | 10 | <div class="right" ng-non-bindable> |
| 11 | @if($activity->user) | 11 | @if($activity->user) |
| 12 | - {{$activity->user->name}} | 12 | + <a href="/user/{{ $activity->user->id }}">{{$activity->user->name}}</a> |
| 13 | @else | 13 | @else |
| 14 | A deleted user | 14 | A deleted user |
| 15 | @endif | 15 | @endif | ... | ... |
| 1 | 1 | ||
| 2 | -@if(count($entities) > 0) | 2 | +<div class="entity-list @if(isset($style)){{ $style }}@endif" ng-non-bindable> |
| 3 | - @foreach($entities as $index => $entity) | 3 | + @if(count($entities) > 0) |
| 4 | - @if($entity->isA('page')) | 4 | + @foreach($entities as $index => $entity) |
| 5 | - @include('pages/list-item', ['page' => $entity]) | 5 | + @if($entity->isA('page')) |
| 6 | - @elseif($entity->isA('book')) | 6 | + @include('pages/list-item', ['page' => $entity]) |
| 7 | - @include('books/list-item', ['book' => $entity]) | 7 | + @elseif($entity->isA('book')) |
| 8 | - @elseif($entity->isA('chapter')) | 8 | + @include('books/list-item', ['book' => $entity]) |
| 9 | - @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true]) | 9 | + @elseif($entity->isA('chapter')) |
| 10 | - @endif | 10 | + @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true]) |
| 11 | + @endif | ||
| 11 | 12 | ||
| 12 | - @if($index !== count($entities) - 1) | 13 | + @if($index !== count($entities) - 1) |
| 13 | - <hr> | 14 | + <hr> |
| 14 | - @endif | 15 | + @endif |
| 15 | 16 | ||
| 16 | - @endforeach | ||
| 17 | -@else | ||
| 18 | - <p class="text-muted"> | ||
| 19 | - No items available | ||
| 20 | - </p> | ||
| 21 | -@endif | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 17 | + @endforeach | ||
| 18 | + @else | ||
| 19 | + <p class="text-muted"> | ||
| 20 | + No items available | ||
| 21 | + </p> | ||
| 22 | + @endif | ||
| 23 | +</div> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -6,41 +6,36 @@ | ... | @@ -6,41 +6,36 @@ |
| 6 | 6 | ||
| 7 | <h1>Search Results <span class="text-muted">{{$searchTerm}}</span></h1> | 7 | <h1>Search Results <span class="text-muted">{{$searchTerm}}</span></h1> |
| 8 | 8 | ||
| 9 | + <p> | ||
| 10 | + <a href="/search/pages?term={{$searchTerm}}" class="text-page"><i class="zmdi zmdi-file-text"></i>View all matched pages</a> | ||
| 11 | + | ||
| 12 | + @if(count($chapters) > 0) | ||
| 13 | + | ||
| 14 | + <a href="/search/chapters?term={{$searchTerm}}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>View all matched chapters</a> | ||
| 15 | + @endif | ||
| 16 | + | ||
| 17 | + @if(count($books) > 0) | ||
| 18 | + | ||
| 19 | + <a href="/search/books?term={{$searchTerm}}" class="text-book"><i class="zmdi zmdi-book"></i>View all matched books</a> | ||
| 20 | + @endif | ||
| 21 | + </p> | ||
| 9 | <div class="row"> | 22 | <div class="row"> |
| 10 | 23 | ||
| 11 | <div class="col-md-6"> | 24 | <div class="col-md-6"> |
| 12 | - <h3>Matching Pages</h3> | 25 | + <h3><a href="/search/pages?term={{$searchTerm}}" class="no-color">Matching Pages</a></h3> |
| 13 | - <div class="page-list"> | 26 | + @include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed']) |
| 14 | - @if(count($pages) > 0) | ||
| 15 | - @foreach($pages as $page) | ||
| 16 | - @include('pages/list-item', ['page' => $page, 'showMeta' => true]) | ||
| 17 | - <hr> | ||
| 18 | - @endforeach | ||
| 19 | - @else | ||
| 20 | - <p class="text-muted">No pages matched this search</p> | ||
| 21 | - @endif | ||
| 22 | - </div> | ||
| 23 | </div> | 27 | </div> |
| 24 | 28 | ||
| 25 | <div class="col-md-5 col-md-offset-1"> | 29 | <div class="col-md-5 col-md-offset-1"> |
| 26 | 30 | ||
| 27 | @if(count($books) > 0) | 31 | @if(count($books) > 0) |
| 28 | - <h3>Matching Books</h3> | 32 | + <h3><a href="/search/books?term={{$searchTerm}}" class="no-color">Matching Books</a></h3> |
| 29 | - <div class="page-list"> | 33 | + @include('partials/entity-list', ['entities' => $books]) |
| 30 | - @foreach($books as $book) | ||
| 31 | - @include('books/list-item', ['book' => $book]) | ||
| 32 | - <hr> | ||
| 33 | - @endforeach | ||
| 34 | - </div> | ||
| 35 | @endif | 34 | @endif |
| 36 | 35 | ||
| 37 | @if(count($chapters) > 0) | 36 | @if(count($chapters) > 0) |
| 38 | - <h3>Matching Chapters</h3> | 37 | + <h3><a href="/search/chapters?term={{$searchTerm}}" class="no-color">Matching Chapters</a></h3> |
| 39 | - <div class="page-list"> | 38 | + @include('partials/entity-list', ['entities' => $chapters]) |
| 40 | - @foreach($chapters as $chapter) | ||
| 41 | - @include('chapters/list-item', ['chapter' => $chapter, 'hidePages' => true]) | ||
| 42 | - @endforeach | ||
| 43 | - </div> | ||
| 44 | @endif | 39 | @endif |
| 45 | 40 | ||
| 46 | </div> | 41 | </div> | ... | ... |
| 1 | +@extends('base') | ||
| 2 | + | ||
| 3 | +@section('content') | ||
| 4 | + | ||
| 5 | + <div class="container"> | ||
| 6 | + <div class="row"> | ||
| 7 | + | ||
| 8 | + <div class="col-sm-7"> | ||
| 9 | + <h1>{{ $title }} <small>{{$searchTerm}}</small></h1> | ||
| 10 | + @include('partials.entity-list', ['entities' => $entities, 'style' => 'detailed']) | ||
| 11 | + {!! $entities->links() !!} | ||
| 12 | + </div> | ||
| 13 | + | ||
| 14 | + <div class="col-sm-4 col-sm-offset-1"></div> | ||
| 15 | + | ||
| 16 | + </div> | ||
| 17 | + </div> | ||
| 18 | +@stop | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | <div class="row"> | 4 | <div class="row"> |
| 5 | <div class="col-md-12 setting-nav"> | 5 | <div class="col-md-12 setting-nav"> |
| 6 | <a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a> | 6 | <a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a> |
| 7 | - <a href="/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a> | 7 | + <a href="/settings/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a> |
| 8 | </div> | 8 | </div> |
| 9 | </div> | 9 | </div> |
| 10 | </div> | 10 | </div> | ... | ... |
| ... | @@ -6,7 +6,7 @@ | ... | @@ -6,7 +6,7 @@ |
| 6 | <div class="container small" ng-non-bindable> | 6 | <div class="container small" ng-non-bindable> |
| 7 | <h1>Create User</h1> | 7 | <h1>Create User</h1> |
| 8 | 8 | ||
| 9 | - <form action="/users/create" method="post"> | 9 | + <form action="/settings/users/create" method="post"> |
| 10 | {!! csrf_field() !!} | 10 | {!! csrf_field() !!} |
| 11 | @include('users.forms.' . $authMethod) | 11 | @include('users.forms.' . $authMethod) |
| 12 | </form> | 12 | </form> | ... | ... |
| ... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
| 7 | <p>This will fully delete this user with the name '<span class="text-neg">{{$user->name}}</span>' from the system.</p> | 7 | <p>This will fully delete this user with the name '<span class="text-neg">{{$user->name}}</span>' from the system.</p> |
| 8 | <p class="text-neg">Are you sure you want to delete this user?</p> | 8 | <p class="text-neg">Are you sure you want to delete this user?</p> |
| 9 | 9 | ||
| 10 | - <form action="/users/{{$user->id}}" method="POST"> | 10 | + <form action="/settings/users/{{$user->id}}" method="POST"> |
| 11 | {!! csrf_field() !!} | 11 | {!! csrf_field() !!} |
| 12 | <input type="hidden" name="_method" value="DELETE"> | 12 | <input type="hidden" name="_method" value="DELETE"> |
| 13 | <a href="/users/{{$user->id}}" class="button muted">Cancel</a> | 13 | <a href="/users/{{$user->id}}" class="button muted">Cancel</a> | ... | ... |
| ... | @@ -9,7 +9,7 @@ | ... | @@ -9,7 +9,7 @@ |
| 9 | <div class="col-sm-6"></div> | 9 | <div class="col-sm-6"></div> |
| 10 | <div class="col-sm-6 faded"> | 10 | <div class="col-sm-6 faded"> |
| 11 | <div class="action-buttons"> | 11 | <div class="action-buttons"> |
| 12 | - <a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a> | 12 | + <a href="/settings/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a> |
| 13 | </div> | 13 | </div> |
| 14 | </div> | 14 | </div> |
| 15 | </div> | 15 | </div> |
| ... | @@ -19,7 +19,7 @@ | ... | @@ -19,7 +19,7 @@ |
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | <div class="container small"> | 21 | <div class="container small"> |
| 22 | - <form action="/users/{{$user->id}}" method="post"> | 22 | + <form action="/settings/users/{{$user->id}}" method="post"> |
| 23 | <div class="row"> | 23 | <div class="row"> |
| 24 | <div class="col-md-6" ng-non-bindable> | 24 | <div class="col-md-6" ng-non-bindable> |
| 25 | <h1>Edit {{ $user->id === $currentUser->id ? 'Profile' : 'User' }}</h1> | 25 | <h1>Edit {{ $user->id === $currentUser->id ? 'Profile' : 'User' }}</h1> | ... | ... |
| ... | @@ -25,6 +25,6 @@ | ... | @@ -25,6 +25,6 @@ |
| 25 | @endif | 25 | @endif |
| 26 | 26 | ||
| 27 | <div class="form-group"> | 27 | <div class="form-group"> |
| 28 | - <a href="/users" class="button muted">Cancel</a> | 28 | + <a href="/settings/users" class="button muted">Cancel</a> |
| 29 | <button class="button pos" type="submit">Save</button> | 29 | <button class="button pos" type="submit">Save</button> |
| 30 | </div> | 30 | </div> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -34,7 +34,7 @@ | ... | @@ -34,7 +34,7 @@ |
| 34 | </div> | 34 | </div> |
| 35 | 35 | ||
| 36 | <div class="form-group"> | 36 | <div class="form-group"> |
| 37 | - <a href="/users" class="button muted">Cancel</a> | 37 | + <a href="/settings/users" class="button muted">Cancel</a> |
| 38 | <button class="button pos" type="submit">Save</button> | 38 | <button class="button pos" type="submit">Save</button> |
| 39 | </div> | 39 | </div> |
| 40 | 40 | ... | ... |
| ... | @@ -10,7 +10,7 @@ | ... | @@ -10,7 +10,7 @@ |
| 10 | <h1>Users</h1> | 10 | <h1>Users</h1> |
| 11 | @if($currentUser->can('user-create')) | 11 | @if($currentUser->can('user-create')) |
| 12 | <p> | 12 | <p> |
| 13 | - <a href="/users/create" class="text-pos"><i class="zmdi zmdi-account-add"></i>Add new user</a> | 13 | + <a href="/settings/users/create" class="text-pos"><i class="zmdi zmdi-account-add"></i>Add new user</a> |
| 14 | </p> | 14 | </p> |
| 15 | @endif | 15 | @endif |
| 16 | <table class="table"> | 16 | <table class="table"> |
| ... | @@ -25,7 +25,7 @@ | ... | @@ -25,7 +25,7 @@ |
| 25 | <td style="line-height: 0;"><img class="avatar med" src="{{$user->getAvatar(40)}}" alt="{{$user->name}}"></td> | 25 | <td style="line-height: 0;"><img class="avatar med" src="{{$user->getAvatar(40)}}" alt="{{$user->name}}"></td> |
| 26 | <td> | 26 | <td> |
| 27 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) | 27 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) |
| 28 | - <a href="/users/{{$user->id}}"> | 28 | + <a href="/settings/users/{{$user->id}}"> |
| 29 | @endif | 29 | @endif |
| 30 | {{ $user->name }} | 30 | {{ $user->name }} |
| 31 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) | 31 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) |
| ... | @@ -34,7 +34,7 @@ | ... | @@ -34,7 +34,7 @@ |
| 34 | </td> | 34 | </td> |
| 35 | <td> | 35 | <td> |
| 36 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) | 36 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) |
| 37 | - <a href="/users/{{$user->id}}"> | 37 | + <a href="/settings/users/{{$user->id}}"> |
| 38 | @endif | 38 | @endif |
| 39 | {{ $user->email }} | 39 | {{ $user->email }} |
| 40 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) | 40 | @if($currentUser->can('user-update') || $currentUser->id == $user->id) | ... | ... |
resources/views/users/profile.blade.php
0 → 100644
| 1 | +@extends('base') | ||
| 2 | + | ||
| 3 | +@section('content') | ||
| 4 | + | ||
| 5 | + <div class="container" ng-non-bindable> | ||
| 6 | + <div class="row"> | ||
| 7 | + <div class="col-sm-7"> | ||
| 8 | + | ||
| 9 | + <div class="padded-top large"></div> | ||
| 10 | + | ||
| 11 | + <div class="row"> | ||
| 12 | + <div class="col-md-7"> | ||
| 13 | + <div class="clearfix"> | ||
| 14 | + <div class="padded-right float left"> | ||
| 15 | + <img class="avatar square huge" src="{{$user->getAvatar(120)}}" alt="{{ $user->name }}"> | ||
| 16 | + </div> | ||
| 17 | + <div> | ||
| 18 | + <h3 style="margin-top: 0;">{{ $user->name }}</h3> | ||
| 19 | + <p class="text-muted"> | ||
| 20 | + User for {{ $user->created_at->diffForHumans(null, true) }} | ||
| 21 | + </p> | ||
| 22 | + </div> | ||
| 23 | + </div> | ||
| 24 | + </div> | ||
| 25 | + <div class="col-md-5 text-bigger" id="content-counts"> | ||
| 26 | + <div class="text-muted">Created Content</div> | ||
| 27 | + <div class="text-book"> | ||
| 28 | + <i class="zmdi zmdi-book zmdi-hc-fw"></i> {{ $assetCounts['books'] }} {{ str_plural('Book', $assetCounts['books']) }} | ||
| 29 | + </div> | ||
| 30 | + <div class="text-chapter"> | ||
| 31 | + <i class="zmdi zmdi-collection-bookmark zmdi-hc-fw"></i> {{ $assetCounts['chapters'] }} {{ str_plural('Chapter', $assetCounts['chapters']) }} | ||
| 32 | + </div> | ||
| 33 | + <div class="text-page"> | ||
| 34 | + <i class="zmdi zmdi-file-text zmdi-hc-fw"></i> {{ $assetCounts['pages'] }} {{ str_plural('Page', $assetCounts['pages']) }} | ||
| 35 | + </div> | ||
| 36 | + </div> | ||
| 37 | + </div> | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + <hr class="even"> | ||
| 41 | + | ||
| 42 | + <h3>Recently Created Pages</h3> | ||
| 43 | + @if (count($recentlyCreated['pages']) > 0) | ||
| 44 | + @include('partials/entity-list', ['entities' => $recentlyCreated['pages']]) | ||
| 45 | + @else | ||
| 46 | + <p class="text-muted">{{ $user->name }} has not created any pages</p> | ||
| 47 | + @endif | ||
| 48 | + | ||
| 49 | + <hr class="even"> | ||
| 50 | + | ||
| 51 | + <h3>Recently Created Chapters</h3> | ||
| 52 | + @if (count($recentlyCreated['chapters']) > 0) | ||
| 53 | + @include('partials/entity-list', ['entities' => $recentlyCreated['chapters']]) | ||
| 54 | + @else | ||
| 55 | + <p class="text-muted">{{ $user->name }} has not created any chapters</p> | ||
| 56 | + @endif | ||
| 57 | + | ||
| 58 | + <hr class="even"> | ||
| 59 | + | ||
| 60 | + <h3>Recently Created Books</h3> | ||
| 61 | + @if (count($recentlyCreated['books']) > 0) | ||
| 62 | + @include('partials/entity-list', ['entities' => $recentlyCreated['books']]) | ||
| 63 | + @else | ||
| 64 | + <p class="text-muted">{{ $user->name }} has not created any books</p> | ||
| 65 | + @endif | ||
| 66 | + </div> | ||
| 67 | + | ||
| 68 | + <div class="col-sm-4 col-sm-offset-1" id="recent-activity"> | ||
| 69 | + <h3>Recent Activity</h3> | ||
| 70 | + @include('partials/activity-list', ['activity' => $activity]) | ||
| 71 | + </div> | ||
| 72 | + | ||
| 73 | + </div> | ||
| 74 | + </div> | ||
| 75 | + | ||
| 76 | + | ||
| 77 | +@stop | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
storage/fonts/.gitignore
100644 → 100755
File mode changed
| ... | @@ -129,7 +129,7 @@ class AuthTest extends TestCase | ... | @@ -129,7 +129,7 @@ class AuthTest extends TestCase |
| 129 | $user = factory(\BookStack\User::class)->make(); | 129 | $user = factory(\BookStack\User::class)->make(); |
| 130 | 130 | ||
| 131 | $this->asAdmin() | 131 | $this->asAdmin() |
| 132 | - ->visit('/users') | 132 | + ->visit('/settings/users') |
| 133 | ->click('Add new user') | 133 | ->click('Add new user') |
| 134 | ->type($user->name, '#name') | 134 | ->type($user->name, '#name') |
| 135 | ->type($user->email, '#email') | 135 | ->type($user->email, '#email') |
| ... | @@ -138,7 +138,7 @@ class AuthTest extends TestCase | ... | @@ -138,7 +138,7 @@ class AuthTest extends TestCase |
| 138 | ->type($user->password, '#password-confirm') | 138 | ->type($user->password, '#password-confirm') |
| 139 | ->press('Save') | 139 | ->press('Save') |
| 140 | ->seeInDatabase('users', $user->toArray()) | 140 | ->seeInDatabase('users', $user->toArray()) |
| 141 | - ->seePageIs('/users') | 141 | + ->seePageIs('/settings/users') |
| 142 | ->see($user->name); | 142 | ->see($user->name); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| ... | @@ -147,13 +147,13 @@ class AuthTest extends TestCase | ... | @@ -147,13 +147,13 @@ class AuthTest extends TestCase |
| 147 | $user = \BookStack\User::all()->last(); | 147 | $user = \BookStack\User::all()->last(); |
| 148 | $password = $user->password; | 148 | $password = $user->password; |
| 149 | $this->asAdmin() | 149 | $this->asAdmin() |
| 150 | - ->visit('/users') | 150 | + ->visit('/settings/users') |
| 151 | ->click($user->name) | 151 | ->click($user->name) |
| 152 | - ->seePageIs('/users/' . $user->id) | 152 | + ->seePageIs('/settings/users/' . $user->id) |
| 153 | ->see($user->email) | 153 | ->see($user->email) |
| 154 | ->type('Barry Scott', '#name') | 154 | ->type('Barry Scott', '#name') |
| 155 | ->press('Save') | 155 | ->press('Save') |
| 156 | - ->seePageIs('/users') | 156 | + ->seePageIs('/settings/users') |
| 157 | ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott', 'password' => $password]) | 157 | ->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott', 'password' => $password]) |
| 158 | ->notSeeInDatabase('users', ['name' => $user->name]); | 158 | ->notSeeInDatabase('users', ['name' => $user->name]); |
| 159 | } | 159 | } |
| ... | @@ -161,7 +161,7 @@ class AuthTest extends TestCase | ... | @@ -161,7 +161,7 @@ class AuthTest extends TestCase |
| 161 | public function test_user_password_update() | 161 | public function test_user_password_update() |
| 162 | { | 162 | { |
| 163 | $user = \BookStack\User::all()->last(); | 163 | $user = \BookStack\User::all()->last(); |
| 164 | - $userProfilePage = '/users/' . $user->id; | 164 | + $userProfilePage = '/settings/users/' . $user->id; |
| 165 | $this->asAdmin() | 165 | $this->asAdmin() |
| 166 | ->visit($userProfilePage) | 166 | ->visit($userProfilePage) |
| 167 | ->type('newpassword', '#password') | 167 | ->type('newpassword', '#password') |
| ... | @@ -172,7 +172,7 @@ class AuthTest extends TestCase | ... | @@ -172,7 +172,7 @@ class AuthTest extends TestCase |
| 172 | ->type('newpassword', '#password') | 172 | ->type('newpassword', '#password') |
| 173 | ->type('newpassword', '#password-confirm') | 173 | ->type('newpassword', '#password-confirm') |
| 174 | ->press('Save') | 174 | ->press('Save') |
| 175 | - ->seePageIs('/users'); | 175 | + ->seePageIs('/settings/users'); |
| 176 | 176 | ||
| 177 | $userPassword = \BookStack\User::find($user->id)->password; | 177 | $userPassword = \BookStack\User::find($user->id)->password; |
| 178 | $this->assertTrue(Hash::check('newpassword', $userPassword)); | 178 | $this->assertTrue(Hash::check('newpassword', $userPassword)); |
| ... | @@ -184,11 +184,11 @@ class AuthTest extends TestCase | ... | @@ -184,11 +184,11 @@ class AuthTest extends TestCase |
| 184 | $user = $this->getNewUser($userDetails->toArray()); | 184 | $user = $this->getNewUser($userDetails->toArray()); |
| 185 | 185 | ||
| 186 | $this->asAdmin() | 186 | $this->asAdmin() |
| 187 | - ->visit('/users/' . $user->id) | 187 | + ->visit('/settings/users/' . $user->id) |
| 188 | ->click('Delete User') | 188 | ->click('Delete User') |
| 189 | ->see($user->name) | 189 | ->see($user->name) |
| 190 | ->press('Confirm') | 190 | ->press('Confirm') |
| 191 | - ->seePageIs('/users') | 191 | + ->seePageIs('/settings/users') |
| 192 | ->notSeeInDatabase('users', ['name' => $user->name]); | 192 | ->notSeeInDatabase('users', ['name' => $user->name]); |
| 193 | } | 193 | } |
| 194 | 194 | ||
| ... | @@ -199,10 +199,10 @@ class AuthTest extends TestCase | ... | @@ -199,10 +199,10 @@ class AuthTest extends TestCase |
| 199 | $this->assertEquals(1, $adminRole->users()->count()); | 199 | $this->assertEquals(1, $adminRole->users()->count()); |
| 200 | $user = $adminRole->users->first(); | 200 | $user = $adminRole->users->first(); |
| 201 | 201 | ||
| 202 | - $this->asAdmin()->visit('/users/' . $user->id) | 202 | + $this->asAdmin()->visit('/settings/users/' . $user->id) |
| 203 | ->click('Delete User') | 203 | ->click('Delete User') |
| 204 | ->press('Confirm') | 204 | ->press('Confirm') |
| 205 | - ->seePageIs('/users/' . $user->id) | 205 | + ->seePageIs('/settings/users/' . $user->id) |
| 206 | ->see('You cannot delete the only admin'); | 206 | ->see('You cannot delete the only admin'); |
| 207 | } | 207 | } |
| 208 | 208 | ... | ... |
| ... | @@ -94,7 +94,7 @@ class LdapTest extends \TestCase | ... | @@ -94,7 +94,7 @@ class LdapTest extends \TestCase |
| 94 | 94 | ||
| 95 | public function test_create_user_form() | 95 | public function test_create_user_form() |
| 96 | { | 96 | { |
| 97 | - $this->asAdmin()->visit('/users/create') | 97 | + $this->asAdmin()->visit('/settings/users/create') |
| 98 | ->dontSee('Password') | 98 | ->dontSee('Password') |
| 99 | ->type($this->mockUser->name, '#name') | 99 | ->type($this->mockUser->name, '#name') |
| 100 | ->type($this->mockUser->email, '#email') | 100 | ->type($this->mockUser->email, '#email') |
| ... | @@ -102,19 +102,19 @@ class LdapTest extends \TestCase | ... | @@ -102,19 +102,19 @@ class LdapTest extends \TestCase |
| 102 | ->see('The external auth id field is required.') | 102 | ->see('The external auth id field is required.') |
| 103 | ->type($this->mockUser->name, '#external_auth_id') | 103 | ->type($this->mockUser->name, '#external_auth_id') |
| 104 | ->press('Save') | 104 | ->press('Save') |
| 105 | - ->seePageIs('/users') | 105 | + ->seePageIs('/settings/users') |
| 106 | ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]); | 106 | ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | public function test_user_edit_form() | 109 | public function test_user_edit_form() |
| 110 | { | 110 | { |
| 111 | $editUser = User::all()->last(); | 111 | $editUser = User::all()->last(); |
| 112 | - $this->asAdmin()->visit('/users/' . $editUser->id) | 112 | + $this->asAdmin()->visit('/settings/users/' . $editUser->id) |
| 113 | ->see('Edit User') | 113 | ->see('Edit User') |
| 114 | ->dontSee('Password') | 114 | ->dontSee('Password') |
| 115 | ->type('test_auth_id', '#external_auth_id') | 115 | ->type('test_auth_id', '#external_auth_id') |
| 116 | ->press('Save') | 116 | ->press('Save') |
| 117 | - ->seePageIs('/users') | 117 | + ->seePageIs('/settings/users') |
| 118 | ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']); | 118 | ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| ... | @@ -127,7 +127,7 @@ class LdapTest extends \TestCase | ... | @@ -127,7 +127,7 @@ class LdapTest extends \TestCase |
| 127 | public function test_non_admins_cannot_change_auth_id() | 127 | public function test_non_admins_cannot_change_auth_id() |
| 128 | { | 128 | { |
| 129 | $testUser = User::all()->last(); | 129 | $testUser = User::all()->last(); |
| 130 | - $this->actingAs($testUser)->visit('/users/' . $testUser->id) | 130 | + $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id) |
| 131 | ->dontSee('External Authentication'); | 131 | ->dontSee('External Authentication'); |
| 132 | } | 132 | } |
| 133 | 133 | ... | ... |
tests/EntitySearchTest.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +use Illuminate\Support\Facades\DB; | ||
| 4 | + | ||
| 5 | +class EntitySearchTest extends TestCase | ||
| 6 | +{ | ||
| 7 | + | ||
| 8 | + public function test_page_search() | ||
| 9 | + { | ||
| 10 | + $book = \BookStack\Book::all()->first(); | ||
| 11 | + $page = $book->pages->first(); | ||
| 12 | + | ||
| 13 | + $this->asAdmin() | ||
| 14 | + ->visit('/') | ||
| 15 | + ->type($page->name, 'term') | ||
| 16 | + ->press('header-search-box-button') | ||
| 17 | + ->see('Search Results') | ||
| 18 | + ->see($page->name) | ||
| 19 | + ->click($page->name) | ||
| 20 | + ->seePageIs($page->getUrl()); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public function test_invalid_page_search() | ||
| 24 | + { | ||
| 25 | + $this->asAdmin() | ||
| 26 | + ->visit('/') | ||
| 27 | + ->type('<p>test</p>', 'term') | ||
| 28 | + ->press('header-search-box-button') | ||
| 29 | + ->see('Search Results') | ||
| 30 | + ->seeStatusCode(200); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public function test_empty_search_redirects_back() | ||
| 34 | + { | ||
| 35 | + $this->asAdmin() | ||
| 36 | + ->visit('/') | ||
| 37 | + ->visit('/search/all') | ||
| 38 | + ->seePageIs('/'); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public function test_book_search() | ||
| 42 | + { | ||
| 43 | + $book = \BookStack\Book::all()->first(); | ||
| 44 | + $page = $book->pages->last(); | ||
| 45 | + $chapter = $book->chapters->last(); | ||
| 46 | + | ||
| 47 | + $this->asAdmin() | ||
| 48 | + ->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name)) | ||
| 49 | + ->see($page->name) | ||
| 50 | + | ||
| 51 | + ->visit('/search/book/' . $book->id . '?term=' . urlencode($chapter->name)) | ||
| 52 | + ->see($chapter->name); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public function test_empty_book_search_redirects_back() | ||
| 56 | + { | ||
| 57 | + $book = \BookStack\Book::all()->first(); | ||
| 58 | + $this->asAdmin() | ||
| 59 | + ->visit('/books') | ||
| 60 | + ->visit('/search/book/' . $book->id . '?term=') | ||
| 61 | + ->seePageIs('/books'); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + | ||
| 65 | + public function test_pages_search_listing() | ||
| 66 | + { | ||
| 67 | + $page = \BookStack\Page::all()->last(); | ||
| 68 | + $this->asAdmin()->visit('/search/pages?term=' . $page->name) | ||
| 69 | + ->see('Page Search Results')->see('.entity-list', $page->name); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + public function test_chapters_search_listing() | ||
| 73 | + { | ||
| 74 | + $chapter = \BookStack\Chapter::all()->last(); | ||
| 75 | + $this->asAdmin()->visit('/search/chapters?term=' . $chapter->name) | ||
| 76 | + ->see('Chapter Search Results')->seeInElement('.entity-list', $chapter->name); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public function test_books_search_listing() | ||
| 80 | + { | ||
| 81 | + $book = \BookStack\Book::all()->last(); | ||
| 82 | + $this->asAdmin()->visit('/search/books?term=' . $book->name) | ||
| 83 | + ->see('Book Search Results')->see('.entity-list', $book->name); | ||
| 84 | + } | ||
| 85 | +} |
| ... | @@ -155,63 +155,6 @@ class EntityTest extends TestCase | ... | @@ -155,63 +155,6 @@ class EntityTest extends TestCase |
| 155 | return $book; | 155 | return $book; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | - public function test_page_search() | ||
| 159 | - { | ||
| 160 | - $book = \BookStack\Book::all()->first(); | ||
| 161 | - $page = $book->pages->first(); | ||
| 162 | - | ||
| 163 | - $this->asAdmin() | ||
| 164 | - ->visit('/') | ||
| 165 | - ->type($page->name, 'term') | ||
| 166 | - ->press('header-search-box-button') | ||
| 167 | - ->see('Search Results') | ||
| 168 | - ->see($page->name) | ||
| 169 | - ->click($page->name) | ||
| 170 | - ->seePageIs($page->getUrl()); | ||
| 171 | - } | ||
| 172 | - | ||
| 173 | - public function test_invalid_page_search() | ||
| 174 | - { | ||
| 175 | - $this->asAdmin() | ||
| 176 | - ->visit('/') | ||
| 177 | - ->type('<p>test</p>', 'term') | ||
| 178 | - ->press('header-search-box-button') | ||
| 179 | - ->see('Search Results') | ||
| 180 | - ->seeStatusCode(200); | ||
| 181 | - } | ||
| 182 | - | ||
| 183 | - public function test_empty_search_redirects_back() | ||
| 184 | - { | ||
| 185 | - $this->asAdmin() | ||
| 186 | - ->visit('/') | ||
| 187 | - ->visit('/search/all') | ||
| 188 | - ->seePageIs('/'); | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - public function test_book_search() | ||
| 192 | - { | ||
| 193 | - $book = \BookStack\Book::all()->first(); | ||
| 194 | - $page = $book->pages->last(); | ||
| 195 | - $chapter = $book->chapters->last(); | ||
| 196 | - | ||
| 197 | - $this->asAdmin() | ||
| 198 | - ->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name)) | ||
| 199 | - ->see($page->name) | ||
| 200 | - | ||
| 201 | - ->visit('/search/book/' . $book->id . '?term=' . urlencode($chapter->name)) | ||
| 202 | - ->see($chapter->name); | ||
| 203 | - } | ||
| 204 | - | ||
| 205 | - public function test_empty_book_search_redirects_back() | ||
| 206 | - { | ||
| 207 | - $book = \BookStack\Book::all()->first(); | ||
| 208 | - $this->asAdmin() | ||
| 209 | - ->visit('/books') | ||
| 210 | - ->visit('/search/book/' . $book->id . '?term=') | ||
| 211 | - ->seePageIs('/books'); | ||
| 212 | - } | ||
| 213 | - | ||
| 214 | - | ||
| 215 | public function test_entities_viewable_after_creator_deletion() | 158 | public function test_entities_viewable_after_creator_deletion() |
| 216 | { | 159 | { |
| 217 | // Create required assets and revisions | 160 | // Create required assets and revisions |
| ... | @@ -250,5 +193,36 @@ class EntityTest extends TestCase | ... | @@ -250,5 +193,36 @@ class EntityTest extends TestCase |
| 250 | ->click('Revisions')->seeStatusCode(200); | 193 | ->click('Revisions')->seeStatusCode(200); |
| 251 | } | 194 | } |
| 252 | 195 | ||
| 196 | + public function test_recently_created_pages_view() | ||
| 197 | + { | ||
| 198 | + $user = $this->getNewUser(); | ||
| 199 | + $content = $this->createEntityChainBelongingToUser($user); | ||
| 200 | + | ||
| 201 | + $this->asAdmin()->visit('/pages/recently-created') | ||
| 202 | + ->seeInNthElement('.entity-list .page', 0, $content['page']->name); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + public function test_recently_updated_pages_view() | ||
| 206 | + { | ||
| 207 | + $user = $this->getNewUser(); | ||
| 208 | + $content = $this->createEntityChainBelongingToUser($user); | ||
| 209 | + | ||
| 210 | + $this->asAdmin()->visit('/pages/recently-updated') | ||
| 211 | + ->seeInNthElement('.entity-list .page', 0, $content['page']->name); | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + public function test_old_page_slugs_redirect_to_new_pages() | ||
| 215 | + { | ||
| 216 | + $page = \BookStack\Page::all()->first(); | ||
| 217 | + $pageUrl = $page->getUrl(); | ||
| 218 | + $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page'; | ||
| 219 | + $this->asAdmin()->visit($pageUrl) | ||
| 220 | + ->clickInElement('#content', 'Edit') | ||
| 221 | + ->type('super test page', '#name') | ||
| 222 | + ->press('Save Page') | ||
| 223 | + ->seePageIs($newPageUrl) | ||
| 224 | + ->visit($pageUrl) | ||
| 225 | + ->seePageIs($newPageUrl); | ||
| 226 | + } | ||
| 253 | 227 | ||
| 254 | } | 228 | } | ... | ... |
| ... | @@ -109,4 +109,18 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase | ... | @@ -109,4 +109,18 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase |
| 109 | 109 | ||
| 110 | return $this; | 110 | return $this; |
| 111 | } | 111 | } |
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * Click the text within the selected element. | ||
| 115 | + * @param $parentElement | ||
| 116 | + * @param $linkText | ||
| 117 | + * @return $this | ||
| 118 | + */ | ||
| 119 | + protected function clickInElement($parentElement, $linkText) | ||
| 120 | + { | ||
| 121 | + $elem = $this->crawler->filter($parentElement); | ||
| 122 | + $link = $elem->selectLink($linkText); | ||
| 123 | + $this->visit($link->link()->getUri()); | ||
| 124 | + return $this; | ||
| 125 | + } | ||
| 112 | } | 126 | } | ... | ... |
tests/UserProfileTest.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +class UserProfileTest extends TestCase | ||
| 4 | +{ | ||
| 5 | + protected $user; | ||
| 6 | + | ||
| 7 | + public function setUp() | ||
| 8 | + { | ||
| 9 | + parent::setUp(); | ||
| 10 | + $this->user = \BookStack\User::all()->last(); | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + public function test_profile_page_shows_name() | ||
| 14 | + { | ||
| 15 | + $this->asAdmin() | ||
| 16 | + ->visit('/user/' . $this->user->id) | ||
| 17 | + ->see($this->user->name); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public function test_profile_page_shows_recent_entities() | ||
| 21 | + { | ||
| 22 | + $content = $this->createEntityChainBelongingToUser($this->user, $this->user); | ||
| 23 | + | ||
| 24 | + $this->asAdmin() | ||
| 25 | + ->visit('/user/' . $this->user->id) | ||
| 26 | + // Check the recently created page is shown | ||
| 27 | + ->see($content['page']->name) | ||
| 28 | + // Check the recently created chapter is shown | ||
| 29 | + ->see($content['chapter']->name) | ||
| 30 | + // Check the recently created book is shown | ||
| 31 | + ->see($content['book']->name); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public function test_profile_page_shows_created_content_counts() | ||
| 35 | + { | ||
| 36 | + $newUser = $this->getNewUser(); | ||
| 37 | + | ||
| 38 | + $this->asAdmin()->visit('/user/' . $newUser->id) | ||
| 39 | + ->see($newUser->name) | ||
| 40 | + ->seeInElement('#content-counts', '0 Books') | ||
| 41 | + ->seeInElement('#content-counts', '0 Chapters') | ||
| 42 | + ->seeInElement('#content-counts', '0 Pages'); | ||
| 43 | + | ||
| 44 | + $this->createEntityChainBelongingToUser($newUser, $newUser); | ||
| 45 | + | ||
| 46 | + $this->asAdmin()->visit('/user/' . $newUser->id) | ||
| 47 | + ->see($newUser->name) | ||
| 48 | + ->seeInElement('#content-counts', '1 Book') | ||
| 49 | + ->seeInElement('#content-counts', '1 Chapter') | ||
| 50 | + ->seeInElement('#content-counts', '1 Page'); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + public function test_profile_page_shows_recent_activity() | ||
| 54 | + { | ||
| 55 | + $newUser = $this->getNewUser(); | ||
| 56 | + $this->actingAs($newUser); | ||
| 57 | + $entities = $this->createEntityChainBelongingToUser($newUser, $newUser); | ||
| 58 | + Activity::add($entities['book'], 'book_update', $entities['book']->id); | ||
| 59 | + Activity::add($entities['page'], 'page_create', $entities['book']->id); | ||
| 60 | + | ||
| 61 | + $this->asAdmin()->visit('/user/' . $newUser->id) | ||
| 62 | + ->seeInElement('#recent-activity', 'updated book') | ||
| 63 | + ->seeInElement('#recent-activity', 'created page') | ||
| 64 | + ->seeInElement('#recent-activity', $entities['page']->name); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + public function test_clicking_user_name_in_activity_leads_to_profile_page() | ||
| 68 | + { | ||
| 69 | + $newUser = $this->getNewUser(); | ||
| 70 | + $this->actingAs($newUser); | ||
| 71 | + $entities = $this->createEntityChainBelongingToUser($newUser, $newUser); | ||
| 72 | + Activity::add($entities['book'], 'book_update', $entities['book']->id); | ||
| 73 | + Activity::add($entities['page'], 'page_create', $entities['book']->id); | ||
| 74 | + | ||
| 75 | + $this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name) | ||
| 76 | + ->seePageIs('/user/' . $newUser->id) | ||
| 77 | + ->see($newUser->name); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | +} |
-
Please register or sign in to post a comment