Merge branch 'master' into release
This commit is contained in:
commit
14b131e850
|
@ -98,7 +98,7 @@ abstract class Entity extends Model
|
|||
* @param string[] array $wheres
|
||||
* @return mixed
|
||||
*/
|
||||
public static function fullTextSearch($fieldsToSearch, $terms, $wheres = [])
|
||||
public static function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = [])
|
||||
{
|
||||
$termString = '';
|
||||
foreach ($terms as $term) {
|
||||
|
@ -107,7 +107,7 @@ abstract class Entity extends Model
|
|||
$fields = implode(',', $fieldsToSearch);
|
||||
$termStringEscaped = \DB::connection()->getPdo()->quote($termString);
|
||||
$search = static::addSelect(\DB::raw('*, MATCH(name) AGAINST('.$termStringEscaped.' IN BOOLEAN MODE) AS title_relevance'));
|
||||
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termStringEscaped]);
|
||||
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
|
||||
|
||||
// Add additional where terms
|
||||
foreach ($wheres as $whereTerm) {
|
||||
|
@ -115,10 +115,13 @@ abstract class Entity extends Model
|
|||
}
|
||||
|
||||
// Load in relations
|
||||
if (!static::isA('book')) $search = $search->with('book');
|
||||
if (static::isA('page')) $search = $search->with('chapter');
|
||||
if (static::isA('page')) {
|
||||
$search = $search->with('book', 'chapter', 'createdBy', 'updatedBy');
|
||||
} else if (static::isA('chapter')) {
|
||||
$search = $search->with('book');
|
||||
}
|
||||
|
||||
return $search->orderBy('title_relevance', 'desc')->get();
|
||||
return $search->orderBy('title_relevance', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -157,7 +157,7 @@ class BookController extends Controller
|
|||
$this->checkPermission('book-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$bookChildren = $this->bookRepo->getChildren($book);
|
||||
$books = $this->bookRepo->getAll();
|
||||
$books = $this->bookRepo->getAll(false);
|
||||
$this->setPageTitle('Sort Book ' . $book->getShortName());
|
||||
return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
|
||||
}
|
||||
|
|
|
@ -3,25 +3,21 @@
|
|||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use BookStack\Repos\EntityRepo;
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Repos\BookRepo;
|
||||
use Views;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
|
||||
protected $activityService;
|
||||
protected $bookRepo;
|
||||
protected $entityRepo;
|
||||
|
||||
/**
|
||||
* HomeController constructor.
|
||||
* @param BookRepo $bookRepo
|
||||
* @param EntityRepo $entityRepo
|
||||
*/
|
||||
public function __construct(BookRepo $bookRepo)
|
||||
public function __construct(EntityRepo $entityRepo)
|
||||
{
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->entityRepo = $entityRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
@ -33,9 +29,16 @@ class HomeController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$activity = Activity::latest();
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(10, 0) : $this->bookRepo->getLatest(10);
|
||||
return view('home', ['activity' => $activity, 'recents' => $recents]);
|
||||
$activity = Activity::latest(10);
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12, 0) : $this->entityRepo->getRecentlyCreatedBooks(10);
|
||||
$recentlyCreatedPages = $this->entityRepo->getRecentlyCreatedPages(5);
|
||||
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdatedPages(5);
|
||||
return view('home', [
|
||||
'activity' => $activity,
|
||||
'recents' => $recents,
|
||||
'recentlyCreatedPages' => $recentlyCreatedPages,
|
||||
'recentlyUpdatedPages' => $recentlyUpdatedPages
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use BookStack\Http\Requests;
|
|||
use BookStack\Repos\BookRepo;
|
||||
use BookStack\Repos\ChapterRepo;
|
||||
use BookStack\Repos\PageRepo;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Views;
|
||||
|
||||
class PageController extends Controller
|
||||
|
@ -81,6 +82,8 @@ class PageController extends Controller
|
|||
|
||||
/**
|
||||
* Display the specified page.
|
||||
* If the page is not found via the slug the
|
||||
* revisions are searched for a match.
|
||||
*
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
|
@ -89,7 +92,15 @@ class PageController extends Controller
|
|||
public function show($bookSlug, $pageSlug)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
|
||||
try {
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
} catch (NotFoundHttpException $e) {
|
||||
$page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug);
|
||||
if ($page === null) abort(404);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
||||
$sidebarTree = $this->bookRepo->getChildren($book);
|
||||
Views::add($page);
|
||||
$this->setPageTitle($page->getShortName());
|
||||
|
@ -278,4 +289,30 @@ class PageController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a listing of recently created pages
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRecentlyCreated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Created Pages',
|
||||
'pages' => $pages
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a listing of recently created pages
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRecentlyUpdated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Updated Pages',
|
||||
'pages' => $pages
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,11 +42,77 @@ class SearchController extends Controller
|
|||
return redirect()->back();
|
||||
}
|
||||
$searchTerm = $request->get('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm);
|
||||
$books = $this->bookRepo->getBySearch($searchTerm);
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm);
|
||||
$paginationAppends = $request->only('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends);
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends);
|
||||
$this->setPageTitle('Search For ' . $searchTerm);
|
||||
return view('search/all', ['pages' => $pages, 'books' => $books, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
|
||||
return view('search/all', [
|
||||
'pages' => $pages,
|
||||
'books' => $books,
|
||||
'chapters' => $chapters,
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the pages in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchPages(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$this->setPageTitle('Page Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $pages,
|
||||
'title' => 'Page Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the chapters in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchChapters(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$this->setPageTitle('Chapter Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $chapters,
|
||||
'title' => 'Chapter Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the books in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchBooks(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends);
|
||||
$this->setPageTitle('Book Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $books,
|
||||
'title' => 'Book Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Activity;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
|
@ -92,10 +93,9 @@ class UserController extends Controller
|
|||
$user->save();
|
||||
}
|
||||
|
||||
return redirect('/users');
|
||||
return redirect('/settings/users');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified user.
|
||||
* @param int $id
|
||||
|
@ -159,7 +159,7 @@ class UserController extends Controller
|
|||
}
|
||||
|
||||
$user->save();
|
||||
return redirect('/users');
|
||||
return redirect('/settings/users');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,6 +197,25 @@ class UserController extends Controller
|
|||
}
|
||||
$this->userRepo->destroy($user);
|
||||
|
||||
return redirect('/users');
|
||||
return redirect('/settings/users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the user profile page
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showProfilePage($id)
|
||||
{
|
||||
$user = $this->userRepo->getById($id);
|
||||
$userActivity = $this->userRepo->getActivity($user);
|
||||
$recentlyCreated = $this->userRepo->getRecentlyCreated($user, 5, 0);
|
||||
$assetCounts = $this->userRepo->getAssetCounts($user);
|
||||
return view('users/profile', [
|
||||
'user' => $user,
|
||||
'activity' => $userActivity,
|
||||
'recentlyCreated' => $recentlyCreated,
|
||||
'assetCounts' => $assetCounts
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
// Authenticated routes...
|
||||
Route::group(['middleware' => 'auth'], function () {
|
||||
|
||||
Route::group(['prefix' => 'pages'], function() {
|
||||
Route::get('/recently-created', 'PageController@showRecentlyCreated');
|
||||
Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'books'], function () {
|
||||
|
||||
// Books
|
||||
|
@ -47,14 +52,8 @@ Route::group(['middleware' => 'auth'], function () {
|
|||
|
||||
});
|
||||
|
||||
// Users
|
||||
Route::get('/users', 'UserController@index');
|
||||
Route::get('/users/create', 'UserController@create');
|
||||
Route::get('/users/{id}/delete', 'UserController@delete');
|
||||
Route::post('/users/create', 'UserController@store');
|
||||
Route::get('/users/{id}', 'UserController@edit');
|
||||
Route::put('/users/{id}', 'UserController@update');
|
||||
Route::delete('/users/{id}', 'UserController@destroy');
|
||||
// User Profile routes
|
||||
Route::get('/user/{userId}', 'UserController@showProfilePage');
|
||||
|
||||
// Image routes
|
||||
Route::group(['prefix' => 'images'], function() {
|
||||
|
@ -75,6 +74,9 @@ Route::group(['middleware' => 'auth'], function () {
|
|||
|
||||
// Search
|
||||
Route::get('/search/all', 'SearchController@searchAll');
|
||||
Route::get('/search/pages', 'SearchController@searchPages');
|
||||
Route::get('/search/books', 'SearchController@searchBooks');
|
||||
Route::get('/search/chapters', 'SearchController@searchChapters');
|
||||
Route::get('/search/book/{bookId}', 'SearchController@searchBook');
|
||||
|
||||
// Other Pages
|
||||
|
@ -82,8 +84,18 @@ Route::group(['middleware' => 'auth'], function () {
|
|||
Route::get('/home', 'HomeController@index');
|
||||
|
||||
// Settings
|
||||
Route::get('/settings', 'SettingController@index');
|
||||
Route::post('/settings', 'SettingController@update');
|
||||
Route::group(['prefix' => 'settings'], function() {
|
||||
Route::get('/', 'SettingController@index');
|
||||
Route::post('/', 'SettingController@update');
|
||||
// Users
|
||||
Route::get('/users', 'UserController@index');
|
||||
Route::get('/users/create', 'UserController@create');
|
||||
Route::get('/users/{id}/delete', 'UserController@delete');
|
||||
Route::post('/users/create', 'UserController@store');
|
||||
Route::get('/users/{id}', 'UserController@edit');
|
||||
Route::put('/users/{id}', 'UserController@update');
|
||||
Route::delete('/users/{id}', 'UserController@destroy');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ class Page extends Entity
|
|||
|
||||
public function getExcerpt($length = 100)
|
||||
{
|
||||
return strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
|
||||
$text = strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
|
||||
return mb_convert_encoding($text, 'UTF-8');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,7 +42,9 @@ class BookRepo
|
|||
*/
|
||||
public function getAll($count = 10)
|
||||
{
|
||||
return $this->book->orderBy('name', 'asc')->take($count)->get();
|
||||
$bookQuery = $this->book->orderBy('name', 'asc');
|
||||
if (!$count) return $bookQuery->get();
|
||||
return $bookQuery->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,12 +220,15 @@ class BookRepo
|
|||
/**
|
||||
* Get books by search term.
|
||||
* @param $term
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term)
|
||||
public function getBySearch($term, $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$books = $this->book->fullTextSearch(['name', 'description'], $terms);
|
||||
$books = $this->book->fullTextSearchQuery(['name', 'description'], $terms)
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
foreach ($books as $book) {
|
||||
//highlight
|
||||
|
|
|
@ -125,12 +125,15 @@ class ChapterRepo
|
|||
* Get chapters by the given search term.
|
||||
* @param $term
|
||||
* @param array $whereTerms
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term, $whereTerms = [])
|
||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$chapters = $this->chapter->fullTextSearch(['name', 'description'], $terms, $whereTerms);
|
||||
$chapters = $this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms)
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
foreach ($chapters as $chapter) {
|
||||
//highlight
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<?php namespace BookStack\Repos;
|
||||
|
||||
|
||||
use BookStack\Book;
|
||||
use BookStack\Chapter;
|
||||
use BookStack\Page;
|
||||
|
||||
class EntityRepo
|
||||
{
|
||||
|
||||
public $book;
|
||||
public $chapter;
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* EntityService constructor.
|
||||
* @param $book
|
||||
* @param $chapter
|
||||
* @param $page
|
||||
*/
|
||||
public function __construct(Book $book, Chapter $chapter, Page $page)
|
||||
{
|
||||
$this->book = $book;
|
||||
$this->chapter = $chapter;
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest books added to the system.
|
||||
* @param $count
|
||||
* @param $page
|
||||
*/
|
||||
public function getRecentlyCreatedBooks($count = 20, $page = 0)
|
||||
{
|
||||
return $this->book->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently updated books.
|
||||
* @param $count
|
||||
* @param int $page
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyUpdatedBooks($count = 20, $page = 0)
|
||||
{
|
||||
return $this->book->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param $count
|
||||
* @param $page
|
||||
*/
|
||||
public function getRecentlyCreatedPages($count = 20, $page = 0)
|
||||
{
|
||||
return $this->page->orderBy('created_at', 'desc')->skip($page*$count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently updated pages.
|
||||
* @param $count
|
||||
* @param int $page
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyUpdatedPages($count = 20, $page = 0)
|
||||
{
|
||||
return $this->page->orderBy('updated_at', 'desc')->skip($page*$count)->take($count)->get();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Log;
|
|||
use Illuminate\Support\Str;
|
||||
use BookStack\Page;
|
||||
use BookStack\PageRevision;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class PageRepo
|
||||
{
|
||||
|
@ -65,11 +66,28 @@ class PageRepo
|
|||
public function getBySlug($slug, $bookId)
|
||||
{
|
||||
$page = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
|
||||
if ($page === null) abort(404);
|
||||
if ($page === null) throw new NotFoundHttpException('Page not found');
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through page revisions and retrieve
|
||||
* the last page in the current book that
|
||||
* has a slug equal to the one given.
|
||||
* @param $pageSlug
|
||||
* @param $bookSlug
|
||||
* @return null | Page
|
||||
*/
|
||||
public function findPageUsingOldSlug($pageSlug, $bookSlug)
|
||||
{
|
||||
$revision = $this->pageRevision->where('slug', '=', $pageSlug)
|
||||
->where('book_slug', '=', $bookSlug)->orderBy('created_at', 'desc')
|
||||
->with('page')->first();
|
||||
return $revision !== null ? $revision->page : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new Page instance from the given input.
|
||||
* @param $input
|
||||
* @return Page
|
||||
*/
|
||||
|
@ -125,21 +143,20 @@ class PageRepo
|
|||
if($htmlText == '') return $htmlText;
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML($htmlText);
|
||||
$doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
|
||||
|
||||
$container = $doc->documentElement;
|
||||
$body = $container->childNodes->item(0);
|
||||
$childNodes = $body->childNodes;
|
||||
|
||||
// Ensure no duplicate ids are used
|
||||
$lastId = false;
|
||||
$idArray = [];
|
||||
|
||||
foreach ($childNodes as $index => $childNode) {
|
||||
/** @var \DOMElement $childNode */
|
||||
if (get_class($childNode) !== 'DOMElement') continue;
|
||||
|
||||
// Overwrite id if not a bookstack custom id
|
||||
// Overwrite id if not a BookStack custom id
|
||||
if ($childNode->hasAttribute('id')) {
|
||||
$id = $childNode->getAttribute('id');
|
||||
if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) {
|
||||
|
@ -149,13 +166,18 @@ class PageRepo
|
|||
}
|
||||
|
||||
// Create an unique id for the element
|
||||
do {
|
||||
$id = 'bkmrk-' . substr(uniqid(), -5);
|
||||
} while ($id == $lastId);
|
||||
$lastId = $id;
|
||||
// Uses the content as a basis to ensure output is the same every time
|
||||
// the same content is passed through.
|
||||
$contentId = 'bkmrk-' . substr(strtolower(preg_replace('/\s+/', '-', trim($childNode->nodeValue))), 0, 20);
|
||||
$newId = urlencode($contentId);
|
||||
$loopIndex = 0;
|
||||
while (in_array($newId, $idArray)) {
|
||||
$newId = urlencode($contentId . '-' . $loopIndex);
|
||||
$loopIndex++;
|
||||
}
|
||||
|
||||
$childNode->setAttribute('id', $id);
|
||||
$idArray[] = $id;
|
||||
$childNode->setAttribute('id', $newId);
|
||||
$idArray[] = $newId;
|
||||
}
|
||||
|
||||
// Generate inner html as a string
|
||||
|
@ -173,12 +195,15 @@ class PageRepo
|
|||
* Highlights page content for showing in results.
|
||||
* @param string $term
|
||||
* @param array $whereTerms
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term, $whereTerms = [])
|
||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$pages = $this->page->fullTextSearch(['name', 'text'], $terms, $whereTerms);
|
||||
$pages = $this->page->fullTextSearchQuery(['name', 'text'], $terms, $whereTerms)
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
|
||||
// Add highlights to page text.
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
|
@ -238,9 +263,13 @@ class PageRepo
|
|||
$this->saveRevision($page);
|
||||
}
|
||||
|
||||
// Prevent slug being updated if no name change
|
||||
if ($page->name !== $input['name']) {
|
||||
$page->slug = $this->findSuitableSlug($input['name'], $book_id, $page->id);
|
||||
}
|
||||
|
||||
// Update with new details
|
||||
$page->fill($input);
|
||||
$page->slug = $this->findSuitableSlug($page->name, $book_id, $page->id);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->updated_by = auth()->user()->id;
|
||||
|
@ -276,6 +305,8 @@ class PageRepo
|
|||
{
|
||||
$revision = $this->pageRevision->fill($page->toArray());
|
||||
$revision->page_id = $page->id;
|
||||
$revision->slug = $page->slug;
|
||||
$revision->book_slug = $page->book->slug;
|
||||
$revision->created_by = auth()->user()->id;
|
||||
$revision->created_at = $page->updated_at;
|
||||
$revision->save();
|
||||
|
@ -358,5 +389,22 @@ class PageRepo
|
|||
$page->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param $count
|
||||
*/
|
||||
public function getRecentlyCreatedPaginated($count = 20)
|
||||
{
|
||||
return $this->page->orderBy('created_at', 'desc')->paginate($count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param $count
|
||||
*/
|
||||
public function getRecentlyUpdatedPaginated($count = 20)
|
||||
{
|
||||
return $this->page->orderBy('updated_at', 'desc')->paginate($count);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
<?php namespace BookStack\Repos;
|
||||
|
||||
|
||||
use BookStack\Role;
|
||||
use BookStack\User;
|
||||
use Setting;
|
||||
|
@ -10,15 +9,19 @@ class UserRepo
|
|||
|
||||
protected $user;
|
||||
protected $role;
|
||||
protected $entityRepo;
|
||||
|
||||
/**
|
||||
* UserRepo constructor.
|
||||
* @param $user
|
||||
* @param User $user
|
||||
* @param Role $role
|
||||
* @param EntityRepo $entityRepo
|
||||
*/
|
||||
public function __construct(User $user, Role $role)
|
||||
public function __construct(User $user, Role $role, EntityRepo $entityRepo)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->role = $role;
|
||||
$this->entityRepo = $entityRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,4 +115,49 @@ class UserRepo
|
|||
$user->socialAccounts()->delete();
|
||||
$user->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest activity for a user.
|
||||
* @param User $user
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
public function getActivity(User $user, $count = 20, $page = 0)
|
||||
{
|
||||
return \Activity::userActivity($user, $count, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recently created content for this given user.
|
||||
* @param User $user
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyCreated(User $user, $count = 20)
|
||||
{
|
||||
return [
|
||||
'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->orderBy('created_at', 'desc')
|
||||
->take($count)->get(),
|
||||
'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->orderBy('created_at', 'desc')
|
||||
->take($count)->get(),
|
||||
'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->orderBy('created_at', 'desc')
|
||||
->take($count)->get()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset created counts for the give user.
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getAssetCounts(User $user)
|
||||
{
|
||||
return [
|
||||
'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->count(),
|
||||
'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->count(),
|
||||
'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->count(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -29,18 +29,19 @@ class ActivityService
|
|||
*/
|
||||
public function add(Entity $entity, $activityKey, $bookId = 0, $extra = false)
|
||||
{
|
||||
$this->activity->user_id = $this->user->id;
|
||||
$this->activity->book_id = $bookId;
|
||||
$this->activity->key = strtolower($activityKey);
|
||||
$activity = $this->activity->newInstance();
|
||||
$activity->user_id = $this->user->id;
|
||||
$activity->book_id = $bookId;
|
||||
$activity->key = strtolower($activityKey);
|
||||
if ($extra !== false) {
|
||||
$this->activity->extra = $extra;
|
||||
$activity->extra = $extra;
|
||||
}
|
||||
$entity->activity()->save($this->activity);
|
||||
$entity->activity()->save($activity);
|
||||
$this->setNotification($activityKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a activity history with a message & without binding to a entitiy.
|
||||
* Adds a activity history with a message & without binding to a entity.
|
||||
* @param $activityKey
|
||||
* @param int $bookId
|
||||
* @param bool|false $extra
|
||||
|
@ -91,14 +92,14 @@ class ActivityService
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the latest activity for an entitiy, Filtering out similar
|
||||
* Gets the latest activity for an entity, Filtering out similar
|
||||
* items to prevent a message activity list.
|
||||
* @param Entity $entity
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
function entityActivity($entity, $count = 20, $page = 0)
|
||||
public function entityActivity($entity, $count = 20, $page = 0)
|
||||
{
|
||||
$activity = $entity->hasMany('BookStack\Activity')->orderBy('created_at', 'desc')
|
||||
->skip($count * $page)->take($count)->get();
|
||||
|
@ -107,15 +108,30 @@ class ActivityService
|
|||
}
|
||||
|
||||
/**
|
||||
* Filters out similar activity.
|
||||
* @param Activity[] $activity
|
||||
* Get latest activity for a user, Filtering out similar
|
||||
* items.
|
||||
* @param $user
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
protected function filterSimilar($activity)
|
||||
public function userActivity($user, $count = 20, $page = 0)
|
||||
{
|
||||
$activity = $this->activity->where('user_id', '=', $user->id)
|
||||
->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get();
|
||||
return $this->filterSimilar($activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out similar activity.
|
||||
* @param Activity[] $activities
|
||||
* @return array
|
||||
*/
|
||||
protected function filterSimilar($activities)
|
||||
{
|
||||
$newActivity = [];
|
||||
$previousItem = false;
|
||||
foreach ($activity as $activityItem) {
|
||||
foreach ($activities as $activityItem) {
|
||||
if ($previousItem === false) {
|
||||
$previousItem = $activityItem;
|
||||
$newActivity[] = $activityItem;
|
||||
|
|
|
@ -164,6 +164,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||
*/
|
||||
public function getEditUrl()
|
||||
{
|
||||
return '/users/' . $this->id;
|
||||
return '/settings/users/' . $this->id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,16 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.14.2",
|
||||
"version": "3.15.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2"
|
||||
"reference": "5e6078913293576de969703481994b77c380ca30"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2",
|
||||
"reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5e6078913293576de969703481994b77c380ca30",
|
||||
"reference": "5e6078913293576de969703481994b77c380ca30",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -40,7 +40,8 @@
|
|||
"ext-simplexml": "*",
|
||||
"ext-spl": "*",
|
||||
"nette/neon": "^2.3",
|
||||
"phpunit/phpunit": "~4.0|~5.0"
|
||||
"phpunit/phpunit": "~4.0|~5.0",
|
||||
"psr/cache": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
|
||||
|
@ -84,7 +85,7 @@
|
|||
"s3",
|
||||
"sdk"
|
||||
],
|
||||
"time": "2016-01-28 21:33:18"
|
||||
"time": "2016-02-11 23:23:31"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
|
@ -918,16 +919,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v5.2.12",
|
||||
"version": "v5.2.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7"
|
||||
"reference": "39e89553c124dce266da03ee3c0260bdd62f1848"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/6b6255ad7bfbdb721b8d00b09d52b146c5d363d7",
|
||||
"reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/39e89553c124dce266da03ee3c0260bdd62f1848",
|
||||
"reference": "39e89553c124dce266da03ee3c0260bdd62f1848",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1042,7 +1043,7 @@
|
|||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2016-01-26 04:15:37"
|
||||
"time": "2016-02-15 17:46:58"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
|
@ -1629,16 +1630,16 @@
|
|||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "1.1.6",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "e6f80ab77885151908d0ec743689ca700886e8b0"
|
||||
"reference": "b0e69d10852716b2ccbdff69c75c477637220790"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/e6f80ab77885151908d0ec743689ca700886e8b0",
|
||||
"reference": "e6f80ab77885151908d0ec743689ca700886e8b0",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/b0e69d10852716b2ccbdff69c75c477637220790",
|
||||
"reference": "b0e69d10852716b2ccbdff69c75c477637220790",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1673,7 +1674,7 @@
|
|||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2016-01-29 16:19:52"
|
||||
"time": "2016-02-06 03:52:05"
|
||||
},
|
||||
{
|
||||
"name": "phenx/php-font-lib",
|
||||
|
@ -2024,16 +2025,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3"
|
||||
"reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
|
||||
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0",
|
||||
"reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2080,20 +2081,20 @@
|
|||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-22 10:39:06"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a"
|
||||
"reference": "29606049ced1ec715475f88d1bbe587252a3476e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/73612266ac709769effdbfc0762e5b07cfd2ac2a",
|
||||
"reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/29606049ced1ec715475f88d1bbe587252a3476e",
|
||||
"reference": "29606049ced1ec715475f88d1bbe587252a3476e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2137,20 +2138,20 @@
|
|||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:39:53"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf"
|
||||
"reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d36355e026905fa5229e1ed7b4e9eda2e67adfcf",
|
||||
"reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa",
|
||||
"reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2197,20 +2198,20 @@
|
|||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-10-30 23:35:59"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "8617895eb798b6bdb338321ce19453dc113e5675"
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/8617895eb798b6bdb338321ce19453dc113e5675",
|
||||
"reference": "8617895eb798b6bdb338321ce19453dc113e5675",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2246,20 +2247,20 @@
|
|||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 11:13:14"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5"
|
||||
"reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5",
|
||||
"reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/9344a87ceedfc50354a39653e54257ee9aa6a77d",
|
||||
"reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2298,20 +2299,20 @@
|
|||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-18 15:43:53"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db"
|
||||
"reference": "cec02604450481ac26710ca4249cc61b57b23942"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f7933e9f19e26e7baba7ec04735b466fedd3a6db",
|
||||
"reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/cec02604450481ac26710ca4249cc61b57b23942",
|
||||
"reference": "cec02604450481ac26710ca4249cc61b57b23942",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2380,7 +2381,7 @@
|
|||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 16:46:13"
|
||||
"time": "2016-02-03 12:38:44"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
|
@ -2551,16 +2552,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3"
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
|
||||
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/dfecef47506179db2501430e732adbf3793099c8",
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2596,20 +2597,20 @@
|
|||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-23 11:04:02"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59"
|
||||
"reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59",
|
||||
"reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/4686baa55a835e1c1ede9b86ba02415c8c8d6166",
|
||||
"reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2670,20 +2671,20 @@
|
|||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2015-12-23 08:00:11"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "dff0867826a7068d673801b7522f8e2634016ef9"
|
||||
"reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/dff0867826a7068d673801b7522f8e2634016ef9",
|
||||
"reference": "dff0867826a7068d673801b7522f8e2634016ef9",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91",
|
||||
"reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2734,20 +2735,20 @@
|
|||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 17:45:07"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "87db8700deb12ba2b65e858f656a1f885530bcb0"
|
||||
"reference": "24bb94807eff00db49374c37ebf56a0304e8aef3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/87db8700deb12ba2b65e858f656a1f885530bcb0",
|
||||
"reference": "87db8700deb12ba2b65e858f656a1f885530bcb0",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/24bb94807eff00db49374c37ebf56a0304e8aef3",
|
||||
"reference": "24bb94807eff00db49374c37ebf56a0304e8aef3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2797,7 +2798,7 @@
|
|||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2015-12-05 11:13:14"
|
||||
"time": "2016-01-07 13:38:51"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
|
@ -3182,22 +3183,24 @@
|
|||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.5.0",
|
||||
"version": "v1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
|
||||
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "~2.0",
|
||||
"sebastian/comparator": "~1.1"
|
||||
"sebastian/comparator": "~1.1",
|
||||
"sebastian/recursion-context": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "~2.0"
|
||||
|
@ -3205,7 +3208,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4.x-dev"
|
||||
"dev-master": "1.5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -3238,7 +3241,7 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2015-08-13 10:07:40"
|
||||
"time": "2016-02-15 07:46:21"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -3482,16 +3485,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "4.8.21",
|
||||
"version": "4.8.23",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c"
|
||||
"reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c",
|
||||
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e351261f9cd33daf205a131a1ba61c6d33bd483",
|
||||
"reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3550,7 +3553,7 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2015-12-12 07:45:58"
|
||||
"time": "2016-02-11 14:56:33"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
|
@ -3981,16 +3984,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3"
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3",
|
||||
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4030,20 +4033,20 @@
|
|||
],
|
||||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 17:45:07"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d"
|
||||
"reference": "b693a9650aa004576b593ff2e91ae749dc90123d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
|
||||
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b693a9650aa004576b593ff2e91ae749dc90123d",
|
||||
"reference": "b693a9650aa004576b593ff2e91ae749dc90123d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4086,20 +4089,20 @@
|
|||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:42:31"
|
||||
"time": "2016-01-25 09:56:57"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691"
|
||||
"reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691",
|
||||
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3cf0709d7fe936e97bee9e954382e449003f1d9a",
|
||||
"reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -4135,7 +4138,7 @@
|
|||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:39:53"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -18,7 +18,7 @@ class CreateUsersTable extends Migration
|
|||
$table->string('email')->unique();
|
||||
$table->string('password', 60);
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
\BookStack\User::forceCreate([
|
||||
|
|
|
@ -17,7 +17,7 @@ class CreateBooksTable extends Migration
|
|||
$table->string('name');
|
||||
$table->string('slug')->indexed();
|
||||
$table->text('description');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class CreatePagesTable extends Migration
|
|||
$table->longText('html');
|
||||
$table->longText('text');
|
||||
$table->integer('priority');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class CreateImagesTable extends Migration
|
|||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->string('url');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class CreateChaptersTable extends Migration
|
|||
$table->text('name');
|
||||
$table->text('description');
|
||||
$table->integer('priority');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class CreatePageRevisionsTable extends Migration
|
|||
$table->longText('html');
|
||||
$table->longText('text');
|
||||
$table->integer('created_by');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class CreateActivitiesTable extends Migration
|
|||
$table->integer('user_id');
|
||||
$table->integer('entity_id');
|
||||
$table->string('entity_type');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class AddRolesAndPermissions extends Migration
|
|||
$table->string('name')->unique();
|
||||
$table->string('display_name')->nullable();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
// Create table for associating roles to users (Many-to-Many)
|
||||
|
@ -50,7 +50,7 @@ class AddRolesAndPermissions extends Migration
|
|||
$table->string('name')->unique();
|
||||
$table->string('display_name')->nullable();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
// Create table for associating permissions to roles (Many-to-Many)
|
||||
|
|
|
@ -15,7 +15,7 @@ class CreateSettingsTable extends Migration
|
|||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->string('setting_key')->primary()->indexed();
|
||||
$table->text('value');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class CreateSocialAccountsTable extends Migration
|
|||
$table->string('driver')->index();
|
||||
$table->string('driver_id');
|
||||
$table->string('avatar');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class AddEmailConfirmationTable extends Migration
|
|||
$table->increments('id');
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('token')->index();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class CreateViewsTable extends Migration
|
|||
$table->integer('viewable_id');
|
||||
$table->string('viewable_type');
|
||||
$table->integer('views');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSlugToRevisions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->string('slug');
|
||||
$table->index('slug');
|
||||
$table->string('book_slug');
|
||||
$table->index('book_slug');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
$table->dropColumn('book_slug');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -25,8 +25,13 @@
|
|||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
<env name="DB_CONNECTION" value="mysql_testing"/>
|
||||
<env name="MAIL_PRETEND" value="true"/>
|
||||
<env name="MAIL_DRIVER" value="log"/>
|
||||
<env name="AUTH_METHOD" value="standard"/>
|
||||
<env name="DISABLE_EXTERNAL_SERVICES" value="false"/>
|
||||
<env name="LDAP_VERSION" value="3"/>
|
||||
<env name="GITHUB_APP_ID" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GITHUB_APP_SECRET" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GOOGLE_APP_ID" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GOOGLE_APP_SECRET" value="aaaaaaaaaaaaaa"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
@ -106,6 +106,12 @@ $(function () {
|
|||
}
|
||||
});
|
||||
|
||||
// Common jQuery actions
|
||||
$('[data-action="expand-entity-list-details"]').click(function() {
|
||||
$('.entity-list.compact').find('p').slideToggle(240);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -139,54 +139,6 @@ form.search-box {
|
|||
height: 43px;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-container ul {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
list-style: none;
|
||||
right: 0;
|
||||
margin: $-m 0;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 1px;
|
||||
border: 1px solid #EEE;
|
||||
min-width: 180px;
|
||||
padding: $-xs 0;
|
||||
color: #555;
|
||||
text-align: left !important;
|
||||
&.wide {
|
||||
min-width: 220px;
|
||||
}
|
||||
.text-muted {
|
||||
color: #999;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
padding: $-xs $-m;
|
||||
color: #555;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: #EEE;
|
||||
}
|
||||
i {
|
||||
margin-right: $-m;
|
||||
padding-right: 0;
|
||||
display: inline;
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
li.border-bottom {
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs span.sep {
|
||||
color: #aaa;
|
||||
padding: 0 $-xs;
|
||||
|
|
|
@ -284,3 +284,86 @@ ul.pagination {
|
|||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.entity-list {
|
||||
>div {
|
||||
padding: $-m 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
p {
|
||||
margin: $-xs 0 0 0;
|
||||
}
|
||||
hr {
|
||||
margin: 0;
|
||||
}
|
||||
.text-small.text-muted {
|
||||
color: #AAA;
|
||||
font-size: 0.75em;
|
||||
margin-top: $-xs;
|
||||
}
|
||||
}
|
||||
.entity-list.compact {
|
||||
font-size: 0.6em;
|
||||
h3, a {
|
||||
line-height: 1.2;
|
||||
}
|
||||
p {
|
||||
display: none;
|
||||
font-size: $fs-m * 0.8;
|
||||
padding-top: $-xs;
|
||||
margin: 0;
|
||||
}
|
||||
hr {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-container ul {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
list-style: none;
|
||||
right: 0;
|
||||
margin: $-m 0;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 1px;
|
||||
border: 1px solid #EEE;
|
||||
min-width: 180px;
|
||||
padding: $-xs 0;
|
||||
color: #555;
|
||||
text-align: left !important;
|
||||
&.wide {
|
||||
min-width: 220px;
|
||||
}
|
||||
.text-muted {
|
||||
color: #999;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
padding: $-xs $-m;
|
||||
color: #555;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: #EEE;
|
||||
}
|
||||
i {
|
||||
margin-right: $-m;
|
||||
padding-right: 0;
|
||||
display: inline;
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
li.border-bottom {
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
background-color: #FFF;
|
||||
border: 1px solid #DDD;
|
||||
color: #666;
|
||||
width: 180px;
|
||||
width: 172px;
|
||||
z-index: 40;
|
||||
}
|
||||
input, button {
|
||||
|
|
|
@ -20,4 +20,8 @@ table.table {
|
|||
|
||||
table {
|
||||
max-width: 100%;
|
||||
thead {
|
||||
background-color: #F8F8F8;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
|
@ -115,7 +115,8 @@ pre {
|
|||
box-shadow: 0 1px 2px 0px rgba(10, 10, 10, 0.06);
|
||||
border: 1px solid rgba(221, 221, 221, 0.66);
|
||||
background-color: #fdf6e3;
|
||||
padding: 0.5em;
|
||||
padding: $-s;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
|
@ -251,6 +252,18 @@ ol {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.text-bigger {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.text-large {
|
||||
font-size: 1.6666em;
|
||||
}
|
||||
|
||||
.no-color {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouping
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,13 @@ body.dragging, body.dragging * {
|
|||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
&.huge {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
&.square {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
// System wide notifications
|
||||
|
|
|
@ -58,10 +58,13 @@
|
|||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-lg"></i>Edit Profile</a>
|
||||
<a href="/user/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a>
|
||||
<a href="/settings/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-fw zmdi-hc-lg"></i>Edit Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<p class="text-muted">{{ $chapter->getExcerpt() }}</p>
|
||||
@endif
|
||||
|
||||
@if(count($chapter->pages) > 0 && !isset($hidePages))
|
||||
@if(!isset($hidePages) && count($chapter->pages) > 0)
|
||||
<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>
|
||||
<div class="inset-list">
|
||||
@foreach($chapter->pages as $page)
|
||||
|
|
|
@ -2,20 +2,44 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<div class="faded-small toolbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 faded">
|
||||
<div class="action-buttons text-left">
|
||||
<a data-action="expand-entity-list-details" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>Toggle Details</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 faded">
|
||||
<div class="action-buttons">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" ng-non-bindable>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="col-sm-4">
|
||||
@if($signedIn)
|
||||
<h2>My Recently Viewed</h2>
|
||||
<h3>My Recently Viewed</h3>
|
||||
@else
|
||||
<h2>Recent Books</h2>
|
||||
<h3>Recent Books</h3>
|
||||
@endif
|
||||
@include('partials/entity-list', ['entities' => $recents])
|
||||
@include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-md-offset-1">
|
||||
<div class="margin-top large"> </div>
|
||||
<div class="col-sm-4">
|
||||
<h3><a class="no-color" href="/pages/recently-created">Recently Created Pages</a></h3>
|
||||
@include('partials/entity-list', ['entities' => $recentlyCreatedPages, 'style' => 'compact'])
|
||||
|
||||
<h3><a class="no-color" href="/pages/recently-updated">Recently Updated Pages</a></h3>
|
||||
@include('partials/entity-list', ['entities' => $recentlyCreatedPages, 'style' => 'compact'])
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4" id="recent-activity">
|
||||
<h3>Recent Activity</h3>
|
||||
@include('partials/activity-list', ['activity' => $activity])
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
@extends('base')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-7">
|
||||
<h1>{{ $title }}</h1>
|
||||
@include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed'])
|
||||
{!! $pages->links() !!}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-1"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
|
@ -3,18 +3,29 @@
|
|||
<a href="{{ $page->getUrl() }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ $page->name }}</a>
|
||||
</h3>
|
||||
|
||||
@if(isset($showMeta) && $showMeta)
|
||||
<div class="meta">
|
||||
<span class="text-book"><i class="zmdi zmdi-book"></i> {{ $page->book->name }}</span>
|
||||
@if($page->chapter)
|
||||
<span class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i> {{ $page->chapter->name }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($page->searchSnippet))
|
||||
<p class="text-muted">{!! $page->searchSnippet !!}</p>
|
||||
@else
|
||||
<p class="text-muted">{{ $page->getExcerpt() }}</p>
|
||||
@endif
|
||||
|
||||
@if(isset($style) && $style === 'detailed')
|
||||
<div class="row meta text-muted text-small">
|
||||
<div class="col-md-4">
|
||||
Created {{$page->created_at->diffForHumans()}} @if($page->createdBy)by {{$page->createdBy->name}}@endif <br>
|
||||
Last updated {{ $page->updated_at->diffForHumans() }} @if($page->updatedBy)by {{$page->updatedBy->name}} @endif
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<a class="text-book" href="{{ $page->book->getUrl() }}"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName(30) }}</a>
|
||||
<br>
|
||||
@if($page->chapter)
|
||||
<a class="text-chapter" href="{{ $page->chapter->getUrl() }}"><i class="zmdi zmdi-collection-bookmark"></i>{{ $page->chapter->getShortName(30) }}</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-collection-bookmark"></i> Page is not in a chapter
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<div class="right" ng-non-bindable>
|
||||
@if($activity->user)
|
||||
{{$activity->user->name}}
|
||||
<a href="/user/{{ $activity->user->id }}">{{$activity->user->name}}</a>
|
||||
@else
|
||||
A deleted user
|
||||
@endif
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
@if(count($entities) > 0)
|
||||
<div class="entity-list @if(isset($style)){{ $style }}@endif" ng-non-bindable>
|
||||
@if(count($entities) > 0)
|
||||
@foreach($entities as $index => $entity)
|
||||
@if($entity->isA('page'))
|
||||
@include('pages/list-item', ['page' => $entity])
|
||||
|
@ -14,8 +15,9 @@
|
|||
@endif
|
||||
|
||||
@endforeach
|
||||
@else
|
||||
@else
|
||||
<p class="text-muted">
|
||||
No items available
|
||||
</p>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
|
@ -6,41 +6,36 @@
|
|||
|
||||
<h1>Search Results <span class="text-muted">{{$searchTerm}}</span></h1>
|
||||
|
||||
<p>
|
||||
<a href="/search/pages?term={{$searchTerm}}" class="text-page"><i class="zmdi zmdi-file-text"></i>View all matched pages</a>
|
||||
|
||||
@if(count($chapters) > 0)
|
||||
|
||||
<a href="/search/chapters?term={{$searchTerm}}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>View all matched chapters</a>
|
||||
@endif
|
||||
|
||||
@if(count($books) > 0)
|
||||
|
||||
<a href="/search/books?term={{$searchTerm}}" class="text-book"><i class="zmdi zmdi-book"></i>View all matched books</a>
|
||||
@endif
|
||||
</p>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<h3>Matching Pages</h3>
|
||||
<div class="page-list">
|
||||
@if(count($pages) > 0)
|
||||
@foreach($pages as $page)
|
||||
@include('pages/list-item', ['page' => $page, 'showMeta' => true])
|
||||
<hr>
|
||||
@endforeach
|
||||
@else
|
||||
<p class="text-muted">No pages matched this search</p>
|
||||
@endif
|
||||
</div>
|
||||
<h3><a href="/search/pages?term={{$searchTerm}}" class="no-color">Matching Pages</a></h3>
|
||||
@include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed'])
|
||||
</div>
|
||||
|
||||
<div class="col-md-5 col-md-offset-1">
|
||||
|
||||
@if(count($books) > 0)
|
||||
<h3>Matching Books</h3>
|
||||
<div class="page-list">
|
||||
@foreach($books as $book)
|
||||
@include('books/list-item', ['book' => $book])
|
||||
<hr>
|
||||
@endforeach
|
||||
</div>
|
||||
<h3><a href="/search/books?term={{$searchTerm}}" class="no-color">Matching Books</a></h3>
|
||||
@include('partials/entity-list', ['entities' => $books])
|
||||
@endif
|
||||
|
||||
@if(count($chapters) > 0)
|
||||
<h3>Matching Chapters</h3>
|
||||
<div class="page-list">
|
||||
@foreach($chapters as $chapter)
|
||||
@include('chapters/list-item', ['chapter' => $chapter, 'hidePages' => true])
|
||||
@endforeach
|
||||
</div>
|
||||
<h3><a href="/search/chapters?term={{$searchTerm}}" class="no-color">Matching Chapters</a></h3>
|
||||
@include('partials/entity-list', ['entities' => $chapters])
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
@extends('base')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-7">
|
||||
<h1>{{ $title }} <small>{{$searchTerm}}</small></h1>
|
||||
@include('partials.entity-list', ['entities' => $entities, 'style' => 'detailed'])
|
||||
{!! $entities->links() !!}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-1"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
|
@ -4,7 +4,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12 setting-nav">
|
||||
<a href="/settings" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>Settings</a>
|
||||
<a href="/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a>
|
||||
<a href="/settings/users" @if($selected == 'users') class="selected text-button" @endif><i class="zmdi zmdi-accounts"></i>Users</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="container small" ng-non-bindable>
|
||||
<h1>Create User</h1>
|
||||
|
||||
<form action="/users/create" method="post">
|
||||
<form action="/settings/users/create" method="post">
|
||||
{!! csrf_field() !!}
|
||||
@include('users.forms.' . $authMethod)
|
||||
</form>
|
||||
|
|
|
@ -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>
|
||||
<p class="text-neg">Are you sure you want to delete this user?</p>
|
||||
|
||||
<form action="/users/{{$user->id}}" method="POST">
|
||||
<form action="/settings/users/{{$user->id}}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<a href="/users/{{$user->id}}" class="button muted">Cancel</a>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="col-sm-6"></div>
|
||||
<div class="col-sm-6 faded">
|
||||
<div class="action-buttons">
|
||||
<a href="/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a>
|
||||
<a href="/settings/users/{{$user->id}}/delete" class="text-neg text-button"><i class="zmdi zmdi-delete"></i>Delete User</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
<div class="container small">
|
||||
<form action="/users/{{$user->id}}" method="post">
|
||||
<form action="/settings/users/{{$user->id}}" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6" ng-non-bindable>
|
||||
<h1>Edit {{ $user->id === $currentUser->id ? 'Profile' : 'User' }}</h1>
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<a href="/users" class="button muted">Cancel</a>
|
||||
<a href="/settings/users" class="button muted">Cancel</a>
|
||||
<button class="button pos" type="submit">Save</button>
|
||||
</div>
|
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<a href="/users" class="button muted">Cancel</a>
|
||||
<a href="/settings/users" class="button muted">Cancel</a>
|
||||
<button class="button pos" type="submit">Save</button>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<h1>Users</h1>
|
||||
@if($currentUser->can('user-create'))
|
||||
<p>
|
||||
<a href="/users/create" class="text-pos"><i class="zmdi zmdi-account-add"></i>Add new user</a>
|
||||
<a href="/settings/users/create" class="text-pos"><i class="zmdi zmdi-account-add"></i>Add new user</a>
|
||||
</p>
|
||||
@endif
|
||||
<table class="table">
|
||||
|
@ -25,7 +25,7 @@
|
|||
<td style="line-height: 0;"><img class="avatar med" src="{{$user->getAvatar(40)}}" alt="{{$user->name}}"></td>
|
||||
<td>
|
||||
@if($currentUser->can('user-update') || $currentUser->id == $user->id)
|
||||
<a href="/users/{{$user->id}}">
|
||||
<a href="/settings/users/{{$user->id}}">
|
||||
@endif
|
||||
{{ $user->name }}
|
||||
@if($currentUser->can('user-update') || $currentUser->id == $user->id)
|
||||
|
@ -34,7 +34,7 @@
|
|||
</td>
|
||||
<td>
|
||||
@if($currentUser->can('user-update') || $currentUser->id == $user->id)
|
||||
<a href="/users/{{$user->id}}">
|
||||
<a href="/settings/users/{{$user->id}}">
|
||||
@endif
|
||||
{{ $user->email }}
|
||||
@if($currentUser->can('user-update') || $currentUser->id == $user->id)
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
@extends('base')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="container" ng-non-bindable>
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
|
||||
<div class="padded-top large"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="clearfix">
|
||||
<div class="padded-right float left">
|
||||
<img class="avatar square huge" src="{{$user->getAvatar(120)}}" alt="{{ $user->name }}">
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="margin-top: 0;">{{ $user->name }}</h3>
|
||||
<p class="text-muted">
|
||||
User for {{ $user->created_at->diffForHumans(null, true) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5 text-bigger" id="content-counts">
|
||||
<div class="text-muted">Created Content</div>
|
||||
<div class="text-book">
|
||||
<i class="zmdi zmdi-book zmdi-hc-fw"></i> {{ $assetCounts['books'] }} {{ str_plural('Book', $assetCounts['books']) }}
|
||||
</div>
|
||||
<div class="text-chapter">
|
||||
<i class="zmdi zmdi-collection-bookmark zmdi-hc-fw"></i> {{ $assetCounts['chapters'] }} {{ str_plural('Chapter', $assetCounts['chapters']) }}
|
||||
</div>
|
||||
<div class="text-page">
|
||||
<i class="zmdi zmdi-file-text zmdi-hc-fw"></i> {{ $assetCounts['pages'] }} {{ str_plural('Page', $assetCounts['pages']) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<hr class="even">
|
||||
|
||||
<h3>Recently Created Pages</h3>
|
||||
@if (count($recentlyCreated['pages']) > 0)
|
||||
@include('partials/entity-list', ['entities' => $recentlyCreated['pages']])
|
||||
@else
|
||||
<p class="text-muted">{{ $user->name }} has not created any pages</p>
|
||||
@endif
|
||||
|
||||
<hr class="even">
|
||||
|
||||
<h3>Recently Created Chapters</h3>
|
||||
@if (count($recentlyCreated['chapters']) > 0)
|
||||
@include('partials/entity-list', ['entities' => $recentlyCreated['chapters']])
|
||||
@else
|
||||
<p class="text-muted">{{ $user->name }} has not created any chapters</p>
|
||||
@endif
|
||||
|
||||
<hr class="even">
|
||||
|
||||
<h3>Recently Created Books</h3>
|
||||
@if (count($recentlyCreated['books']) > 0)
|
||||
@include('partials/entity-list', ['entities' => $recentlyCreated['books']])
|
||||
@else
|
||||
<p class="text-muted">{{ $user->name }} has not created any books</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-sm-offset-1" id="recent-activity">
|
||||
<h3>Recent Activity</h3>
|
||||
@include('partials/activity-list', ['activity' => $activity])
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@stop
|
|
@ -129,7 +129,7 @@ class AuthTest extends TestCase
|
|||
$user = factory(\BookStack\User::class)->make();
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/users')
|
||||
->visit('/settings/users')
|
||||
->click('Add new user')
|
||||
->type($user->name, '#name')
|
||||
->type($user->email, '#email')
|
||||
|
@ -138,7 +138,7 @@ class AuthTest extends TestCase
|
|||
->type($user->password, '#password-confirm')
|
||||
->press('Save')
|
||||
->seeInDatabase('users', $user->toArray())
|
||||
->seePageIs('/users')
|
||||
->seePageIs('/settings/users')
|
||||
->see($user->name);
|
||||
}
|
||||
|
||||
|
@ -147,13 +147,13 @@ class AuthTest extends TestCase
|
|||
$user = \BookStack\User::all()->last();
|
||||
$password = $user->password;
|
||||
$this->asAdmin()
|
||||
->visit('/users')
|
||||
->visit('/settings/users')
|
||||
->click($user->name)
|
||||
->seePageIs('/users/' . $user->id)
|
||||
->seePageIs('/settings/users/' . $user->id)
|
||||
->see($user->email)
|
||||
->type('Barry Scott', '#name')
|
||||
->press('Save')
|
||||
->seePageIs('/users')
|
||||
->seePageIs('/settings/users')
|
||||
->seeInDatabase('users', ['id' => $user->id, 'name' => 'Barry Scott', 'password' => $password])
|
||||
->notSeeInDatabase('users', ['name' => $user->name]);
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class AuthTest extends TestCase
|
|||
public function test_user_password_update()
|
||||
{
|
||||
$user = \BookStack\User::all()->last();
|
||||
$userProfilePage = '/users/' . $user->id;
|
||||
$userProfilePage = '/settings/users/' . $user->id;
|
||||
$this->asAdmin()
|
||||
->visit($userProfilePage)
|
||||
->type('newpassword', '#password')
|
||||
|
@ -172,7 +172,7 @@ class AuthTest extends TestCase
|
|||
->type('newpassword', '#password')
|
||||
->type('newpassword', '#password-confirm')
|
||||
->press('Save')
|
||||
->seePageIs('/users');
|
||||
->seePageIs('/settings/users');
|
||||
|
||||
$userPassword = \BookStack\User::find($user->id)->password;
|
||||
$this->assertTrue(Hash::check('newpassword', $userPassword));
|
||||
|
@ -184,11 +184,11 @@ class AuthTest extends TestCase
|
|||
$user = $this->getNewUser($userDetails->toArray());
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/users/' . $user->id)
|
||||
->visit('/settings/users/' . $user->id)
|
||||
->click('Delete User')
|
||||
->see($user->name)
|
||||
->press('Confirm')
|
||||
->seePageIs('/users')
|
||||
->seePageIs('/settings/users')
|
||||
->notSeeInDatabase('users', ['name' => $user->name]);
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,10 @@ class AuthTest extends TestCase
|
|||
$this->assertEquals(1, $adminRole->users()->count());
|
||||
$user = $adminRole->users->first();
|
||||
|
||||
$this->asAdmin()->visit('/users/' . $user->id)
|
||||
$this->asAdmin()->visit('/settings/users/' . $user->id)
|
||||
->click('Delete User')
|
||||
->press('Confirm')
|
||||
->seePageIs('/users/' . $user->id)
|
||||
->seePageIs('/settings/users/' . $user->id)
|
||||
->see('You cannot delete the only admin');
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class LdapTest extends \TestCase
|
|||
|
||||
public function test_create_user_form()
|
||||
{
|
||||
$this->asAdmin()->visit('/users/create')
|
||||
$this->asAdmin()->visit('/settings/users/create')
|
||||
->dontSee('Password')
|
||||
->type($this->mockUser->name, '#name')
|
||||
->type($this->mockUser->email, '#email')
|
||||
|
@ -102,19 +102,19 @@ class LdapTest extends \TestCase
|
|||
->see('The external auth id field is required.')
|
||||
->type($this->mockUser->name, '#external_auth_id')
|
||||
->press('Save')
|
||||
->seePageIs('/users')
|
||||
->seePageIs('/settings/users')
|
||||
->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
|
||||
}
|
||||
|
||||
public function test_user_edit_form()
|
||||
{
|
||||
$editUser = User::all()->last();
|
||||
$this->asAdmin()->visit('/users/' . $editUser->id)
|
||||
$this->asAdmin()->visit('/settings/users/' . $editUser->id)
|
||||
->see('Edit User')
|
||||
->dontSee('Password')
|
||||
->type('test_auth_id', '#external_auth_id')
|
||||
->press('Save')
|
||||
->seePageIs('/users')
|
||||
->seePageIs('/settings/users')
|
||||
->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ class LdapTest extends \TestCase
|
|||
public function test_non_admins_cannot_change_auth_id()
|
||||
{
|
||||
$testUser = User::all()->last();
|
||||
$this->actingAs($testUser)->visit('/users/' . $testUser->id)
|
||||
$this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
|
||||
->dontSee('External Authentication');
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EntitySearchTest extends TestCase
|
||||
{
|
||||
|
||||
public function test_page_search()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$page = $book->pages->first();
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->type($page->name, 'term')
|
||||
->press('header-search-box-button')
|
||||
->see('Search Results')
|
||||
->see($page->name)
|
||||
->click($page->name)
|
||||
->seePageIs($page->getUrl());
|
||||
}
|
||||
|
||||
public function test_invalid_page_search()
|
||||
{
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->type('<p>test</p>', 'term')
|
||||
->press('header-search-box-button')
|
||||
->see('Search Results')
|
||||
->seeStatusCode(200);
|
||||
}
|
||||
|
||||
public function test_empty_search_redirects_back()
|
||||
{
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->visit('/search/all')
|
||||
->seePageIs('/');
|
||||
}
|
||||
|
||||
public function test_book_search()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$page = $book->pages->last();
|
||||
$chapter = $book->chapters->last();
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name))
|
||||
->see($page->name)
|
||||
|
||||
->visit('/search/book/' . $book->id . '?term=' . urlencode($chapter->name))
|
||||
->see($chapter->name);
|
||||
}
|
||||
|
||||
public function test_empty_book_search_redirects_back()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$this->asAdmin()
|
||||
->visit('/books')
|
||||
->visit('/search/book/' . $book->id . '?term=')
|
||||
->seePageIs('/books');
|
||||
}
|
||||
|
||||
|
||||
public function test_pages_search_listing()
|
||||
{
|
||||
$page = \BookStack\Page::all()->last();
|
||||
$this->asAdmin()->visit('/search/pages?term=' . $page->name)
|
||||
->see('Page Search Results')->see('.entity-list', $page->name);
|
||||
}
|
||||
|
||||
public function test_chapters_search_listing()
|
||||
{
|
||||
$chapter = \BookStack\Chapter::all()->last();
|
||||
$this->asAdmin()->visit('/search/chapters?term=' . $chapter->name)
|
||||
->see('Chapter Search Results')->seeInElement('.entity-list', $chapter->name);
|
||||
}
|
||||
|
||||
public function test_books_search_listing()
|
||||
{
|
||||
$book = \BookStack\Book::all()->last();
|
||||
$this->asAdmin()->visit('/search/books?term=' . $book->name)
|
||||
->see('Book Search Results')->see('.entity-list', $book->name);
|
||||
}
|
||||
}
|
|
@ -155,63 +155,6 @@ class EntityTest extends TestCase
|
|||
return $book;
|
||||
}
|
||||
|
||||
public function test_page_search()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$page = $book->pages->first();
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->type($page->name, 'term')
|
||||
->press('header-search-box-button')
|
||||
->see('Search Results')
|
||||
->see($page->name)
|
||||
->click($page->name)
|
||||
->seePageIs($page->getUrl());
|
||||
}
|
||||
|
||||
public function test_invalid_page_search()
|
||||
{
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->type('<p>test</p>', 'term')
|
||||
->press('header-search-box-button')
|
||||
->see('Search Results')
|
||||
->seeStatusCode(200);
|
||||
}
|
||||
|
||||
public function test_empty_search_redirects_back()
|
||||
{
|
||||
$this->asAdmin()
|
||||
->visit('/')
|
||||
->visit('/search/all')
|
||||
->seePageIs('/');
|
||||
}
|
||||
|
||||
public function test_book_search()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$page = $book->pages->last();
|
||||
$chapter = $book->chapters->last();
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/search/book/' . $book->id . '?term=' . urlencode($page->name))
|
||||
->see($page->name)
|
||||
|
||||
->visit('/search/book/' . $book->id . '?term=' . urlencode($chapter->name))
|
||||
->see($chapter->name);
|
||||
}
|
||||
|
||||
public function test_empty_book_search_redirects_back()
|
||||
{
|
||||
$book = \BookStack\Book::all()->first();
|
||||
$this->asAdmin()
|
||||
->visit('/books')
|
||||
->visit('/search/book/' . $book->id . '?term=')
|
||||
->seePageIs('/books');
|
||||
}
|
||||
|
||||
|
||||
public function test_entities_viewable_after_creator_deletion()
|
||||
{
|
||||
// Create required assets and revisions
|
||||
|
@ -250,5 +193,36 @@ class EntityTest extends TestCase
|
|||
->click('Revisions')->seeStatusCode(200);
|
||||
}
|
||||
|
||||
public function test_recently_created_pages_view()
|
||||
{
|
||||
$user = $this->getNewUser();
|
||||
$content = $this->createEntityChainBelongingToUser($user);
|
||||
|
||||
$this->asAdmin()->visit('/pages/recently-created')
|
||||
->seeInNthElement('.entity-list .page', 0, $content['page']->name);
|
||||
}
|
||||
|
||||
public function test_recently_updated_pages_view()
|
||||
{
|
||||
$user = $this->getNewUser();
|
||||
$content = $this->createEntityChainBelongingToUser($user);
|
||||
|
||||
$this->asAdmin()->visit('/pages/recently-updated')
|
||||
->seeInNthElement('.entity-list .page', 0, $content['page']->name);
|
||||
}
|
||||
|
||||
public function test_old_page_slugs_redirect_to_new_pages()
|
||||
{
|
||||
$page = \BookStack\Page::all()->first();
|
||||
$pageUrl = $page->getUrl();
|
||||
$newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
|
||||
$this->asAdmin()->visit($pageUrl)
|
||||
->clickInElement('#content', 'Edit')
|
||||
->type('super test page', '#name')
|
||||
->press('Save Page')
|
||||
->seePageIs($newPageUrl)
|
||||
->visit($pageUrl)
|
||||
->seePageIs($newPageUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -109,4 +109,18 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click the text within the selected element.
|
||||
* @param $parentElement
|
||||
* @param $linkText
|
||||
* @return $this
|
||||
*/
|
||||
protected function clickInElement($parentElement, $linkText)
|
||||
{
|
||||
$elem = $this->crawler->filter($parentElement);
|
||||
$link = $elem->selectLink($linkText);
|
||||
$this->visit($link->link()->getUri());
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
class UserProfileTest extends TestCase
|
||||
{
|
||||
protected $user;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->user = \BookStack\User::all()->last();
|
||||
}
|
||||
|
||||
public function test_profile_page_shows_name()
|
||||
{
|
||||
$this->asAdmin()
|
||||
->visit('/user/' . $this->user->id)
|
||||
->see($this->user->name);
|
||||
}
|
||||
|
||||
public function test_profile_page_shows_recent_entities()
|
||||
{
|
||||
$content = $this->createEntityChainBelongingToUser($this->user, $this->user);
|
||||
|
||||
$this->asAdmin()
|
||||
->visit('/user/' . $this->user->id)
|
||||
// Check the recently created page is shown
|
||||
->see($content['page']->name)
|
||||
// Check the recently created chapter is shown
|
||||
->see($content['chapter']->name)
|
||||
// Check the recently created book is shown
|
||||
->see($content['book']->name);
|
||||
}
|
||||
|
||||
public function test_profile_page_shows_created_content_counts()
|
||||
{
|
||||
$newUser = $this->getNewUser();
|
||||
|
||||
$this->asAdmin()->visit('/user/' . $newUser->id)
|
||||
->see($newUser->name)
|
||||
->seeInElement('#content-counts', '0 Books')
|
||||
->seeInElement('#content-counts', '0 Chapters')
|
||||
->seeInElement('#content-counts', '0 Pages');
|
||||
|
||||
$this->createEntityChainBelongingToUser($newUser, $newUser);
|
||||
|
||||
$this->asAdmin()->visit('/user/' . $newUser->id)
|
||||
->see($newUser->name)
|
||||
->seeInElement('#content-counts', '1 Book')
|
||||
->seeInElement('#content-counts', '1 Chapter')
|
||||
->seeInElement('#content-counts', '1 Page');
|
||||
}
|
||||
|
||||
public function test_profile_page_shows_recent_activity()
|
||||
{
|
||||
$newUser = $this->getNewUser();
|
||||
$this->actingAs($newUser);
|
||||
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
||||
Activity::add($entities['book'], 'book_update', $entities['book']->id);
|
||||
Activity::add($entities['page'], 'page_create', $entities['book']->id);
|
||||
|
||||
$this->asAdmin()->visit('/user/' . $newUser->id)
|
||||
->seeInElement('#recent-activity', 'updated book')
|
||||
->seeInElement('#recent-activity', 'created page')
|
||||
->seeInElement('#recent-activity', $entities['page']->name);
|
||||
}
|
||||
|
||||
public function test_clicking_user_name_in_activity_leads_to_profile_page()
|
||||
{
|
||||
$newUser = $this->getNewUser();
|
||||
$this->actingAs($newUser);
|
||||
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
||||
Activity::add($entities['book'], 'book_update', $entities['book']->id);
|
||||
Activity::add($entities['page'], 'page_create', $entities['book']->id);
|
||||
|
||||
$this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
|
||||
->seePageIs('/user/' . $newUser->id)
|
||||
->see($newUser->name);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue