mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-02-18 03:09:40 +03:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef2ff5e093 | ||
|
|
7caed3b0db | ||
|
|
11960d9d3a | ||
|
|
bbd8fff021 | ||
|
|
ec17bd8608 | ||
|
|
0dbb8babee | ||
|
|
b14e9fc619 | ||
|
|
63c6d3478d | ||
|
|
781f0e7887 | ||
|
|
23e014cb25 | ||
|
|
5b64358ef1 | ||
|
|
56df64063d | ||
|
|
3f81eba13b | ||
|
|
7973412c29 | ||
|
|
f83de5f834 | ||
|
|
f2ceba978a | ||
|
|
96c074bb56 | ||
|
|
45641d0754 | ||
|
|
4b1d08ba99 | ||
|
|
f8a299caee | ||
|
|
437dce7756 | ||
|
|
f8ad820281 | ||
|
|
757f16a3b5 | ||
|
|
632ecc668f | ||
|
|
92d393537c | ||
|
|
160fa99ba4 | ||
|
|
d2a5ab49ed | ||
|
|
43d9d2eba7 | ||
|
|
baa260a03d | ||
|
|
b157a9927a | ||
|
|
b246a67e8a | ||
|
|
2d958e88bf | ||
|
|
07c7d5af17 | ||
|
|
42976ca48c | ||
|
|
d05e85efa9 | ||
|
|
547e117760 | ||
|
|
50a5d3c546 | ||
|
|
3fd82200cc | ||
|
|
7215392784 | ||
|
|
8a9a8dfae5 | ||
|
|
c44314def3 | ||
|
|
8b899a9cf0 | ||
|
|
0ebdfa4825 | ||
|
|
32a06f119b | ||
|
|
ec30864ce5 | ||
|
|
6bc72e157a |
@@ -3,6 +3,10 @@ APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=SomeRandomString
|
||||
|
||||
# The below url has to be set if using social auth options
|
||||
# or if you are not using BookStack at the root path of your domain.
|
||||
# APP_URL=http://bookstack.dev
|
||||
|
||||
# Database details
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=database_database
|
||||
@@ -42,8 +46,6 @@ GITHUB_APP_ID=false
|
||||
GITHUB_APP_SECRET=false
|
||||
GOOGLE_APP_ID=false
|
||||
GOOGLE_APP_SECRET=false
|
||||
# URL used for social login redirects, NO TRAILING SLASH
|
||||
APP_URL=http://bookstack.dev
|
||||
|
||||
# External services such as Gravatar
|
||||
DISABLE_EXTERNAL_SERVICES=false
|
||||
|
||||
11
.github/ISSUE_TEMPLATE.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
### For Feature Requests
|
||||
Desired Feature:
|
||||
|
||||
### For Bug Reports
|
||||
PHP Version:
|
||||
|
||||
MySQL Version:
|
||||
|
||||
Expected Behavior:
|
||||
|
||||
Actual Behavior:
|
||||
12
.travis.yml
12
.travis.yml
@@ -6,8 +6,6 @@ php:
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- node_modules
|
||||
- $HOME/.composer/cache
|
||||
|
||||
addons:
|
||||
@@ -17,19 +15,17 @@ addons:
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
before_install:
|
||||
- npm install -g npm@latest
|
||||
|
||||
before_script:
|
||||
- mysql -u root -e 'create database `bookstack-test`;'
|
||||
- composer config -g github-oauth.github.com $GITHUB_ACCESS_TOKEN
|
||||
- phpenv config-rm xdebug.ini
|
||||
- composer self-update
|
||||
- composer dump-autoload --no-interaction
|
||||
- composer install --prefer-dist --no-interaction
|
||||
- npm install
|
||||
- ./node_modules/.bin/gulp
|
||||
- php artisan clear-compiled -n
|
||||
- php artisan optimize -n
|
||||
- php artisan migrate --force -n --database=mysql_testing
|
||||
- php artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit
|
||||
- phpunit
|
||||
@@ -7,11 +7,15 @@ class Book extends Entity
|
||||
|
||||
/**
|
||||
* Get the url for this book.
|
||||
* @param string|bool $path
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
public function getUrl($path = false)
|
||||
{
|
||||
return '/books/' . $this->slug;
|
||||
if ($path !== false) {
|
||||
return baseUrl('/books/' . $this->slug . '/' . trim($path, '/'));
|
||||
}
|
||||
return baseUrl('/books/' . $this->slug);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -25,12 +25,16 @@ class Chapter extends Entity
|
||||
|
||||
/**
|
||||
* Get the url of this chapter.
|
||||
* @param string|bool $path
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
public function getUrl($path = false)
|
||||
{
|
||||
$bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
|
||||
return '/books/' . $bookSlug. '/chapter/' . $this->slug;
|
||||
if ($path !== false) {
|
||||
return baseUrl('/books/' . $bookSlug. '/chapter/' . $this->slug . '/' . trim($path, '/'));
|
||||
}
|
||||
return baseUrl('/books/' . $bookSlug. '/chapter/' . $this->slug);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -167,7 +167,8 @@ class Entity extends Ownable
|
||||
foreach ($terms as $key => $term) {
|
||||
$term = htmlentities($term, ENT_QUOTES);
|
||||
$term = preg_replace('/[+\-><\(\)~*\"@]+/', ' ', $term);
|
||||
if (preg_match('/\s/', $term)) {
|
||||
if (preg_match('/".*?"/', $term)) {
|
||||
$term = str_replace('"', '', $term);
|
||||
$exactTerms[] = '%' . $term . '%';
|
||||
$term = '"' . $term . '"';
|
||||
} else {
|
||||
@@ -206,5 +207,5 @@ class Entity extends Ownable
|
||||
|
||||
return $search->orderBy($orderBy, 'desc');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -47,19 +47,44 @@ class Handler extends ExceptionHandler
|
||||
{
|
||||
// Handle notify exceptions which will redirect to the
|
||||
// specified location then show a notification message.
|
||||
if ($e instanceof NotifyException) {
|
||||
\Session::flash('error', $e->message);
|
||||
return response()->redirectTo($e->redirectLocation);
|
||||
if ($this->isExceptionType($e, NotifyException::class)) {
|
||||
session()->flash('error', $this->getOriginalMessage($e));
|
||||
return redirect($e->redirectLocation);
|
||||
}
|
||||
|
||||
// Handle pretty exceptions which will show a friendly application-fitting page
|
||||
// Which will include the basic message to point the user roughly to the cause.
|
||||
if (($e instanceof PrettyException || $e->getPrevious() instanceof PrettyException) && !config('app.debug')) {
|
||||
$message = ($e instanceof PrettyException) ? $e->getMessage() : $e->getPrevious()->getMessage();
|
||||
if ($this->isExceptionType($e, PrettyException::class) && !config('app.debug')) {
|
||||
$message = $this->getOriginalMessage($e);
|
||||
$code = ($e->getCode() === 0) ? 500 : $e->getCode();
|
||||
return response()->view('errors/' . $code, ['message' => $message], $code);
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the exception chain to compare against the original exception type.
|
||||
* @param Exception $e
|
||||
* @param $type
|
||||
* @return bool
|
||||
*/
|
||||
protected function isExceptionType(Exception $e, $type) {
|
||||
do {
|
||||
if (is_a($e, $type)) return true;
|
||||
} while ($e = $e->getPrevious());
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original exception message.
|
||||
* @param Exception $e
|
||||
* @return string
|
||||
*/
|
||||
protected function getOriginalMessage(Exception $e) {
|
||||
do {
|
||||
$message = $e->getMessage();
|
||||
} while ($e = $e->getPrevious());
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<?php namespace BookStack\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PrettyException extends Exception {}
|
||||
class PrettyException extends \Exception {}
|
||||
@@ -1,9 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Controllers\Auth;
|
||||
<?php namespace BookStack\Http\Controllers\Auth;
|
||||
|
||||
use BookStack\Exceptions\AuthException;
|
||||
use BookStack\Exceptions\PrettyException;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Http\Request;
|
||||
use BookStack\Exceptions\SocialSignInException;
|
||||
@@ -36,7 +33,6 @@ class AuthController extends Controller
|
||||
protected $redirectAfterLogout = '/login';
|
||||
protected $username = 'email';
|
||||
|
||||
|
||||
protected $socialAuthService;
|
||||
protected $emailConfirmationService;
|
||||
protected $userRepo;
|
||||
@@ -53,6 +49,8 @@ class AuthController extends Controller
|
||||
$this->socialAuthService = $socialAuthService;
|
||||
$this->emailConfirmationService = $emailConfirmationService;
|
||||
$this->userRepo = $userRepo;
|
||||
$this->redirectPath = baseUrl('/');
|
||||
$this->redirectAfterLogout = baseUrl('/login');
|
||||
$this->username = config('auth.method') === 'standard' ? 'email' : 'username';
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -147,7 +145,9 @@ class AuthController extends Controller
|
||||
auth()->login($user);
|
||||
}
|
||||
|
||||
return redirect()->intended($this->redirectPath());
|
||||
$path = session()->pull('url.intended', '/');
|
||||
$path = baseUrl($path, true);
|
||||
return redirect($path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
use Activity;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Repos\BookRepo;
|
||||
use BookStack\Repos\ChapterRepo;
|
||||
@@ -180,21 +179,31 @@ class BookController extends Controller
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
$sortedBooks = [];
|
||||
// Sort pages and chapters
|
||||
$sortedBooks = [];
|
||||
$updatedModels = collect();
|
||||
$sortMap = json_decode($request->get('sort-tree'));
|
||||
$defaultBookId = $book->id;
|
||||
foreach ($sortMap as $index => $bookChild) {
|
||||
$id = $bookChild->id;
|
||||
|
||||
// Loop through contents of provided map and update entities accordingly
|
||||
foreach ($sortMap as $bookChild) {
|
||||
$priority = $bookChild->sort;
|
||||
$id = intval($bookChild->id);
|
||||
$isPage = $bookChild->type == 'page';
|
||||
$bookId = $this->bookRepo->exists($bookChild->book) ? $bookChild->book : $defaultBookId;
|
||||
$bookId = $this->bookRepo->exists($bookChild->book) ? intval($bookChild->book) : $defaultBookId;
|
||||
$chapterId = ($isPage && $bookChild->parentChapter === false) ? 0 : intval($bookChild->parentChapter);
|
||||
$model = $isPage ? $this->pageRepo->getById($id) : $this->chapterRepo->getById($id);
|
||||
$isPage ? $this->pageRepo->changeBook($bookId, $model) : $this->chapterRepo->changeBook($bookId, $model);
|
||||
$model->priority = $index;
|
||||
if ($isPage) {
|
||||
$model->chapter_id = ($bookChild->parentChapter === false) ? 0 : $bookChild->parentChapter;
|
||||
|
||||
// Update models only if there's a change in parent chain or ordering.
|
||||
if ($model->priority !== $priority || $model->book_id !== $bookId || ($isPage && $model->chapter_id !== $chapterId)) {
|
||||
$isPage ? $this->pageRepo->changeBook($bookId, $model) : $this->chapterRepo->changeBook($bookId, $model);
|
||||
$model->priority = $priority;
|
||||
if ($isPage) $model->chapter_id = $chapterId;
|
||||
$model->save();
|
||||
$updatedModels->push($model);
|
||||
}
|
||||
$model->save();
|
||||
|
||||
// Store involved books to be sorted later
|
||||
if (!in_array($bookId, $sortedBooks)) {
|
||||
$sortedBooks[] = $bookId;
|
||||
}
|
||||
@@ -203,10 +212,12 @@ class BookController extends Controller
|
||||
// Add activity for books
|
||||
foreach ($sortedBooks as $bookId) {
|
||||
$updatedBook = $this->bookRepo->getById($bookId);
|
||||
$this->bookRepo->updateBookPermissions($updatedBook);
|
||||
Activity::add($updatedBook, 'book_sort', $updatedBook->id);
|
||||
}
|
||||
|
||||
// Update permissions on changed models
|
||||
$this->bookRepo->buildJointPermissions($updatedModels);
|
||||
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ class ChapterController extends Controller
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
$this->chapterRepo->changeBook($parent->id, $chapter);
|
||||
$this->chapterRepo->changeBook($parent->id, $chapter, true);
|
||||
Activity::add($chapter, 'chapter_move', $chapter->book->id);
|
||||
session()->flash('success', sprintf('Chapter moved to "%s"', $parent->name));
|
||||
|
||||
|
||||
@@ -412,7 +412,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRecentlyCreated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
|
||||
$pages = $this->pageRepo->getRecentlyCreatedPaginated(20)->setPath(baseUrl('/pages/recently-created'));
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Created Pages',
|
||||
'pages' => $pages
|
||||
@@ -425,7 +425,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRecentlyUpdated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
|
||||
$pages = $this->pageRepo->getRecentlyUpdatedPaginated(20)->setPath(baseUrl('/pages/recently-updated'));
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Updated Pages',
|
||||
'pages' => $pages
|
||||
|
||||
@@ -33,14 +33,14 @@ class Authenticate
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) {
|
||||
return redirect()->guest('/register/confirm/awaiting');
|
||||
return redirect()->guest(baseUrl('/register/confirm/awaiting'));
|
||||
}
|
||||
|
||||
if ($this->auth->guest() && !setting('app-public')) {
|
||||
if ($request->ajax()) {
|
||||
return response('Unauthorized.', 401);
|
||||
} else {
|
||||
return redirect()->guest('/login');
|
||||
return redirect()->guest(baseUrl('/login'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
app/Page.php
10
app/Page.php
@@ -56,14 +56,20 @@ class Page extends Entity
|
||||
|
||||
/**
|
||||
* Get the url for this page.
|
||||
* @param string|bool $path
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
public function getUrl($path = false)
|
||||
{
|
||||
$bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
|
||||
$midText = $this->draft ? '/draft/' : '/page/';
|
||||
$idComponent = $this->draft ? $this->id : $this->slug;
|
||||
return '/books/' . $bookSlug . $midText . $idComponent;
|
||||
|
||||
if ($path !== false) {
|
||||
return baseUrl('/books/' . $bookSlug . $midText . $idComponent . '/' . trim($path, '/'));
|
||||
}
|
||||
|
||||
return baseUrl('/books/' . $bookSlug . $midText . $idComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
class PageRevision extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'html', 'text', 'markdown'];
|
||||
protected $fillable = ['name', 'html', 'text', 'markdown', 'summary'];
|
||||
|
||||
/**
|
||||
* Get the user that created the page revision
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<?php
|
||||
<?php namespace BookStack\Providers;
|
||||
|
||||
namespace BookStack\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use BookStack\User;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
30
app/Providers/PaginationServiceProvider.php
Normal file
30
app/Providers/PaginationServiceProvider.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php namespace BookStack\Providers;
|
||||
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
|
||||
class PaginationServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
Paginator::currentPathResolver(function () {
|
||||
return baseUrl($this->app['request']->path());
|
||||
});
|
||||
|
||||
Paginator::currentPageResolver(function ($pageName = 'page') {
|
||||
$page = $this->app['request']->input($pageName);
|
||||
|
||||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
|
||||
return $page;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use Alpha\B;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use BookStack\Book;
|
||||
use Views;
|
||||
@@ -173,15 +174,6 @@ class BookRepo extends EntityRepo
|
||||
$book->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias method to update the book jointPermissions in the PermissionService.
|
||||
* @param Book $book
|
||||
*/
|
||||
public function updateBookPermissions(Book $book)
|
||||
{
|
||||
$this->permissionService->buildJointPermissionsForEntity($book);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next child element priority.
|
||||
* @param Book $book
|
||||
@@ -216,12 +208,10 @@ class BookRepo extends EntityRepo
|
||||
*/
|
||||
public function findSuitableSlug($name, $currentId = false)
|
||||
{
|
||||
$originalSlug = Str::slug($name);
|
||||
$slug = $originalSlug;
|
||||
$count = 2;
|
||||
$slug = Str::slug($name);
|
||||
if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
|
||||
while ($this->doesSlugExist($slug, $currentId)) {
|
||||
$slug = $originalSlug . '-' . $count;
|
||||
$count++;
|
||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
||||
}
|
||||
return $slug;
|
||||
}
|
||||
@@ -229,7 +219,7 @@ class BookRepo extends EntityRepo
|
||||
/**
|
||||
* Get all child objects of a book.
|
||||
* Returns a sorted collection of Pages and Chapters.
|
||||
* Loads the bookslug onto child elements to prevent access database access for getting the slug.
|
||||
* Loads the book slug onto child elements to prevent access database access for getting the slug.
|
||||
* @param Book $book
|
||||
* @param bool $filterDrafts
|
||||
* @return mixed
|
||||
@@ -245,7 +235,7 @@ class BookRepo extends EntityRepo
|
||||
|
||||
$pages = $pageQuery->get();
|
||||
|
||||
$chapterQuery = $book->chapters()->with(['pages' => function($query) use ($filterDrafts) {
|
||||
$chapterQuery = $book->chapters()->with(['pages' => function ($query) use ($filterDrafts) {
|
||||
$this->permissionService->enforcePageRestrictions($query, 'view');
|
||||
if ($filterDrafts) $query->where('draft', '=', false);
|
||||
}]);
|
||||
@@ -263,7 +253,7 @@ class BookRepo extends EntityRepo
|
||||
$child->pages->each(function ($page) use ($bookSlug) {
|
||||
$page->setAttribute('bookSlug', $bookSlug);
|
||||
});
|
||||
$child->pages = $child->pages->sortBy(function($child, $key) {
|
||||
$child->pages = $child->pages->sortBy(function ($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->draft) $score -= 100;
|
||||
return $score;
|
||||
@@ -272,7 +262,7 @@ class BookRepo extends EntityRepo
|
||||
});
|
||||
|
||||
// Sort items with drafts first then by priority.
|
||||
return $children->sortBy(function($child, $key) {
|
||||
return $children->sortBy(function ($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->isA('page') && $child->draft) $score -= 100;
|
||||
return $score;
|
||||
|
||||
@@ -81,7 +81,7 @@ class ChapterRepo extends EntityRepo
|
||||
{
|
||||
$pages = $this->permissionService->enforcePageRestrictions($chapter->pages())->get();
|
||||
// Sort items with drafts first then by priority.
|
||||
return $pages->sortBy(function($child, $key) {
|
||||
return $pages->sortBy(function ($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->draft) $score -= 100;
|
||||
return $score;
|
||||
@@ -151,6 +151,7 @@ class ChapterRepo extends EntityRepo
|
||||
public function findSuitableSlug($name, $bookId, $currentId = false)
|
||||
{
|
||||
$slug = Str::slug($name);
|
||||
if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
|
||||
while ($this->doesSlugExist($slug, $bookId, $currentId)) {
|
||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
||||
}
|
||||
@@ -194,11 +195,12 @@ class ChapterRepo extends EntityRepo
|
||||
|
||||
/**
|
||||
* Changes the book relation of this chapter.
|
||||
* @param $bookId
|
||||
* @param $bookId
|
||||
* @param Chapter $chapter
|
||||
* @param bool $rebuildPermissions
|
||||
* @return Chapter
|
||||
*/
|
||||
public function changeBook($bookId, Chapter $chapter)
|
||||
public function changeBook($bookId, Chapter $chapter, $rebuildPermissions = false)
|
||||
{
|
||||
$chapter->book_id = $bookId;
|
||||
// Update related activity
|
||||
@@ -212,9 +214,12 @@ class ChapterRepo extends EntityRepo
|
||||
foreach ($chapter->pages as $page) {
|
||||
$this->pageRepo->changeBook($bookId, $page);
|
||||
}
|
||||
// Update permissions
|
||||
$chapter->load('book');
|
||||
$this->permissionService->buildJointPermissionsForEntity($chapter->book);
|
||||
|
||||
// Update permissions if applicable
|
||||
if ($rebuildPermissions) {
|
||||
$chapter->load('book');
|
||||
$this->permissionService->buildJointPermissionsForEntity($chapter->book);
|
||||
}
|
||||
|
||||
return $chapter;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use BookStack\Entity;
|
||||
use BookStack\Page;
|
||||
use BookStack\Services\PermissionService;
|
||||
use BookStack\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class EntityRepo
|
||||
@@ -168,15 +169,16 @@ class EntityRepo
|
||||
* @param $termString
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareSearchTerms($termString)
|
||||
public function prepareSearchTerms($termString)
|
||||
{
|
||||
$termString = $this->cleanSearchTermString($termString);
|
||||
preg_match_all('/"(.*?)"/', $termString, $matches);
|
||||
preg_match_all('/(".*?")/', $termString, $matches);
|
||||
$terms = [];
|
||||
if (count($matches[1]) > 0) {
|
||||
$terms = $matches[1];
|
||||
foreach ($matches[1] as $match) {
|
||||
$terms[] = $match;
|
||||
}
|
||||
$termString = trim(preg_replace('/"(.*?)"/', '', $termString));
|
||||
} else {
|
||||
$terms = [];
|
||||
}
|
||||
if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString));
|
||||
return $terms;
|
||||
@@ -259,6 +261,15 @@ class EntityRepo
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias method to update the book jointPermissions in the PermissionService.
|
||||
* @param Collection $collection collection on entities
|
||||
*/
|
||||
public function buildJointPermissions(Collection $collection)
|
||||
{
|
||||
$this->permissionService->buildJointPermissionsForEntities($collection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class ImageRepo
|
||||
|
||||
protected $image;
|
||||
protected $imageService;
|
||||
protected $restictionService;
|
||||
protected $restrictionService;
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ class ImageRepo
|
||||
{
|
||||
$this->image = $image;
|
||||
$this->imageService = $imageService;
|
||||
$this->restictionService = $permissionService;
|
||||
$this->restrictionService = $permissionService;
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class ImageRepo
|
||||
*/
|
||||
private function returnPaginated($query, $page = 0, $pageSize = 24)
|
||||
{
|
||||
$images = $this->restictionService->filterRelatedPages($query, 'images', 'uploaded_to');
|
||||
$images = $this->restrictionService->filterRelatedPages($query, 'images', 'uploaded_to');
|
||||
$images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get();
|
||||
$hasMore = count($images) > $pageSize;
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ class PageRepo extends EntityRepo
|
||||
$draftPage->fill($input);
|
||||
|
||||
// Save page tags if present
|
||||
if(isset($input['tags'])) {
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
|
||||
}
|
||||
|
||||
@@ -157,6 +157,8 @@ class PageRepo extends EntityRepo
|
||||
$draftPage->draft = false;
|
||||
|
||||
$draftPage->save();
|
||||
$this->saveRevision($draftPage, 'Initial Publish');
|
||||
|
||||
return $draftPage;
|
||||
}
|
||||
|
||||
@@ -308,10 +310,9 @@ class PageRepo extends EntityRepo
|
||||
*/
|
||||
public function updatePage(Page $page, $book_id, $input)
|
||||
{
|
||||
// Save a revision before updating
|
||||
if ($page->html !== $input['html'] || $page->name !== $input['name']) {
|
||||
$this->saveRevision($page);
|
||||
}
|
||||
// Hold the old details to compare later
|
||||
$oldHtml = $page->html;
|
||||
$oldName = $page->name;
|
||||
|
||||
// Prevent slug being updated if no name change
|
||||
if ($page->name !== $input['name']) {
|
||||
@@ -319,7 +320,7 @@ class PageRepo extends EntityRepo
|
||||
}
|
||||
|
||||
// Save page tags if present
|
||||
if(isset($input['tags'])) {
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($page, $input['tags']);
|
||||
}
|
||||
|
||||
@@ -335,6 +336,11 @@ class PageRepo extends EntityRepo
|
||||
// Remove all update drafts for this user & page.
|
||||
$this->userUpdateDraftsQuery($page, $userId)->delete();
|
||||
|
||||
// Save a revision after updating
|
||||
if ($oldHtml !== $input['html'] || $oldName !== $input['name'] || $input['summary'] !== null) {
|
||||
$this->saveRevision($page, $input['summary']);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
@@ -360,9 +366,10 @@ class PageRepo extends EntityRepo
|
||||
/**
|
||||
* Saves a page revision into the system.
|
||||
* @param Page $page
|
||||
* @param null|string $summary
|
||||
* @return $this
|
||||
*/
|
||||
public function saveRevision(Page $page)
|
||||
public function saveRevision(Page $page, $summary = null)
|
||||
{
|
||||
$revision = $this->pageRevision->fill($page->toArray());
|
||||
if (setting('app-editor') !== 'markdown') $revision->markdown = '';
|
||||
@@ -372,6 +379,7 @@ class PageRepo extends EntityRepo
|
||||
$revision->created_by = auth()->user()->id;
|
||||
$revision->created_at = $page->updated_at;
|
||||
$revision->type = 'version';
|
||||
$revision->summary = $summary;
|
||||
$revision->save();
|
||||
// Clear old revisions
|
||||
if ($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) {
|
||||
@@ -405,7 +413,7 @@ class PageRepo extends EntityRepo
|
||||
|
||||
$draft->fill($data);
|
||||
if (setting('app-editor') !== 'markdown') $draft->markdown = '';
|
||||
|
||||
|
||||
$draft->save();
|
||||
return $draft;
|
||||
}
|
||||
@@ -591,14 +599,15 @@ class PageRepo extends EntityRepo
|
||||
|
||||
/**
|
||||
* Gets a suitable slug for the resource
|
||||
* @param $name
|
||||
* @param $bookId
|
||||
* @param string $name
|
||||
* @param int $bookId
|
||||
* @param bool|false $currentId
|
||||
* @return string
|
||||
*/
|
||||
public function findSuitableSlug($name, $bookId, $currentId = false)
|
||||
{
|
||||
$slug = Str::slug($name);
|
||||
if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
|
||||
while ($this->doesSlugExist($slug, $bookId, $currentId)) {
|
||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
||||
}
|
||||
|
||||
@@ -48,11 +48,13 @@ class ExportService
|
||||
foreach ($imageTagsOutput[0] as $index => $imgMatch) {
|
||||
$oldImgString = $imgMatch;
|
||||
$srcString = $imageTagsOutput[2][$index];
|
||||
if (strpos(trim($srcString), 'http') !== 0) {
|
||||
$pathString = public_path($srcString);
|
||||
$isLocal = strpos(trim($srcString), 'http') !== 0;
|
||||
if ($isLocal) {
|
||||
$pathString = public_path(trim($srcString, '/'));
|
||||
} else {
|
||||
$pathString = $srcString;
|
||||
}
|
||||
if ($isLocal && !file_exists($pathString)) continue;
|
||||
$imageContent = file_get_contents($pathString);
|
||||
$imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent);
|
||||
$newImageString = str_replace($srcString, $imageEncoded, $oldImgString);
|
||||
|
||||
@@ -95,6 +95,7 @@ class ImageService
|
||||
|
||||
try {
|
||||
$storage->put($fullPath, $imageData);
|
||||
$storage->setVisibility($fullPath, 'public');
|
||||
} catch (Exception $e) {
|
||||
throw new ImageUploadException('Image Path ' . $fullPath . ' is not writable by the server.');
|
||||
}
|
||||
@@ -167,6 +168,7 @@ class ImageService
|
||||
|
||||
$thumbData = (string)$thumb->encode();
|
||||
$storage->put($thumbFilePath, $thumbData);
|
||||
$storage->setVisibility($thumbFilePath, 'public');
|
||||
$this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 72);
|
||||
|
||||
return $this->getPublicUrl($thumbFilePath);
|
||||
@@ -257,16 +259,22 @@ class ImageService
|
||||
$storageUrl = config('filesystems.url');
|
||||
|
||||
// Get the standard public s3 url if s3 is set as storage type
|
||||
// Uses the nice, short URL if bucket name has no periods in otherwise the longer
|
||||
// region-based url will be used to prevent http issues.
|
||||
if ($storageUrl == false && config('filesystems.default') === 's3') {
|
||||
$storageDetails = config('filesystems.disks.s3');
|
||||
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
|
||||
if (strpos($storageDetails['bucket'], '.') === false) {
|
||||
$storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com';
|
||||
} else {
|
||||
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->storageUrl = $storageUrl;
|
||||
}
|
||||
|
||||
return ($this->storageUrl == false ? '' : rtrim($this->storageUrl, '/')) . $filePath;
|
||||
return ($this->storageUrl == false ? rtrim(baseUrl(''), '/') : rtrim($this->storageUrl, '/')) . $filePath;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use BookStack\Ownable;
|
||||
use BookStack\Page;
|
||||
use BookStack\Role;
|
||||
use BookStack\User;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class PermissionService
|
||||
{
|
||||
@@ -25,6 +25,8 @@ class PermissionService
|
||||
protected $jointPermission;
|
||||
protected $role;
|
||||
|
||||
protected $entityCache;
|
||||
|
||||
/**
|
||||
* PermissionService constructor.
|
||||
* @param JointPermission $jointPermission
|
||||
@@ -48,6 +50,57 @@ class PermissionService
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the local entity cache and ensure it's empty
|
||||
*/
|
||||
protected function readyEntityCache()
|
||||
{
|
||||
$this->entityCache = [
|
||||
'books' => collect(),
|
||||
'chapters' => collect()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a book via ID, Checks local cache
|
||||
* @param $bookId
|
||||
* @return Book
|
||||
*/
|
||||
protected function getBook($bookId)
|
||||
{
|
||||
if (isset($this->entityCache['books']) && $this->entityCache['books']->has($bookId)) {
|
||||
return $this->entityCache['books']->get($bookId);
|
||||
}
|
||||
|
||||
$book = $this->book->find($bookId);
|
||||
if ($book === null) $book = false;
|
||||
if (isset($this->entityCache['books'])) {
|
||||
$this->entityCache['books']->put($bookId, $book);
|
||||
}
|
||||
|
||||
return $book;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chapter via ID, Checks local cache
|
||||
* @param $chapterId
|
||||
* @return Book
|
||||
*/
|
||||
protected function getChapter($chapterId)
|
||||
{
|
||||
if (isset($this->entityCache['chapters']) && $this->entityCache['chapters']->has($chapterId)) {
|
||||
return $this->entityCache['chapters']->get($chapterId);
|
||||
}
|
||||
|
||||
$chapter = $this->chapter->find($chapterId);
|
||||
if ($chapter === null) $chapter = false;
|
||||
if (isset($this->entityCache['chapters'])) {
|
||||
$this->entityCache['chapters']->put($chapterId, $chapter);
|
||||
}
|
||||
|
||||
return $chapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the roles for the current user;
|
||||
* @return array|bool
|
||||
@@ -76,6 +129,7 @@ class PermissionService
|
||||
public function buildJointPermissions()
|
||||
{
|
||||
$this->jointPermission->truncate();
|
||||
$this->readyEntityCache();
|
||||
|
||||
// Get all roles (Should be the most limited dimension)
|
||||
$roles = $this->role->with('permissions')->get();
|
||||
@@ -97,7 +151,7 @@ class PermissionService
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the entity jointPermissions for a particular entity.
|
||||
* Rebuild the entity jointPermissions for a particular entity.
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function buildJointPermissionsForEntity(Entity $entity)
|
||||
@@ -116,6 +170,17 @@ class PermissionService
|
||||
$this->createManyJointPermissions($entities, $roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the entity jointPermissions for a collection of entities.
|
||||
* @param Collection $entities
|
||||
*/
|
||||
public function buildJointPermissionsForEntities(Collection $entities)
|
||||
{
|
||||
$roles = $this->role->with('jointPermissions')->get();
|
||||
$this->deleteManyJointPermissionsForEntities($entities);
|
||||
$this->createManyJointPermissions($entities, $roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the entity jointPermissions for a particular role.
|
||||
* @param Role $role
|
||||
@@ -177,9 +242,14 @@ class PermissionService
|
||||
*/
|
||||
protected function deleteManyJointPermissionsForEntities($entities)
|
||||
{
|
||||
$query = $this->jointPermission->newQuery();
|
||||
foreach ($entities as $entity) {
|
||||
$entity->jointPermissions()->delete();
|
||||
$query->orWhere(function($query) use ($entity) {
|
||||
$query->where('entity_id', '=', $entity->id)
|
||||
->where('entity_type', '=', $entity->getMorphClass());
|
||||
});
|
||||
}
|
||||
$query->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,6 +259,7 @@ class PermissionService
|
||||
*/
|
||||
protected function createManyJointPermissions($entities, $roles)
|
||||
{
|
||||
$this->readyEntityCache();
|
||||
$jointPermissions = [];
|
||||
foreach ($entities as $entity) {
|
||||
foreach ($roles as $role) {
|
||||
@@ -248,8 +319,9 @@ class PermissionService
|
||||
} elseif ($entity->isA('chapter')) {
|
||||
|
||||
if (!$entity->restricted) {
|
||||
$hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToBook = !$entity->book->restricted;
|
||||
$book = $this->getBook($entity->book_id);
|
||||
$hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToBook = !$book->restricted;
|
||||
return $this->createJointPermissionDataArray($entity, $role, $action,
|
||||
($hasExplicitAccessToBook || ($roleHasPermission && $hasPermissiveAccessToBook)),
|
||||
($hasExplicitAccessToBook || ($roleHasPermissionOwn && $hasPermissiveAccessToBook)));
|
||||
@@ -261,11 +333,14 @@ class PermissionService
|
||||
} elseif ($entity->isA('page')) {
|
||||
|
||||
if (!$entity->restricted) {
|
||||
$hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToBook = !$entity->book->restricted;
|
||||
$hasExplicitAccessToChapter = $entity->chapter && $entity->chapter->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToChapter = $entity->chapter && !$entity->chapter->restricted;
|
||||
$acknowledgeChapter = ($entity->chapter && $entity->chapter->restricted);
|
||||
$book = $this->getBook($entity->book_id);
|
||||
$hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToBook = !$book->restricted;
|
||||
|
||||
$chapter = $this->getChapter($entity->chapter_id);
|
||||
$hasExplicitAccessToChapter = $chapter && $chapter->hasActiveRestriction($role->id, $restrictionAction);
|
||||
$hasPermissiveAccessToChapter = $chapter && !$chapter->restricted;
|
||||
$acknowledgeChapter = ($chapter && $chapter->restricted);
|
||||
|
||||
$hasExplicitAccessToParents = $acknowledgeChapter ? $hasExplicitAccessToChapter : $hasExplicitAccessToBook;
|
||||
$hasPermissiveAccessToParents = $acknowledgeChapter ? $hasPermissiveAccessToChapter : $hasPermissiveAccessToBook;
|
||||
|
||||
@@ -113,20 +113,20 @@ class SocialAuthService
|
||||
if ($isLoggedIn && $socialAccount === null) {
|
||||
$this->fillSocialAccount($socialDriver, $socialUser);
|
||||
$currentUser->socialAccounts()->save($this->socialAccount);
|
||||
\Session::flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.');
|
||||
session()->flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.');
|
||||
return redirect($currentUser->getEditUrl());
|
||||
}
|
||||
|
||||
// When a user is logged in and the social account exists and is already linked to the current user.
|
||||
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) {
|
||||
\Session::flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.');
|
||||
session()->flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.');
|
||||
return redirect($currentUser->getEditUrl());
|
||||
}
|
||||
|
||||
// When a user is logged in, A social account exists but the users do not match.
|
||||
// Change the user that the social account is assigned to.
|
||||
if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) {
|
||||
\Session::flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.');
|
||||
session()->flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.');
|
||||
return redirect($currentUser->getEditUrl());
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ class SocialAuthService
|
||||
if (setting('registration-enabled')) {
|
||||
$message .= ' or, If you do not yet have an account, You can register an account using the ' . $socialDriver . ' option';
|
||||
}
|
||||
|
||||
throw new SocialSignInException($message . '.', '/login');
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ class SocialAuthService
|
||||
$driver = trim(strtolower($socialDriver));
|
||||
|
||||
if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
|
||||
if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured;
|
||||
if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured("Your {$driver} social settings are not configured correctly.");
|
||||
|
||||
return $driver;
|
||||
}
|
||||
@@ -214,7 +215,7 @@ class SocialAuthService
|
||||
{
|
||||
session();
|
||||
auth()->user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
|
||||
\Session::flash('success', $socialDriver . ' account successfully detached');
|
||||
session()->flash('success', title_case($socialDriver) . ' account successfully detached');
|
||||
return redirect(auth()->user()->getEditUrl());
|
||||
}
|
||||
|
||||
|
||||
15
app/User.php
15
app/User.php
@@ -138,8 +138,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function getAvatar($size = 50)
|
||||
{
|
||||
if ($this->image_id === 0 || $this->image_id === '0' || $this->image_id === null) return '/user_avatar.png';
|
||||
return $this->avatar->getThumb($size, $size, false);
|
||||
if ($this->image_id === 0 || $this->image_id === '0' || $this->image_id === null) return baseUrl('/user_avatar.png');
|
||||
return baseUrl($this->avatar->getThumb($size, $size, false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function getEditUrl()
|
||||
{
|
||||
return '/settings/users/' . $this->id;
|
||||
return baseUrl('/settings/users/' . $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url that links to this user's profile.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getProfileUrl()
|
||||
{
|
||||
return baseUrl('/user/' . $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,33 +2,38 @@
|
||||
|
||||
use BookStack\Ownable;
|
||||
|
||||
if (!function_exists('versioned_asset')) {
|
||||
/**
|
||||
* Get the path to a versioned file.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
function versioned_asset($file)
|
||||
{
|
||||
static $manifest = null;
|
||||
/**
|
||||
* Get the path to a versioned file.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
function versioned_asset($file = '')
|
||||
{
|
||||
// Don't require css and JS assets for testing
|
||||
if (config('app.env') === 'testing') return '';
|
||||
|
||||
if (is_null($manifest)) {
|
||||
$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
|
||||
static $manifest = null;
|
||||
$manifestPath = 'build/manifest.json';
|
||||
|
||||
if (is_null($manifest) && file_exists($manifestPath)) {
|
||||
$manifest = json_decode(file_get_contents(public_path($manifestPath)), true);
|
||||
} else if (!file_exists($manifestPath)) {
|
||||
if (config('app.env') !== 'production') {
|
||||
$path = public_path($manifestPath);
|
||||
$error = "No {$path} file found, Ensure you have built the css/js assets using gulp.";
|
||||
} else {
|
||||
$error = "No {$manifestPath} file found, Ensure you are using the release version of BookStack";
|
||||
}
|
||||
|
||||
if (isset($manifest[$file])) {
|
||||
return '/' . $manifest[$file];
|
||||
}
|
||||
|
||||
if (file_exists(public_path($file))) {
|
||||
return '/' . $file;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
if (isset($manifest[$file])) {
|
||||
return baseUrl($manifest[$file]);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,6 +67,48 @@ function setting($key, $default = false)
|
||||
return $settingService->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create url's relative to the applications root path.
|
||||
* @param string $path
|
||||
* @param bool $forceAppDomain
|
||||
* @return string
|
||||
*/
|
||||
function baseUrl($path, $forceAppDomain = false)
|
||||
{
|
||||
$isFullUrl = strpos($path, 'http') === 0;
|
||||
if ($isFullUrl && !$forceAppDomain) return $path;
|
||||
$path = trim($path, '/');
|
||||
|
||||
if ($isFullUrl && $forceAppDomain) {
|
||||
$explodedPath = explode('/', $path);
|
||||
$path = implode('/', array_splice($explodedPath, 3));
|
||||
}
|
||||
|
||||
return rtrim(config('app.url'), '/') . '/' . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the redirector.
|
||||
* Overrides the default laravel redirect helper.
|
||||
* Ensures it redirects even when the app is in a subdirectory.
|
||||
*
|
||||
* @param string|null $to
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @param bool $secure
|
||||
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
function redirect($to = null, $status = 302, $headers = [], $secure = null)
|
||||
{
|
||||
if (is_null($to)) {
|
||||
return app('redirect');
|
||||
}
|
||||
|
||||
$to = baseUrl($to);
|
||||
|
||||
return app('redirect')->to($to, $status, $headers, $secure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a url with multiple parameters for sorting purposes.
|
||||
* Works out the logic to set the correct sorting direction
|
||||
@@ -91,5 +138,5 @@ function sortUrl($path, $data, $overrideData = [])
|
||||
|
||||
if (count($queryStringSections) === 0) return $path;
|
||||
|
||||
return $path . '?' . implode('&', $queryStringSections);
|
||||
return baseUrl($path . '?' . implode('&', $queryStringSections));
|
||||
}
|
||||
@@ -14,6 +14,7 @@ define('LARAVEL_START', microtime(true));
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/../app/helpers.php';
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
],
|
||||
"psr-4": {
|
||||
"BookStack\\": "app/"
|
||||
},
|
||||
"files": [
|
||||
"app/helpers.php"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
|
||||
@@ -31,7 +31,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
'url' => env('APP_URL', '') === 'http://bookstack.dev' ? '' : env('APP_URL', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -130,7 +130,6 @@ return [
|
||||
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||
Illuminate\Hashing\HashServiceProvider::class,
|
||||
Illuminate\Mail\MailServiceProvider::class,
|
||||
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||
Illuminate\Queue\QueueServiceProvider::class,
|
||||
Illuminate\Redis\RedisServiceProvider::class,
|
||||
@@ -153,6 +152,8 @@ return [
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
BookStack\Providers\PaginationServiceProvider::class,
|
||||
|
||||
BookStack\Providers\AuthServiceProvider::class,
|
||||
BookStack\Providers\AppServiceProvider::class,
|
||||
BookStack\Providers\EventServiceProvider::class,
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
*/
|
||||
return [
|
||||
|
||||
'app-editor' => 'wysiwyg',
|
||||
'app-color' => '#0288D1',
|
||||
'app-name' => 'BookStack',
|
||||
'app-editor' => 'wysiwyg',
|
||||
'app-color' => '#0288D1',
|
||||
'app-color-light' => 'rgba(21, 101, 192, 0.15)'
|
||||
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSummaryToPageRevisions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('page_revisions', function ($table) {
|
||||
$table->string('summary')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('page_revisions', function ($table) {
|
||||
$table->dropColumn('summary');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
"babel-runtime": "^5.8.29",
|
||||
"bootstrap-sass": "^3.0.0",
|
||||
"dropzone": "^4.0.1",
|
||||
"laravel-elixir": "^3.4.0",
|
||||
"laravel-elixir": "^5.0.0",
|
||||
"marked": "^0.3.5",
|
||||
"moment": "^2.12.0",
|
||||
"zeroclipboard": "^2.2.0"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<env name="DB_CONNECTION" value="mysql_testing"/>
|
||||
<env name="MAIL_DRIVER" value="log"/>
|
||||
<env name="AUTH_METHOD" value="standard"/>
|
||||
<env name="DISABLE_EXTERNAL_SERVICES" value="false"/>
|
||||
<env name="DISABLE_EXTERNAL_SERVICES" value="true"/>
|
||||
<env name="LDAP_VERSION" value="3"/>
|
||||
<env name="GITHUB_APP_ID" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GITHUB_APP_SECRET" value="aaaaaaaaaaaaaa"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"css/styles.css": "css/styles.css?version=02c6599",
|
||||
"css/print-styles.css": "css/print-styles.css?version=02c6599",
|
||||
"js/common.js": "js/common.js?version=02c6599"
|
||||
"css/styles.css": "css/styles.css?version=ba2edb5",
|
||||
"css/print-styles.css": "css/print-styles.css?version=ba2edb5",
|
||||
"js/common.js": "js/common.js?version=ba2edb5"
|
||||
}
|
||||
2
public/css/export-styles.css
vendored
2
public/css/export-styles.css
vendored
File diff suppressed because one or more lines are too long
2
public/css/print-styles.css
vendored
2
public/css/print-styles.css
vendored
@@ -1 +1 @@
|
||||
.faded-small,.print-hidden,header{display:none}body{font-size:12px}.page-content{margin:0 auto}.print-full-width{width:100%;float:none;display:block}h2{font-size:2em;line-height:1;margin-top:.6em;margin-bottom:.3em}
|
||||
header{display:none}body{font-size:12px}.faded-small{display:none}.page-content{margin:0 auto}.print-hidden{display:none}.print-full-width{width:100%;float:none;display:block}h2{font-size:2em;line-height:1;margin-top:.6em;margin-bottom:.3em}
|
||||
2
public/css/styles.css
vendored
2
public/css/styles.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -50,3 +50,4 @@ These are the great projects used to help build BookStack:
|
||||
* [ZeroClipboard](http://zeroclipboard.org/)
|
||||
* [TinyColorPicker](http://www.dematte.at/tinyColorPicker/index.html)
|
||||
* [Marked](https://github.com/chjj/marked)
|
||||
* [Moment.js](http://momentjs.com/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
var moment = require('moment');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = function (ngApp, events) {
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = function (ngApp, events) {
|
||||
* @returns {string}
|
||||
*/
|
||||
$scope.getUploadUrl = function () {
|
||||
return '/images/' + $scope.imageType + '/upload';
|
||||
return window.baseUrl('/images/' + $scope.imageType + '/upload');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ module.exports = function (ngApp, events) {
|
||||
*/
|
||||
function callbackAndHide(returnData) {
|
||||
if (callback) callback(returnData);
|
||||
$scope.showing = false;
|
||||
$scope.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +109,7 @@ module.exports = function (ngApp, events) {
|
||||
function show(doneCallback) {
|
||||
callback = doneCallback;
|
||||
$scope.showing = true;
|
||||
$('#image-manager').find('.overlay').css('display', 'flex').hide().fadeIn(240);
|
||||
// Get initial images if they have not yet been loaded in.
|
||||
if (!dataLoaded) {
|
||||
fetchData();
|
||||
@@ -131,9 +132,10 @@ module.exports = function (ngApp, events) {
|
||||
*/
|
||||
$scope.hide = function () {
|
||||
$scope.showing = false;
|
||||
$('#image-manager').find('.overlay').fadeOut(240);
|
||||
};
|
||||
|
||||
var baseUrl = '/images/' + $scope.imageType + '/all/'
|
||||
var baseUrl = window.baseUrl('/images/' + $scope.imageType + '/all/');
|
||||
|
||||
/**
|
||||
* Fetch the list image data from the server.
|
||||
@@ -178,7 +180,7 @@ module.exports = function (ngApp, events) {
|
||||
$scope.images = [];
|
||||
$scope.hasMore = false;
|
||||
page = 0;
|
||||
baseUrl = '/images/' + $scope.imageType + '/search/';
|
||||
baseUrl = window.baseUrl('/images/' + $scope.imageType + '/search/');
|
||||
fetchData();
|
||||
};
|
||||
|
||||
@@ -192,7 +194,7 @@ module.exports = function (ngApp, events) {
|
||||
$scope.hasMore = false;
|
||||
page = 0;
|
||||
$scope.view = viewName;
|
||||
baseUrl = '/images/' + $scope.imageType + '/' + viewName + '/';
|
||||
baseUrl = window.baseUrl('/images/' + $scope.imageType + '/' + viewName + '/');
|
||||
fetchData();
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ module.exports = function (ngApp, events) {
|
||||
*/
|
||||
$scope.saveImageDetails = function (event) {
|
||||
event.preventDefault();
|
||||
var url = '/images/update/' + $scope.selectedImage.id;
|
||||
var url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
|
||||
$http.put(url, this.selectedImage).then((response) => {
|
||||
events.emit('success', 'Image details updated');
|
||||
}, (response) => {
|
||||
@@ -228,7 +230,7 @@ module.exports = function (ngApp, events) {
|
||||
$scope.deleteImage = function (event) {
|
||||
event.preventDefault();
|
||||
var force = $scope.dependantPages !== false;
|
||||
var url = '/images/' + $scope.selectedImage.id;
|
||||
var url = window.baseUrl('/images/' + $scope.selectedImage.id);
|
||||
if (force) url += '?force=true';
|
||||
$http.delete(url).then((response) => {
|
||||
$scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
|
||||
@@ -267,7 +269,7 @@ module.exports = function (ngApp, events) {
|
||||
if (term.length == 0) return;
|
||||
$scope.searching = true;
|
||||
$scope.searchResults = '';
|
||||
var searchUrl = '/search/book/' + $attrs.bookId;
|
||||
var searchUrl = window.baseUrl('/search/book/' + $attrs.bookId);
|
||||
searchUrl += '?term=' + encodeURIComponent(term);
|
||||
$http.get(searchUrl).then((response) => {
|
||||
$scope.searchResults = $sce.trustAsHtml(response.data);
|
||||
@@ -357,8 +359,6 @@ module.exports = function (ngApp, events) {
|
||||
|
||||
/**
|
||||
* Save a draft update into the system via an AJAX request.
|
||||
* @param title
|
||||
* @param html
|
||||
*/
|
||||
function saveDraft() {
|
||||
var data = {
|
||||
@@ -368,13 +368,22 @@ module.exports = function (ngApp, events) {
|
||||
|
||||
if (isMarkdown) data.markdown = $scope.editContent;
|
||||
|
||||
$http.put('/ajax/page/' + pageId + '/save-draft', data).then((responseData) => {
|
||||
let url = window.baseUrl('/ajax/page/' + pageId + '/save-draft');
|
||||
$http.put(url, data).then((responseData) => {
|
||||
var updateTime = moment.utc(moment.unix(responseData.data.timestamp)).toDate();
|
||||
$scope.draftText = responseData.data.message + moment(updateTime).format('HH:mm');
|
||||
if (!$scope.isNewPageDraft) $scope.isUpdateDraft = true;
|
||||
showDraftSaveNotification();
|
||||
});
|
||||
}
|
||||
|
||||
function showDraftSaveNotification() {
|
||||
$scope.draftUpdated = true;
|
||||
$timeout(() => {
|
||||
$scope.draftUpdated = false;
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
$scope.forceDraftSave = function() {
|
||||
saveDraft();
|
||||
};
|
||||
@@ -393,7 +402,8 @@ module.exports = function (ngApp, events) {
|
||||
* content from the system via an AJAX request.
|
||||
*/
|
||||
$scope.discardDraft = function () {
|
||||
$http.get('/ajax/page/' + pageId).then((responseData) => {
|
||||
let url = window.baseUrl('/ajax/page/' + pageId);
|
||||
$http.get(url).then((responseData) => {
|
||||
if (autoSave) $interval.cancel(autoSave);
|
||||
$scope.draftText = 'Editing Page';
|
||||
$scope.isUpdateDraft = false;
|
||||
@@ -437,7 +447,8 @@ module.exports = function (ngApp, events) {
|
||||
* Get all tags for the current book and add into scope.
|
||||
*/
|
||||
function getTags() {
|
||||
$http.get('/ajax/tags/get/page/' + pageId).then((responseData) => {
|
||||
let url = window.baseUrl('/ajax/tags/get/page/' + pageId);
|
||||
$http.get(url).then((responseData) => {
|
||||
$scope.tags = responseData.data;
|
||||
addEmptyTag();
|
||||
});
|
||||
@@ -486,7 +497,8 @@ module.exports = function (ngApp, events) {
|
||||
$scope.saveTags = function() {
|
||||
setTagOrder();
|
||||
let postData = {tags: $scope.tags};
|
||||
$http.post('/ajax/tags/update/page/' + pageId, postData).then((responseData) => {
|
||||
let url = window.baseUrl('/ajax/tags/update/page/' + pageId);
|
||||
$http.post(url, postData).then((responseData) => {
|
||||
$scope.tags = responseData.data.tags;
|
||||
addEmptyTag();
|
||||
events.emit('success', responseData.data.message);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use strict";
|
||||
var DropZone = require('dropzone');
|
||||
var markdown = require('marked');
|
||||
const DropZone = require('dropzone');
|
||||
const markdown = require('marked');
|
||||
|
||||
var toggleSwitchTemplate = require('./components/toggle-switch.html');
|
||||
var imagePickerTemplate = require('./components/image-picker.html');
|
||||
var dropZoneTemplate = require('./components/drop-zone.html');
|
||||
const toggleSwitchTemplate = require('./components/toggle-switch.html');
|
||||
const imagePickerTemplate = require('./components/image-picker.html');
|
||||
const dropZoneTemplate = require('./components/drop-zone.html');
|
||||
|
||||
module.exports = function (ngApp, events) {
|
||||
|
||||
@@ -54,7 +54,7 @@ module.exports = function (ngApp, events) {
|
||||
imageClass: '@'
|
||||
},
|
||||
link: function (scope, element, attrs) {
|
||||
var usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
|
||||
let usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
|
||||
scope.image = scope.currentImage;
|
||||
scope.value = scope.currentImage || '';
|
||||
if (usingIds) scope.value = scope.currentId;
|
||||
@@ -80,7 +80,7 @@ module.exports = function (ngApp, events) {
|
||||
};
|
||||
|
||||
scope.updateImageFromModel = function (model) {
|
||||
var isResized = scope.resizeWidth && scope.resizeHeight;
|
||||
let isResized = scope.resizeWidth && scope.resizeHeight;
|
||||
|
||||
if (!isResized) {
|
||||
scope.$apply(() => {
|
||||
@@ -89,8 +89,9 @@ module.exports = function (ngApp, events) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cropped = scope.resizeCrop ? 'true' : 'false';
|
||||
var requestString = '/images/thumb/' + model.id + '/' + scope.resizeWidth + '/' + scope.resizeHeight + '/' + cropped;
|
||||
let cropped = scope.resizeCrop ? 'true' : 'false';
|
||||
let requestString = '/images/thumb/' + model.id + '/' + scope.resizeWidth + '/' + scope.resizeHeight + '/' + cropped;
|
||||
requestString = window.baseUrl(requestString);
|
||||
$http.get(requestString).then((response) => {
|
||||
setImage(model, response.data.url);
|
||||
});
|
||||
@@ -157,9 +158,22 @@ module.exports = function (ngApp, events) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function (scope, element, attrs) {
|
||||
var menu = element.find('ul');
|
||||
const menu = element.find('ul');
|
||||
element.find('[dropdown-toggle]').on('click', function () {
|
||||
menu.show().addClass('anim menuIn');
|
||||
let inputs = menu.find('input');
|
||||
let hasInput = inputs.length > 0;
|
||||
if (hasInput) {
|
||||
inputs.first().focus();
|
||||
element.on('keypress', 'input', event => {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
menu.hide();
|
||||
menu.removeClass('anim menuIn');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
element.mouseleave(function () {
|
||||
menu.hide();
|
||||
menu.removeClass('anim menuIn');
|
||||
@@ -252,11 +266,12 @@ module.exports = function (ngApp, events) {
|
||||
link: function (scope, element, attrs) {
|
||||
|
||||
// Set initial model content
|
||||
var content = element.val();
|
||||
element = element.find('textarea').first();
|
||||
let content = element.val();
|
||||
scope.mdModel = content;
|
||||
scope.mdChange(markdown(content));
|
||||
|
||||
element.on('change input', (e) => {
|
||||
element.on('change input', (event) => {
|
||||
content = element.val();
|
||||
$timeout(() => {
|
||||
scope.mdModel = content;
|
||||
@@ -284,9 +299,10 @@ module.exports = function (ngApp, events) {
|
||||
link: function (scope, element, attrs) {
|
||||
|
||||
// Elements
|
||||
const input = element.find('textarea[markdown-input]');
|
||||
const input = element.find('[markdown-input] textarea').first();
|
||||
const display = element.find('.markdown-display').first();
|
||||
const insertImage = element.find('button[data-action="insertImage"]');
|
||||
const insertEntityLink = element.find('button[data-action="insertEntityLink"]')
|
||||
|
||||
let currentCaretPos = 0;
|
||||
|
||||
@@ -329,15 +345,22 @@ module.exports = function (ngApp, events) {
|
||||
// Insert image shortcut
|
||||
if (event.which === 73 && event.ctrlKey && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
var caretPos = input[0].selectionStart;
|
||||
var currentContent = input.val();
|
||||
var mdImageText = "";
|
||||
let caretPos = input[0].selectionStart;
|
||||
let currentContent = input.val();
|
||||
const mdImageText = "";
|
||||
input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
|
||||
input.focus();
|
||||
input[0].selectionStart = caretPos + (";
|
||||
input[0].selectionEnd = caretPos + (';
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert entity link shortcut
|
||||
if (event.which === 75 && event.ctrlKey && event.shiftKey) {
|
||||
showLinkSelector();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass key presses to controller via event
|
||||
scope.$emit('editor-keydown', event);
|
||||
});
|
||||
@@ -345,14 +368,111 @@ module.exports = function (ngApp, events) {
|
||||
// Insert image from image manager
|
||||
insertImage.click(event => {
|
||||
window.ImageManager.showExternal(image => {
|
||||
var caretPos = currentCaretPos;
|
||||
var currentContent = input.val();
|
||||
var mdImageText = "";
|
||||
let caretPos = currentCaretPos;
|
||||
let currentContent = input.val();
|
||||
let mdImageText = "";
|
||||
input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
|
||||
input.change();
|
||||
});
|
||||
});
|
||||
|
||||
function showLinkSelector() {
|
||||
window.showEntityLinkSelector((entity) => {
|
||||
let selectionStart = currentCaretPos;
|
||||
let selectionEnd = input[0].selectionEnd;
|
||||
let textSelected = (selectionEnd !== selectionStart);
|
||||
let currentContent = input.val();
|
||||
|
||||
if (textSelected) {
|
||||
let selectedText = currentContent.substring(selectionStart, selectionEnd);
|
||||
let linkText = `[${selectedText}](${entity.link})`;
|
||||
input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionEnd));
|
||||
} else {
|
||||
let linkText = ` [${entity.name}](${entity.link}) `;
|
||||
input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionStart))
|
||||
}
|
||||
input.change();
|
||||
});
|
||||
}
|
||||
insertEntityLink.click(showLinkSelector);
|
||||
|
||||
// Upload and insert image on paste
|
||||
function editorPaste(e) {
|
||||
e = e.originalEvent;
|
||||
if (!e.clipboardData) return
|
||||
var items = e.clipboardData.items;
|
||||
if (!items) return;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
uploadImage(items[i].getAsFile());
|
||||
}
|
||||
}
|
||||
|
||||
input.on('paste', editorPaste);
|
||||
|
||||
// Handle image drop, Uploads images to BookStack.
|
||||
function handleImageDrop(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
let files = event.originalEvent.dataTransfer.files;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
uploadImage(files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
input.on('drop', handleImageDrop);
|
||||
|
||||
// Handle image upload and add image into markdown content
|
||||
function uploadImage(file) {
|
||||
if (file.type.indexOf('image') !== 0) return;
|
||||
var formData = new FormData();
|
||||
var ext = 'png';
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
if (file.name) {
|
||||
var fileNameMatches = file.name.match(/\.(.+)$/);
|
||||
if (fileNameMatches) {
|
||||
ext = fileNameMatches[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Insert image into markdown
|
||||
let id = "image-" + Math.random().toString(16).slice(2);
|
||||
let selectStart = input[0].selectionStart;
|
||||
let selectEnd = input[0].selectionEnd;
|
||||
let content = input[0].value;
|
||||
let selectText = content.substring(selectStart, selectEnd);
|
||||
let placeholderImage = window.baseUrl(`/loading.gif#upload${id}`);
|
||||
let innerContent = ((selectEnd > selectStart) ? `![${selectText}]` : '![]') + `(${placeholderImage})`;
|
||||
input[0].value = content.substring(0, selectStart) + innerContent + content.substring(selectEnd);
|
||||
|
||||
input.focus();
|
||||
input[0].selectionStart = selectStart;
|
||||
input[0].selectionEnd = selectStart;
|
||||
|
||||
let remoteFilename = "image-" + Date.now() + "." + ext;
|
||||
formData.append('file', file, remoteFilename);
|
||||
formData.append('_token', document.querySelector('meta[name="token"]').getAttribute('content'));
|
||||
|
||||
xhr.open('POST', window.baseUrl('/images/gallery/upload'));
|
||||
xhr.onload = function () {
|
||||
let selectStart = input[0].selectionStart;
|
||||
if (xhr.status === 200 || xhr.status === 201) {
|
||||
var result = JSON.parse(xhr.responseText);
|
||||
input[0].value = input[0].value.replace(placeholderImage, result.thumbs.display);
|
||||
input.change();
|
||||
} else {
|
||||
console.log('An error occurred uploading the image');
|
||||
console.log(xhr.responseText);
|
||||
input[0].value = input[0].value.replace(innerContent, '');
|
||||
input.change();
|
||||
}
|
||||
input.focus();
|
||||
input[0].selectionStart = selectStart;
|
||||
input[0].selectionEnd = selectStart;
|
||||
};
|
||||
xhr.send(formData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}]);
|
||||
@@ -583,6 +703,58 @@ module.exports = function (ngApp, events) {
|
||||
}
|
||||
}]);
|
||||
|
||||
ngApp.directive('entityLinkSelector', [function($http) {
|
||||
return {
|
||||
restict: '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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -596,32 +768,66 @@ module.exports = function (ngApp, events) {
|
||||
// Add input for forms
|
||||
const input = element.find('[entity-selector-input]').first();
|
||||
|
||||
// Detect double click events
|
||||
var 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);
|
||||
itemSelect(item, isDoubleClick());
|
||||
});
|
||||
element.on('click', '[data-entity-type]', function(event) {
|
||||
itemSelect($(this));
|
||||
itemSelect($(this), isDoubleClick());
|
||||
});
|
||||
|
||||
// Select entity action
|
||||
function itemSelect(item) {
|
||||
function itemSelect(item, doubleClick) {
|
||||
let entityType = item.attr('data-entity-type');
|
||||
let entityId = item.attr('data-entity-id');
|
||||
let isSelected = !item.hasClass('selected');
|
||||
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 `/ajax/search/entities?types=${types}`;
|
||||
return window.baseUrl(`/ajax/search/entities?types=${types}`);
|
||||
}
|
||||
|
||||
// Get initial contents
|
||||
|
||||
@@ -7,12 +7,23 @@ var ngAnimate = require('angular-animate');
|
||||
var ngSanitize = require('angular-sanitize');
|
||||
require('angular-ui-sortable');
|
||||
|
||||
// Url retrieval function
|
||||
window.baseUrl = function(path) {
|
||||
let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content');
|
||||
if (basePath[basePath.length-1] === '/') basePath = basePath.slice(0, basePath.length-1);
|
||||
if (path[0] === '/') path = path.slice(1);
|
||||
return basePath + '/' + path;
|
||||
};
|
||||
|
||||
var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
|
||||
|
||||
// Global Event System
|
||||
var Events = {
|
||||
listeners: {},
|
||||
emit: function (eventName, eventData) {
|
||||
class EventManager {
|
||||
constructor() {
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
emit(eventName, eventData) {
|
||||
if (typeof this.listeners[eventName] === 'undefined') return this;
|
||||
var eventsToStart = this.listeners[eventName];
|
||||
for (let i = 0; i < eventsToStart.length; i++) {
|
||||
@@ -20,32 +31,35 @@ var Events = {
|
||||
event(eventData);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
listen: function (eventName, callback) {
|
||||
}
|
||||
|
||||
listen(eventName, callback) {
|
||||
if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
|
||||
this.listeners[eventName].push(callback);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
window.Events = Events;
|
||||
window.Events = new EventManager();
|
||||
|
||||
var services = require('./services')(ngApp, Events);
|
||||
var directives = require('./directives')(ngApp, Events);
|
||||
var controllers = require('./controllers')(ngApp, Events);
|
||||
|
||||
var services = require('./services')(ngApp, window.Events);
|
||||
var directives = require('./directives')(ngApp, window.Events);
|
||||
var controllers = require('./controllers')(ngApp, window.Events);
|
||||
|
||||
//Global jQuery Config & Extensions
|
||||
|
||||
// Smooth scrolling
|
||||
jQuery.fn.smoothScrollTo = function () {
|
||||
if (this.length === 0) return;
|
||||
$('body').animate({
|
||||
let scrollElem = document.documentElement.scrollTop === 0 ? document.body : document.documentElement;
|
||||
$(scrollElem).animate({
|
||||
scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
|
||||
}, 800); // Adjust to change animations speed (ms)
|
||||
return this;
|
||||
};
|
||||
|
||||
// Making contains text expression not worry about casing
|
||||
$.expr[":"].contains = $.expr.createPseudo(function (arg) {
|
||||
jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
|
||||
return function (elem) {
|
||||
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
|
||||
};
|
||||
@@ -95,13 +109,14 @@ $(function () {
|
||||
var scrollTop = document.getElementById('back-to-top');
|
||||
var scrollTopBreakpoint = 1200;
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!scrollTopShowing && document.body.scrollTop > scrollTopBreakpoint) {
|
||||
let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
|
||||
scrollTop.style.display = 'block';
|
||||
scrollTopShowing = true;
|
||||
setTimeout(() => {
|
||||
scrollTop.style.opacity = 0.4;
|
||||
}, 1);
|
||||
} else if (scrollTopShowing && document.body.scrollTop < scrollTopBreakpoint) {
|
||||
} else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
|
||||
scrollTop.style.opacity = 0;
|
||||
scrollTopShowing = false;
|
||||
setTimeout(() => {
|
||||
@@ -115,6 +130,27 @@ $(function () {
|
||||
$('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
|
||||
});
|
||||
|
||||
// Popup close
|
||||
$('.popup-close').click(function() {
|
||||
$(this).closest('.overlay').fadeOut(240);
|
||||
});
|
||||
$('.overlay').click(function(event) {
|
||||
if (!$(event.target).hasClass('overlay')) return;
|
||||
$(this).fadeOut(240);
|
||||
});
|
||||
|
||||
// Prevent markdown display link click redirect
|
||||
$('.markdown-display').on('click', 'a', function(event) {
|
||||
event.preventDefault();
|
||||
window.open($(this).attr('href'));
|
||||
});
|
||||
|
||||
// Detect IE for css
|
||||
if(navigator.userAgent.indexOf('MSIE')!==-1
|
||||
|| navigator.appVersion.indexOf('Trident/') > 0
|
||||
|| navigator.userAgent.indexOf('Safari') !== -1){
|
||||
$('body').addClass('flexbox-support');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,75 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Handle pasting images from clipboard.
|
||||
* @param e - event
|
||||
* @param editor - editor instance
|
||||
*/
|
||||
function editorPaste(e, editor) {
|
||||
if (!e.clipboardData) return
|
||||
let items = e.clipboardData.items;
|
||||
if (!items) return;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") === -1) return
|
||||
|
||||
let file = items[i].getAsFile();
|
||||
let formData = new FormData();
|
||||
let ext = 'png';
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
if (file.name) {
|
||||
let fileNameMatches = file.name.match(/\.(.+)$/);
|
||||
if (fileNameMatches) {
|
||||
ext = fileNameMatches[1];
|
||||
}
|
||||
}
|
||||
|
||||
let id = "image-" + Math.random().toString(16).slice(2);
|
||||
let loadingImage = window.baseUrl('/loading.gif');
|
||||
editor.execCommand('mceInsertContent', false, `<img src="${loadingImage}" id="${id}">`);
|
||||
|
||||
let remoteFilename = "image-" + Date.now() + "." + ext;
|
||||
formData.append('file', file, remoteFilename);
|
||||
formData.append('_token', document.querySelector('meta[name="token"]').getAttribute('content'));
|
||||
|
||||
xhr.open('POST', window.baseUrl('/images/gallery/upload'));
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200 || xhr.status === 201) {
|
||||
let result = JSON.parse(xhr.responseText);
|
||||
editor.dom.setAttrib(id, 'src', result.thumbs.display);
|
||||
} else {
|
||||
console.log('An error occurred uploading the image', xhr.responseText);
|
||||
editor.dom.remove(id);
|
||||
}
|
||||
};
|
||||
xhr.send(formData);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function registerEditorShortcuts(editor) {
|
||||
// Headers
|
||||
for (let i = 1; i < 5; i++) {
|
||||
editor.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
|
||||
}
|
||||
|
||||
// Other block shortcuts
|
||||
editor.addShortcut('ctrl+q', '', ['FormatBlock', false, 'blockquote']);
|
||||
editor.addShortcut('ctrl+d', '', ['FormatBlock', false, 'p']);
|
||||
editor.addShortcut('ctrl+e', '', ['FormatBlock', false, 'pre']);
|
||||
editor.addShortcut('ctrl+s', '', ['FormatBlock', false, 'code']);
|
||||
}
|
||||
|
||||
var mceOptions = module.exports = {
|
||||
selector: '#html-editor',
|
||||
content_css: [
|
||||
'/css/styles.css',
|
||||
'/libs/material-design-iconic-font/css/material-design-iconic-font.min.css'
|
||||
window.baseUrl('/css/styles.css'),
|
||||
window.baseUrl('/libs/material-design-iconic-font/css/material-design-iconic-font.min.css')
|
||||
],
|
||||
body_class: 'page-content',
|
||||
relative_urls: false,
|
||||
remove_script_host: false,
|
||||
document_base_url: window.baseUrl('/'),
|
||||
statusbar: false,
|
||||
menubar: false,
|
||||
paste_data_images: false,
|
||||
@@ -38,23 +102,41 @@ var mceOptions = module.exports = {
|
||||
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-right'},
|
||||
},
|
||||
file_browser_callback: function (field_name, url, type, win) {
|
||||
window.ImageManager.showExternal(function (image) {
|
||||
win.document.getElementById(field_name).value = image.url;
|
||||
if ("createEvent" in document) {
|
||||
var evt = document.createEvent("HTMLEvents");
|
||||
evt.initEvent("change", false, true);
|
||||
win.document.getElementById(field_name).dispatchEvent(evt);
|
||||
} else {
|
||||
win.document.getElementById(field_name).fireEvent("onchange");
|
||||
}
|
||||
var html = '<a href="' + image.url + '" target="_blank">';
|
||||
html += '<img src="' + image.thumbs.display + '" alt="' + image.name + '">';
|
||||
html += '</a>';
|
||||
win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html);
|
||||
});
|
||||
|
||||
if (type === 'file') {
|
||||
window.showEntityLinkSelector(function(entity) {
|
||||
let originalField = win.document.getElementById(field_name);
|
||||
originalField.value = entity.link;
|
||||
$(originalField).closest('.mce-form').find('input').eq(2).val(entity.name);
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'image') {
|
||||
// Show image manager
|
||||
window.ImageManager.showExternal(function (image) {
|
||||
|
||||
// Set popover link input to image url then fire change event
|
||||
// to ensure the new value sticks
|
||||
win.document.getElementById(field_name).value = image.url;
|
||||
if ("createEvent" in document) {
|
||||
let evt = document.createEvent("HTMLEvents");
|
||||
evt.initEvent("change", false, true);
|
||||
win.document.getElementById(field_name).dispatchEvent(evt);
|
||||
} else {
|
||||
win.document.getElementById(field_name).fireEvent("onchange");
|
||||
}
|
||||
|
||||
// Replace the actively selected content with the linked image
|
||||
let html = `<a href="${image.url}" target="_blank">`;
|
||||
html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
|
||||
html += '</a>';
|
||||
win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
paste_preprocess: function (plugin, args) {
|
||||
var content = args.content;
|
||||
let content = args.content;
|
||||
if (content.indexOf('<img src="file://') !== -1) {
|
||||
args.content = '';
|
||||
}
|
||||
@@ -62,10 +144,14 @@ var mceOptions = module.exports = {
|
||||
extraSetups: [],
|
||||
setup: function (editor) {
|
||||
|
||||
for (var i = 0; i < mceOptions.extraSetups.length; i++) {
|
||||
// Run additional setup actions
|
||||
// Used by the angular side of things
|
||||
for (let i = 0; i < mceOptions.extraSetups.length; i++) {
|
||||
mceOptions.extraSetups[i](editor);
|
||||
}
|
||||
|
||||
registerEditorShortcuts(editor);
|
||||
|
||||
(function () {
|
||||
var wrap;
|
||||
|
||||
@@ -76,12 +162,11 @@ var mceOptions = module.exports = {
|
||||
editor.on('dragstart', function () {
|
||||
var node = editor.selection.getNode();
|
||||
|
||||
if (node.nodeName === 'IMG') {
|
||||
wrap = editor.dom.getParent(node, '.mceTemp');
|
||||
if (node.nodeName !== 'IMG') return;
|
||||
wrap = editor.dom.getParent(node, '.mceTemp');
|
||||
|
||||
if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
|
||||
wrap = node.parentNode;
|
||||
}
|
||||
if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
|
||||
wrap = node.parentNode;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -106,15 +191,15 @@ var mceOptions = module.exports = {
|
||||
});
|
||||
})();
|
||||
|
||||
// Image picker button
|
||||
// Custom Image picker button
|
||||
editor.addButton('image-insert', {
|
||||
title: 'My title',
|
||||
icon: 'image',
|
||||
tooltip: 'Insert an image',
|
||||
onclick: function () {
|
||||
window.ImageManager.showExternal(function (image) {
|
||||
var html = '<a href="' + image.url + '" target="_blank">';
|
||||
html += '<img src="' + image.thumbs.display + '" alt="' + image.name + '">';
|
||||
let html = `<a href="${image.url}" target="_blank">`;
|
||||
html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
|
||||
html += '</a>';
|
||||
editor.execCommand('mceInsertContent', false, html);
|
||||
});
|
||||
@@ -122,49 +207,8 @@ var mceOptions = module.exports = {
|
||||
});
|
||||
|
||||
// Paste image-uploads
|
||||
editor.on('paste', function (e) {
|
||||
if (e.clipboardData) {
|
||||
var items = e.clipboardData.items;
|
||||
if (items) {
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") !== -1) {
|
||||
|
||||
var file = items[i].getAsFile();
|
||||
var formData = new FormData();
|
||||
var ext = 'png';
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
if (file.name) {
|
||||
var fileNameMatches = file.name.match(/\.(.+)$/);
|
||||
if (fileNameMatches) {
|
||||
ext = fileNameMatches[1];
|
||||
}
|
||||
}
|
||||
|
||||
var id = "image-" + Math.random().toString(16).slice(2);
|
||||
editor.execCommand('mceInsertContent', false, '<img src="/loading.gif" id="' + id + '">');
|
||||
|
||||
var remoteFilename = "image-" + Date.now() + "." + ext;
|
||||
formData.append('file', file, remoteFilename);
|
||||
formData.append('_token', document.querySelector('meta[name="token"]').getAttribute('content'));
|
||||
|
||||
xhr.open('POST', '/images/gallery/upload');
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200 || xhr.status === 201) {
|
||||
var result = JSON.parse(xhr.responseText);
|
||||
editor.dom.setAttrib(id, 'src', result.url);
|
||||
} else {
|
||||
console.log('An error occured uploading the image');
|
||||
console.log(xhr.responseText);
|
||||
editor.dom.remove(id);
|
||||
}
|
||||
};
|
||||
xhr.send(formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
editor.on('paste', function(event) {
|
||||
editorPaste(event, editor);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
// Configure ZeroClipboard
|
||||
var zeroClipBoard = require('zeroclipboard');
|
||||
zeroClipBoard.config({
|
||||
swfPath: '/ZeroClipboard.swf'
|
||||
swfPath: window.baseUrl('/ZeroClipboard.swf')
|
||||
});
|
||||
|
||||
window.setupPageShow = module.exports = function (pageId) {
|
||||
@@ -36,7 +36,8 @@ window.setupPageShow = module.exports = function (pageId) {
|
||||
|
||||
// Show pointer and set link
|
||||
var $elem = $(this);
|
||||
var link = window.location.protocol + "//" + window.location.host + '/link/' + pageId + '#' + $elem.attr('id');
|
||||
let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id'));
|
||||
if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link;
|
||||
$pointer.find('input').val(link);
|
||||
$pointer.find('button').first().attr('data-clipboard-text', link);
|
||||
$elem.before($pointer);
|
||||
|
||||
@@ -100,3 +100,13 @@ $button-border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.button[disabled] {
|
||||
background-color: #BBB;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
background-color: #BBB;
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.overlay {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
background-color: rgba(0, 0, 0, 0.333);
|
||||
position: fixed;
|
||||
z-index: 95536;
|
||||
width: 100%;
|
||||
@@ -10,26 +10,76 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.image-manager-body {
|
||||
.popup-body-wrap {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
background-color: #FFF;
|
||||
max-height: 90%;
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
width: 1200px;
|
||||
height: auto;
|
||||
margin: 2% 5%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
h1, h2, h3 {
|
||||
font-weight: 300;
|
||||
flex-direction: column;
|
||||
&.small {
|
||||
margin: 2% auto;
|
||||
width: 800px;
|
||||
max-width: 90%;
|
||||
}
|
||||
&:before {
|
||||
display: flex;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
//body.ie .popup-body {
|
||||
// min-height: 100%;
|
||||
//}
|
||||
|
||||
.corner-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
height: 40px;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.popup-header, .popup-footer {
|
||||
display: block !important;
|
||||
position: relative;
|
||||
height: 40px;
|
||||
flex: none !important;
|
||||
.popup-title {
|
||||
color: #FFF;
|
||||
padding: 8px $-m;
|
||||
}
|
||||
}
|
||||
body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
||||
height: 444px;
|
||||
min-height: 444px;
|
||||
}
|
||||
#entity-selector-wrap .popup-body .form-group {
|
||||
margin: 0;
|
||||
}
|
||||
//body.ie #entity-selector-wrap .popup-body .form-group {
|
||||
// min-height: 60vh;
|
||||
//}
|
||||
|
||||
.image-manager-body {
|
||||
min-height: 70vh;
|
||||
}
|
||||
|
||||
#image-manager .dropzone-container {
|
||||
@@ -37,12 +87,6 @@
|
||||
border: 3px dashed #DDD;
|
||||
}
|
||||
|
||||
.image-manager-bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.image-manager-list .image {
|
||||
display: block;
|
||||
position: relative;
|
||||
@@ -103,18 +147,13 @@
|
||||
|
||||
.image-manager-sidebar {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
margin-left: 1px;
|
||||
padding: 0 $-l;
|
||||
padding: $-m $-l;
|
||||
overflow-y: auto;
|
||||
border-left: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.image-manager-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
.dropzone-container {
|
||||
margin-top: $-m;
|
||||
}
|
||||
}
|
||||
|
||||
.image-manager-list {
|
||||
@@ -125,7 +164,6 @@
|
||||
.image-manager-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
.container {
|
||||
width: 100%;
|
||||
@@ -141,12 +179,13 @@
|
||||
* Copyright (c) 2012 Matias Meno <m@tias.me>
|
||||
*/
|
||||
.dz-message {
|
||||
font-size: 1.4em;
|
||||
font-size: 1.2em;
|
||||
line-height: 1.1;
|
||||
font-style: italic;
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
padding: $-xl $-m;
|
||||
padding: $-l $-m;
|
||||
transition: all ease-in-out 120ms;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin'), local('Roboto-Thin'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-100.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-100.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-100.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-100.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-100italic - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -15,8 +15,8 @@
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
src: local('Roboto Thin Italic'), local('Roboto-ThinItalic'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-100italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-100italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-100italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-100italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-300 - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -24,8 +24,8 @@
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-300italic - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -33,8 +33,8 @@
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light Italic'), local('Roboto-LightItalic'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-300italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-300italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-regular - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -42,8 +42,8 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-italic - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -51,8 +51,8 @@
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-500 - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -60,8 +60,8 @@
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-500italic - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -69,8 +69,8 @@
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium Italic'), local('Roboto-MediumItalic'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-500italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-500italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-500italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-500italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-700 - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -78,8 +78,8 @@
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
/* roboto-700italic - cyrillic_latin */
|
||||
@font-face {
|
||||
@@ -87,8 +87,8 @@
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold Italic'), local('Roboto-BoldItalic'),
|
||||
url('/fonts/roboto-v15-cyrillic_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-v15-cyrillic_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-700italic.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-v15-cyrillic_latin-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* roboto-mono-regular - latin */
|
||||
@@ -97,6 +97,6 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'),
|
||||
url('/fonts/roboto-mono-v4-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('/fonts/roboto-mono-v4-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url('../fonts/roboto-mono-v4-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */
|
||||
url('../fonts/roboto-mono-v4-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
@@ -25,6 +25,14 @@ body.flexbox {
|
||||
}
|
||||
}
|
||||
|
||||
.flex-child > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
//body.ie .flex-child > div {
|
||||
// flex: 1 0 0px;
|
||||
//}
|
||||
|
||||
/** Rules for all columns */
|
||||
div[class^="col-"] img {
|
||||
max-width: 100%;
|
||||
@@ -39,6 +47,9 @@ div[class^="col-"] img {
|
||||
&.fluid {
|
||||
max-width: 100%;
|
||||
}
|
||||
&.medium {
|
||||
max-width: 992px;
|
||||
}
|
||||
&.small {
|
||||
max-width: 840px;
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ form.search-box {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.faded span.faded-text {
|
||||
|
||||
@@ -23,7 +23,7 @@ button {
|
||||
|
||||
table {
|
||||
min-width: 100px;
|
||||
td {
|
||||
td, th {
|
||||
min-width: 10px;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid #DDD;
|
||||
|
||||
@@ -375,6 +375,9 @@ ul.pagination {
|
||||
.text-muted {
|
||||
color: #999;
|
||||
}
|
||||
li.padded {
|
||||
padding: $-xs $-m;
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
padding: $-xs $-m;
|
||||
@@ -384,10 +387,10 @@ ul.pagination {
|
||||
background-color: #EEE;
|
||||
}
|
||||
i {
|
||||
margin-right: $-m;
|
||||
margin-right: $-s;
|
||||
padding-right: 0;
|
||||
display: inline;
|
||||
width: 22px;
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
li.border-bottom {
|
||||
|
||||
@@ -20,6 +20,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.draft-notification {
|
||||
pointer-events: none;
|
||||
transform: scale(0);
|
||||
transition: transform ease-in-out 120ms;
|
||||
transform-origin: 50% 50%;
|
||||
&.visible {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.page-style.editor {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@@ -158,8 +168,8 @@
|
||||
.tabs {
|
||||
display: block;
|
||||
border-right: 1px solid #DDD;
|
||||
width: 54px;
|
||||
flex: 0;
|
||||
width: 48px;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.tabs i {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
@@ -238,7 +248,7 @@
|
||||
}
|
||||
|
||||
.tag-display {
|
||||
margin: $-xl $-xs;
|
||||
margin: $-xl $-m;
|
||||
border: 1px solid #DDD;
|
||||
min-width: 180px;
|
||||
max-width: 320px;
|
||||
@@ -250,13 +260,10 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
span {
|
||||
color: #666;
|
||||
margin-left: $-s;
|
||||
}
|
||||
.heading {
|
||||
.heading th {
|
||||
padding: $-xs $-s;
|
||||
color: #444;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
}
|
||||
td {
|
||||
border: 0;
|
||||
@@ -267,9 +274,6 @@
|
||||
.tag-value {
|
||||
color: #888;
|
||||
}
|
||||
td i {
|
||||
color: #888;
|
||||
}
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@@ -24,21 +24,22 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.edit-area.flex > .mce-tinymce.mce-container.mce-panel {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
flex: 1;
|
||||
.edit-area.flex > div > .mce-tinymce.mce-container.mce-panel {
|
||||
flex: 1 1 auto;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin: 0 -1px;
|
||||
> .mce-container-body {
|
||||
flex: 1;
|
||||
flex: 1 1 auto;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
> .mce-toolbar-grp {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
> .mce-edit-area {
|
||||
flex: 1;
|
||||
flex: 1 1 auto;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@import "animations";
|
||||
@import "tinymce";
|
||||
@import "highlightjs";
|
||||
@import "image-manager";
|
||||
@import "components";
|
||||
@import "header";
|
||||
@import "lists";
|
||||
@import "pages";
|
||||
@@ -72,7 +72,7 @@ body.dragging, body.dragging * {
|
||||
border-radius: 3px;
|
||||
box-shadow: $bs-med;
|
||||
z-index: 999999;
|
||||
display: table;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
max-width: 480px;
|
||||
i, span {
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
@include('form/password', ['name' => 'password', 'tabindex' => 2])
|
||||
<span class="block small"><a href="/password/email">Forgot Password?</a></span>
|
||||
<span class="block small"><a href="{{ baseUrl('/password/email') }}">Forgot Password?</a></span>
|
||||
</div>
|
||||
@@ -1,8 +1,8 @@
|
||||
@extends('public')
|
||||
|
||||
@section('header-buttons')
|
||||
@if(Setting::get('registration-enabled'))
|
||||
<a href="/register"><i class="zmdi zmdi-account-add"></i>Sign up</a>
|
||||
@if(setting('registration-enabled', false))
|
||||
<a href="{{ baseUrl("/register") }}"><i class="zmdi zmdi-account-add"></i>Sign up</a>
|
||||
@endif
|
||||
@stop
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="center-box">
|
||||
<h1>Log In</h1>
|
||||
|
||||
<form action="/login" method="POST" id="login-form">
|
||||
<form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
|
||||
{!! csrf_field() !!}
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
<hr class="margin-top">
|
||||
<h3 class="text-muted">Social Login</h3>
|
||||
@if(isset($socialDrivers['google']))
|
||||
<a href="/login/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
|
||||
<a href="{{ baseUrl("/login/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
|
||||
@endif
|
||||
@if(isset($socialDrivers['github']))
|
||||
<a href="/login/service/github" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
|
||||
<a href="{{ baseUrl("/login/service/github") }}" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
@extends('public')
|
||||
|
||||
@section('body-class', 'image-cover login')
|
||||
|
||||
@section('content')
|
||||
|
||||
|
||||
@@ -11,7 +9,7 @@
|
||||
|
||||
<p class="muted small">Enter your email below and you will be sent an email with a password reset link.</p>
|
||||
|
||||
<form action="/password/email" method="POST">
|
||||
<form action="{{ baseUrl("/password/email") }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('header-buttons')
|
||||
@if(!$signedIn)
|
||||
<a href="/login"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
|
||||
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
|
||||
@endif
|
||||
@stop
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="text-center">
|
||||
<div class="center-box">
|
||||
<h2>Thanks for registering!</h2>
|
||||
<p>Please check your email and click the confirmation button to access {{ \Setting::get('app-name') }}.</p>
|
||||
<p>Please check your email and click the confirmation button to access {{ setting('app-name', 'BookStack') }}.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@extends('public')
|
||||
|
||||
@section('header-buttons')
|
||||
<a href="/login"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
|
||||
<a href="{{ baseUrl("/login") }}"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="center-box">
|
||||
<h1>Sign Up</h1>
|
||||
|
||||
<form action="/register" method="POST">
|
||||
<form action="{{ baseUrl("/register") }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
|
||||
<div class="form-group">
|
||||
@@ -38,10 +38,10 @@
|
||||
<h3 class="text-muted">Social Registration</h3>
|
||||
<p class="text-small">Register and sign in using another service.</p>
|
||||
@if(isset($socialDrivers['google']))
|
||||
<a href="/register/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
|
||||
<a href="{{ baseUrl("/register/service/google") }}" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
|
||||
@endif
|
||||
@if(isset($socialDrivers['github']))
|
||||
<a href="/register/service/github" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
|
||||
<a href="{{ baseUrl("/register/service/github") }}" style="color:#444;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="center-box text-left">
|
||||
<h1>Reset Password</h1>
|
||||
|
||||
<form action="/password/reset" method="POST">
|
||||
<form action="{{ baseUrl("/password/reset") }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="token" value="{{ $token }}">
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
If you cannot find the email you can re-send the confirmation email by submitting the form below.
|
||||
</p>
|
||||
<hr>
|
||||
<form action="/register/confirm/resend" method="POST">
|
||||
<form action="{{ baseUrl("/register/confirm/resend") }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="@yield('body-class')">
|
||||
<head>
|
||||
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name', 'BookStack') }}</title>
|
||||
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
|
||||
|
||||
<!-- Meta -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="token" content="{{ csrf_token() }}">
|
||||
<meta name="base-url" content="{{ baseUrl('/') }}">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Styles and Fonts -->
|
||||
<link rel="stylesheet" href="{{ versioned_asset('css/styles.css') }}">
|
||||
<link rel="stylesheet" media="print" href="{{ versioned_asset('css/print-styles.css') }}">
|
||||
<link rel="stylesheet" href="/libs/material-design-iconic-font/css/material-design-iconic-font.min.css">
|
||||
<link rel="stylesheet" href="{{ baseUrl('/libs/material-design-iconic-font/css/material-design-iconic-font.min.css') }}">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="/libs/jquery/jquery.min.js?version=2.1.4"></script>
|
||||
<script src="/libs/jquery/jquery-ui.min.js?version=1.11.4"></script>
|
||||
<script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
|
||||
<script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
|
||||
|
||||
@yield('head')
|
||||
|
||||
@@ -34,15 +35,15 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-sm-4" ng-non-bindable>
|
||||
<a href="/" class="logo">
|
||||
<a href="{{ baseUrl('/') }}" class="logo">
|
||||
@if(setting('app-logo', '') !== 'none')
|
||||
<img class="logo-image" src="{{ setting('app-logo', '') === '' ? '/logo.png' : setting('app-logo', '') }}" alt="Logo">
|
||||
<img class="logo-image" src="{{ setting('app-logo', '') === '' ? baseUrl('/logo.png') : baseUrl(setting('app-logo', '')) }}" alt="Logo">
|
||||
@endif
|
||||
<span class="logo-text">{{ setting('app-name', 'BookStack') }}</span>
|
||||
<span class="logo-text">{{ setting('app-name') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-3 text-center">
|
||||
<form action="/search/all" method="GET" class="search-box">
|
||||
<form action="{{ baseUrl('/search/all') }}" method="GET" class="search-box">
|
||||
<input id="header-search-box-input" type="text" name="term" tabindex="2" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
|
||||
<button id="header-search-box-button" type="submit" class="text-button"><i class="zmdi zmdi-search"></i></button>
|
||||
</form>
|
||||
@@ -50,12 +51,12 @@
|
||||
<div class="col-lg-4 col-sm-5">
|
||||
<div class="float right">
|
||||
<div class="links text-center">
|
||||
<a href="/books"><i class="zmdi zmdi-book"></i>Books</a>
|
||||
<a href="{{ baseUrl('/books') }}"><i class="zmdi zmdi-book"></i>Books</a>
|
||||
@if(isset($currentUser) && userCan('settings-manage'))
|
||||
<a href="/settings"><i class="zmdi zmdi-settings"></i>Settings</a>
|
||||
<a href="{{ baseUrl('/settings') }}"><i class="zmdi zmdi-settings"></i>Settings</a>
|
||||
@endif
|
||||
@if(!isset($signedIn) || !$signedIn)
|
||||
<a href="/login"><i class="zmdi zmdi-sign-in"></i>Sign In</a>
|
||||
<a href="{{ baseUrl('/login') }}"><i class="zmdi zmdi-sign-in"></i>Sign In</a>
|
||||
@endif
|
||||
</div>
|
||||
@if(isset($signedIn) && $signedIn)
|
||||
@@ -66,13 +67,13 @@
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/user/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a>
|
||||
<a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary"><i class="zmdi zmdi-account zmdi-hc-fw zmdi-hc-lg"></i>View Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/users/{{$currentUser->id}}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-fw zmdi-hc-lg"></i>Edit Profile</a>
|
||||
<a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}" class="text-primary"><i class="zmdi zmdi-edit zmdi-hc-fw zmdi-hc-lg"></i>Edit Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a>
|
||||
<a href="{{ baseUrl('/logout') }}" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-fw zmdi-hc-lg"></i>Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="container small" ng-non-bindable>
|
||||
<h1>Create New Book</h1>
|
||||
<form action="/books" method="POST">
|
||||
<form action="{{ baseUrl("/books") }}" method="POST">
|
||||
@include('books/form')
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="container small" ng-non-bindable>
|
||||
<h1>Edit Book</h1>
|
||||
<form action="/books/{{$book->slug}}" method="POST">
|
||||
<form action="{{ $book->getUrl() }}" method="POST">
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
@include('books/form', ['model' => $book])
|
||||
</form>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="col-xs-11 faded">
|
||||
<div class="action-buttons">
|
||||
@if($currentUser->can('book-create-all'))
|
||||
<a href="/books/create" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a>
|
||||
<a href="{{ baseUrl("/books/create") }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>Add new book</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@
|
||||
@else
|
||||
<p class="text-muted">No books have been created.</p>
|
||||
@if(userCan('books-create-all'))
|
||||
<a href="/books/create" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a>
|
||||
<a href="{{ baseUrl("/books/create") }}" class="text-pos"><i class="zmdi zmdi-edit"></i>Create one now</a>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="book entity-list-item" data-entity-type="book" data-entity-id="{{$book->id}}">
|
||||
<h3 class="text-book"><a class="text-book" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></h3>
|
||||
<h3 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h3>
|
||||
@if(isset($book->searchSnippet))
|
||||
<p class="text-muted">{!! $book->searchSnippet !!}</p>
|
||||
@else
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
<div class="col-md-12">
|
||||
<div class="action-buttons faded">
|
||||
@if(userCan('page-create', $book))
|
||||
<a href="{{$book->getUrl() . '/page/create'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Page</a>
|
||||
<a href="{{ $book->getUrl('/page/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Page</a>
|
||||
@endif
|
||||
@if(userCan('chapter-create', $book))
|
||||
<a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Chapter</a>
|
||||
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i> New Chapter</a>
|
||||
@endif
|
||||
@if(userCan('book-update', $book))
|
||||
<a href="{{$book->getEditUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
@@ -21,13 +21,13 @@
|
||||
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-more-vert"></i></a>
|
||||
<ul>
|
||||
@if(userCan('book-update', $book))
|
||||
<li><a href="{{ $book->getUrl() }}/sort" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a></li>
|
||||
<li><a href="{{ $book->getUrl('/sort') }}" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a></li>
|
||||
@endif
|
||||
@if(userCan('restrictions-manage', $book))
|
||||
<li><a href="{{$book->getUrl()}}/permissions" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
<li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
@endif
|
||||
@if(userCan('book-delete', $book))
|
||||
<li><a href="{{ $book->getUrl() }}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
<li><a href="{{ $book->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@@ -61,16 +61,16 @@
|
||||
@else
|
||||
<p class="text-muted">No pages or chapters have been created for this book.</p>
|
||||
<p>
|
||||
<a href="{{$book->getUrl() . '/page/create'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
|
||||
<a href="{{ $book->getUrl('/page/create') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
|
||||
<em class="text-muted">-or-</em>
|
||||
<a href="{{$book->getUrl() . '/chapter/create'}}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>Add a chapter</a>
|
||||
<a href="{{ $book->getUrl('/chapter/create') }}" class="text-chapter"><i class="zmdi zmdi-collection-bookmark"></i>Add a chapter</a>
|
||||
</p>
|
||||
<hr>
|
||||
@endif
|
||||
<p class="text-muted small">
|
||||
Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by <a href="/user/{{ $book->createdBy->id }}">{{$book->createdBy->name}}</a> @endif
|
||||
Created {{$book->created_at->diffForHumans()}} @if($book->createdBy) by <a href="{{ $book->createdBy->getProfileUrl() }}">{{$book->createdBy->name}}</a> @endif
|
||||
<br>
|
||||
Last Updated {{$book->updated_at->diffForHumans()}} @if($book->updatedBy) by <a href="/user/{{ $book->updatedBy->id }}">{{$book->updatedBy->name}}</a> @endif
|
||||
Last Updated {{$book->updated_at->diffForHumans()}} @if($book->updatedBy) by <a href="{{ $book->updatedBy->getProfileUrl() }}">{{$book->updatedBy->name}}</a> @endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,7 +90,7 @@
|
||||
@if($book->restricted)
|
||||
<p class="text-muted">
|
||||
@if(userCan('restrictions-manage', $book))
|
||||
<a href="{{ $book->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
|
||||
@endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@extends('base')
|
||||
|
||||
@section('head')
|
||||
<script src="/libs/jquery-sortable/jquery-sortable.min.js"></script>
|
||||
<script src="{{ baseUrl("/libs/jquery-sortable/jquery-sortable.min.js") }}"></script>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@@ -22,7 +22,7 @@
|
||||
@foreach($books as $otherBook)
|
||||
@if($otherBook->id !== $book->id)
|
||||
<div>
|
||||
<a href="/books/{{ $otherBook->slug }}/sort-item" class="text-book"><i class="zmdi zmdi-book"></i>{{ $otherBook->name }}</a>
|
||||
<a href="{{ $otherBook->getUrl('/sort-item') }}" class="text-book"><i class="zmdi zmdi-book"></i>{{ $otherBook->name }}</a>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
@@ -32,12 +32,12 @@
|
||||
|
||||
</div>
|
||||
|
||||
<form action="{{$book->getUrl()}}/sort" method="POST">
|
||||
<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">Cancel</a>
|
||||
<a href="{{ $book->getUrl() }}" class="button muted">Cancel</a>
|
||||
<button class="button pos" type="submit">Save Order</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -50,7 +50,7 @@
|
||||
var sortableOptions = {
|
||||
group: 'serialization',
|
||||
onDrop: function($item, container, _super) {
|
||||
var pageMap = buildPageMap();
|
||||
var pageMap = buildEntityMap();
|
||||
$('#sort-tree-input').val(JSON.stringify(pageMap));
|
||||
_super($item, container);
|
||||
},
|
||||
@@ -74,29 +74,42 @@
|
||||
$link.remove();
|
||||
});
|
||||
|
||||
function buildPageMap() {
|
||||
var pageMap = [];
|
||||
/**
|
||||
* Build up a mapping of entities with their ordering and nesting.
|
||||
* @returns {Array}
|
||||
*/
|
||||
function buildEntityMap() {
|
||||
var entityMap = [];
|
||||
var $lists = $('.sort-list');
|
||||
$lists.each(function(listIndex) {
|
||||
var list = $(this);
|
||||
var bookId = list.closest('[data-type="book"]').attr('data-id');
|
||||
var $childElements = list.find('[data-type="page"], [data-type="chapter"]');
|
||||
$childElements.each(function(childIndex) {
|
||||
var $directChildren = list.find('> [data-type="page"], > [data-type="chapter"]');
|
||||
$directChildren.each(function(directChildIndex) {
|
||||
var $childElem = $(this);
|
||||
var type = $childElem.attr('data-type');
|
||||
var parentChapter = false;
|
||||
if(type === 'page' && $childElem.closest('[data-type="chapter"]').length === 1) {
|
||||
parentChapter = $childElem.closest('[data-type="chapter"]').attr('data-id');
|
||||
}
|
||||
pageMap.push({
|
||||
id: $childElem.attr('data-id'),
|
||||
var childId = $childElem.attr('data-id');
|
||||
entityMap.push({
|
||||
id: childId,
|
||||
sort: directChildIndex,
|
||||
parentChapter: parentChapter,
|
||||
type: type,
|
||||
book: bookId
|
||||
});
|
||||
$chapterChildren = $childElem.find('[data-type="page"]').each(function(pageIndex) {
|
||||
var $chapterChild = $(this);
|
||||
entityMap.push({
|
||||
id: $chapterChild.attr('data-id'),
|
||||
sort: pageIndex,
|
||||
parentChapter: childId,
|
||||
type: 'page',
|
||||
book: bookId
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return pageMap;
|
||||
return entityMap;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="container small" ng-non-bindable>
|
||||
<h1>Create New Chapter</h1>
|
||||
<form action="{{$book->getUrl()}}/chapter/create" method="POST">
|
||||
<form action="{{ $book->getUrl('/chapter/create') }}" method="POST">
|
||||
@include('chapters/form')
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
and added directly to the book.</p>
|
||||
<p class="text-neg">Are you sure you want to delete this chapter?</p>
|
||||
|
||||
<form action="{{$chapter->getUrl()}}" method="POST">
|
||||
<form action="{{ $chapter->getUrl() }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<a href="{{$chapter->getUrl()}}" class="button primary">Cancel</a>
|
||||
<a href="{{ $chapter->getUrl() }}" class="button primary">Cancel</a>
|
||||
<button type="submit" class="button neg">Confirm</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div class="container small" ng-non-bindable>
|
||||
<h1>Edit Chapter</h1>
|
||||
<form action="{{$chapter->getUrl()}}" method="POST">
|
||||
<form action="{{ $chapter->getUrl() }}" method="POST">
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
@include('chapters/form', ['model' => $chapter])
|
||||
</form>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</a>
|
||||
<span class="text-muted"> » </span>
|
||||
@endif
|
||||
<a href="{{ $chapter->getUrl() }}" class="text-chapter">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->name }}
|
||||
<a href="{{ $chapter->getUrl() }}" class="text-chapter entity-list-item-link">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i><span class="entity-list-item-name">{{ $chapter->name }}</span>
|
||||
</a>
|
||||
</h3>
|
||||
@if(isset($chapter->searchSnippet))
|
||||
@@ -20,7 +20,7 @@
|
||||
<p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ count($chapter->pages) }} Pages</span></p>
|
||||
<div class="inset-list">
|
||||
@foreach($chapter->pages as $page)
|
||||
<h4 class="@if($page->draft) draft @endif"><a href="{{$page->getUrl()}}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h4>
|
||||
<h4 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h4>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<div class="row">
|
||||
<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>
|
||||
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
<span class="sep">»</span>
|
||||
<a href="{{$chapter->getUrl()}}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->getShortName() }}</a>
|
||||
<a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->getShortName() }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="container">
|
||||
<h1>Move Chapter <small class="subheader">{{$chapter->name}}</small></h1>
|
||||
|
||||
<form action="{{ $chapter->getUrl() }}/move" method="POST">
|
||||
<form action="{{ $chapter->getUrl('/move') }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12 faded">
|
||||
<div class="breadcrumbs">
|
||||
<a href="{{$chapter->book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
|
||||
<a href="{{ $chapter->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $chapter->book->getShortName() }}</a>
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $chapter->getUrl() }}" class="text-chapter text-button"><i class="zmdi zmdi-collection-bookmark"></i>{{$chapter->getShortName()}}</a>
|
||||
</div>
|
||||
|
||||
@@ -7,29 +7,29 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-8 faded" ng-non-bindable>
|
||||
<div class="breadcrumbs">
|
||||
<a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 faded">
|
||||
<div class="action-buttons">
|
||||
@if(userCan('page-create', $chapter))
|
||||
<a href="{{$chapter->getUrl() . '/create-page'}}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>New Page</a>
|
||||
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-pos text-button"><i class="zmdi zmdi-plus"></i>New Page</a>
|
||||
@endif
|
||||
@if(userCan('chapter-update', $chapter))
|
||||
<a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
<a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button"><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
@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>
|
||||
<ul>
|
||||
@if(userCan('chapter-update', $chapter))
|
||||
<li><a href="{{$chapter->getUrl() . '/move'}}" class="text-primary"><i class="zmdi zmdi-folder"></i>Move</a></li>
|
||||
<li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary"><i class="zmdi zmdi-folder"></i>Move</a></li>
|
||||
@endif
|
||||
@if(userCan('restrictions-manage', $chapter))
|
||||
<li><a href="{{$chapter->getUrl()}}/permissions" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
<li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
@endif
|
||||
@if(userCan('chapter-delete', $chapter))
|
||||
<li><a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
<li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@@ -60,22 +60,22 @@
|
||||
<p class="text-muted">No pages are currently in this chapter.</p>
|
||||
<p>
|
||||
@if(userCan('page-create', $chapter))
|
||||
<a href="{{$chapter->getUrl() . '/create-page'}}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
|
||||
<a href="{{ $chapter->getUrl('/create-page') }}" class="text-page"><i class="zmdi zmdi-file-text"></i>Create a new page</a>
|
||||
@endif
|
||||
@if(userCan('page-create', $chapter) && userCan('book-update', $book))
|
||||
<em class="text-muted">-or-</em>
|
||||
@endif
|
||||
@if(userCan('book-update', $book))
|
||||
<a href="{{$book->getUrl() . '/sort'}}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
|
||||
<a href="{{ $book->getUrl('/sort') }}" class="text-book"><i class="zmdi zmdi-book"></i>Sort the current book</a>
|
||||
@endif
|
||||
</p>
|
||||
<hr>
|
||||
@endif
|
||||
|
||||
<p class="text-muted small">
|
||||
Created {{$chapter->created_at->diffForHumans()}} @if($chapter->createdBy) by <a href="/user/{{ $chapter->createdBy->id }}">{{ $chapter->createdBy->name}}</a> @endif
|
||||
Created {{ $chapter->created_at->diffForHumans() }} @if($chapter->createdBy) by <a href="{{ $chapter->createdBy->getProfileUrl() }}">{{ $chapter->createdBy->name}}</a> @endif
|
||||
<br>
|
||||
Last Updated {{$chapter->updated_at->diffForHumans()}} @if($chapter->updatedBy) by <a href="/user/{{ $chapter->updatedBy->id }}">{{ $chapter->updatedBy->name}}</a> @endif
|
||||
Last Updated {{ $chapter->updated_at->diffForHumans() }} @if($chapter->updatedBy) by <a href="{{ $chapter->updatedBy->getProfileUrl() }}">{{ $chapter->updatedBy->name}}</a> @endif
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1">
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
@if($book->restricted)
|
||||
@if(userCan('restrictions-manage', $book))
|
||||
<a href="{{ $book->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
|
||||
@endif
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
@if($chapter->restricted)
|
||||
@if(userCan('restrictions-manage', $chapter))
|
||||
<a href="{{ $chapter->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
|
||||
<a href="{{ $chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active
|
||||
@endif
|
||||
|
||||
@@ -1,176 +1,190 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
|
||||
<head style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<meta name="viewport" content="width=device-width" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;" />
|
||||
<title>Confirm Your Email At {{ Setting::get('app-name')}}</title>
|
||||
<meta name="viewport" content="width=device-width"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"/>
|
||||
<title>Confirm Your Email At {{ setting('app-name')}}</title>
|
||||
<style style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100%!important;
|
||||
height: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #348eda;
|
||||
color: #348eda;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #348eda;
|
||||
border: solid #348eda;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #348eda;
|
||||
border: solid #348eda;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #aaa;
|
||||
border: solid #aaa;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 25px;
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #aaa;
|
||||
border: solid #aaa;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 10px 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
table.body-wrap {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
table.body-wrap .container {
|
||||
border: 1px solid #f0f0f0;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
color: #444;
|
||||
margin: 10px 0 10px;
|
||||
line-height: 1.2;
|
||||
font-weight: 200;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
color: #444;
|
||||
margin: 10px 0 10px;
|
||||
line-height: 1.2;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 22px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block!important;
|
||||
max-width: 600px!important;
|
||||
margin: 0 auto!important;
|
||||
clear: both!important;
|
||||
display: block !important;
|
||||
max-width: 600px !important;
|
||||
margin: 0 auto !important;
|
||||
clear: both !important;
|
||||
}
|
||||
|
||||
.body-wrap .container {
|
||||
padding: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content table {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#f6f6f6" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;">
|
||||
<!-- body -->
|
||||
<table class="body-wrap" bgcolor="#f6f6f6" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;">
|
||||
<body bgcolor="#f6f6f6"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;">
|
||||
<!-- body -->
|
||||
<table class="body-wrap" bgcolor="#f6f6f6"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"></td>
|
||||
<td class="container" bgcolor="#FFFFFF" style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;display:block!important;max-width:600px!important;margin-top:0 !important;margin-bottom:0 !important;margin-right:auto !important;margin-left:auto !important;clear:both!important;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;border-width:1px;border-style:solid;border-color:#f0f0f0;">
|
||||
<!-- content -->
|
||||
<div class="content" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;display:block;">
|
||||
<table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<h1 style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;color:#444;margin-top:10px;margin-bottom:10px;margin-right:0;margin-left:0;line-height:1.2;font-weight:200;font-size:36px;">Email Confirmation</h1>
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;">Thanks for joining <a href="{{ url('/') }}">{{ Setting::get('app-name')}}</a>. <br />
|
||||
Please confirm your email address by clicking the button below.</p>
|
||||
<table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td class="padding" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;padding-top:10px;padding-bottom:10px;padding-right:0;padding-left:0;">
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;"><a class="btn-primary" href="{{ url('/register/confirm/'.$token) }}" style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm Email</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /content -->
|
||||
</td>
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /body -->
|
||||
</body>
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"></td>
|
||||
<td class="container" bgcolor="#FFFFFF"
|
||||
style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;display:block!important;max-width:600px!important;margin-top:0 !important;margin-bottom:0 !important;margin-right:auto !important;margin-left:auto !important;clear:both!important;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;border-width:1px;border-style:solid;border-color:#f0f0f0;">
|
||||
<!-- content -->
|
||||
<div class="content"
|
||||
style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;display:block;">
|
||||
<table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<h1 style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;color:#444;margin-top:10px;margin-bottom:10px;margin-right:0;margin-left:0;line-height:1.2;font-weight:200;font-size:36px;">
|
||||
Email Confirmation</h1>
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;">
|
||||
Thanks for joining <a href="{{ baseUrl('/') }}">{{ setting('app-name')}}</a>. <br/>
|
||||
Please confirm your email address by clicking the button below.</p>
|
||||
<table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td class="padding"
|
||||
style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;padding-top:10px;padding-bottom:10px;padding-right:0;padding-left:0;">
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;">
|
||||
<a class="btn-primary" href="{{ baseUrl('/register/confirm/' . $token) }}"
|
||||
style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm
|
||||
Email</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /content -->
|
||||
</td>
|
||||
<td style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /body -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
<div class="container">
|
||||
<h1 class="text-muted">{{ $message or 'Page Not Found' }}</h1>
|
||||
<p>Sorry, The page you were looking for could not be found.</p>
|
||||
<a href="/" class="button">Return To Home</a>
|
||||
<a href="{{ baseUrl('/') }}" class="button">Return To Home</a>
|
||||
</div>
|
||||
|
||||
@stop
|
||||
@@ -1,47 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Be right back.</title>
|
||||
@extends('public')
|
||||
|
||||
<link href="//fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">
|
||||
@section('content')
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
<div class="container">
|
||||
<h1 class="text-muted">{{ setting('app-name') }} is down right now</h1>
|
||||
<p>It will be back up soon.</p>
|
||||
</div>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: #B0BEC5;
|
||||
display: table;
|
||||
font-weight: 100;
|
||||
font-family: 'Lato';
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 72px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="title">Be right back.</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@stop
|
||||
@@ -1,4 +1,4 @@
|
||||
<form action="{{ $model->getUrl() }}/permissions" method="POST">
|
||||
<form action="{{ $model->getUrl('/permissions') }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<h3><a class="no-color" href="/pages/recently-created">Recently Created Pages</a></h3>
|
||||
<h3><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">Recently Created Pages</a></h3>
|
||||
<div id="recently-created-pages">
|
||||
@include('partials/entity-list', [
|
||||
'entities' => $recentlyCreatedPages,
|
||||
@@ -51,7 +51,7 @@
|
||||
])
|
||||
</div>
|
||||
|
||||
<h3><a class="no-color" href="/pages/recently-updated">Recently Updated Pages</a></h3>
|
||||
<h3><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">Recently Updated Pages</a></h3>
|
||||
<div id="recently-updated-pages">
|
||||
@include('partials/entity-list', [
|
||||
'entities' => $recentlyUpdatedPages,
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
<h1>Delete {{ $page->draft ? 'Draft' : '' }} Page</h1>
|
||||
<p class="text-neg">Are you sure you want to delete this {{ $page->draft ? 'draft' : '' }} page?</p>
|
||||
|
||||
<form action="{{$page->getUrl()}}" method="POST">
|
||||
<form action="{{ $page->getUrl() }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="DELETE">
|
||||
<a href="{{$page->getUrl()}}" class="button primary">Cancel</a>
|
||||
<a href="{{ $page->getUrl() }}" class="button primary">Cancel</a>
|
||||
<button type="submit" class="button neg">Confirm</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@extends('base')
|
||||
|
||||
@section('head')
|
||||
<script src="/libs/tinymce/tinymce.min.js?ver=4.3.7"></script>
|
||||
<script src="{{ baseUrl('/libs/tinymce/tinymce.min.js?ver=4.3.7') }}"></script>
|
||||
@stop
|
||||
|
||||
@section('body-class', 'flexbox')
|
||||
@@ -9,7 +9,7 @@
|
||||
@section('content')
|
||||
|
||||
<div class="flex-fill flex">
|
||||
<form action="{{$page->getUrl()}}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
|
||||
<form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
|
||||
@if(!isset($isDraft))
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
@endif
|
||||
@@ -19,6 +19,14 @@
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
|
||||
@include('partials/entity-selector-popup')
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
@stop
|
||||
@@ -14,8 +14,8 @@
|
||||
<tbody ui-sortable="sortOptions" ng-model="tags" >
|
||||
<tr ng-repeat="tag in tags track by $index">
|
||||
<td width="20" ><i class="handle zmdi zmdi-menu"></i></td>
|
||||
<td><input autosuggest="/ajax/tags/suggest/names" autosuggest-type="name" class="outline" ng-attr-name="tags[@{{$index}}][name]" type="text" ng-model="tag.name" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag"></td>
|
||||
<td><input autosuggest="/ajax/tags/suggest/values" autosuggest-type="value" class="outline" ng-attr-name="tags[@{{$index}}][value]" type="text" ng-model="tag.value" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag Value (Optional)"></td>
|
||||
<td><input autosuggest="{{ baseUrl('/ajax/tags/suggest/names') }}" autosuggest-type="name" class="outline" ng-attr-name="tags[@{{$index}}][name]" type="text" ng-model="tag.name" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag"></td>
|
||||
<td><input autosuggest="{{ baseUrl('/ajax/tags/suggest/values') }}" autosuggest-type="value" class="outline" ng-attr-name="tags[@{{$index}}][value]" type="text" ng-model="tag.value" ng-change="tagChange(tag)" ng-blur="tagBlur(tag)" placeholder="Tag Value (Optional)"></td>
|
||||
<td width="10" ng-show="tags.length != 1" class="text-center text-neg" style="padding: 0;" ng-click="removeTag(tag)"><i class="zmdi zmdi-close"></i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -13,23 +13,35 @@
|
||||
</div>
|
||||
<div class="col-sm-4 faded text-center">
|
||||
|
||||
<div dropdown class="dropdown-container">
|
||||
<div dropdown class="dropdown-container draft-display">
|
||||
<a dropdown-toggle class="text-primary text-button"><span class="faded-text" ng-bind="draftText"></span> <i class="zmdi zmdi-more-vert"></i></a>
|
||||
<i class="zmdi zmdi-check-circle text-pos draft-notification" ng-class="{visible: draftUpdated}"></i>
|
||||
<ul>
|
||||
<li>
|
||||
<a ng-click="forceDraftSave()" class="text-pos"><i class="zmdi zmdi-save"></i>Save Draft</a>
|
||||
</li>
|
||||
<li ng-if="isNewPageDraft">
|
||||
<a href="{{$model->getUrl()}}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete Draft</a>
|
||||
<a href="{{ $model->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete Draft</a>
|
||||
</li>
|
||||
<li>
|
||||
<a type="button" ng-if="isUpdateDraft" ng-click="discardDraft()" class="text-neg"><i class="zmdi zmdi-close-circle"></i>Discard Draft</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 faded">
|
||||
<div class="action-buttons" ng-cloak>
|
||||
<div dropdown class="dropdown-container">
|
||||
<a dropdown-toggle class="text-primary text-button"><i class="zmdi zmdi-edit"></i> @{{(changeSummary | limitTo:16) + (changeSummary.length>16?'...':'') || 'Set Changelog'}}</a>
|
||||
<ul class="wide">
|
||||
<li class="padded">
|
||||
<p class="text-muted">Enter a brief description of the changes you've made</p>
|
||||
<input name="summary" id="summary-input" type="text" placeholder="Enter Changelog" ng-model="changeSummary" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button type="button" ng-if="isUpdateDraft" ng-click="discardDraft()" class="text-button text-neg"><i class="zmdi zmdi-close-circle"></i>Discard Draft</button>
|
||||
<button type="submit" id="save-button" class="text-button text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</button>
|
||||
<button type="submit" id="save-button" class="text-button text-pos"><i class="zmdi zmdi-floppy"></i>Save Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,8 +56,11 @@
|
||||
|
||||
<div class="edit-area flex-fill flex">
|
||||
@if(setting('app-editor') === 'wysiwyg')
|
||||
<textarea id="html-editor" tinymce="editorOptions" mce-change="editorChange" mce-model="editContent" name="html" rows="5"
|
||||
@if($errors->has('html')) class="neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
|
||||
<div tinymce="editorOptions" mce-change="editorChange" mce-model="editContent" class="flex-fill flex">
|
||||
<textarea id="html-editor" name="html" rows="5" ng-non-bindable
|
||||
@if($errors->has('html')) class="neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
|
||||
</div>
|
||||
|
||||
@if($errors->has('html'))
|
||||
<div class="text-neg text-small">{{ $errors->first('html') }}</div>
|
||||
@endif
|
||||
@@ -59,10 +74,16 @@
|
||||
<span class="float left">Editor</span>
|
||||
<div class="float right buttons">
|
||||
<button class="text-button" type="button" data-action="insertImage"><i class="zmdi zmdi-image"></i>Insert Image</button>
|
||||
|
|
||||
<button class="text-button" type="button" data-action="insertEntityLink"><i class="zmdi zmdi-link"></i>Insert Entity Link</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea markdown-input md-change="editorChange" id="markdown-editor-input" md-model="editContent" name="markdown" rows="5"
|
||||
@if($errors->has('markdown')) class="neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
|
||||
|
||||
<div markdown-input md-change="editorChange" md-model="editContent" class="flex flex-fill">
|
||||
<textarea ng-non-bindable id="markdown-editor-input" name="markdown" rows="5"
|
||||
@if($errors->has('markdown')) class="neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="markdown-editor-wrap">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}">
|
||||
<h3>
|
||||
<a href="{{ $page->getUrl() }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ $page->name }}</a>
|
||||
<a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link"><i class="zmdi zmdi-file-text"></i><span class="entity-list-item-name">{{ $page->name }}</span></a>
|
||||
</h3>
|
||||
|
||||
@if(isset($page->searchSnippet))
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
<div class="row">
|
||||
<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>
|
||||
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
@if($page->hasChapter())
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>
|
||||
{{$page->chapter->getShortName()}}
|
||||
{{ $page->chapter->getShortName() }}
|
||||
</a>
|
||||
@endif
|
||||
<span class="sep">»</span>
|
||||
<a href="{{$page->getUrl()}}" class="text-page text-button"><i class="zmdi zmdi-file-text"></i>{{ $page->getShortName() }}</a>
|
||||
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file-text"></i>{{ $page->getShortName() }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="container">
|
||||
<h1>Move Page <small class="subheader">{{$page->name}}</small></h1>
|
||||
|
||||
<form action="{{ $page->getUrl() }}/move" method="POST">
|
||||
<form action="{{ $page->getUrl('/move') }}" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<input type="hidden" name="_method" value="PUT">
|
||||
|
||||
|
||||
@@ -4,14 +4,20 @@
|
||||
|
||||
@if(count($page->tags) > 0)
|
||||
<div class="tag-display float right">
|
||||
<div class="heading primary-background-light">Page Tags</div>
|
||||
<table>
|
||||
@foreach($page->tags as $tag)
|
||||
<tr class="tag">
|
||||
<td @if(!$tag->value) colspan="2" @endif><a href="/search/all?term=%5B{{ urlencode($tag->name) }}%5D">{{ $tag->name }}</a></td>
|
||||
@if($tag->value) <td class="tag-value"><a href="/search/all?term=%5B{{ urlencode($tag->name) }}%3D{{ urlencode($tag->value) }}%5D">{{$tag->value}}</a></td> @endif
|
||||
<thead>
|
||||
<tr class="text-left heading primary-background-light">
|
||||
<th colspan="2">Page Tags</th>
|
||||
</tr>
|
||||
@endforeach
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($page->tags as $tag)
|
||||
<tr class="tag">
|
||||
<td @if(!$tag->value) colspan="2" @endif><a href="{{ baseUrl('/search/all?term=%5B' . urlencode($tag->name) .'%5D') }}">{{ $tag->name }}</a></td>
|
||||
@if($tag->value) <td class="tag-value"><a href="{{ baseUrl('/search/all?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></td> @endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
@section('head')
|
||||
<style>
|
||||
body {
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -21,10 +21,21 @@
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.page-content .float {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.page-content img.align-left, .page-content img.align-right {
|
||||
float: none !important;
|
||||
clear: both;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tag-display {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
@stop
|
||||
@@ -7,16 +7,16 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12 faded">
|
||||
<div class="breadcrumbs">
|
||||
<a href="{{$page->book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
|
||||
<a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
|
||||
@if($page->hasChapter())
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>
|
||||
{{$page->chapter->getShortName()}}
|
||||
{{ $page->chapter->getShortName() }}
|
||||
</a>
|
||||
@endif
|
||||
<span class="sep">»</span>
|
||||
<a href="{{$page->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
|
||||
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,45 +5,59 @@
|
||||
<div class="faded-small toolbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 faded">
|
||||
<div class="col-sm-12 faded">
|
||||
<div class="breadcrumbs">
|
||||
<a href="{{$page->getUrl()}}" class="text-primary text-button"><i class="zmdi zmdi-arrow-left"></i>Back to page</a>
|
||||
<a href="{{ $page->book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $page->book->getShortName() }}</a>
|
||||
@if($page->hasChapter())
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>
|
||||
{{ $page->chapter->getShortName() }}
|
||||
</a>
|
||||
@endif
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->getUrl() }}" class="text-page text-button"><i class="zmdi zmdi-file"></i>{{ $page->getShortName() }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 faded">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container small" ng-non-bindable>
|
||||
|
||||
<div class="container" ng-non-bindable>
|
||||
<h1>Page Revisions <span class="subheader">For "{{ $page->name }}"</span></h1>
|
||||
|
||||
@if(count($page->revisions) > 0)
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th width="40%">Name</th>
|
||||
<th colspan="2" width="20%">Created By</th>
|
||||
<th width="20%">Revision Date</th>
|
||||
<th width="20%">Actions</th>
|
||||
<th width="25%">Name</th>
|
||||
<th colspan="2" width="10%">Created By</th>
|
||||
<th width="15%">Revision Date</th>
|
||||
<th width="25%">Changelog</th>
|
||||
<th width="15%">Actions</th>
|
||||
</tr>
|
||||
@foreach($page->revisions as $revision)
|
||||
@foreach($page->revisions as $index => $revision)
|
||||
<tr>
|
||||
<td>{{$revision->name}}</td>
|
||||
<td>{{ $revision->name }}</td>
|
||||
<td style="line-height: 0;">
|
||||
@if($revision->createdBy)
|
||||
<img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{$revision->createdBy->name}}">
|
||||
<img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
|
||||
@endif
|
||||
</td>
|
||||
<td> @if($revision->createdBy) {{$revision->createdBy->name}} @else Deleted User @endif</td>
|
||||
<td><small>{{$revision->created_at->format('jS F, Y H:i:s')}} <br> ({{$revision->created_at->diffForHumans()}})</small></td>
|
||||
<td>
|
||||
<a href="{{$revision->getUrl()}}" target="_blank">Preview</a>
|
||||
<span class="text-muted"> | </span>
|
||||
<a href="{{$revision->getUrl()}}/restore">Restore</a>
|
||||
</td>
|
||||
<td> @if($revision->createdBy) {{ $revision->createdBy->name }} @else Deleted User @endif</td>
|
||||
<td><small>{{ $revision->created_at->format('jS F, Y H:i:s') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
|
||||
<td>{{ $revision->summary }}</td>
|
||||
@if ($index !== 0)
|
||||
<td>
|
||||
<a href="{{ $revision->getUrl() }}" target="_blank">Preview</a>
|
||||
<span class="text-muted"> | </span>
|
||||
<a href="{{ $revision->getUrl() }}/restore">Restore</a>
|
||||
</td>
|
||||
@else
|
||||
<td><a target="_blank" href="{{ $page->getUrl() }}"><i>Current Version</i></a></td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-6 faded">
|
||||
<div class="breadcrumbs">
|
||||
<a href="{{$book->getUrl()}}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
<a href="{{ $book->getUrl() }}" class="text-book text-button"><i class="zmdi zmdi-book"></i>{{ $book->getShortName() }}</a>
|
||||
@if($page->hasChapter())
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>
|
||||
{{$page->chapter->getShortName()}}
|
||||
{{ $page->chapter->getShortName() }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@@ -22,27 +22,27 @@
|
||||
<span dropdown class="dropdown-container">
|
||||
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>Export</div>
|
||||
<ul class="wide">
|
||||
<li><a href="{{$page->getUrl()}}/export/html" target="_blank">Contained Web File <span class="text-muted float right">.html</span></a></li>
|
||||
<li><a href="{{$page->getUrl()}}/export/pdf" target="_blank">PDF File <span class="text-muted float right">.pdf</span></a></li>
|
||||
<li><a href="{{$page->getUrl()}}/export/plaintext" target="_blank">Plain Text File <span class="text-muted float right">.txt</span></a></li>
|
||||
<li><a href="{{ $page->getUrl('/export/html') }}" target="_blank">Contained Web File <span class="text-muted float right">.html</span></a></li>
|
||||
<li><a href="{{ $page->getUrl('/export/pdf') }}" target="_blank">PDF File <span class="text-muted float right">.pdf</span></a></li>
|
||||
<li><a href="{{ $page->getUrl('/export/plaintext') }}" target="_blank">Plain Text File <span class="text-muted float right">.txt</span></a></li>
|
||||
</ul>
|
||||
</span>
|
||||
@if(userCan('page-update', $page))
|
||||
<a href="{{$page->getUrl()}}/edit" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
<a href="{{ $page->getUrl('/edit') }}" class="text-primary text-button" ><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
@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>
|
||||
<ul>
|
||||
@if(userCan('page-update', $page))
|
||||
<li><a href="{{$page->getUrl()}}/move" class="text-primary" ><i class="zmdi zmdi-folder"></i>Move</a></li>
|
||||
<li><a href="{{$page->getUrl()}}/revisions" class="text-primary"><i class="zmdi zmdi-replay"></i>Revisions</a></li>
|
||||
<li><a href="{{ $page->getUrl('/move') }}" class="text-primary" ><i class="zmdi zmdi-folder"></i>Move</a></li>
|
||||
<li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary"><i class="zmdi zmdi-replay"></i>Revisions</a></li>
|
||||
@endif
|
||||
@if(userCan('restrictions-manage', $page))
|
||||
<li><a href="{{$page->getUrl()}}/permissions" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
<li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary"><i class="zmdi zmdi-lock-outline"></i>Permissions</a></li>
|
||||
@endif
|
||||
@if(userCan('page-delete', $page))
|
||||
<li><a href="{{$page->getUrl()}}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
<li><a href="{{ $page->getUrl('/delete') }}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a></li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="container" id="page-show" ng-non-bindable>
|
||||
<div class="row">
|
||||
<div class="col-md-9 print-full-width">
|
||||
<div class="page-content anim fadeIn">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="pointer-container" id="pointer">
|
||||
<div class="pointer anim">
|
||||
@@ -73,13 +73,14 @@
|
||||
<hr>
|
||||
|
||||
<p class="text-muted small">
|
||||
Created {{$page->created_at->diffForHumans()}} @if($page->createdBy) by <a href="/user/{{ $page->createdBy->id }}">{{$page->createdBy->name}}</a> @endif
|
||||
Created {{ $page->created_at->diffForHumans() }} @if($page->createdBy) by <a href="{{ $page->createdBy->getProfileUrl() }}">{{$page->createdBy->name}}</a> @endif
|
||||
<br>
|
||||
Last Updated {{$page->updated_at->diffForHumans()}} @if($page->updatedBy) by <a href="/user/{{ $page->updatedBy->id }}">{{$page->updatedBy->name}}</a> @endif
|
||||
Last Updated {{ $page->updated_at->diffForHumans() }} @if($page->updatedBy) by <a href="{{ $page->updatedBy->getProfileUrl() }}">{{$page->updatedBy->name}}</a> @endif
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 print-hidden">
|
||||
<div class="margin-top large"></div>
|
||||
@if($book->restricted || ($page->chapter && $page->chapter->restricted) || $page->restricted)
|
||||
@@ -87,7 +88,7 @@
|
||||
|
||||
@if($book->restricted)
|
||||
@if(userCan('restrictions-manage', $book))
|
||||
<a href="{{ $book->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
<a href="{{ $book->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Book Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Book Permissions Active
|
||||
@endif
|
||||
@@ -96,7 +97,7 @@
|
||||
|
||||
@if($page->chapter && $page->chapter->restricted)
|
||||
@if(userCan('restrictions-manage', $page->chapter))
|
||||
<a href="{{ $page->chapter->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
|
||||
<a href="{{ $page->chapter->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Chapter Permissions Active
|
||||
@endif
|
||||
@@ -105,7 +106,7 @@
|
||||
|
||||
@if($page->restricted)
|
||||
@if(userCan('restrictions-manage', $page))
|
||||
<a href="{{ $page->getUrl() }}/permissions"><i class="zmdi zmdi-lock-outline"></i>Page Permissions Active</a>
|
||||
<a href="{{ $page->getUrl('/permissions') }}"><i class="zmdi zmdi-lock-outline"></i>Page Permissions Active</a>
|
||||
@else
|
||||
<i class="zmdi zmdi-lock-outline"></i>Page Permissions Active
|
||||
@endif
|
||||
@@ -114,8 +115,8 @@
|
||||
</div>
|
||||
@endif
|
||||
@include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree])
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<div class="book-tree" ng-non-bindable>
|
||||
<h6 class="text-muted">Book Navigation</h6>
|
||||
<ul class="sidebar-page-list menu">
|
||||
<li class="book-header"><a href="{{$book->getUrl()}}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
|
||||
<li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
|
||||
|
||||
|
||||
@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' : '' }}">
|
||||
<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>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<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' : '' }}">
|
||||
<a href="{{ $childPage->getUrl() }}" class="page {{ $current->matches($childPage)? 'selected' : '' }}">
|
||||
<i class="zmdi zmdi-file-text"></i> {{ $childPage->name }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
@if($activity->user)
|
||||
<div class="left">
|
||||
<img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{$activity->user->name}}">
|
||||
<img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{ $activity->user->name }}">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="right" ng-non-bindable>
|
||||
@if($activity->user)
|
||||
<a href="/user/{{ $activity->user->id }}">{{$activity->user->name}}</a>
|
||||
<a href="{{ $activity->user->getProfileUrl() }}">{{ $activity->user->name }}</a>
|
||||
@else
|
||||
A deleted user
|
||||
@endif
|
||||
@@ -20,7 +20,7 @@
|
||||
<a href="{{ $activity->entity->getUrl() }}">{{ $activity->entity->name }}</a>
|
||||
@endif
|
||||
|
||||
@if($activity->extra) "{{$activity->extra}}" @endif
|
||||
@if($activity->extra) "{{ $activity->extra }}" @endif
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<style>
|
||||
header, #back-to-top, .primary-background {
|
||||
background-color: {{ Setting::get('app-color') }} !important;
|
||||
background-color: {{ setting('app-color') }} !important;
|
||||
}
|
||||
.faded-small, .primary-background-light {
|
||||
background-color: {{ Setting::get('app-color-light') }};
|
||||
background-color: {{ setting('app-color-light') }};
|
||||
}
|
||||
.button-base, .button, input[type="button"], input[type="submit"] {
|
||||
background-color: {{ Setting::get('app-color') }};
|
||||
background-color: {{ setting('app-color') }};
|
||||
}
|
||||
.button-base:hover, .button:hover, input[type="button"]:hover, input[type="submit"]:hover, .button:focus {
|
||||
background-color: {{ Setting::get('app-color') }};
|
||||
background-color: {{ setting('app-color') }};
|
||||
}
|
||||
.nav-tabs a.selected, .nav-tabs .tab-item.selected {
|
||||
border-bottom-color: {{ Setting::get('app-color') }};
|
||||
border-bottom-color: {{ setting('app-color') }};
|
||||
}
|
||||
p.primary:hover, p .primary:hover, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus {
|
||||
color: {{ Setting::get('app-color') }};
|
||||
color: {{ setting('app-color') }};
|
||||
}
|
||||
</style>
|
||||
14
resources/views/partials/entity-selector-popup.blade.php
Normal file
14
resources/views/partials/entity-selector-popup.blade.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<div id="entity-selector-wrap">
|
||||
<div class="overlay" entity-link-selector>
|
||||
<div class="popup-body small flex-child">
|
||||
<div class="popup-header primary-background">
|
||||
<div class="popup-title">Entity Select</div>
|
||||
<button type="button" class="corner-button neg button popup-close">x</button>
|
||||
</div>
|
||||
@include('partials/entity-selector', ['name' => 'entity-selector'])
|
||||
<div class="popup-footer">
|
||||
<button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">Select</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
<script src="/libs/highlightjs/highlight.min.js"></script>
|
||||
<script src="{{ baseUrl('/libs/highlightjs/highlight.min.js') }}"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
var aCodes = document.getElementsByTagName('pre');
|
||||
|
||||
@@ -1,84 +1,94 @@
|
||||
<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
|
||||
<div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()">
|
||||
<div class="image-manager-body" ng-click="$event.stopPropagation()">
|
||||
<div class="overlay" ng-cloak ng-click="hide()">
|
||||
<div class="popup-body" ng-click="$event.stopPropagation()">
|
||||
|
||||
<div class="image-manager-content">
|
||||
<div ng-if="imageType === 'gallery'" class="container">
|
||||
<div class="image-manager-header row faded-small nav-tabs">
|
||||
<div class="col-xs-4 tab-item" title="View all images" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> All</div>
|
||||
<div class="col-xs-4 tab-item" title="View images uploaded to this book" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> Book</div>
|
||||
<div class="col-xs-4 tab-item" title="View images uploaded to this page" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> Page</div>
|
||||
<div class="popup-header primary-background">
|
||||
<div class="popup-title">Image Select</div>
|
||||
<button class="popup-close neg corner-button button">x</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill image-manager-body">
|
||||
|
||||
<div class="image-manager-content">
|
||||
<div ng-if="imageType === 'gallery'" class="container">
|
||||
<div class="image-manager-header row faded-small nav-tabs">
|
||||
<div class="col-xs-4 tab-item" title="View all images" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> All</div>
|
||||
<div class="col-xs-4 tab-item" title="View images uploaded to this book" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> Book</div>
|
||||
<div class="col-xs-4 tab-item" title="View images uploaded to this page" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> Page</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="view === 'all'" >
|
||||
<form ng-submit="searchImages()" class="contained-search-box">
|
||||
<input type="text" placeholder="Search by image name" ng-model="searchTerm">
|
||||
<button ng-class="{active: searching}" title="Clear Search" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
|
||||
<button title="Search" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="image-manager-list">
|
||||
<div ng-repeat="image in images">
|
||||
<div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"
|
||||
ng-class="{selected: (image==selectedImage)}" ng-click="imageSelect(image)">
|
||||
<img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
|
||||
<div class="image-meta">
|
||||
<span class="name" ng-bind="image.name"></span>
|
||||
<span class="date">Uploaded @{{ getDate(image.created_at) | date:'mediumDate' }}</span>
|
||||
<div ng-show="view === 'all'" >
|
||||
<form ng-submit="searchImages()" class="contained-search-box">
|
||||
<input type="text" placeholder="Search by image name" ng-model="searchTerm">
|
||||
<button ng-class="{active: searching}" title="Clear Search" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
|
||||
<button title="Search" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="image-manager-list">
|
||||
<div ng-repeat="image in images">
|
||||
<div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"
|
||||
ng-class="{selected: (image==selectedImage)}" ng-click="imageSelect(image)">
|
||||
<img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
|
||||
<div class="image-meta">
|
||||
<span class="name" ng-bind="image.name"></span>
|
||||
<span class="date">Uploaded @{{ getDate(image.created_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div>
|
||||
</div>
|
||||
<div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div>
|
||||
</div>
|
||||
|
||||
<div class="image-manager-sidebar">
|
||||
<div class="inner">
|
||||
|
||||
<div class="image-manager-details anim fadeIn" ng-show="selectedImage">
|
||||
|
||||
<form ng-submit="saveImageDetails($event)">
|
||||
<div>
|
||||
<a ng-href="@{{selectedImage.url}}" target="_blank" style="display: block;">
|
||||
<img ng-src="@{{selectedImage.thumbs.gallery}}" ng-attr-alt="@{{selectedImage.title}}" ng-attr-title="@{{selectedImage.name}}">
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Image Name</label>
|
||||
<input type="text" id="name" name="name" ng-model="selectedImage.name">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div ng-show="dependantPages">
|
||||
<p class="text-neg text-small">
|
||||
This image is used in the pages below, Click delete again to confirm you want to delete
|
||||
this image.
|
||||
</p>
|
||||
<ul class="text-neg">
|
||||
<li ng-repeat="page in dependantPages">
|
||||
<a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<form class="float left" ng-submit="deleteImage($event)">
|
||||
<button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
|
||||
</form>
|
||||
<button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
|
||||
<i class="zmdi zmdi-square-right"></i>Select Image
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<button class="neg button image-manager-close" ng-click="hide()">x</button>
|
||||
|
||||
<div class="image-manager-sidebar">
|
||||
<h2>Images</h2>
|
||||
<drop-zone upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
|
||||
<div class="image-manager-details anim fadeIn" ng-show="selectedImage">
|
||||
|
||||
<hr class="even">
|
||||
|
||||
<form ng-submit="saveImageDetails($event)">
|
||||
<div>
|
||||
<a ng-href="@{{selectedImage.url}}" target="_blank" style="display: block;">
|
||||
<img ng-src="@{{selectedImage.thumbs.gallery}}" ng-attr-alt="@{{selectedImage.title}}" ng-attr-title="@{{selectedImage.name}}">
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Image Name</label>
|
||||
<input type="text" id="name" name="name" ng-model="selectedImage.name">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<hr class="even">
|
||||
|
||||
<div ng-show="dependantPages">
|
||||
<p class="text-neg text-small">
|
||||
This image is used in the pages below, Click delete again to confirm you want to delete
|
||||
this image.
|
||||
</p>
|
||||
<ul class="text-neg">
|
||||
<li ng-repeat="page in dependantPages">
|
||||
<a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form ng-submit="deleteImage($event)">
|
||||
<button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="image-manager-bottom">
|
||||
<button class="button pos anim fadeIn" ng-show="selectedImage" ng-click="selectButtonClick()">
|
||||
<i class="zmdi zmdi-square-right"></i>Select Image
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user