Merge branch 'master' of git://github.com/BookStackApp/BookStack into BookStackApp-master

Conflicts:
	app/Http/Controllers/BookController.php
	resources/lang/en/common.php
	resources/views/books/create.blade.php
	resources/views/books/form.blade.php
	resources/views/books/index.blade.php
	resources/views/users/edit.blade.php
	tests/Entity/EntityTest.php
This commit is contained in:
Bharadwaja G 2017-08-29 12:19:00 +05:30
commit 6200948eec
99 changed files with 2562 additions and 1986 deletions

View File

@ -231,7 +231,6 @@ class RegisterController extends Controller
return redirect('/register/confirm');
}
$this->emailConfirmationService->sendConfirmation($user);
session()->flash('success', trans('auth.email_confirm_resent'));
return redirect('/register/confirm');
}

View File

@ -36,12 +36,17 @@ class BookController extends Controller
*/
public function index()
{
$books = $this->entityRepo->getAllPaginated('book', 16);
$books = $this->entityRepo->getAllPaginated('book', 20);
$recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
$popular = $this->entityRepo->getPopular('book', 3, 0);
$books_display = $this->currentUser->books_display;
$popular = $this->entityRepo->getPopular('book', 4, 0);
$new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
$this->setPageTitle('Books');
return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular, 'books_display' => $books_display] );
return view('books/index', [
'books' => $books,
'recents' => $recents,
'popular' => $popular,
'new' => $new
]);
}
/**
@ -85,7 +90,12 @@ class BookController extends Controller
$bookChildren = $this->entityRepo->getBookChildren($book);
Views::add($book);
$this->setPageTitle($book->getShortName());
return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
return view('books/show', [
'book' => $book,
'current' => $book,
'bookChildren' => $bookChildren,
'activity' => Activity::entityActivity($book, 20, 0)
]);
}
/**

View File

@ -29,15 +29,25 @@ class HomeController extends Controller
$activity = Activity::latest(10);
$draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : [];
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 10*$recentFactor);
$recentlyCreatedPages = $this->entityRepo->getRecentlyCreated('page', 5);
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 5);
return view('home', [
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 12*$recentFactor);
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 12);
// Custom homepage
$customHomepage = false;
$homepageSetting = setting('app-homepage');
if ($homepageSetting) {
$id = intval(explode(':', $homepageSetting)[0]);
$customHomepage = $this->entityRepo->getById('page', $id, false, true);
$this->entityRepo->renderPage($customHomepage, true);
}
$view = $customHomepage ? 'home-custom' : 'home';
return view($view, [
'activity' => $activity,
'recents' => $recents,
'recentlyCreatedPages' => $recentlyCreatedPages,
'recentlyUpdatedPages' => $recentlyUpdatedPages,
'draftPages' => $draftPages
'draftPages' => $draftPages,
'customHomepage' => $customHomepage
]);
}

View File

@ -167,7 +167,7 @@ class PageController extends Controller
return view('pages/show', [
'page' => $page,'book' => $page->book,
'current' => $page, 'sidebarTree' => $sidebarTree,
'pageNav' => $pageNav, 'pageContent' => $pageContent]);
'pageNav' => $pageNav]);
}
/**
@ -380,6 +380,7 @@ class PageController extends Controller
return view('pages/revision', [
'page' => $page,
'book' => $page->book,
'revision' => $revision
]);
}
@ -409,6 +410,7 @@ class PageController extends Controller
'page' => $page,
'book' => $page->book,
'diff' => $diff,
'revision' => $revision
]);
}

View File

@ -47,4 +47,16 @@ class PageRevision extends Model
return null;
}
/**
* Allows checking of the exact class, Used to check entity type.
* Included here to align with entities in similar use cases.
* (Yup, Bit of an awkward hack)
* @param $type
* @return bool
*/
public static function isA($type)
{
return $type === 'revision';
}
}

View File

@ -137,10 +137,15 @@ class EntityRepo
* @param string $type
* @param integer $id
* @param bool $allowDrafts
* @param bool $ignorePermissions
* @return Entity
*/
public function getById($type, $id, $allowDrafts = false)
public function getById($type, $id, $allowDrafts = false, $ignorePermissions = false)
{
if ($ignorePermissions) {
$entity = $this->getEntity($type);
return $entity->newQuery()->find($id);
}
return $this->entityQuery($type, $allowDrafts)->find($id);
}
@ -671,9 +676,10 @@ class EntityRepo
/**
* Render the page for viewing, Parsing and performing features such as page transclusion.
* @param Page $page
* @param bool $ignorePermissions
* @return mixed|string
*/
public function renderPage(Page $page)
public function renderPage(Page $page, $ignorePermissions = false)
{
$content = $page->html;
$matches = [];
@ -685,7 +691,7 @@ class EntityRepo
$pageId = intval($splitInclude[0]);
if (is_nan($pageId)) continue;
$page = $this->getById('page', $pageId);
$page = $this->getById('page', $pageId, false, $ignorePermissions);
if ($page === null) {
$content = str_replace($matches[0][$index], '', $content);
continue;
@ -710,6 +716,7 @@ class EntityRepo
$content = str_replace($matches[0][$index], trim($innerContent), $content);
}
$page->renderedHTML = $content;
return $content;
}

View File

@ -27,9 +27,9 @@ class ExportService
*/
public function pageToContainedHtml(Page $page)
{
$this->entityRepo->renderPage($page);
$pageHtml = view('pages/export', [
'page' => $page,
'pageContent' => $this->entityRepo->renderPage($page)
'page' => $page
])->render();
return $this->containHtml($pageHtml);
}
@ -74,9 +74,9 @@ class ExportService
*/
public function pageToPdf(Page $page)
{
$this->entityRepo->renderPage($page);
$html = view('pages/pdf', [
'page' => $page,
'pageContent' => $this->entityRepo->renderPage($page)
'page' => $page
])->render();
return $this->htmlToPdf($html);
}

View File

@ -92,7 +92,7 @@ class SearchService
return [
'total' => $total,
'count' => count($results),
'results' => $results->sortByDesc('score')
'results' => $results->sortByDesc('score')->values()
];
}

View File

@ -62,7 +62,7 @@ class ViewService
$query->whereIn('viewable_type', $filterModel);
} else if ($filterModel) {
$query->where('viewable_type', '=', get_class($filterModel));
};
}
return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
}

View File

@ -3,16 +3,20 @@
const argv = require('yargs').argv;
const gulp = require('gulp'),
plumber = require('gulp-plumber');
const autoprefixer = require('gulp-autoprefixer');
const uglify = require('gulp-uglify');
const minifycss = require('gulp-clean-css');
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const browserify = require("browserify");
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const babelify = require("babelify");
const watchify = require("watchify");
const envify = require("envify");
const uglify = require('gulp-uglify');
const gutil = require("gulp-util");
const liveReload = require('gulp-livereload');
@ -21,6 +25,7 @@ let isProduction = argv.production || process.env.NODE_ENV === 'production';
gulp.task('styles', () => {
let chain = gulp.src(['resources/assets/sass/**/*.scss'])
.pipe(sourcemaps.init())
.pipe(plumber({
errorHandler: function (error) {
console.log(error.message);
@ -29,6 +34,7 @@ gulp.task('styles', () => {
.pipe(sass())
.pipe(autoprefixer('last 2 versions'));
if (isProduction) chain = chain.pipe(minifycss());
chain = chain.pipe(sourcemaps.write());
return chain.pipe(gulp.dest('public/css/')).pipe(liveReload());
});

View File

@ -36,6 +36,7 @@
"clipboard": "^1.7.1",
"codemirror": "^5.26.0",
"dropzone": "^4.0.1",
"gulp-sourcemaps": "^2.6.1",
"gulp-util": "^3.0.8",
"markdown-it": "^8.3.1",
"markdown-it-task-lists": "^2.0.0",

View File

@ -1,7 +1,7 @@
# BookStack
[![GitHub release](https://img.shields.io/github/release/BookStackApp/BookStack.svg?maxAge=2592000)](https://github.com/BookStackApp/BookStack/releases/latest)
[![license](https://img.shields.io/github/license/BookStackApp/BookStack.svg?maxAge=2592000)](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/BookStackApp/BookStack.svg)](https://github.com/BookStackApp/BookStack/releases/latest)
[![license](https://img.shields.io/github/license/BookStackApp/BookStack.svg)](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
[![Build Status](https://travis-ci.org/BookStackApp/BookStack.svg)](https://travis-ci.org/BookStackApp/BookStack)
A platform for storing and organising information and documentation. General information and documentation for BookStack can be found at https://www.bookstackapp.com/.
@ -13,6 +13,12 @@ A platform for storing and organising information and documentation. General inf
* *Password: `password`*
* [BookStack Blog](https://www.bookstackapp.com/blog)
## Project Definition
BookStack is an opinionated wiki system that provides a pleasant and simple out of the box experience. New users to an instance should find the experience intuitive and only basic word-processing skills should be required to get involved in creating content on BookStack. The platform should provide advanced power features to those that desire it but they should not interfere with the core simple user experience.
BookStack is not designed as an extensible platform to be used for purposes that differ to the statement above.
## Development & Testing
All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at it's version. Here are the current development requirements:

View File

@ -0,0 +1,47 @@
class EntitySelectorPopup {
constructor(elem) {
this.elem = elem;
window.EntitySelectorPopup = this;
this.callback = null;
this.selection = null;
this.selectButton = elem.querySelector('.entity-link-selector-confirm');
this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
}
show(callback) {
this.callback = callback;
this.elem.components.overlay.show();
}
hide() {
this.elem.components.overlay.hide();
}
onSelectButtonClick() {
this.hide();
if (this.selection !== null && this.callback) this.callback(this.selection);
}
onSelectionConfirm(entity) {
this.hide();
if (this.callback && entity) this.callback(entity);
}
onSelectionChange(entity) {
this.selection = entity;
if (entity === null) {
this.selectButton.setAttribute('disabled', 'true');
} else {
this.selectButton.removeAttribute('disabled');
}
}
}
module.exports = EntitySelectorPopup;

View File

@ -0,0 +1,118 @@
class EntitySelector {
constructor(elem) {
this.elem = elem;
this.search = '';
this.lastClick = 0;
let entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter';
this.searchUrl = window.baseUrl(`/ajax/search/entities?types=${encodeURIComponent(entityTypes)}`);
this.input = elem.querySelector('[entity-selector-input]');
this.searchInput = elem.querySelector('[entity-selector-search]');
this.loading = elem.querySelector('[entity-selector-loading]');
this.resultsContainer = elem.querySelector('[entity-selector-results]');
this.elem.addEventListener('click', this.onClick.bind(this));
let lastSearch = 0;
this.searchInput.addEventListener('input', event => {
lastSearch = Date.now();
this.showLoading();
setTimeout(() => {
if (Date.now() - lastSearch < 199) return;
this.searchEntities(this.searchInput.value);
}, 200);
});
this.searchInput.addEventListener('keydown', event => {
if (event.keyCode === 13) event.preventDefault();
});
this.showLoading();
this.initialLoad();
}
showLoading() {
this.loading.style.display = 'block';
this.resultsContainer.style.display = 'none';
}
hideLoading() {
this.loading.style.display = 'none';
this.resultsContainer.style.display = 'block';
}
initialLoad() {
window.$http.get(this.searchUrl).then(resp => {
this.resultsContainer.innerHTML = resp.data;
this.hideLoading();
})
}
searchEntities(searchTerm) {
this.input.value = '';
let url = this.searchUrl + `&term=${encodeURIComponent(searchTerm)}`;
window.$http.get(url).then(resp => {
this.resultsContainer.innerHTML = resp.data;
this.hideLoading();
});
}
isDoubleClick() {
let now = Date.now();
let answer = now - this.lastClick < 300;
this.lastClick = now;
return answer;
}
onClick(event) {
let t = event.target;
console.log('click', t);
if (t.matches('.entity-list-item *')) {
event.preventDefault();
event.stopPropagation();
let item = t.closest('[data-entity-type]');
this.selectItem(item);
} else if (t.matches('[data-entity-type]')) {
this.selectItem(t)
}
}
selectItem(item) {
let isDblClick = this.isDoubleClick();
let type = item.getAttribute('data-entity-type');
let id = item.getAttribute('data-entity-id');
let isSelected = !item.classList.contains('selected') || isDblClick;
this.unselectAll();
this.input.value = isSelected ? `${type}:${id}` : '';
if (!isSelected) window.$events.emit('entity-select-change', null);
if (isSelected) {
item.classList.add('selected');
item.classList.add('primary-background');
}
if (!isDblClick && !isSelected) return;
let link = item.querySelector('.entity-list-item-link').getAttribute('href');
let name = item.querySelector('.entity-list-item-name').textContent;
let data = {id: Number(id), name: name, link: link};
if (isDblClick) window.$events.emit('entity-select-confirm', data);
if (isSelected) window.$events.emit('entity-select-change', data);
}
unselectAll() {
let selected = this.elem.querySelectorAll('.selected');
for (let i = 0, len = selected.length; i < len; i++) {
selected[i].classList.remove('selected');
selected[i].classList.remove('primary-background');
}
}
}
module.exports = EntitySelector;

View File

@ -6,6 +6,10 @@ let componentMapping = {
'notification': require('./notification'),
'chapter-toggle': require('./chapter-toggle'),
'expand-toggle': require('./expand-toggle'),
'entity-selector-popup': require('./entity-selector-popup'),
'entity-selector': require('./entity-selector'),
'sidebar': require('./sidebar'),
'page-picker': require('./page-picker'),
};
window.components = {};

View File

@ -6,7 +6,7 @@ class Notification {
this.type = elem.getAttribute('notification');
this.textElem = elem.querySelector('span');
this.autohide = this.elem.hasAttribute('data-autohide');
window.Events.listen(this.type, text => {
window.$events.listen(this.type, text => {
this.show(text);
});
elem.addEventListener('click', this.hide.bind(this));

View File

@ -0,0 +1,60 @@
class PagePicker {
constructor(elem) {
this.elem = elem;
this.input = elem.querySelector('input');
this.resetButton = elem.querySelector('[page-picker-reset]');
this.selectButton = elem.querySelector('[page-picker-select]');
this.display = elem.querySelector('[page-picker-display]');
this.defaultDisplay = elem.querySelector('[page-picker-default]');
this.buttonSep = elem.querySelector('span.sep');
this.value = this.input.value;
this.setupListeners();
}
setupListeners() {
// Select click
this.selectButton.addEventListener('click', event => {
window.EntitySelectorPopup.show(entity => {
this.setValue(entity.id, entity.name);
});
});
this.resetButton.addEventListener('click', event => {
this.setValue('', '');
});
}
setValue(value, name) {
this.value = value;
this.input.value = value;
this.controlView(name);
}
controlView(name) {
let hasValue = this.value && this.value !== 0;
toggleElem(this.resetButton, hasValue);
toggleElem(this.buttonSep, hasValue);
toggleElem(this.defaultDisplay, !hasValue);
toggleElem(this.display, hasValue);
if (hasValue) {
let id = this.getAssetIdFromVal();
this.display.textContent = `#${id}, ${name}`;
this.display.href = window.baseUrl(`/link/${id}`);
}
}
getAssetIdFromVal() {
return Number(this.value);
}
}
function toggleElem(elem, show) {
let display = (elem.tagName === 'BUTTON' || elem.tagName === 'SPAN') ? 'inline-block' : 'block';
elem.style.display = show ? display : 'none';
}
module.exports = PagePicker;

View File

@ -0,0 +1,16 @@
class Sidebar {
constructor(elem) {
this.elem = elem;
this.toggleElem = elem.querySelector('.sidebar-toggle');
this.toggleElem.addEventListener('click', this.toggle.bind(this));
}
toggle(show = true) {
this.elem.classList.toggle('open');
}
}
module.exports = Sidebar;

View File

@ -252,7 +252,7 @@ module.exports = function (ngApp, events) {
// Show the popup link selector and insert a link when finished
function showLinkSelector() {
let cursorPos = cm.getCursor('from');
window.showEntityLinkSelector(entity => {
window.EntitySelectorPopup.show(entity => {
let selectedText = cm.getSelection() || entity.name;
let newText = `[${selectedText}](${entity.link})`;
cm.focus();
@ -387,154 +387,6 @@ module.exports = function (ngApp, events) {
}
}]);
ngApp.directive('entityLinkSelector', [function($http) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
const selectButton = element.find('.entity-link-selector-confirm');
let callback = false;
let entitySelection = null;
// Handle entity selection change, Stores the selected entity locally
function entitySelectionChange(entity) {
entitySelection = entity;
if (entity === null) {
selectButton.attr('disabled', 'true');
} else {
selectButton.removeAttr('disabled');
}
}
events.listen('entity-select-change', entitySelectionChange);
// Handle selection confirm button click
selectButton.click(event => {
hide();
if (entitySelection !== null) callback(entitySelection);
});
// Show selector interface
function show() {
element.fadeIn(240);
}
// Hide selector interface
function hide() {
element.fadeOut(240);
}
scope.hide = hide;
// Listen to confirmation of entity selections (doubleclick)
events.listen('entity-select-confirm', entity => {
hide();
callback(entity);
});
// Show entity selector, Accessible globally, and store the callback
window.showEntityLinkSelector = function(passedCallback) {
show();
callback = passedCallback;
};
}
};
}]);
ngApp.directive('entitySelector', ['$http', '$sce', function ($http, $sce) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
scope.loading = true;
scope.entityResults = false;
scope.search = '';
// Add input for forms
const input = element.find('[entity-selector-input]').first();
// Detect double click events
let lastClick = 0;
function isDoubleClick() {
let now = Date.now();
let answer = now - lastClick < 300;
lastClick = now;
return answer;
}
// Listen to entity item clicks
element.on('click', '.entity-list a', function(event) {
event.preventDefault();
event.stopPropagation();
let item = $(this).closest('[data-entity-type]');
itemSelect(item, isDoubleClick());
});
element.on('click', '[data-entity-type]', function(event) {
itemSelect($(this), isDoubleClick());
});
// Select entity action
function itemSelect(item, doubleClick) {
let entityType = item.attr('data-entity-type');
let entityId = item.attr('data-entity-id');
let isSelected = !item.hasClass('selected') || doubleClick;
element.find('.selected').removeClass('selected').removeClass('primary-background');
if (isSelected) item.addClass('selected').addClass('primary-background');
let newVal = isSelected ? `${entityType}:${entityId}` : '';
input.val(newVal);
if (!isSelected) {
events.emit('entity-select-change', null);
}
if (!doubleClick && !isSelected) return;
let link = item.find('.entity-list-item-link').attr('href');
let name = item.find('.entity-list-item-name').text();
if (doubleClick) {
events.emit('entity-select-confirm', {
id: Number(entityId),
name: name,
link: link
});
}
if (isSelected) {
events.emit('entity-select-change', {
id: Number(entityId),
name: name,
link: link
});
}
}
// Get search url with correct types
function getSearchUrl() {
let types = (attrs.entityTypes) ? encodeURIComponent(attrs.entityTypes) : encodeURIComponent('page,book,chapter');
return window.baseUrl(`/ajax/search/entities?types=${types}`);
}
// Get initial contents
$http.get(getSearchUrl()).then(resp => {
scope.entityResults = $sce.trustAsHtml(resp.data);
scope.loading = false;
});
// Search when typing
scope.searchEntities = function() {
scope.loading = true;
input.val('');
let url = getSearchUrl() + '&term=' + encodeURIComponent(scope.search);
$http.get(url).then(resp => {
scope.entityResults = $sce.trustAsHtml(resp.data);
scope.loading = false;
});
};
}
};
}]);
ngApp.directive('commentReply', [function () {
return {
restrict: 'E',

View File

@ -0,0 +1,20 @@
/**
* Polyfills for DOM API's
*/
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
var el = this;
var ancestor = this;
if (!document.documentElement.contains(el)) return null;
do {
if (ancestor.matches(s)) return ancestor;
ancestor = ancestor.parentElement;
} while (ancestor !== null);
return null;
};
}

View File

@ -1,5 +1,6 @@
"use strict";
require("babel-polyfill");
require('./dom-polyfills');
// Url retrieval function
window.baseUrl = function(path) {
@ -13,9 +14,11 @@ window.baseUrl = function(path) {
class EventManager {
constructor() {
this.listeners = {};
this.stack = [];
}
emit(eventName, eventData) {
this.stack.push({name: eventName, data: eventData});
if (typeof this.listeners[eventName] === 'undefined') return this;
let eventsToStart = this.listeners[eventName];
for (let i = 0; i < eventsToStart.length; i++) {
@ -32,7 +35,7 @@ class EventManager {
}
}
window.Events = new EventManager();
window.$events = new EventManager();
const Vue = require("vue");
const axios = require("axios");
@ -47,13 +50,13 @@ axiosInstance.interceptors.request.use(resp => {
return resp;
}, err => {
if (typeof err.response === "undefined" || typeof err.response.data === "undefined") return Promise.reject(err);
if (typeof err.response.data.error !== "undefined") window.Events.emit('error', err.response.data.error);
if (typeof err.response.data.message !== "undefined") window.Events.emit('error', err.response.data.message);
if (typeof err.response.data.error !== "undefined") window.$events.emit('error', err.response.data.error);
if (typeof err.response.data.message !== "undefined") window.$events.emit('error', err.response.data.message);
});
window.$http = axiosInstance;
Vue.prototype.$http = axiosInstance;
Vue.prototype.$events = window.Events;
Vue.prototype.$events = window.$events;
// AngularJS - Create application and load components
@ -78,8 +81,8 @@ require("./components");
// Load in angular specific items
const Directives = require('./directives');
const Controllers = require('./controllers');
Directives(ngApp, window.Events);
Controllers(ngApp, window.Events);
Directives(ngApp, window.$events);
Controllers(ngApp, window.$events);
//Global jQuery Config & Extensions

View File

@ -274,7 +274,7 @@ module.exports = function() {
file_browser_callback: function (field_name, url, type, win) {
if (type === 'file') {
window.showEntityLinkSelector(function(entity) {
window.EntitySelectorPopup.show(function(entity) {
let originalField = win.document.getElementById(field_name);
originalField.value = entity.link;
$(originalField).closest('.mce-form').find('input').eq(2).val(entity.name);

View File

@ -106,25 +106,25 @@ let setupPageShow = window.setupPageShow = function (pageId) {
goToText(event.target.getAttribute('href').substr(1));
});
// Make the book-tree sidebar stick in view on scroll
// Make the sidebar stick in view on scroll
let $window = $(window);
let $bookTree = $(".book-tree");
let $bookTreeParent = $bookTree.parent();
let $sidebar = $("#sidebar .scroll-body");
let $bookTreeParent = $sidebar.parent();
// Check the page is scrollable and the content is taller than the tree
let pageScrollable = ($(document).height() > $window.height()) && ($bookTree.height() < $('.page-content').height());
let pageScrollable = ($(document).height() > $window.height()) && ($sidebar.height() < $('.page-content').height());
// Get current tree's width and header height
let headerHeight = $("#header").height() + $(".toolbar").height();
let isFixed = $window.scrollTop() > headerHeight;
// Function to fix the tree as a sidebar
function stickTree() {
$bookTree.width($bookTreeParent.width() + 15);
$bookTree.addClass("fixed");
$sidebar.width($bookTreeParent.width() + 15);
$sidebar.addClass("fixed");
isFixed = true;
}
// Function to un-fix the tree back into position
function unstickTree() {
$bookTree.css('width', 'auto');
$bookTree.removeClass("fixed");
$sidebar.css('width', 'auto');
$sidebar.removeClass("fixed");
isFixed = false;
}
// Checks if the tree stickiness state should change

View File

@ -181,3 +181,27 @@
content: '\f1f1';
}
}
.card {
margin: $-m;
background-color: #FFF;
box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.2);
h3 {
padding: $-m;
border-bottom: 1px solid #E8E8E8;
margin: 0;
font-size: $fs-s;
color: #888;
font-weight: 400;
text-transform: uppercase;
}
.body, p.empty-text {
padding: $-m;
}
}
.well {
background-color: #F8F8F8;
padding: $-m;
border: 1px solid #DDD;
}

View File

@ -2,9 +2,12 @@
@mixin generate-button-colors($textColor, $backgroundColor) {
background-color: $backgroundColor;
color: $textColor;
text-transform: uppercase;
border: 1px solid $backgroundColor;
vertical-align: top;
&:hover {
background-color: lighten($backgroundColor, 8%);
box-shadow: $bs-med;
//box-shadow: $bs-med;
text-decoration: none;
color: $textColor;
}
@ -26,16 +29,16 @@ $button-border-radius: 2px;
text-decoration: none;
font-size: $fs-m;
line-height: 1.4em;
padding: $-xs $-m;
padding: $-xs*1.3 $-m;
margin: $-xs $-xs $-xs 0;
display: inline-block;
border: none;
font-weight: 500;
font-weight: 400;
outline: 0;
border-radius: $button-border-radius;
cursor: pointer;
transition: all ease-in-out 120ms;
box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21);
box-shadow: 0;
@include generate-button-colors(#EEE, $primary);
}
@ -51,13 +54,47 @@ $button-border-radius: 2px;
@include generate-button-colors(#EEE, $secondary);
}
&.muted {
@include generate-button-colors(#EEE, #888);
@include generate-button-colors(#EEE, #AAA);
}
&.muted-light {
@include generate-button-colors(#666, #e4e4e4);
}
}
.button.outline {
background-color: transparent;
color: #888;
border: 1px solid #DDD;
&:hover, &:focus, &:active {
box-shadow: none;
background-color: #EEE;
}
&.page {
border-color: $color-page;
color: $color-page;
&:hover, &:focus, &:active {
background-color: $color-page;
color: #FFF;
}
}
&.chapter {
border-color: $color-chapter;
color: $color-chapter;
&:hover, &:focus, &:active {
background-color: $color-chapter;
color: #FFF;
}
}
&.book {
border-color: $color-book;
color: $color-book;
&:hover, &:focus, &:active {
background-color: $color-book;
color: #FFF;
}
}
}
.text-button {
@extend .link;
background-color: transparent;

View File

@ -366,9 +366,9 @@ span.CodeMirror-selectedtext { background: none; }
.cm-s-base16-light span.cm-atom { color: #aa759f; }
.cm-s-base16-light span.cm-number { color: #aa759f; }
.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #90a959; }
.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #678c30; }
.cm-s-base16-light span.cm-keyword { color: #ac4142; }
.cm-s-base16-light span.cm-string { color: #f4bf75; }
.cm-s-base16-light span.cm-string { color: #e09c3c; }
.cm-s-base16-light span.cm-variable { color: #90a959; }
.cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
@ -392,7 +392,7 @@ span.CodeMirror-selectedtext { background: none; }
}
.cm-s-base16-light .CodeMirror-gutters { background: #f5f5f5; border-right: 1px solid #DDD; }
.flex-fill .CodeMirror {
.code-fill .CodeMirror {
position: absolute;
top: 0;
bottom: 0;

View File

@ -2,14 +2,13 @@
.input-base {
background-color: #FFF;
border-radius: 3px;
border: 1px solid #CCC;
border: 1px solid #D4D4D4;
display: inline-block;
font-size: $fs-s;
padding: $-xs;
color: #222;
padding: $-xs*1.5;
color: #666;
width: 250px;
max-width: 100%;
//-webkit-appearance:none;
&.neg, &.invalid {
border: 1px solid $negative;
}
@ -24,6 +23,11 @@
}
}
.fake-input {
@extend .input-base;
overflow: auto;
}
#html-editor {
display: none;
}
@ -84,8 +88,9 @@ label {
display: block;
line-height: 1.4em;
font-size: 0.94em;
font-weight: 500;
color: #666;
font-weight: 400;
color: #999;
text-transform: uppercase;
padding-bottom: 2px;
margin-bottom: 0.2em;
&.inline {
@ -186,28 +191,15 @@ input:checked + .toggle-switch {
}
.inline-input-style {
border: 2px dotted #BBB;
display: block;
width: 100%;
padding: $-xs $-s;
}
.title-input .input {
width: 100%;
}
.title-input label, .description-input label{
margin-top: $-m;
color: #666;
padding: $-s;
}
.title-input input[type="text"] {
@extend h1;
@extend .inline-input-style;
margin-top: 0;
padding-right: 0;
width: 100%;
color: #444;
font-size: 2em;
}
.title-input.page-title {
@ -250,8 +242,8 @@ div[editor-type="markdown"] .title-input.page-title input[type="text"] {
padding: 0;
cursor: pointer;
position: absolute;
left: 7px;
top: 7px;
left: 8px;
top: 9.5px;
}
input {
display: block;

View File

@ -20,19 +20,122 @@ body.flexbox {
align-items: stretch;
min-height: 0;
position: relative;
.flex, &.flex {
&.rows {
flex-direction: row;
}
&.columns {
flex-direction: column;
}
}
.flex {
min-height: 0;
flex: 1;
}
.flex.scroll {
//overflow-y: auto;
display: flex;
&.sidebar {
margin-right: -14px;
}
}
.flex.scroll .scroll-body {
overflow-y: scroll;
flex: 1;
}
.flex-child > div {
flex: 1;
}
//body.ie .flex-child > div {
// flex: 1 0 0px;
//}
.flex.sidebar {
flex: 1;
background-color: #F2F2F2;
max-width: 360px;
min-height: 90vh;
}
.flex.sidebar + .flex.content {
flex: 3;
background-color: #FFFFFF;
padding: 0 $-l;
border-left: 1px solid #DDD;
max-width: 100%;
}
.flex.sidebar .sidebar-toggle {
display: none;
}
@include smaller-than($xl) {
body.sidebar-layout {
padding-left: 30px;
}
.flex.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 100;
padding-right: 30px;
width: 360px;
box-shadow: none;
transform: translate3d(-330px, 0, 0);
transition: transform ease-in-out 120ms;
}
.flex.sidebar.open {
box-shadow: 1px 2px 2px 1px rgba(0,0,0,.10);
transform: translate3d(0, 0, 0);
.sidebar-toggle i {
transform: rotate(180deg);
}
}
.flex.sidebar .sidebar-toggle {
display: block;
position: absolute;
opacity: 0.9;
right: 0;
top: 0;
bottom: 0;
width: 30px;
color: #666;
font-size: 20px;
vertical-align: middle;
text-align: center;
border: 1px solid #DDD;
border-top: 1px solid #BBB;
padding-top: $-m;
cursor: pointer;
i {
opacity: 0.5;
transition: all ease-in-out 120ms;
padding: 0;
}
&:hover i {
opacity: 1;
}
}
#sidebar .scroll-body.fixed {
width: auto !important;
}
}
@include larger-than($xl) {
#sidebar .scroll-body.fixed {
z-index: 5;
position: fixed;
top: 0;
padding-right: $-m;
width: 30%;
left: 0;
height: 100%;
overflow-y: scroll;
-ms-overflow-style: none;
//background-color: $primary-faded;
border-left: 1px solid #DDD;
&::-webkit-scrollbar { width: 0 !important }
}
}
/** Rules for all columns */
div[class^="col-"] img {

View File

@ -85,7 +85,7 @@ header {
}
header .search-box {
display: inline-block;
margin-top: $-s;
margin-top: 10px;
input {
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
@ -171,6 +171,10 @@ header .search-box {
background-color: $primary-faded;
}
.toolbar-container {
background-color: #FFF;
}
.breadcrumbs .text-button, .action-buttons .text-button {
display: inline-block;
padding: $-s;

View File

@ -9,6 +9,9 @@ html {
&.flexbox {
overflow-y: hidden;
}
&.shaded {
background-color: #F2F2F2;
}
}
body {
@ -16,6 +19,9 @@ body {
line-height: 1.6;
color: #616161;
-webkit-font-smoothing: antialiased;
&.shaded {
background-color: #F2F2F2;
}
}
button {

View File

@ -86,31 +86,8 @@
// Sidebar list
.book-tree {
padding: $-xs 0 0 0;
position: relative;
right: 0;
top: 0;
transition: ease-in-out 240ms;
transition-property: right, border;
border-left: 0px solid #FFF;
background-color: #FFF;
max-width: 320px;
&.fixed {
background-color: #FFF;
z-index: 5;
position: fixed;
top: 0;
padding-left: $-l;
padding-right: $-l + 15;
width: 30%;
right: -15px;
height: 100%;
overflow-y: scroll;
-ms-overflow-style: none;
//background-color: $primary-faded;
border-left: 1px solid #DDD;
&::-webkit-scrollbar { width: 0 !important }
}
}
.book-tree h4 {
padding: $-m $-s 0 $-s;
@ -245,6 +222,9 @@
.left + .right {
margin-left: 30px + $-s;
}
&:last-of-type {
border-bottom: 0;
}
}
ul.pagination {
@ -297,9 +277,6 @@ ul.pagination {
h4 {
margin: 0;
}
p {
margin: $-xs 0 0 0;
}
hr {
margin: 0;
}
@ -316,6 +293,11 @@ ul.pagination {
}
}
.card .entity-list-item, .card .activity-list-item {
padding-left: $-m;
padding-right: $-m;
}
.entity-list.compact {
font-size: 0.6em;
h4, a {
@ -324,9 +306,11 @@ ul.pagination {
.entity-item-snippet {
display: none;
}
p {
.entity-list-item p {
font-size: $fs-m * 0.8;
padding-top: $-xs;
}
p {
margin: 0;
}
> p.empty-text {

View File

@ -1,12 +1,3 @@
#page-show {
>.row .col-md-9 {
z-index: 2;
}
>.row .col-md-3 {
z-index: 1;
}
}
.page-editor {
display: flex;
flex-direction: column;
@ -36,6 +27,8 @@
.page-content {
max-width: 840px;
margin: 0 auto;
margin-top: $-xxl;
overflow-wrap: break-word;
.align-left {
text-align: left;
@ -252,8 +245,6 @@
}
.tag-display {
width: 100%;
//opacity: 0.7;
position: relative;
table {
width: 100%;

View File

@ -2,7 +2,7 @@
* Fonts
*/
body, button, input, select, label {
body, button, input, select, label, textarea {
font-family: $text;
}
.Codemirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
@ -378,12 +378,6 @@ span.sep {
display: block;
}
.action-header {
h1 {
margin-top: $-m;
}
}
/**
* Icons
*/

View File

@ -217,22 +217,15 @@ $btt-size: 40px;
}
.center-box {
margin: $-xl auto 0 auto;
padding: $-m $-xxl $-xl $-xxl;
margin: $-xxl auto 0 auto;
width: 420px;
max-width: 100%;
display: inline-block;
text-align: left;
vertical-align: top;
//border: 1px solid #DDD;
input {
width: 100%;
}
&.login {
background-color: #EEE;
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #DDD;
}
}

View File

@ -10,6 +10,7 @@ return [
'save' => 'Save',
'continue' => 'Continue',
'select' => 'Select',
'more' => 'More',
/**
* Form Labels
@ -36,7 +37,6 @@ return [
'remove' => 'Remove',
'add' => 'Add',
/**
* Misc
*/
@ -46,7 +46,7 @@ return [
'back_to_top' => 'Back to top',
'toggle_details' => 'Toggle Details',
'toggle_thumbnails' => 'Toggle Thumbnails',
'details' => 'Details',
/**
* Header
*/

View File

@ -73,11 +73,13 @@ return [
'books_empty' => 'No books have been created',
'books_popular' => 'Popular Books',
'books_recent' => 'Recent Books',
'books_new' => 'New Books',
'books_popular_empty' => 'The most popular books will appear here.',
'books_new_empty' => 'The most recently created books will appear here.',
'books_create' => 'Create New Book',
'books_delete' => 'Delete Book',
'books_delete_named' => 'Delete Book :bookName',
'books_delete_explain' => 'This will delete the book with the name \':bookName\', All pages and chapters will be removed.',
'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.',
'books_delete_confirmation' => 'Are you sure you want to delete this book?',
'books_edit' => 'Edit Book',
'books_edit_named' => 'Edit Book :bookName',
@ -108,8 +110,7 @@ return [
'chapters_create' => 'Create New Chapter',
'chapters_delete' => 'Delete Chapter',
'chapters_delete_named' => 'Delete Chapter :chapterName',
'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\', All pages will be removed
and added directly to the parent book.',
'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
'chapters_edit' => 'Edit Chapter',
'chapters_edit_named' => 'Edit Chapter :chapterName',
@ -164,6 +165,7 @@ return [
'pages_move_success' => 'Page moved to ":parentName"',
'pages_permissions' => 'Page Permissions',
'pages_permissions_success' => 'Page permissions updated',
'pages_revision' => 'Revision',
'pages_revisions' => 'Page Revisions',
'pages_revisions_named' => 'Page Revisions for :pageName',
'pages_revision_named' => 'Page Revision for :pageName',

View File

@ -31,6 +31,9 @@ return [
'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
'app_primary_color' => 'Application primary color',
'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
'app_homepage' => 'Application Homepage',
'app_homepage_desc' => 'Select a page to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
'app_homepage_default' => 'Default homepage view chosen',
/**
* Registration settings

View File

@ -9,9 +9,10 @@
@section('content')
<div class="text-center">
<div class="center-box">
<h1>{{ title_case(trans('auth.log_in')) }}</h1>
<div class="card center-box">
<h3><i class="zmdi zmdi-sign-in"></i> {{ title_case(trans('auth.log_in')) }}</h3>
<div class="body">
<form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
{!! csrf_field() !!}
@ -41,5 +42,6 @@
@endif
</div>
</div>
</div>
@stop

View File

@ -9,10 +9,12 @@
@section('content')
<div class="text-center">
<div class="center-box">
<h2>{{ trans('auth.register_thanks') }}</h2>
<div class="card center-box">
<h3><i class="zmdi zmdi-accounts"></i> {{ trans('auth.register_thanks') }}</h3>
<div class="body">
<p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
</div>
</div>
</div>
@stop

View File

@ -7,9 +7,9 @@
@section('content')
<div class="text-center">
<div class="center-box">
<h1>{{ title_case(trans('auth.sign_up')) }}</h1>
<div class="card center-box">
<h3><i class="zmdi zmdi-account-add"></i> {{ title_case(trans('auth.sign_up')) }}</h3>
<div class="body">
<form action="{{ baseUrl("/register") }}" method="POST">
{!! csrf_field() !!}
@ -44,6 +44,7 @@
@endif
</div>
</div>
</div>
@stop

View File

@ -2,9 +2,11 @@
@section('content')
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2>{{ trans('auth.email_not_confirmed') }}</h2>
<div class="container small">
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-accounts"></i> {{ trans('auth.email_not_confirmed') }}</h3>
<div class="body">
<p class="text-muted">{{ trans('auth.email_not_confirmed_text') }}<br>
{{ trans('auth.email_not_confirmed_click_link') }} <br>
{{ trans('auth.email_not_confirmed_resend') }}
@ -27,4 +29,6 @@
</div>
</div>
</div>
@stop

View File

@ -34,7 +34,7 @@
@include('partials/notifications')
<header id="header">
<div class="container">
<div class="container fluid">
<div class="row">
<div class="col-sm-4" ng-non-bindable>
<a href="{{ baseUrl('/') }}" class="logo">

View File

@ -1,13 +1,28 @@
@extends('base')
@extends('simple-layout')
@section('content')
@section('toolbar')
<div class="col-sm-8 faded">
<div class="breadcrumbs">
<a href="{{ baseUrl('/books') }}" class="text-button"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
<span class="sep">&raquo;</span>
<a href="{{ baseUrl('/books/create') }}" class="text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
</div>
</div>
@stop
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.books_create') }}</h1>
@section('body')
<div ng-non-bindable class="container small">
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-plus"></i> {{ trans('entities.books_create') }}</h3>
<div class="body">
<form action="{{ baseUrl("/books") }}" method="POST" enctype="multipart/form-data">
@include('books/form')
</form>
</div>
</div>
</div>
<p class="margin-top large"><br></p>
@include('components.image-manager', ['imageType' => 'cover'])
@stop

View File

@ -1,28 +1,30 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.books_delete') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-delete"></i> {{ trans('entities.books_delete') }}</h3>
<div class="body">
<p>{{ trans('entities.books_delete_explain', ['bookName' => $book->name]) }}</p>
<p class="text-neg">{{ trans('entities.books_delete_confirmation') }}</p>
<form action="{{$book->getUrl()}}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{$book->getUrl()}}" class="button">{{ trans('common.cancel') }}</a>
<a href="{{$book->getUrl()}}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,23 +1,24 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.books_edit') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.books_edit') }}</h3>
<div class="body">
<form action="{{ $book->getUrl() }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('books/form', ['model' => $book])
</form>
</div>
</div>
</div>
@include('components.image-manager', ['imageType' => 'cover'])
@stop

View File

@ -24,7 +24,8 @@
'imageClass' => 'cover'
])
</div>
<div class="form-group">
<a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
</div>

View File

@ -1,43 +1,53 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-xs-12 faded">
<div class="action-buttons text-left">
<a data-action="expand-thumbnail" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_thumbnails') }}</a>
@section('toolbar')
<div class="col-xs-1"></div>
<div class="col-xs-11 faded">
<div class="action-buttons">
@if($currentUser->can('book-create-all'))
<a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
@endif
</div>
</div>
@stop
@section('sidebar')
@if($recents)
<div id="recents" class="card">
<h3><i class="zmdi zmdi-eye"></i> {{ trans('entities.recently_viewed') }}</h3>
@include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
</div>
</div>
@endif
<div id="popular" class="card">
<h3><i class="zmdi zmdi-fire"></i> {{ trans('entities.books_popular') }}</h3>
@if(count($popular) > 0)
@include('partials/entity-list', ['entities' => $popular, 'style' => 'compact'])
@else
<div class="body text-muted">{{ trans('entities.books_popular_empty') }}</div>
@endif
</div>
<div id="new" class="card">
<h3><i class="zmdi zmdi-star-circle"></i> {{ trans('entities.books_new') }}</h3>
@if(count($popular) > 0)
@include('partials/entity-list', ['entities' => $new, 'style' => 'compact'])
@else
<div class="body text-muted">{{ trans('entities.books_new_empty') }}</div>
@endif
</div>
@stop
<div class="container" ng-non-bindable>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-9">
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.books') }}</h1>
@if(count($books) > 0)
@if($books_display=='grid')
<div class="row">
@foreach($books as $book)
@include('books/grid-item', ['book' => $book])
@endforeach
<div class="col-xs-12">
{!! $books->render() !!}
</div>
</div>
@else
@foreach($books as $book)
@include('books/list-item', ['book' => $book])
<hr>
@endforeach
{!! $books->render() !!}
@endif
@else
<p class="text-muted">{{ trans('entities.books_empty') }}</p>
@if(userCan('books-create-all'))
@ -45,25 +55,5 @@
@endif
@endif
</div>
<div class="col-xs-12 col-sm-12 col-md-3">
<div id="recents">
@if($recents)
<div class="margin-top">&nbsp;</div>
<h3>{{ trans('entities.recently_viewed') }}</h3>
@include('partials/entity-list', ['entities' => $recents])
@endif
</div>
<div class="margin-top large">&nbsp;</div>
<div id="popular">
<h3>{{ trans('entities.books_popular') }}</h3>
@if(count($popular) > 0)
@include('partials/entity-list', ['entities' => $popular])
@else
<p class="text-muted">{{ trans('entities.books_popular_empty') }}</p>
@endif
</div>
</div>
</div>
</div>
@stop

View File

@ -1,21 +1,21 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<h1>{{ trans('entities.books_permissions') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.books_permissions') }}</h3>
<div class="body">
@include('form/restriction-form', ['model' => $book])
</div>
</div>
</div>
@stop

View File

@ -1,10 +1,6 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-6 col-xs-1 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
@ -24,14 +20,12 @@
@if(userCan('chapter-create', $book))
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.chapters_new') }}</a>
@endif
@if(userCan('book-update', $book))
<a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a>
@endif
@if(userCan('book-update', $book) || userCan('restrictions-manage', $book) || userCan('book-delete', $book))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i>{{ trans('common.more') }}</a>
<ul>
@if(userCan('book-update', $book))
<li><a href="{{$book->getEditUrl()}}" class="text-primary"><i class="zmdi zmdi-edit"></i>{{ trans('common.edit') }}</a></li>
<li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>{{ trans('common.sort') }}</a></li>
@endif
@if(userCan('restrictions-manage', $book))
@ -45,22 +39,65 @@
@endif
</div>
</div>
</div>
@stop
@section('sidebar')
<div class="card">
<div class="body">
<form v-on:submit.prevent="searchBook" class="search-box">
<input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
<button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
</div>
</div>
@if($book->restricted)
<div class="card">
<h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
<div class="body">
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
</p>
</div>
</div>
@endif
<div ng-non-bindable class="container" id="entity-dashboard" entity-id="{{ $book->id }}" entity-type="book">
<div class="row">
<div class="col-md-7">
@if(count($activity) > 0)
<div class="activity card">
<h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => $activity])
</div>
@endif
<div class="card">
<h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
<div class="body">
@include('partials.entity-meta', ['entity' => $book])
</div>
</div>
@stop
@section('container-attrs')
id="entity-dashboard"
entity-id="{{ $book->id }}"
entity-type="book"
@stop
@section('body')
<div ng-non-bindable class="container small">
<h1>{{$book->name}}</h1>
<div class="book-content" v-show="!searching">
<p class="text-muted" v-pre>{!! nl2br(e($book->description)) !!}</p>
@if(count($bookChildren) > 0)
<div class="page-list" v-pre>
<hr>
@if(count($bookChildren) > 0)
@foreach($bookChildren as $childElement)
@if($childElement->isA('chapter'))
@include('chapters/list-item', ['chapter' => $childElement])
@ -69,23 +106,22 @@
@endif
<hr>
@endforeach
</div>
@else
<p class="text-muted">{{ trans('entities.books_empty_contents') }}</p>
<p>
<div class="well">
<p class="text-muted italic">{{ trans('entities.books_empty_contents') }}</p>
@if(userCan('page-create', $book))
<a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
<a href="{{ $book->getUrl('/page/create') }}" class="button outline page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
@endif
@if(userCan('page-create', $book) && userCan('chapter-create', $book))
&nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp;&nbsp;
@endif
@if(userCan('chapter-create', $book))
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
<a href="{{ $book->getUrl('/chapter/create') }}" class="button outline chapter"><i class="zmdi zmdi-collection-bookmark"></i>{{ trans('entities.books_empty_add_chapter') }}</a>
@endif
</p>
<hr>
@endif
@include('partials.entity-meta', ['entity' => $book])
</div>
@endif
</div>
<div class="search-results" v-cloak v-show="searching">
<h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
@ -94,35 +130,6 @@
</div>
<div v-html="searchResults"></div>
</div>
</div>
<div class="col-md-4 col-md-offset-1">
<div class="margin-top large"></div>
@if($book->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
</p>
@endif
<form v-on:submit.prevent="searchBook" class="search-box">
<input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
<button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
<div class="activity">
<h3>{{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
</div>
</div>
</div>
</div>
@stop

View File

@ -1,34 +1,45 @@
@extends('base')
@extends('simple-layout')
@section('head')
<script src="{{ baseUrl("/libs/jquery-sortable/jquery-sortable.min.js") }}"></script>
@stop
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('books._breadcrumbs', ['book' => $book])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<h1>{{ trans('entities.books_sort') }}</h1>
<div class="row">
<div class="col-md-8" id="sort-boxes">
<div class="col-md-8">
<div class="card">
<h3><i class="zmdi zmdi-sort"></i> {{ trans('entities.books_sort') }}</h3>
<div class="body">
<div id="sort-boxes">
@include('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
</div>
<form action="{{ $book->getUrl('/sort') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
<input type="hidden" id="sort-tree-input" name="sort-tree">
<div class="list">
<a href="{{ $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
</div>
</form>
</div>
</div>
</div>
@if(count($books) > 1)
<div class="col-md-4">
<h3>{{ trans('entities.books_sort_show_other') }}</h3>
<div id="additional-books">
<div class="card">
<h3><i class="zmdi zmdi-book"></i> {{ trans('entities.books_sort_show_other') }}</h3>
<div class="body" id="additional-books">
@foreach($books as $otherBook)
@if($otherBook->id !== $book->id)
<div>
@ -38,19 +49,11 @@
@endforeach
</div>
</div>
</div>
@endif
</div>
<form action="{{ $book->getUrl('/sort') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
<input type="hidden" id="sort-tree-input" name="sort-tree">
<div class="list">
<a href="{{ $book->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<button class="button pos" type="submit">{{ trans('entities.books_sort_save') }}</button>
</div>
</form>
</div>

View File

@ -1,12 +1,26 @@
@extends('base')
@extends('simple-layout')
@section('content')
@section('toolbar')
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
<span class="sep">&raquo;</span>
<a href="{{ baseUrl('/books/chapter/create') }}" class="text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.chapters_create') }}</a>
</div>
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.chapters_create') }}</h1>
<div class="card">
<h3><i class="zmdi zmdi-plus"></i> {{ trans('entities.chapters_create') }}</h3>
<div class="body">
<form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
@include('chapters/form')
</form>
</div>
</div>
</div>
@stop

View File

@ -1,28 +1,30 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.chapters_delete') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-delete"></i> {{ trans('entities.chapters_delete') }}</h3>
<div class="body">
<p>{{ trans('entities.chapters_delete_explain', ['chapterName' => $chapter->name]) }}</p>
<p class="text-neg">{{ trans('entities.chapters_delete_confirm') }}</p>
<form action="{{ $chapter->getUrl() }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{ $chapter->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
<a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,13 +1,24 @@
@extends('base')
@extends('simple-layout')
@section('content')
@section('toolbar')
<div class="col-sm-12 faded">
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('entities.chapters_edit') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.chapters_edit') }}</h3>
<div class="body">
<form action="{{ $chapter->getUrl() }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('chapters/form', ['model' => $chapter])
</form>
</div>
</div>
</div>
@stop

View File

@ -11,7 +11,7 @@
@include('form/textarea', ['name' => 'description'])
</div>
<div class="form-group">
<a href="{{ back()->getTargetUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ back()->getTargetUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
</div>

View File

@ -1,29 +1,34 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container">
<h1>{{ trans('entities.chapters_move') }}</h1>
<div class="card">
<h3><i class="zmdi zmdi-folder"></i> {{ trans('entities.chapters_move') }}</h3>
<div class="body">
<form action="{{ $chapter->getUrl('/move') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
<a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,20 +1,21 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<h1>{{ trans('entities.chapters_permissions') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.chapters_permissions') }}</h3>
<div class="body">
@include('form/restriction-form', ['model' => $chapter])
</div>
</div>
</div>
@stop

View File

@ -1,10 +1,6 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-6 col-xs-3 faded" ng-non-bindable>
@include('chapters._breadcrumbs', ['chapter' => $chapter])
</div>
@ -26,7 +22,7 @@
@endif
@if(userCan('chapter-update', $chapter) || userCan('restrictions-manage', $chapter) || userCan('chapter-delete', $chapter))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i> {{ trans('common.more') }}</a>
<ul>
@if(userCan('chapter-update', $chapter))
<li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
@ -42,14 +38,65 @@
@endif
</div>
</div>
</div>
@stop
@section('container-attrs')
id="entity-dashboard"
entity-id="{{ $chapter->id }}"
entity-type="chapter"
@stop
@section('sidebar')
<div class="card">
<div class="body">
<form @submit.prevent="searchBook" class="search-box">
<input v-model="searchTerm" @change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
<button v-if="searching" v-cloak class="text-neg" @click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
</div>
</div>
@if($book->restricted || $chapter->restricted)
<div class="card">
<h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
<div class="body">
@if($book->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
</p>
@endif
<div class="container" id="entity-dashboard" ng-non-bindable entity-id="{{ $chapter->id }}" entity-type="chapter">
<div class="row">
<div class="col-md-7">
@if($chapter->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $chapter))
<a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
@endif
</p>
@endif
</div>
</div>
@endif
<div class="card">
<h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
<div class="body">
@include('partials.entity-meta', ['entity' => $chapter])
</div>
</div>
@include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
@stop
@section('body')
<div class="container small" ng-non-bindable >
<h1>{{ $chapter->name }}</h1>
<div class="chapter-content" v-show="!searching">
<p class="text-muted">{!! nl2br(e($chapter->description)) !!}</p>
@ -63,71 +110,30 @@
@endforeach
</div>
@else
<hr>
<p class="text-muted">{{ trans('entities.chapters_empty') }}</p>
<div class="well">
<p class="text-muted italic">{{ trans('entities.chapters_empty') }}</p>
<p>
@if(userCan('page-create', $chapter))
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
<a href="{{ $chapter->getUrl('/create-page') }}" class="button outline page"><i class="zmdi zmdi-file-text"></i>{{ trans('entities.books_empty_create_page') }}</a>
@endif
@if(userCan('page-create', $chapter) && userCan('book-update', $book))
&nbsp;&nbsp;<em class="text-muted">-{{ trans('entities.books_empty_or') }}-</em>&nbsp;&nbsp; &nbsp;
@endif
@if(userCan('book-update', $book))
<a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ trans('entities.books_empty_sort_current_book') }}</a>
<a href="{{ $book->getUrl('/sort') }}" class="button outline book"><i class="zmdi zmdi-book"></i>{{ trans('entities.books_empty_sort_current_book') }}</a>
@endif
</p>
<hr>
</div>
@endif
@include('partials.entity-meta', ['entity' => $chapter])
</div>
<div class="search-results" v-cloak v-show="searching">
<h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" v-on:click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
<h3 class="text-muted">{{ trans('entities.search_results') }} <a v-if="searching" @click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>{{ trans('entities.search_clear') }}</a></h3>
<div v-if="!searchResults">
@include('partials/loading-icon')
</div>
<div v-html="searchResults"></div>
</div>
</div>
<div class="col-md-4 col-md-offset-1">
<div class="margin-top large"></div>
@if($book->restricted || $chapter->restricted)
<div class="text-muted">
@if($book->restricted)
<p class="text-muted">
@if(userCan('restrictions-manage', $book))
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.books_permissions_active') }}
@endif
</p>
@endif
@if($chapter->restricted)
@if(userCan('restrictions-manage', $chapter))
<a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}</a>
@else
<i class="zmdi zmdi-lock-outline"></i>{{ trans('entities.chapters_permissions_active') }}
@endif
@endif
</div>
@endif
<form v-on:submit.prevent="searchBook" class="search-box">
<input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
<button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
@include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree])
</div>
</div>
</div>
@stop

View File

@ -1,5 +1,5 @@
<div id="entity-selector-wrap">
<div overlay entity-link-selector>
<div overlay entity-selector-popup>
<div class="popup-body small flex-child">
<div class="popup-header primary-background">
<div class="popup-title">{{ trans('entities.entity_select') }}</div>

View File

@ -1,8 +1,8 @@
<div class="form-group">
<div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}">
<input type="hidden" entity-selector-input name="{{$name}}" value="">
<input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
<div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div>
<div ng-show="!loading" ng-bind-html="entityResults"></div>
<input type="text" placeholder="{{ trans('common.search') }}" entity-selector-search>
<div class="text-center loading" entity-selector-loading>@include('partials.loading-icon')</div>
<div entity-selector-results></div>
</div>
</div>

View File

@ -0,0 +1,13 @@
{{--Depends on entity selector popup--}}
<div page-picker>
<div class="input-base">
<span @if($value) style="display: none" @endif page-picker-default class="text-muted italic">{{ $placeholder }}</span>
<a @if(!$value) style="display: none" @endif href="{{ baseUrl('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Page::find($value)->name : '' }}</a>
</div>
<br>
<input type="hidden" value="{{$value}}" name="{{$name}}" id="{{$name}}">
<button @if(!$value) style="display: none" @endif type="button" page-picker-reset class="text-button">{{ trans('common.reset') }}</button>
<span @if(!$value) style="display: none" @endif class="sep">|</span>
<button type="button" page-picker-select class="text-button">{{ trans('common.select') }}</button>
</div>

View File

@ -1,32 +1,42 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="container">
<p>&nbsp;</p>
<h1>{{ $message or trans('errors.404_page_not_found') }}</h1>
<p>{{ trans('errors.sorry_page_not_found') }}</p>
<p><a href="{{ baseUrl('/') }}" class="button">{{ trans('errors.return_home') }}</a></p>
<div class="card">
<h3><i class="zmdi zmdi-alert-octagon"></i> {{ $message or trans('errors.404_page_not_found') }}</h3>
<div class="body">
<h5>{{ trans('errors.sorry_page_not_found') }}</h5>
<p><a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
</div>
</div>
@if (setting('app-public') || !user()->isDefault())
<hr>
<div class="row">
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.pages_popular') }}</h3>
<div class="card">
<h3 class="text-muted"><i class="zmdi zmdi-file-text"></i> {{ trans('entities.pages_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Page::class]), 'style' => 'compact'])
</div>
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.books_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
</div>
<div class="col-md-4">
<h3 class="text-muted">{{ trans('entities.chapters_popular') }}</h3>
<div class="card">
<h3 class="text-muted"><i class="zmdi zmdi-book"></i> {{ trans('entities.books_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Book::class]), 'style' => 'compact'])
</div>
</div>
<div class="col-md-4">
<div class="card">
<h3 class="text-muted"><i class="zmdi zmdi-collection-bookmark"></i> {{ trans('entities.chapters_popular') }}</h3>
@include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Chapter::class]), 'style' => 'compact'])
</div>
</div>
</div>
@endif
</div>

View File

@ -3,8 +3,13 @@
@section('content')
<div class="container">
<h1 class="text-muted">{{ trans('errors.error_occurred') }}</h1>
<p>{{ $message }}</p>
<div class="card">
<h3 class="text-muted">{{ trans('errors.error_occurred') }}</h3>
<div class="body">
<h5>{{ $message }}</h5>
<p><a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
</div>
</div>
</div>
@stop

View File

@ -2,9 +2,13 @@
@section('content')
<div class="container">
<h1 class="text-muted">{{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h1>
<div class="container small">
<div class="card">
<div class="body">
<h4 class="text-muted"><i class="zmdi zmdi-alert-octagon"></i> {{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h4>
<p>{{ trans('errors.back_soon') }}</p>
</div>
</div>
</div>
@stop

View File

@ -27,6 +27,8 @@
@endforeach
</table>
<a href="{{ $model->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="text-right">
<a href="{{ $model->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.permissions_save') }}</button>
</div>
</form>

View File

@ -0,0 +1,56 @@
@extends('sidebar-layout')
@section('toolbar')
<div class="col-sm-6 faded">
<div class="action-buttons text-left">
<a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
</div>
</div>
@stop
@section('sidebar')
@if(count($draftPages) > 0)
<div id="recent-drafts" class="card">
<h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.my_recent_drafts') }}</h3>
@include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
</div>
@endif
<div class="card">
<h3><i class="zmdi zmdi-{{ $signedIn ? 'eye' : 'star-circle' }}"></i> {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
@include('partials/entity-list', [
'entities' => $recents,
'style' => 'compact',
'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
])
</div>
<div class="card">
<h3><i class="zmdi zmdi-file"></i> <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
<div id="recently-updated-pages">
@include('partials/entity-list', [
'entities' => $recentlyUpdatedPages,
'style' => 'compact',
'emptyText' => trans('entities.no_pages_recently_updated')
])
</div>
</div>
<div id="recent-activity" class="card">
<h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => $activity])
</div>
@stop
@section('body')
<div class="page-content" ng-non-bindable>
@include('pages/page-display', ['page' => $customHomepage])
</div>
@stop
@section('scripts')
<script>
setupPageShow({{$customHomepage->id}});
</script>
@stop

View File

@ -1,52 +1,39 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-6 faded">
<div class="action-buttons text-left">
<a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
</div>
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<div class="row">
<div class="col-sm-4">
<div id="recent-drafts">
@if(count($draftPages) > 0)
<h4>{{ trans('entities.my_recent_drafts') }}</h4>
<div id="recent-drafts" class="card">
<h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.my_recent_drafts') }}</h3>
@include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
@endif
</div>
@if($signedIn)
<h4>{{ trans('entities.my_recently_viewed') }}</h4>
@else
<h4>{{ trans('entities.books_recent') }}</h4>
@endif
<div class="card">
<h3><i class="zmdi zmdi-{{ $signedIn ? 'eye' : 'star-circle' }}"></i> {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
@include('partials/entity-list', [
'entities' => $recents,
'style' => 'compact',
'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
])
</div>
<div class="col-sm-4">
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">{{ trans('entities.recently_created_pages') }}</a></h4>
<div id="recently-created-pages">
@include('partials/entity-list', [
'entities' => $recentlyCreatedPages,
'style' => 'compact',
'emptyText' => trans('entities.no_pages_recently_created')
])
</div>
<h4><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h4>
<div class="col-sm-4">
<div class="card">
<h3><i class="zmdi zmdi-file"></i> <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
<div id="recently-updated-pages">
@include('partials/entity-list', [
'entities' => $recentlyUpdatedPages,
@ -55,11 +42,14 @@
])
</div>
</div>
</div>
<div class="col-sm-4" id="recent-activity">
<h4>{{ trans('entities.recent_activity') }}</h4>
<div class="card">
<h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => $activity])
</div>
</div>
</div>
</div>

View File

@ -1,27 +1,30 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ $page->draft ? trans('entities.pages_delete_draft') : trans('entities.pages_delete') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-delete"></i> {{ $page->draft ? trans('entities.pages_delete_draft') : trans('entities.pages_delete') }}</h3>
<div class="body">
<p class="text-neg">{{ $page->draft ? trans('entities.pages_delete_draft_confirm'): trans('entities.pages_delete_confirm') }}</p>
<form action="{{ $page->getUrl() }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{ $page->getUrl() }}" class="button primary">{{ trans('common.cancel') }}</a>
<div class="form-group">
<a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,18 +1,15 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="container">
<div class="row">
<div class="col-sm-7">
<h1>{{ $title }}</h1>
@section('body')
<div class="container small">
<p>&nbsp;</p>
<div class="card">
<h3>{{ $title }}</h3>
@include('partials/entity-list', ['entities' => $pages, 'style' => 'detailed'])
<div class="body text-center">
{!! $pages->links() !!}
</div>
<div class="col-sm-4 col-sm-offset-1"></div>
</div>
</div>
@stop

View File

@ -16,8 +16,6 @@
@include('pages/form', ['model' => $page])
@include('pages/form-toolbox')
</form>
</div>
@include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])

View File

@ -5,7 +5,7 @@
{{--Header Bar--}}
<div class="faded-small toolbar">
<div class="container">
<div class="container fluid">
<div class="row">
<div class="col-sm-4 faded">
<div class="action-buttons text-left">
@ -74,7 +74,7 @@
{{--Markdown Editor--}}
@if(setting('app-editor') === 'markdown')
<div id="markdown-editor" markdown-editor class="flex-fill flex">
<div id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
<div class="markdown-editor-wrap">
<div class="editor-toolbar">

View File

@ -1,29 +1,31 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container">
<h1>{{ trans('entities.pages_move') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-folder"></i> {{ trans('entities.pages_move') }}</h3>
<div class="body">
<form action="{{ $page->getUrl('/move') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
<a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -7,6 +7,6 @@
@if (isset($diff) && $diff)
{!! $diff !!}
@else
{!! isset($pageContent) ? $pageContent : $page->html !!}
{!! isset($page->renderedHTML) ? $page->renderedHTML : $page->html !!}
@endif
</div>

View File

@ -1,20 +1,19 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<h1>{{ trans('entities.pages_permissions') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-lock-outline"></i> {{ trans('entities.pages_permissions') }}</h3>
<div class="body">
@include('form.restriction-form', ['model' => $page])
</div>
</div>
</div>
@stop

View File

@ -1,12 +1,20 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
@section('sidebar')
<div class="card">
<h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
<div class="body">
@include('partials.entity-meta', ['entity' => $revision])
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<div class="row">
<div class="col-md-9">
<div class="page-content anim fadeIn">
<div class="page-content">
@include('pages.page-display')
</div>
</div>

View File

@ -1,20 +1,18 @@
@extends('base')
@extends('simple-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container" ng-non-bindable>
<h1>{{ trans('entities.pages_revisions') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-replay"></i> {{ trans('entities.pages_revisions') }}</h3>
<div class="body">
@if(count($page->revisions) > 0)
<table class="table">
@ -57,6 +55,8 @@
@else
<p>{{ trans('entities.pages_revisions_none') }}</p>
@endif
</div>
</div>
</div>

View File

@ -1,10 +1,6 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-8 col-xs-5 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
@ -23,7 +19,7 @@
@endif
@if(userCan('page-update', $page) || userCan('restrictions-manage', $page) || userCan('page-delete', $page))
<div dropdown class="dropdown-container">
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i> {{ trans('common.more') }}</a>
<ul>
@if(userCan('page-update', $page))
<li><a href="{{ $page->getUrl('/move') }}" class="text-primary" ><i class="zmdi zmdi-folder"></i>{{ trans('common.move') }}</a></li>
@ -41,37 +37,13 @@
</div>
</div>
</div>
</div>
</div>
@stop
<div class="container" id="page-show">
<div class="row">
<div class="col-md-9 print-full-width">
<div class="page-content" ng-non-bindable>
<div class="pointer-container" id="pointer">
<div class="pointer anim" >
<span class="icon text-primary"><i class="zmdi zmdi-link"></i></span>
<input readonly="readonly" type="text" id="pointer-url" placeholder="url">
<button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}"><i class="zmdi zmdi-copy"></i></button>
</div>
</div>
@include('pages/page-display')
<hr>
@include('partials.entity-meta', ['entity' => $page])
</div>
@include('comments/comments', ['pageId' => $page->id])
</div>
<div class="col-md-3 print-hidden">
<div class="margin-top large"></div>
@section('sidebar')
@if($book->restricted || ($page->chapter && $page->chapter->restricted) || $page->restricted)
<div class="card">
<h3><i class="zmdi zmdi-key"></i> {{ trans('entities.permissions') }}</h3>
<div class="body">
<div class="text-muted">
@if($book->restricted)
@ -101,14 +73,82 @@
<br>
@endif
</div>
</div>
</div>
@endif
@if($page->tags->count() > 0)
<div class="card tag-display">
<h3><i class="zmdi zmdi-tag"></i> {{ trans('entities.page_tags') }}</h3>
<div class="body">
<table>
<tbody>
@foreach($page->tags as $tag)
<tr class="tag">
<td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
@if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@if ($page->attachments->count() > 0)
<div class="card">
<h3><i class="zmdi zmdi-attachment-alt"></i> {{ trans('entities.pages_attachments') }}</h3>
<div class="body">
@foreach($page->attachments as $attachment)
<div class="attachment">
<a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif><i class="zmdi zmdi-{{ $attachment->external ? 'open-in-new' : 'file' }}"></i>{{ $attachment->name }}</a>
</div>
@endforeach
</div>
</div>
@endif
@include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav])
@if (isset($pageNav) && count($pageNav))
<div class="card">
<h3><i class="zmdi zmdi-compass"></i> {{ trans('entities.pages_navigation') }}</h3>
<div class="body">
<div class="sidebar-page-nav menu">
@foreach($pageNav as $navItem)
<li class="page-nav-item h{{ $navItem['level'] }}">
<a href="{{ $navItem['link'] }}">{{ $navItem['text'] }}</a>
</li>
@endforeach
</div>
</div>
</div>
@endif
@include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
<div class="card">
<h3><i class="zmdi zmdi-info-outline"></i> {{ trans('common.details') }}</h3>
<div class="body">
@include('partials.entity-meta', ['entity' => $page])
</div>
</div>
@stop
@section('body')
<div class="page-content" ng-non-bindable>
<div class="pointer-container" id="pointer">
<div class="pointer anim" >
<span class="icon text-primary"><i class="zmdi zmdi-link"></i></span>
<input readonly="readonly" type="text" id="pointer-url" placeholder="url">
<button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}"><i class="zmdi zmdi-copy"></i></button>
</div>
</div>
@include('pages/page-display')
</div>
<div class="container small">
@include('comments/comments', ['pageId' => $page->id])
</div>
@stop

View File

@ -1,74 +0,0 @@
<div class="book-tree" ng-non-bindable>
@if(isset($page) && $page->tags->count() > 0)
<div class="tag-display">
<h6 class="text-muted">{{ trans('entities.page_tags') }}</h6>
<table>
<tbody>
@foreach($page->tags as $tag)
<tr class="tag">
<td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
@if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@if (isset($page) && $page->attachments->count() > 0)
<h6 class="text-muted">{{ trans('entities.pages_attachments') }}</h6>
@foreach($page->attachments as $attachment)
<div class="attachment">
<a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif><i class="zmdi zmdi-{{ $attachment->external ? 'open-in-new' : 'file' }}"></i>{{ $attachment->name }}</a>
</div>
@endforeach
@endif
@if (isset($pageNav) && count($pageNav))
<h6 class="text-muted">{{ trans('entities.pages_navigation') }}</h6>
<div class="sidebar-page-nav menu">
@foreach($pageNav as $navItem)
<li class="page-nav-item h{{ $navItem['level'] }}">
<a href="{{ $navItem['link'] }}">{{ $navItem['text'] }}</a>
</li>
@endforeach
</div>
@endif
<h6 class="text-muted">{{ trans('entities.books_navigation') }}</h6>
<ul class="sidebar-page-list menu">
@if (userCan('view', $book))
<li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
@endif
@foreach($sidebarTree as $bookChild)
<li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
<a href="{{ $bookChild->getUrl() }}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
@if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }}
</a>
@if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
<p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
<i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $bookChild->pages->count()]) }}</span>
</p>
<ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
@foreach($bookChild->pages as $childPage)
<li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
<a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
<i class="zmdi zmdi-file-text"></i> {{ $childPage->name }}
</a>
</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
</div>

View File

@ -8,5 +8,5 @@
@endforeach
</div>
@else
<p class="text-muted">{{ trans('common.no_activity') }}</p>
<p class="text-muted empty-text">{{ trans('common.no_activity') }}</p>
@endif

View File

@ -0,0 +1,36 @@
<div class="card book-tree" ng-non-bindable>
<h3><i class="zmdi zmdi-book"></i> {{ trans('entities.books_navigation') }}</h3>
<div class="body">
<ul class="sidebar-page-list menu">
@if (userCan('view', $book))
<li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
@endif
@foreach($sidebarTree as $bookChild)
<li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
<a href="{{ $bookChild->getUrl() }}" class="{{ $bookChild->getClassName() }} {{ $current->matches($bookChild)? 'selected' : '' }}">
@if($bookChild->isA('chapter'))<i class="zmdi zmdi-collection-bookmark"></i>@else <i class="zmdi zmdi-file-text"></i>@endif{{ $bookChild->name }}
</a>
@if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
<p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
<i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $bookChild->pages->count()]) }}</span>
</p>
<ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
@foreach($bookChild->pages as $childPage)
<li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}">
<a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
<i class="zmdi zmdi-file-text"></i> {{ $childPage->name }}
</a>
</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
</div>
</div>

View File

@ -1,5 +1,4 @@
<style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
@if(setting('app-color'))
header, [back-to-top], .primary-background {
background-color: {{ setting('app-color') }} !important;
}
@ -18,5 +17,4 @@
.text-primary, p.primary, p .primary, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus {
color: {{ setting('app-color') }};
}
@endif
</style>

View File

@ -1,4 +1,9 @@
<p class="text-muted small">
@if($entity->isA('revision'))
{{ trans('entities.pages_revision') }}
{{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
<br>
@endif
@if ($entity->isA('page')) {{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br> @endif
@if ($entity->createdBy)
{!! trans('entities.meta_created_name', [
@ -14,7 +19,7 @@
'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
]) !!}
@else
@elseif (!$entity->isA('revision'))
<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
@endif
</p>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html class="shaded">
<head>
<title>{{ setting('app-name') }}</title>
@ -23,12 +23,12 @@
{!! setting('app-custom-head') !!}
@endif
</head>
<body class="@yield('body-class')" ng-app="bookStack">
<body class="@yield('body-class')">
@include('partials.notifications')
<header id="header">
<div class="container">
<div class="container fluid">
<div class="row">
<div class="col-sm-6">

View File

@ -1,39 +1,23 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
<input type="hidden" name="searchTerm" value="{{$searchTerm}}">
<div id="search-system">
<div class="faded-small toolbar">
<div class="container">
<div class="row">
@section('toolbar')
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ baseUrl("/search?term=" . urlencode($searchTerm)) }}" class="text-button"><i class="zmdi zmdi-search"></i>{{ trans('entities.search_for_term', ['term' => $searchTerm]) }}</a>
</div>
</div>
</div>
</div>
</div>
@stop
<div class="container" ng-non-bindable id="searchSystem">
@section('container-attrs')
id="search-system"
ng-non-bindable=""
@stop
<div class="row">
<div class="col-md-6">
<h1>{{ trans('entities.search_results') }}</h1>
<h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
@include('partials/entity-list', ['entities' => $entities])
@if ($hasNextPage)
<a href="{{ $nextPageLink }}" class="button">{{ trans('entities.search_more') }}</a>
@endif
</div>
<div class="col-md-5 col-md-offset-1">
@section('sidebar')
<div class="card">
<h3>{{ trans('entities.search_filters') }}</h3>
<div class="body">
<form v-on:submit="updateSearch" v-cloak class="v-cloak anim fadeIn">
<h6 class="text-muted">{{ trans('entities.search_content_type') }}</h6>
<div class="form-group">
@ -200,14 +184,21 @@
<button type="submit" class="button primary">{{ trans('entities.search_update') }}</button>
</form>
</div>
</div>
</div>
</div>
@stop
@section('body')
<div class="container small">
<input type="hidden" name="searchTerm" value="{{$searchTerm}}">
<h1>{{ trans('entities.search_results') }}</h1>
<h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
@include('partials/entity-list', ['entities' => $entities])
@if ($hasNextPage)
<a href="{{ $nextPageLink }}" class="button">{{ trans('entities.search_more') }}</a>
@endif
</div>
@stop

View File

@ -1,18 +1,22 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'settings'])
@stop
<div class="container small settings-container">
@section('body')
<div class="container small">
<h1>{{ trans('settings.settings') }}</h1>
<div class="text-right text-muted container">
<br>
BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
</div>
<div class="card">
<h3><i class="zmdi zmdi-settings-square"></i> {{ trans('settings.app_settings') }}</h3>
<div class="body">
<form action="{{ baseUrl("/settings") }}" method="POST">
{!! csrf_field() !!}
<h3>{{ trans('settings.app_settings') }}</h3>
<div class="row">
<div class="col-md-6">
@ -64,8 +68,13 @@
<div class="form-group" id="color-control">
<label for="setting-app-color">{{ trans('settings.app_primary_color') }}</label>
<p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
<input type="text" value="{{ setting('app-color', '') }}" name="setting-app-color" id="setting-app-color" placeholder="#0288D1">
<input type="hidden" value="{{ setting('app-color-light', '') }}" name="setting-app-color-light" id="setting-app-color-light" placeholder="rgba(21, 101, 192, 0.15)">
<input type="text" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#0288D1">
<input type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
</div>
<div class="form-group" id="homepage-control">
<label for="setting-app-homepage">{{ trans('settings.app_homepage') }}</label>
<p class="small">{{ trans('settings.app_homepage_desc') }}</p>
@include('components.page-picker', ['name' => 'setting-app-homepage', 'placeholder' => trans('settings.app_homepage_default'), 'value' => setting('app-homepage')])
</div>
</div>
@ -77,9 +86,20 @@
<textarea name="setting-app-custom-head" id="setting-app-custom-head">{{ setting('app-custom-head', '') }}</textarea>
</div>
<hr class="margin-top">
<div class="form-group text-right">
<button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
</div>
</form>
</div>
</div>
<h3>{{ trans('settings.reg_settings') }}</h3>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-accounts-add"></i> {{ trans('settings.reg_settings') }}</h3>
<div class="body">
<form action="{{ baseUrl("/settings") }}" method="POST">
{!! csrf_field() !!}
<div class="row">
<div class="col-md-6">
@ -114,19 +134,18 @@
</div>
</div>
<hr class="margin-top">
<div class="form-group">
<span class="float right muted">
BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
</span>
<div class="form-group text-right">
<button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
</div>
</form>
</div>
</div>
</div>
@include('components.image-manager', ['imageType' => 'system'])
@include('components.entity-selector-popup', ['entityTypes' => 'page'])
@stop
@ -139,6 +158,7 @@
var hexVal = '#' + this.color.colors.HEX;
var rgb = this.color.colors.RND.rgb;
var rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';
// Set textbox color to hex color code.
var isEmpty = $.trim($elm.val()).length === 0;
if (!isEmpty) $elm.val(hexVal);

View File

@ -1,7 +1,4 @@
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-md-12 setting-nav nav-tabs">
@if($currentUser->can('settings-manage'))
<a href="{{ baseUrl('/settings') }}" @if($selected == 'settings') class="selected text-button" @endif><i class="zmdi zmdi-settings"></i>{{ trans('settings.settings') }}</a>
@ -13,6 +10,3 @@
<a href="{{ baseUrl('/settings/roles') }}" @if($selected == 'roles') class="selected text-button" @endif><i class="zmdi zmdi-lock-open"></i>{{ trans('settings.roles') }}</a>
@endif
</div>
</div>
</div>
</div>

View File

@ -1,15 +1,17 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'roles'])
@stop
<div class="container">
<h1>{{ trans('settings.role_create') }}</h1>
@section('body')
<form action="{{ baseUrl("/settings/roles/new") }}" method="POST">
@include('settings/roles/form')
</form>
<div class="container">
<div class="row">
@include('settings/roles/form', ['title' => trans('settings.role_create'), 'icon' => 'plus'])
</div>
</div>
</form>
@stop

View File

@ -1,11 +1,15 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'roles'])
@stop
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('settings.role_delete') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-delete"></i> {{ trans('settings.role_delete') }}</h3>
<div class="body">
<p>{{ trans('settings.role_delete_confirm', ['roleName' => $role->display_name]) }}</p>
<form action="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" method="POST">
@ -20,9 +24,13 @@
@endif
<p class="text-neg">{{ trans('settings.role_delete_sure') }}</p>
<a href="{{ baseUrl("/settings/roles/{$role->id}") }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group">
<a href="{{ baseUrl("/settings/roles/{$role->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,24 +1,17 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'roles'])
@stop
<div class="container">
<div class="row">
<div class="col-sm-6">
<h1>{{ trans('settings.role_edit') }}</h1>
</div>
<div class="col-sm-6">
<p></p>
<a href="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" class="button neg float right">{{ trans('settings.role_delete') }}</a>
</div>
</div>
@section('body')
<form action="{{ baseUrl("/settings/roles/{$role->id}") }}" method="POST">
<input type="hidden" name="_method" value="PUT">
@include('settings/roles/form', ['model' => $role])
</form>
<div class="container">
<div class="row">
@include('settings/roles/form', ['model' => $role, 'title' => trans('settings.role_edit'), 'icon' => 'edit'])
</div>
</div>
</form>
@stop

View File

@ -1,11 +1,12 @@
{!! csrf_field() !!}
<div class="row">
<div class="col-md-9">
<div class="card">
<h3><i class="zmdi zmdi-{{$icon}}"></i> {{$title}}</h3>
<div class="body">
<div class="row">
<div class="col-md-5">
<h3>{{ trans('settings.role_details') }}</h3>
<h5>{{ trans('settings.role_details') }}</h5>
<div class="form-group">
<label for="name">{{ trans('settings.role_name') }}</label>
@include('form/text', ['name' => 'display_name'])
@ -14,7 +15,7 @@
<label for="name">{{ trans('settings.role_desc') }}</label>
@include('form/text', ['name' => 'description'])
</div>
<h3>{{ trans('settings.role_system') }}</h3>
<h5>{{ trans('settings.role_system') }}</h5>
<label>@include('settings/roles/checkbox', ['permission' => 'users-manage']) {{ trans('settings.role_manage_users') }}</label>
<label>@include('settings/roles/checkbox', ['permission' => 'user-roles-manage']) {{ trans('settings.role_manage_roles') }}</label>
<label>@include('settings/roles/checkbox', ['permission' => 'restrictions-manage-all']) {{ trans('settings.role_manage_entity_permissions') }}</label>
@ -24,7 +25,7 @@
<div class="col-md-6">
<h3>{{ trans('settings.role_asset') }}</h3>
<h5>{{ trans('settings.role_asset') }}</h5>
<p>{{ trans('settings.role_asset_desc') }}</p>
<table class="table">
@ -133,12 +134,20 @@
</table>
</div>
</div>
<a href="{{ baseUrl("/settings/roles") }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ baseUrl("/settings/roles") }}" class="button outline">{{ trans('common.cancel') }}</a>
@if (isset($role) && $role->id)
<a href="{{ baseUrl("/settings/roles/delete/{$role->id}") }}" class="button neg">{{ trans('settings.role_delete') }}</a>
@endif
<button type="submit" class="button pos">{{ trans('settings.role_save') }}</button>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<h3>{{ trans('settings.role_users') }}</h3>
<div class="card">
<h3><i class="zmdi zmdi-accounts"></i> {{ trans('settings.role_users') }}</h3>
<div class="body">
@if(isset($role) && count($role->users) > 0)
<table class="list-table">
@foreach($role->users as $user)
@ -161,9 +170,6 @@
{{ trans('settings.role_users_none') }}
</p>
@endif
</div>
</div>
</div>

View File

@ -1,21 +1,16 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'roles'])
@stop
@section('body')
<div class="container small">
<div class="row action-header">
<div class="col-sm-8">
<h1>{{ trans('settings.role_user_roles') }}</h1>
</div>
<div class="col-sm-4">
<p></p>
<a href="{{ baseUrl("/settings/roles/new") }}" class="button float right pos"><i class="zmdi zmdi-lock-open"></i>{{ trans('settings.role_create') }}</a>
</div>
</div>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-lock-open"></i> {{ trans('settings.role_user_roles') }}</h3>
<div class="body">
<table class="table">
<tr>
<th>{{ trans('settings.role_name') }}</th>
@ -30,6 +25,12 @@
</tr>
@endforeach
</table>
<div class="form-group">
<a href="{{ baseUrl("/settings/roles/new") }}" class="button pos">{{ trans('settings.role_create') }}</a>
</div>
</div>
</div>
</div>
@stop

View File

@ -0,0 +1,34 @@
@extends('base')
@section('body-class', 'sidebar-layout')
@section('content')
<div class="toolbar-container">
<div class="faded-small toolbar">
<div class="container fluid">
<div class="row">
@yield('toolbar')
</div>
</div>
</div>
</div>
<div class="flex-fill flex" @yield('container-attrs') >
<div sidebar class="sidebar flex print-hidden" id="sidebar">
<div class="sidebar-toggle primary-background-light"><i class="zmdi zmdi-caret-right-circle"></i>
</div>
<div class="scroll-body">
@yield('sidebar')
</div>
</div>
<div class="content flex">
@yield('body')
</div>
</div>
@stop

View File

@ -0,0 +1,27 @@
@extends('base')
@section('body-class', 'shaded')
@section('content')
<div class="toolbar-container">
<div class="faded-small toolbar">
<div class="container fluid">
<div class="row">
@yield('toolbar')
</div>
</div>
</div>
</div>
<div class="flex-fill flex">
<div class="content flex">
<div class="scroll-body">
@yield('body')
</div>
</div>
</div>
@stop

View File

@ -1,31 +1,26 @@
@extends('base')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'users'])
@stop
@section('content')
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ baseUrl('/settings/users') }}" class="text-button"><i class="zmdi zmdi-accounts"></i>{{ trans('settings.users') }}</a>
</div>
</div>
</div>
</div>
</div>
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('settings.users_add_new') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-accounts-add"></i> {{ trans('settings.users_add_new') }}</h3>
<div class="body">
<form action="{{ baseUrl("/settings/users/create") }}" method="post">
{!! csrf_field() !!}
@include('users/forms/' . $authMethod)
<div class="form-group">
<a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
<button class="button pos" type="submit">{{ trans('common.save') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,32 +1,27 @@
@extends('base')
@extends('simple-layout')
@section('content')
@section('toolbar')
@include('settings/navbar', ['selected' => 'users'])
@stop
<div class="faded-small toolbar">
<div class="container">
<div class="row">
<div class="col-sm-12 faded">
<div class="breadcrumbs">
<a href="{{ baseUrl("/settings/users") }}" class="text-button"><i class="zmdi zmdi-accounts"></i>Users</a>
<span class="sep">&raquo;</span>
<a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="text-button"><i class="zmdi zmdi-account"></i>{{ $user->name }}</a>
</div>
</div>
</div>
</div>
</div>
@section('body')
<div class="container small" ng-non-bindable>
<h1>{{ trans('settings.users_delete') }}</h1>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-delete"></i> {{ trans('settings.users_delete') }}</h3>
<div class="body">
<p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
<p class="text-neg">{{ trans('settings.users_delete_confirm') }}</p>
<form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="DELETE">
<a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="button muted">{{ trans('common.cancel') }}</a>
<a href="{{ baseUrl("/settings/users/{$user->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
</form>
</div>
</div>
</div>
@stop

View File

@ -1,23 +1,17 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'users'])
@stop
@section('body')
<div class="container small">
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi-edit zmdi"></i> {{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h3>
<div class="body">
<form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
<div class="row">
<div class="col-sm-8">
<h1>{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
</div>
<div class="col-sm-4">
<p></p>
@if($authMethod !== 'system')
<a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button float right">{{ trans('settings.users_delete') }}</a>
@endif
</div>
</div>
<div class="row">
<div class="col-sm-6" ng-non-bindable>
{!! csrf_field() !!}
@ -49,29 +43,28 @@
@endforeach
</select>
</div>
<div class="form-group">
<label for="books_display">{{ trans('settings.users_books_display_type') }}</label>
<select name="books_display" id="books_display">
<option @if($user->books_display === 'grid') selected @endif value="grid">Grid</option>
<option @if($user->books_display === 'list') selected @endif value="list">List</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
<div class="form-group text-right">
<a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
@if($authMethod !== 'system')
<a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button">{{ trans('settings.users_delete') }}</a>
@endif
<button class="button pos" type="submit">{{ trans('common.save') }}</button>
</div>
</form>
<hr class="margin-top large">
</div>
</div>
@if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
<h3>{{ trans('settings.users_social_accounts') }}</h3>
<div class="card">
<h3><i class="zmdi zmdi-sign-in"></i> {{ trans('settings.users_social_accounts') }}</h3>
<div class="body">
<p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
<div class="container">
<div class="row">
@foreach($activeSocialDrivers as $driver => $enabled)
<div class="col-sm-3 col-xs-6 text-center">
<div class="col-sm-4 col-xs-6 text-center">
<div>@icon($driver, ['width' => 56])</div>
<div>
@if($user->hasSocialAccount($driver))
@ -80,9 +73,13 @@
<a href="{{ baseUrl("/login/service/{$driver}") }}" class="button pos">{{ trans('settings.users_social_connect') }}</a>
@endif
</div>
<div>&nbsp;</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
@endif

View File

@ -1,38 +1,31 @@
@extends('base')
@section('content')
@extends('simple-layout')
@section('toolbar')
@include('settings/navbar', ['selected' => 'users'])
@stop
@section('body')
<div class="container small" ng-non-bindable>
<div class="row action-header">
<div class="col-sm-8">
<h1>{{ trans('settings.users') }}</h1>
</div>
<div class="col-sm-4">
<p></p>
@if(userCan('users-manage'))
<a href="{{ baseUrl("/settings/users/create") }}" class="pos button float right"><i class="zmdi zmdi-account-add"></i>{{ trans('settings.users_add_new') }}</a>
@endif
</div>
</div>
<p>&nbsp;</p>
<div class="card">
<h3><i class="zmdi zmdi-accounts"></i> {{ trans('settings.users') }}</h3>
<div class="body">
<div class="container">
<div class="row">
<div class="col-sm-8">
<div class="compact">
{{ $users->links() }}
</div>
</div>
<div class="col-sm-4">
<form method="get" class="float right" action="{{ baseUrl("/settings/users") }}">
<form method="get" action="{{ baseUrl("/settings/users") }}">
@foreach(collect($listDetails)->except('search') as $name => $val)
<input type="hidden" name="{{ $name }}" value="{{ $val }}">
@endforeach
<input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
</form>
</div>
<div class="col-sm-8 text-right">
@if(userCan('users-manage'))
<a href="{{ baseUrl("/settings/users/create") }}" style="margin-top: 0;" class="pos button">{{ trans('settings.users_add_new') }}</a>
@endif
</div>
</div>
</div>
<table class="table">
@ -75,6 +68,10 @@
<div>
{{ $users->links() }}
</div>
</div>
</div>
</div>
@stop

View File

@ -1,10 +1,23 @@
@extends('base')
@extends('sidebar-layout')
@section('content')
@section('toolbar')
<div class="col-sm-6 col-xs-1 faded">
<div class="breadcrumbs">
<a href="{{ $user->getProfileUrl() }}" class="text-button"><i class="zmdi zmdi-account"></i>{{ $user->name }}</a>
</div>
</div>
@stop
<div class="container" ng-non-bindable>
<div class="row">
<div class="col-sm-7">
@section('sidebar')
<div class="card" id="recent-activity">
<h3><i class="zmdi zmdi-time"></i> {{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => $activity])
</div>
@stop
@section('body')
<div class="container small" ng-non-bindable>
<div class="padded-top large"></div>
@ -65,13 +78,5 @@
@endif
</div>
<div class="col-sm-4 col-sm-offset-1" id="recent-activity">
<h3>{{ trans('entities.recent_activity') }}</h3>
@include('partials/activity-list', ['activity' => $activity])
</div>
</div>
</div>
@stop

33
tests/HomepageTest.php Normal file
View File

@ -0,0 +1,33 @@
<?php namespace Tests;
use BookStack\JointPermission;
use BookStack\Page;
use BookStack\Repos\EntityRepo;
class HomepageTest extends TestCase
{
public function test_default_homepage_visible()
{
$this->asEditor();
$homeVisit = $this->get('/');
$homeVisit->assertSee('My Recently Viewed');
$homeVisit->assertSee('Recently Updated Pages');
$homeVisit->assertSee('Recent Activity');
}
public function test_custom_homepage() {
$this->asEditor();
$name = 'My custom homepage';
$content = 'This is the body content of my custom homepage.';
$customPage = $this->newPage(['name' => $name, 'html' => $content]);
$this->setSettings(['app-homepage' => $customPage->id]);
$homeVisit = $this->get('/');
$homeVisit->assertSee($name);
$homeVisit->assertSee($content);
$homeVisit->assertSee('My Recently Viewed');
$homeVisit->assertSee('Recently Updated Pages');
$homeVisit->assertSee('Recent Activity');
}
}

View File

@ -4,6 +4,7 @@ use BookStack\Book;
use BookStack\Chapter;
use BookStack\Repos\EntityRepo;
use BookStack\Role;
use BookStack\Services\SettingService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
@ -88,4 +89,16 @@ abstract class TestCase extends BaseTestCase
$draftPage = $entityRepo->getDraftPage($book);
return $entityRepo->publishPageDraft($draftPage, $input);
}
/**
* Quickly sets an array of settings.
* @param $settingsArray
*/
protected function setSettings($settingsArray)
{
$settings = app(SettingService::class);
foreach ($settingsArray as $key => $value) {
$settings->put($key, $value);
}
}
}