mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-02-10 03:12:20 +03:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0497feddd | ||
|
|
789693bde9 | ||
|
|
8b109bac13 | ||
|
|
097d9c9f3c | ||
|
|
1fe933e4ea | ||
|
|
491f73e0cd | ||
|
|
724b4b5a70 | ||
|
|
1778a56146 | ||
|
|
4656c12f6d | ||
|
|
a06321675a | ||
|
|
dbe11c1360 | ||
|
|
75ecf1c44d | ||
|
|
5283919d24 | ||
|
|
ced8c8e497 | ||
|
|
bf7852ce85 | ||
|
|
30214fde74 | ||
|
|
e9c213f803 | ||
|
|
9f11e045a5 | ||
|
|
93ebdf724b | ||
|
|
59ce228c2e | ||
|
|
744865fcb2 | ||
|
|
7f8c8b448d | ||
|
|
1d6137f7e2 | ||
|
|
66c56e9d02 | ||
|
|
e744d4c82c | ||
|
|
0774ecc89c | ||
|
|
5e7a4c7fb5 | ||
|
|
76eaf64f94 | ||
|
|
80865b30a5 | ||
|
|
8e6248f57f | ||
|
|
268db6b1d0 | ||
|
|
479dd80a8c | ||
|
|
069431db72 | ||
|
|
bc2b310638 | ||
|
|
33bf20cfc8 | ||
|
|
e3bdc391cd | ||
|
|
5681f4dd69 | ||
|
|
38d822e04c | ||
|
|
8e274a5a84 | ||
|
|
985d2f1c2c | ||
|
|
7f5872372d | ||
|
|
201f788806 | ||
|
|
a14b5c33fd | ||
|
|
473261be35 | ||
|
|
a54be85185 | ||
|
|
a67c53826d | ||
|
|
14b131e850 | ||
|
|
54e3122540 | ||
|
|
d339ab1125 | ||
|
|
3ab09ef708 | ||
|
|
c86a122d80 | ||
|
|
3a58e37838 | ||
|
|
6bd49bcd4b | ||
|
|
61577cf6bf | ||
|
|
b4dec2a99c | ||
|
|
fe0b122aca | ||
|
|
8eb2960950 | ||
|
|
c2369a740d | ||
|
|
bab6fd1f2f | ||
|
|
86fbc9a936 | ||
|
|
4d9726dbdd | ||
|
|
4442a2e6d1 | ||
|
|
293be7093c | ||
|
|
990acbb9ac |
11
.env.example
11
.env.example
@@ -12,8 +12,17 @@ DB_PASSWORD=database_user_password
|
||||
# Cache and session
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
# If using Memcached, comment the above and uncomment these
|
||||
#CACHE_DRIVER=memcached
|
||||
#SESSION_DRIVER=memcached
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
# Memcached settings
|
||||
# If using a UNIX socket path for the host, set the port to 0
|
||||
# This follows the following format: HOST:PORT:WEIGHT
|
||||
# For multiple servers separate with a comma
|
||||
MEMCACHED_SERVERS=127.0.0.1:11211:100
|
||||
|
||||
# Storage
|
||||
STORAGE_TYPE=local
|
||||
# Amazon S3 Config
|
||||
@@ -53,4 +62,4 @@ MAIL_HOST=localhost
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_ENCRYPTION=null
|
||||
@@ -15,15 +15,11 @@ class Activity extends Model
|
||||
|
||||
/**
|
||||
* Get the entity for this activity.
|
||||
* @return bool
|
||||
*/
|
||||
public function entity()
|
||||
{
|
||||
if ($this->entity_id) {
|
||||
return $this->morphTo('entity')->first();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if ($this->entity_type === '') $this->entity_type = null;
|
||||
return $this->morphTo('entity');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<?php
|
||||
<?php namespace BookStack;
|
||||
|
||||
namespace BookStack;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class Entity extends Model
|
||||
abstract class Entity extends Ownable
|
||||
{
|
||||
|
||||
use Ownable;
|
||||
|
||||
/**
|
||||
* Compares this entity to another given entity.
|
||||
* Matches by comparing class and id.
|
||||
@@ -53,13 +48,31 @@ abstract class Entity extends Model
|
||||
|
||||
/**
|
||||
* Get View objects for this entity.
|
||||
* @return mixed
|
||||
*/
|
||||
public function views()
|
||||
{
|
||||
return $this->morphMany('BookStack\View', 'viewable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this entities restrictions.
|
||||
*/
|
||||
public function restrictions()
|
||||
{
|
||||
return $this->morphMany('BookStack\Restriction', 'restrictable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this entity has a specific restriction set against it.
|
||||
* @param $role_id
|
||||
* @param $action
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRestriction($role_id, $action)
|
||||
{
|
||||
return $this->restrictions->where('role_id', $role_id)->where('action', $action)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows checking of the exact class, Used to check entity type.
|
||||
* Cleaner method for is_a.
|
||||
@@ -72,23 +85,14 @@ abstract class Entity extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
* @return string
|
||||
*/
|
||||
public static function getClassName()
|
||||
{
|
||||
return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
*Gets a limited-length version of the entities name.
|
||||
* Gets a limited-length version of the entities name.
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public function getShortName($length = 25)
|
||||
{
|
||||
if(strlen($this->name) <= $length) return $this->name;
|
||||
return substr($this->name, 0, $length-3) . '...';
|
||||
if (strlen($this->name) <= $length) return $this->name;
|
||||
return substr($this->name, 0, $length - 3) . '...';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,27 +102,48 @@ abstract class Entity extends Model
|
||||
* @param string[] array $wheres
|
||||
* @return mixed
|
||||
*/
|
||||
public static function fullTextSearch($fieldsToSearch, $terms, $wheres = [])
|
||||
public static function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = [])
|
||||
{
|
||||
$termString = '';
|
||||
foreach ($terms as $term) {
|
||||
$termString .= htmlentities($term) . '* ';
|
||||
$exactTerms = [];
|
||||
foreach ($terms as $key => $term) {
|
||||
$term = htmlentities($term, ENT_QUOTES);
|
||||
$term = preg_replace('/[+\-><\(\)~*\"@]+/', ' ', $term);
|
||||
if (preg_match('/\s/', $term)) {
|
||||
$exactTerms[] = '%' . $term . '%';
|
||||
$term = '"' . $term . '"';
|
||||
} else {
|
||||
$term = '' . $term . '*';
|
||||
}
|
||||
if ($term !== '*') $terms[$key] = $term;
|
||||
}
|
||||
$termString = implode(' ', $terms);
|
||||
$fields = implode(',', $fieldsToSearch);
|
||||
$termStringEscaped = \DB::connection()->getPdo()->quote($termString);
|
||||
$search = static::addSelect(\DB::raw('*, MATCH(name) AGAINST('.$termStringEscaped.' IN BOOLEAN MODE) AS title_relevance'));
|
||||
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termStringEscaped]);
|
||||
$search = static::selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
|
||||
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
|
||||
|
||||
// Ensure at least one exact term matches if in search
|
||||
if (count($exactTerms) > 0) {
|
||||
$search = $search->where(function ($query) use ($exactTerms, $fieldsToSearch) {
|
||||
foreach ($exactTerms as $exactTerm) {
|
||||
foreach ($fieldsToSearch as $field) {
|
||||
$query->orWhere($field, 'like', $exactTerm);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add additional where terms
|
||||
foreach ($wheres as $whereTerm) {
|
||||
$search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]);
|
||||
}
|
||||
|
||||
// Load in relations
|
||||
if (!static::isA('book')) $search = $search->with('book');
|
||||
if (static::isA('page')) $search = $search->with('chapter');
|
||||
if (static::isA('page')) {
|
||||
$search = $search->with('book', 'chapter', 'createdBy', 'updatedBy');
|
||||
} else if (static::isA('chapter')) {
|
||||
$search = $search->with('book');
|
||||
}
|
||||
|
||||
return $search->orderBy('title_relevance', 'desc')->get();
|
||||
return $search->orderBy('title_relevance', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,8 @@ class Handler extends ExceptionHandler
|
||||
// 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();
|
||||
return response()->view('errors/500', ['message' => $message], 500);
|
||||
$code = ($e->getCode() === 0) ? 500 : $e->getCode();
|
||||
return response()->view('errors/' . $code, ['message' => $message], $code);
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
|
||||
14
app/Exceptions/NotFoundException.php
Normal file
14
app/Exceptions/NotFoundException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace BookStack\Exceptions;
|
||||
|
||||
|
||||
class NotFoundException extends PrettyException {
|
||||
|
||||
/**
|
||||
* NotFoundException constructor.
|
||||
* @param string $message
|
||||
*/
|
||||
public function __construct($message = 'Item not found')
|
||||
{
|
||||
parent::__construct($message, 404);
|
||||
}
|
||||
}
|
||||
6
app/Exceptions/PermissionsException.php
Normal file
6
app/Exceptions/PermissionsException.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php namespace BookStack\Exceptions;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
class PermissionsException extends Exception {}
|
||||
@@ -41,9 +41,9 @@ class AuthController extends Controller
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
* @param SocialAuthService $socialAuthService
|
||||
* @param SocialAuthService $socialAuthService
|
||||
* @param EmailConfirmationService $emailConfirmationService
|
||||
* @param UserRepo $userRepo
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
|
||||
{
|
||||
@@ -63,15 +63,15 @@ class AuthController extends Controller
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|min:6',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function checkRegistrationAllowed()
|
||||
{
|
||||
if (!\Setting::get('registration-enabled')) {
|
||||
if (!setting('registration-enabled')) {
|
||||
throw new UserRegistrationException('Registrations are currently disabled.', '/login');
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class AuthController extends Controller
|
||||
/**
|
||||
* Overrides the action when a user is authenticated.
|
||||
* If the user authenticated but does not exist in the user table we create them.
|
||||
* @param Request $request
|
||||
* @param Request $request
|
||||
* @param Authenticatable $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
@@ -153,8 +153,8 @@ class AuthController extends Controller
|
||||
|
||||
// Create an array of the user data to create a new user instance
|
||||
$userData = [
|
||||
'name' => $socialUser->getName(),
|
||||
'email' => $socialUser->getEmail(),
|
||||
'name' => $socialUser->getName(),
|
||||
'email' => $socialUser->getEmail(),
|
||||
'password' => str_random(30)
|
||||
];
|
||||
return $this->registerUser($userData, $socialAccount);
|
||||
@@ -162,7 +162,7 @@ class AuthController extends Controller
|
||||
|
||||
/**
|
||||
* The registrations flow for all users.
|
||||
* @param array $userData
|
||||
* @param array $userData
|
||||
* @param bool|false|SocialAccount $socialAccount
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws UserRegistrationException
|
||||
@@ -170,8 +170,8 @@ class AuthController extends Controller
|
||||
*/
|
||||
protected function registerUser(array $userData, $socialAccount = false)
|
||||
{
|
||||
if (\Setting::get('registration-restrict')) {
|
||||
$restrictedEmailDomains = explode(',', str_replace(' ', '', \Setting::get('registration-restrict')));
|
||||
if (setting('registration-restrict')) {
|
||||
$restrictedEmailDomains = explode(',', str_replace(' ', '', setting('registration-restrict')));
|
||||
$userEmailDomain = $domain = substr(strrchr($userData['email'], "@"), 1);
|
||||
if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
|
||||
throw new UserRegistrationException('That email domain does not have access to this application', '/register');
|
||||
@@ -183,7 +183,7 @@ class AuthController extends Controller
|
||||
$newUser->socialAccounts()->save($socialAccount);
|
||||
}
|
||||
|
||||
if (\Setting::get('registration-confirmation') || \Setting::get('registration-restrict')) {
|
||||
if (setting('registration-confirmation') || setting('registration-restrict')) {
|
||||
$newUser->email_confirmed = false;
|
||||
$newUser->save();
|
||||
$this->emailConfirmationService->sendConfirmation($newUser);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -19,18 +20,21 @@ class BookController extends Controller
|
||||
protected $bookRepo;
|
||||
protected $pageRepo;
|
||||
protected $chapterRepo;
|
||||
protected $userRepo;
|
||||
|
||||
/**
|
||||
* BookController constructor.
|
||||
* @param BookRepo $bookRepo
|
||||
* @param PageRepo $pageRepo
|
||||
* @param BookRepo $bookRepo
|
||||
* @param PageRepo $pageRepo
|
||||
* @param ChapterRepo $chapterRepo
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(BookRepo $bookRepo, PageRepo $pageRepo, ChapterRepo $chapterRepo)
|
||||
public function __construct(BookRepo $bookRepo, PageRepo $pageRepo, ChapterRepo $chapterRepo, UserRepo $userRepo)
|
||||
{
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->pageRepo = $pageRepo;
|
||||
$this->chapterRepo = $chapterRepo;
|
||||
$this->userRepo = $userRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
@@ -55,7 +59,7 @@ class BookController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission('book-create');
|
||||
$this->checkPermission('book-create-all');
|
||||
$this->setPageTitle('Create New Book');
|
||||
return view('books/create');
|
||||
}
|
||||
@@ -68,9 +72,9 @@ class BookController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->checkPermission('book-create');
|
||||
$this->checkPermission('book-create-all');
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'string|max:1000'
|
||||
]);
|
||||
$book = $this->bookRepo->newFromInput($request->all());
|
||||
@@ -105,8 +109,8 @@ class BookController extends Controller
|
||||
*/
|
||||
public function edit($slug)
|
||||
{
|
||||
$this->checkPermission('book-update');
|
||||
$book = $this->bookRepo->getBySlug($slug);
|
||||
$this->checkOwnablePermission('book-update', $book);
|
||||
$this->setPageTitle('Edit Book ' . $book->getShortName());
|
||||
return view('books/edit', ['book' => $book, 'current' => $book]);
|
||||
}
|
||||
@@ -120,10 +124,10 @@ class BookController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $slug)
|
||||
{
|
||||
$this->checkPermission('book-update');
|
||||
$book = $this->bookRepo->getBySlug($slug);
|
||||
$this->checkOwnablePermission('book-update', $book);
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255',
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'string|max:1000'
|
||||
]);
|
||||
$book->fill($request->all());
|
||||
@@ -141,8 +145,8 @@ class BookController extends Controller
|
||||
*/
|
||||
public function showDelete($bookSlug)
|
||||
{
|
||||
$this->checkPermission('book-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('book-delete', $book);
|
||||
$this->setPageTitle('Delete Book ' . $book->getShortName());
|
||||
return view('books/delete', ['book' => $book, 'current' => $book]);
|
||||
}
|
||||
@@ -154,10 +158,10 @@ class BookController extends Controller
|
||||
*/
|
||||
public function sort($bookSlug)
|
||||
{
|
||||
$this->checkPermission('book-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('book-update', $book);
|
||||
$bookChildren = $this->bookRepo->getChildren($book);
|
||||
$books = $this->bookRepo->getAll();
|
||||
$books = $this->bookRepo->getAll(false);
|
||||
$this->setPageTitle('Sort Book ' . $book->getShortName());
|
||||
return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
|
||||
}
|
||||
@@ -177,15 +181,14 @@ class BookController extends Controller
|
||||
|
||||
/**
|
||||
* Saves an array of sort mapping to pages and chapters.
|
||||
*
|
||||
* @param string $bookSlug
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function saveSort($bookSlug, Request $request)
|
||||
{
|
||||
$this->checkPermission('book-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('book-update', $book);
|
||||
|
||||
// Return if no map sent
|
||||
if (!$request->has('sort-tree')) {
|
||||
@@ -223,17 +226,48 @@ class BookController extends Controller
|
||||
|
||||
/**
|
||||
* Remove the specified book from storage.
|
||||
*
|
||||
* @param $bookSlug
|
||||
* @return Response
|
||||
*/
|
||||
public function destroy($bookSlug)
|
||||
{
|
||||
$this->checkPermission('book-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('book-delete', $book);
|
||||
Activity::addMessage('book_delete', 0, $book->name);
|
||||
Activity::removeEntity($book);
|
||||
$this->bookRepo->destroyBySlug($bookSlug);
|
||||
return redirect('/books');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the Restrictions view.
|
||||
* @param $bookSlug
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRestrict($bookSlug)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $book);
|
||||
$roles = $this->userRepo->getRestrictableRoles();
|
||||
return view('books/restrictions', [
|
||||
'book' => $book,
|
||||
'roles' => $roles
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the restrictions for this book.
|
||||
* @param $bookSlug
|
||||
* @param $bookSlug
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function restrict($bookSlug, Request $request)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $book);
|
||||
$this->bookRepo->updateRestrictionsFromRequest($request, $book);
|
||||
session()->flash('success', 'Page Restrictions Updated');
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Http\Controllers\Controller;
|
||||
use BookStack\Repos\BookRepo;
|
||||
use BookStack\Repos\ChapterRepo;
|
||||
use Views;
|
||||
@@ -17,20 +13,22 @@ class ChapterController extends Controller
|
||||
|
||||
protected $bookRepo;
|
||||
protected $chapterRepo;
|
||||
protected $userRepo;
|
||||
|
||||
/**
|
||||
* ChapterController constructor.
|
||||
* @param $bookRepo
|
||||
* @param $chapterRepo
|
||||
* @param BookRepo $bookRepo
|
||||
* @param ChapterRepo $chapterRepo
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(BookRepo $bookRepo, ChapterRepo $chapterRepo)
|
||||
public function __construct(BookRepo $bookRepo, ChapterRepo $chapterRepo, UserRepo $userRepo)
|
||||
{
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->chapterRepo = $chapterRepo;
|
||||
$this->userRepo = $userRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for creating a new chapter.
|
||||
* @param $bookSlug
|
||||
@@ -38,8 +36,8 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function create($bookSlug)
|
||||
{
|
||||
$this->checkPermission('chapter-create');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('chapter-create', $book);
|
||||
$this->setPageTitle('Create New Chapter');
|
||||
return view('chapters/create', ['book' => $book, 'current' => $book]);
|
||||
}
|
||||
@@ -52,12 +50,13 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function store($bookSlug, Request $request)
|
||||
{
|
||||
$this->checkPermission('chapter-create');
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255'
|
||||
]);
|
||||
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$this->checkOwnablePermission('chapter-create', $book);
|
||||
|
||||
$chapter = $this->chapterRepo->newFromInput($request->all());
|
||||
$chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id);
|
||||
$chapter->priority = $this->bookRepo->getNewPriority($book);
|
||||
@@ -81,7 +80,14 @@ class ChapterController extends Controller
|
||||
$sidebarTree = $this->bookRepo->getChildren($book);
|
||||
Views::add($chapter);
|
||||
$this->setPageTitle($chapter->getShortName());
|
||||
return view('chapters/show', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter, 'sidebarTree' => $sidebarTree]);
|
||||
$pages = $this->chapterRepo->getChildren($chapter);
|
||||
return view('chapters/show', [
|
||||
'book' => $book,
|
||||
'chapter' => $chapter,
|
||||
'current' => $chapter,
|
||||
'sidebarTree' => $sidebarTree,
|
||||
'pages' => $pages
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,9 +98,9 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function edit($bookSlug, $chapterSlug)
|
||||
{
|
||||
$this->checkPermission('chapter-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('chapter-update', $chapter);
|
||||
$this->setPageTitle('Edit Chapter' . $chapter->getShortName());
|
||||
return view('chapters/edit', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
|
||||
}
|
||||
@@ -108,9 +114,9 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $bookSlug, $chapterSlug)
|
||||
{
|
||||
$this->checkPermission('chapter-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('chapter-update', $chapter);
|
||||
$chapter->fill($request->all());
|
||||
$chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id, $chapter->id);
|
||||
$chapter->updated_by = auth()->user()->id;
|
||||
@@ -127,9 +133,9 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function showDelete($bookSlug, $chapterSlug)
|
||||
{
|
||||
$this->checkPermission('chapter-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('chapter-delete', $chapter);
|
||||
$this->setPageTitle('Delete Chapter' . $chapter->getShortName());
|
||||
return view('chapters/delete', ['book' => $book, 'chapter' => $chapter, 'current' => $chapter]);
|
||||
}
|
||||
@@ -142,11 +148,46 @@ class ChapterController extends Controller
|
||||
*/
|
||||
public function destroy($bookSlug, $chapterSlug)
|
||||
{
|
||||
$this->checkPermission('chapter-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('chapter-delete', $chapter);
|
||||
Activity::addMessage('chapter_delete', $book->id, $chapter->name);
|
||||
$this->chapterRepo->destroy($chapter);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the Restrictions view.
|
||||
* @param $bookSlug
|
||||
* @param $chapterSlug
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRestrict($bookSlug, $chapterSlug)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('restrictions-manage', $chapter);
|
||||
$roles = $this->userRepo->getRestrictableRoles();
|
||||
return view('chapters/restrictions', [
|
||||
'chapter' => $chapter,
|
||||
'roles' => $roles
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the restrictions for this chapter.
|
||||
* @param $bookSlug
|
||||
* @param $chapterSlug
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function restrict($bookSlug, $chapterSlug, Request $request)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $this->chapterRepo->getBySlug($chapterSlug, $book->id);
|
||||
$this->checkOwnablePermission('restrictions-manage', $chapter);
|
||||
$this->chapterRepo->updateRestrictionsFromRequest($request, $chapter);
|
||||
session()->flash('success', 'Page Restrictions Updated');
|
||||
return redirect($chapter->getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Ownable;
|
||||
use HttpRequestException;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Exception\HttpResponseException;
|
||||
@@ -61,21 +62,19 @@ abstract class Controller extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* On a permission error redirect to home and display
|
||||
* On a permission error redirect to home and display.
|
||||
* the error as a notification.
|
||||
*/
|
||||
protected function showPermissionError()
|
||||
{
|
||||
Session::flash('error', trans('errors.permission'));
|
||||
throw new HttpResponseException(
|
||||
redirect('/')
|
||||
);
|
||||
$response = request()->wantsJson() ? response()->json(['error' => trans('errors.permissionJson')], 403) : redirect('/');
|
||||
throw new HttpResponseException($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a permission.
|
||||
*
|
||||
* @param $permissionName
|
||||
* @param string $permissionName
|
||||
* @return bool|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function checkPermission($permissionName)
|
||||
@@ -83,10 +82,21 @@ abstract class Controller extends BaseController
|
||||
if (!$this->currentUser || !$this->currentUser->can($permissionName)) {
|
||||
$this->showPermissionError();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the current user's permissions against an ownable item.
|
||||
* @param $permission
|
||||
* @param Ownable $ownable
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkOwnablePermission($permission, Ownable $ownable)
|
||||
{
|
||||
if (userCan($permission, $ownable)) return true;
|
||||
return $this->showPermissionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user has a permission or bypass if the callback is true.
|
||||
* @param $permissionName
|
||||
|
||||
@@ -3,39 +3,44 @@
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use BookStack\Repos\EntityRepo;
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Repos\BookRepo;
|
||||
use Views;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
|
||||
protected $activityService;
|
||||
protected $bookRepo;
|
||||
protected $entityRepo;
|
||||
|
||||
/**
|
||||
* HomeController constructor.
|
||||
* @param BookRepo $bookRepo
|
||||
* @param EntityRepo $entityRepo
|
||||
*/
|
||||
public function __construct(BookRepo $bookRepo)
|
||||
public function __construct(EntityRepo $entityRepo)
|
||||
{
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->entityRepo = $entityRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the homepage.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$activity = Activity::latest();
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(10, 0) : $this->bookRepo->getLatest(10);
|
||||
return view('home', ['activity' => $activity, 'recents' => $recents]);
|
||||
$activity = Activity::latest(10);
|
||||
$draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : [];
|
||||
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreatedBooks(10*$recentFactor);
|
||||
$recentlyCreatedPages = $this->entityRepo->getRecentlyCreatedPages(5);
|
||||
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdatedPages(5);
|
||||
return view('home', [
|
||||
'activity' => $activity,
|
||||
'recents' => $recents,
|
||||
'recentlyCreatedPages' => $recentlyCreatedPages,
|
||||
'recentlyUpdatedPages' => $recentlyUpdatedPages,
|
||||
'draftPages' => $draftPages
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ class ImageController extends Controller
|
||||
|
||||
/**
|
||||
* ImageController constructor.
|
||||
* @param Image $image
|
||||
* @param File $file
|
||||
* @param Image $image
|
||||
* @param File $file
|
||||
* @param ImageRepo $imageRepo
|
||||
*/
|
||||
public function __construct(Image $image, File $file, ImageRepo $imageRepo)
|
||||
@@ -32,9 +32,9 @@ class ImageController extends Controller
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all images for a specific type, Paginated
|
||||
* @param string $type
|
||||
* @param int $page
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
@@ -55,16 +55,15 @@ class ImageController extends Controller
|
||||
return response()->json($imgData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles image uploads for use on pages.
|
||||
* @param string $type
|
||||
* @param string $type
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function uploadByType($type, Request $request)
|
||||
{
|
||||
$this->checkPermission('image-create');
|
||||
$this->checkPermission('image-create-all');
|
||||
$this->validate($request, [
|
||||
'file' => 'image|mimes:jpeg,gif,png'
|
||||
]);
|
||||
@@ -72,7 +71,8 @@ class ImageController extends Controller
|
||||
$imageUpload = $request->file('file');
|
||||
|
||||
try {
|
||||
$image = $this->imageRepo->saveNew($imageUpload, $type);
|
||||
$uploadedTo = $request->has('uploaded_to') ? $request->get('uploaded_to') : 0;
|
||||
$image = $this->imageRepo->saveNew($imageUpload, $type, $uploadedTo);
|
||||
} catch (ImageUploadException $e) {
|
||||
return response($e->getMessage(), 500);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class ImageController extends Controller
|
||||
*/
|
||||
public function getThumbnail($id, $width, $height, $crop)
|
||||
{
|
||||
$this->checkPermission('image-create');
|
||||
$this->checkPermission('image-create-all');
|
||||
$image = $this->imageRepo->getById($id);
|
||||
$thumbnailUrl = $this->imageRepo->getThumbnail($image, $width, $height, $crop == 'false');
|
||||
return response()->json(['url' => $thumbnailUrl]);
|
||||
@@ -98,33 +98,32 @@ class ImageController extends Controller
|
||||
|
||||
/**
|
||||
* Update image details
|
||||
* @param $imageId
|
||||
* @param integer $imageId
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update($imageId, Request $request)
|
||||
{
|
||||
$this->checkPermission('image-update');
|
||||
$this->validate($request, [
|
||||
'name' => 'required|min:2|string'
|
||||
]);
|
||||
$image = $this->imageRepo->getById($imageId);
|
||||
$this->checkOwnablePermission('image-update', $image);
|
||||
$image = $this->imageRepo->updateImageDetails($image, $request->all());
|
||||
return response()->json($image);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes an image and all thumbnail/image files
|
||||
* @param PageRepo $pageRepo
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy(PageRepo $pageRepo, Request $request, $id)
|
||||
{
|
||||
$this->checkPermission('image-delete');
|
||||
$image = $this->imageRepo->getById($id);
|
||||
$this->checkOwnablePermission('image-delete', $image);
|
||||
|
||||
// Check if this image is used on any pages
|
||||
$isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use BookStack\Services\ExportService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Repos\BookRepo;
|
||||
use BookStack\Repos\ChapterRepo;
|
||||
use BookStack\Repos\PageRepo;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Views;
|
||||
|
||||
class PageController extends Controller
|
||||
@@ -20,26 +19,28 @@ class PageController extends Controller
|
||||
protected $bookRepo;
|
||||
protected $chapterRepo;
|
||||
protected $exportService;
|
||||
protected $userRepo;
|
||||
|
||||
/**
|
||||
* PageController constructor.
|
||||
* @param PageRepo $pageRepo
|
||||
* @param BookRepo $bookRepo
|
||||
* @param ChapterRepo $chapterRepo
|
||||
* @param PageRepo $pageRepo
|
||||
* @param BookRepo $bookRepo
|
||||
* @param ChapterRepo $chapterRepo
|
||||
* @param ExportService $exportService
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService)
|
||||
public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService, UserRepo $userRepo)
|
||||
{
|
||||
$this->pageRepo = $pageRepo;
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->chapterRepo = $chapterRepo;
|
||||
$this->exportService = $exportService;
|
||||
$this->userRepo = $userRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new page.
|
||||
*
|
||||
* @param $bookSlug
|
||||
* @param bool $chapterSlug
|
||||
* @return Response
|
||||
@@ -47,33 +48,55 @@ class PageController extends Controller
|
||||
*/
|
||||
public function create($bookSlug, $chapterSlug = false)
|
||||
{
|
||||
$this->checkPermission('page-create');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : false;
|
||||
$chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : null;
|
||||
$parent = $chapter ? $chapter : $book;
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
$this->setPageTitle('Create New Page');
|
||||
return view('pages/create', ['book' => $book, 'chapter' => $chapter]);
|
||||
|
||||
$draft = $this->pageRepo->getDraftPage($book, $chapter);
|
||||
return redirect($draft->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created page in storage.
|
||||
*
|
||||
* Show form to continue editing a draft page.
|
||||
* @param $bookSlug
|
||||
* @param $pageId
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function editDraft($bookSlug, $pageId)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$draft = $this->pageRepo->getById($pageId, true);
|
||||
$this->checkOwnablePermission('page-create', $draft);
|
||||
$this->setPageTitle('Edit Page Draft');
|
||||
|
||||
return view('pages/create', ['draft' => $draft, 'book' => $book]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new page by changing a draft into a page.
|
||||
* @param Request $request
|
||||
* @param $bookSlug
|
||||
* @param string $bookSlug
|
||||
* @return Response
|
||||
*/
|
||||
public function store(Request $request, $bookSlug)
|
||||
public function store(Request $request, $bookSlug, $pageId)
|
||||
{
|
||||
$this->checkPermission('page-create');
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255'
|
||||
'name' => 'required|string|max:255'
|
||||
]);
|
||||
|
||||
$input = $request->all();
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$chapterId = ($request->has('chapter') && $this->chapterRepo->idExists($request->get('chapter'))) ? $request->get('chapter') : null;
|
||||
$input['priority'] = $this->bookRepo->getNewPriority($book);
|
||||
|
||||
$page = $this->pageRepo->saveNew($input, $book, $chapterId);
|
||||
$draftPage = $this->pageRepo->getById($pageId, true);
|
||||
|
||||
$chapterId = $draftPage->chapter_id;
|
||||
$parent = $chapterId !== 0 ? $this->chapterRepo->getById($chapterId) : $book;
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
|
||||
$page = $this->pageRepo->publishDraft($draftPage, $input);
|
||||
|
||||
Activity::add($page, 'page_create', $book->id);
|
||||
return redirect($page->getUrl());
|
||||
@@ -81,7 +104,8 @@ class PageController extends Controller
|
||||
|
||||
/**
|
||||
* Display the specified page.
|
||||
*
|
||||
* If the page is not found via the slug the
|
||||
* revisions are searched for a match.
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
* @return Response
|
||||
@@ -89,32 +113,68 @@ class PageController extends Controller
|
||||
public function show($bookSlug, $pageSlug)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
|
||||
try {
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
} catch (NotFoundException $e) {
|
||||
$page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug);
|
||||
if ($page === null) abort(404);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
||||
$sidebarTree = $this->bookRepo->getChildren($book);
|
||||
Views::add($page);
|
||||
$this->setPageTitle($page->getShortName());
|
||||
return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page from an ajax request.
|
||||
* @param $pageId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getPageAjax($pageId)
|
||||
{
|
||||
$page = $this->pageRepo->getById($pageId);
|
||||
return response()->json($page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified page.
|
||||
*
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
* @return Response
|
||||
*/
|
||||
public function edit($bookSlug, $pageSlug)
|
||||
{
|
||||
$this->checkPermission('page-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->setPageTitle('Editing Page ' . $page->getShortName());
|
||||
$page->isDraft = false;
|
||||
|
||||
// Check for active editing
|
||||
$warnings = [];
|
||||
if ($this->pageRepo->isPageEditingActive($page, 60)) {
|
||||
$warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
|
||||
}
|
||||
|
||||
// Check for a current draft version for this user
|
||||
if ($this->pageRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
|
||||
$draft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
|
||||
$page->name = $draft->name;
|
||||
$page->html = $draft->html;
|
||||
$page->isDraft = true;
|
||||
$warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
|
||||
}
|
||||
|
||||
if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
|
||||
|
||||
return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified page in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
@@ -122,17 +182,36 @@ class PageController extends Controller
|
||||
*/
|
||||
public function update(Request $request, $bookSlug, $pageSlug)
|
||||
{
|
||||
$this->checkPermission('page-update');
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255'
|
||||
'name' => 'required|string|max:255'
|
||||
]);
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->pageRepo->updatePage($page, $book->id, $request->all());
|
||||
Activity::add($page, 'page_update', $book->id);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a draft update as a revision.
|
||||
* @param Request $request
|
||||
* @param $pageId
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function saveDraft(Request $request, $pageId)
|
||||
{
|
||||
$page = $this->pageRepo->getById($pageId, true);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
if ($page->draft) {
|
||||
$draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html']));
|
||||
} else {
|
||||
$draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html']));
|
||||
}
|
||||
$updateTime = $draft->updated_at->format('H:i');
|
||||
return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect from a special link url which
|
||||
* uses the page id rather than the name.
|
||||
@@ -153,16 +232,32 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showDelete($bookSlug, $pageSlug)
|
||||
{
|
||||
$this->checkPermission('page-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
$this->setPageTitle('Delete Page ' . $page->getShortName());
|
||||
return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the deletion page for the specified page.
|
||||
* @param $bookSlug
|
||||
* @param $pageId
|
||||
* @return \Illuminate\View\View
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function showDeleteDraft($bookSlug, $pageId)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getById($pageId, true);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->setPageTitle('Delete Draft Page ' . $page->getShortName());
|
||||
return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified page from storage.
|
||||
*
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
* @return Response
|
||||
@@ -170,10 +265,28 @@ class PageController extends Controller
|
||||
*/
|
||||
public function destroy($bookSlug, $pageSlug)
|
||||
{
|
||||
$this->checkPermission('page-delete');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
Activity::addMessage('page_delete', $book->id, $page->name);
|
||||
session()->flash('success', 'Page deleted');
|
||||
$this->pageRepo->destroy($page);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified draft page from storage.
|
||||
* @param $bookSlug
|
||||
* @param $pageId
|
||||
* @return Response
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function destroyDraft($bookSlug, $pageId)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getById($pageId, true);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
session()->flash('success', 'Draft deleted');
|
||||
$this->pageRepo->destroy($page);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
@@ -218,9 +331,9 @@ class PageController extends Controller
|
||||
*/
|
||||
public function restoreRevision($bookSlug, $pageSlug, $revisionId)
|
||||
{
|
||||
$this->checkPermission('page-update');
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
|
||||
Activity::add($page, 'page_restore', $book->id);
|
||||
return redirect($page->getUrl());
|
||||
@@ -239,8 +352,8 @@ class PageController extends Controller
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$pdfContent = $this->exportService->pageToPdf($page);
|
||||
return response()->make($pdfContent, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.pdf'
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -256,8 +369,8 @@ class PageController extends Controller
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$containedHtml = $this->exportService->pageToContainedHtml($page);
|
||||
return response()->make($containedHtml, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.html'
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -273,9 +386,70 @@ class PageController extends Controller
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$containedHtml = $this->exportService->pageToPlainText($page);
|
||||
return response()->make($containedHtml, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.txt'
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.txt'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a listing of recently created pages
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRecentlyCreated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Created Pages',
|
||||
'pages' => $pages
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a listing of recently created pages
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRecentlyUpdated()
|
||||
{
|
||||
$pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => 'Recently Updated Pages',
|
||||
'pages' => $pages
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the Restrictions view.
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showRestrict($bookSlug, $pageSlug)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('restrictions-manage', $page);
|
||||
$roles = $this->userRepo->getRestrictableRoles();
|
||||
return view('pages/restrictions', [
|
||||
'page' => $page,
|
||||
'roles' => $roles
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the restrictions for this page.
|
||||
* @param $bookSlug
|
||||
* @param $pageSlug
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function restrict($bookSlug, $pageSlug, Request $request)
|
||||
{
|
||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||
$page = $this->pageRepo->getBySlug($pageSlug, $book->id);
|
||||
$this->checkOwnablePermission('restrictions-manage', $page);
|
||||
$this->pageRepo->updateRestrictionsFromRequest($request, $page);
|
||||
session()->flash('success', 'Page Restrictions Updated');
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
129
app/Http/Controllers/PermissionController.php
Normal file
129
app/Http/Controllers/PermissionController.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Repos\PermissionsRepo;
|
||||
use Illuminate\Http\Request;
|
||||
use BookStack\Http\Requests;
|
||||
|
||||
class PermissionController extends Controller
|
||||
{
|
||||
|
||||
protected $permissionsRepo;
|
||||
|
||||
/**
|
||||
* PermissionController constructor.
|
||||
* @param PermissionsRepo $permissionsRepo
|
||||
*/
|
||||
public function __construct(PermissionsRepo $permissionsRepo)
|
||||
{
|
||||
$this->permissionsRepo = $permissionsRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a listing of the roles in the system.
|
||||
*/
|
||||
public function listRoles()
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
$roles = $this->permissionsRepo->getAllRoles();
|
||||
return view('settings/roles/index', ['roles' => $roles]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form to create a new role
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function createRole()
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
return view('settings/roles/create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new role in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function storeRole(Request $request)
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
$this->validate($request, [
|
||||
'display_name' => 'required|min:3|max:200',
|
||||
'description' => 'max:250'
|
||||
]);
|
||||
|
||||
$this->permissionsRepo->saveNewRole($request->all());
|
||||
session()->flash('success', 'Role successfully created');
|
||||
return redirect('/settings/roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing a user role.
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function editRole($id)
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
$role = $this->permissionsRepo->getRoleById($id);
|
||||
return view('settings/roles/edit', ['role' => $role]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a user role.
|
||||
* @param $id
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function updateRole($id, Request $request)
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
$this->validate($request, [
|
||||
'display_name' => 'required|min:3|max:200',
|
||||
'description' => 'max:250'
|
||||
]);
|
||||
|
||||
$this->permissionsRepo->updateRole($id, $request->all());
|
||||
session()->flash('success', 'Role successfully updated');
|
||||
return redirect('/settings/roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the view to delete a role.
|
||||
* Offers the chance to migrate users.
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showDeleteRole($id)
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
$role = $this->permissionsRepo->getRoleById($id);
|
||||
$roles = $this->permissionsRepo->getAllRolesExcept($role);
|
||||
$blankRole = $role->newInstance(['display_name' => 'Don\'t migrate users']);
|
||||
$roles->prepend($blankRole);
|
||||
return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a role from the system,
|
||||
* Migrate from a previous role if set.
|
||||
* @param $id
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function deleteRole($id, Request $request)
|
||||
{
|
||||
$this->checkPermission('user-roles-manage');
|
||||
|
||||
try {
|
||||
$this->permissionsRepo->deleteRole($id, $request->get('migrate_role_id'));
|
||||
} catch (PermissionsException $e) {
|
||||
session()->flash('error', $e->getMessage());
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
session()->flash('success', 'Role successfully deleted');
|
||||
return redirect('/settings/roles');
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,77 @@ class SearchController extends Controller
|
||||
return redirect()->back();
|
||||
}
|
||||
$searchTerm = $request->get('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm);
|
||||
$books = $this->bookRepo->getBySearch($searchTerm);
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm);
|
||||
$paginationAppends = $request->only('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$books = $this->bookRepo->getBySearch($searchTerm, 10, $paginationAppends);
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 10, $paginationAppends);
|
||||
$this->setPageTitle('Search For ' . $searchTerm);
|
||||
return view('search/all', ['pages' => $pages, 'books' => $books, 'chapters' => $chapters, 'searchTerm' => $searchTerm]);
|
||||
return view('search/all', [
|
||||
'pages' => $pages,
|
||||
'books' => $books,
|
||||
'chapters' => $chapters,
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the pages in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchPages(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$pages = $this->pageRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$this->setPageTitle('Page Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $pages,
|
||||
'title' => 'Page Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the chapters in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchChapters(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$chapters = $this->chapterRepo->getBySearch($searchTerm, [], 20, $paginationAppends);
|
||||
$this->setPageTitle('Chapter Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $chapters,
|
||||
'title' => 'Chapter Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search only the books in the system.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function searchBooks(Request $request)
|
||||
{
|
||||
if (!$request->has('term')) return redirect()->back();
|
||||
|
||||
$searchTerm = $request->get('term');
|
||||
$paginationAppends = $request->only('term');
|
||||
$books = $this->bookRepo->getBySearch($searchTerm, 20, $paginationAppends);
|
||||
$this->setPageTitle('Book Search For ' . $searchTerm);
|
||||
return view('search/entity-search-list', [
|
||||
'entities' => $books,
|
||||
'title' => 'Book Search Results',
|
||||
'searchTerm' => $searchTerm
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,42 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use BookStack\Http\Requests;
|
||||
use BookStack\Http\Controllers\Controller;
|
||||
use Setting;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the settings.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkPermission('settings-update');
|
||||
$this->checkPermission('settings-manage');
|
||||
$this->setPageTitle('Settings');
|
||||
return view('settings/index');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified settings in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->preventAccessForDemoUsers();
|
||||
$this->checkPermission('settings-update');
|
||||
$this->checkPermission('settings-manage');
|
||||
|
||||
// Cycles through posted settings and update them
|
||||
foreach($request->all() as $name => $value) {
|
||||
if(strpos($name, 'setting-') !== 0) continue;
|
||||
foreach ($request->all() as $name => $value) {
|
||||
if (strpos($name, 'setting-') !== 0) continue;
|
||||
$key = str_replace('setting-', '', trim($name));
|
||||
Setting::put($key, $value);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Activity;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
@@ -34,7 +35,8 @@ class UserController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = $this->user->all();
|
||||
$this->checkPermission('users-manage');
|
||||
$users = $this->userRepo->getAllUsers();
|
||||
$this->setPageTitle('Users');
|
||||
return view('users/index', ['users' => $users]);
|
||||
}
|
||||
@@ -45,7 +47,7 @@ class UserController extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission('user-create');
|
||||
$this->checkPermission('users-manage');
|
||||
$authMethod = config('auth.method');
|
||||
return view('users/create', ['authMethod' => $authMethod]);
|
||||
}
|
||||
@@ -57,11 +59,10 @@ class UserController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->checkPermission('user-create');
|
||||
$this->checkPermission('users-manage');
|
||||
$validationRules = [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'role' => 'required|exists:roles,id'
|
||||
'email' => 'required|email|unique:users,email'
|
||||
];
|
||||
|
||||
$authMethod = config('auth.method');
|
||||
@@ -83,7 +84,11 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
$user->save();
|
||||
$user->attachRoleId($request->get('role'));
|
||||
|
||||
if ($request->has('roles')) {
|
||||
$roles = $request->get('roles');
|
||||
$user->roles()->sync($roles);
|
||||
}
|
||||
|
||||
// Get avatar from gravatar and save
|
||||
if (!config('services.disable_services')) {
|
||||
@@ -92,10 +97,9 @@ class UserController extends Controller
|
||||
$user->save();
|
||||
}
|
||||
|
||||
return redirect('/users');
|
||||
return redirect('/settings/users');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified user.
|
||||
* @param int $id
|
||||
@@ -104,7 +108,7 @@ class UserController extends Controller
|
||||
*/
|
||||
public function edit($id, SocialAuthService $socialAuthService)
|
||||
{
|
||||
$this->checkPermissionOr('user-update', function () use ($id) {
|
||||
$this->checkPermissionOr('users-manage', function () use ($id) {
|
||||
return $this->currentUser->id == $id;
|
||||
});
|
||||
|
||||
@@ -125,7 +129,7 @@ class UserController extends Controller
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$this->preventAccessForDemoUsers();
|
||||
$this->checkPermissionOr('user-update', function () use ($id) {
|
||||
$this->checkPermissionOr('users-manage', function () use ($id) {
|
||||
return $this->currentUser->id == $id;
|
||||
});
|
||||
|
||||
@@ -133,8 +137,7 @@ class UserController extends Controller
|
||||
'name' => 'min:2',
|
||||
'email' => 'min:2|email|unique:users,email,' . $id,
|
||||
'password' => 'min:5|required_with:password_confirm',
|
||||
'password-confirm' => 'same:password|required_with:password',
|
||||
'role' => 'exists:roles,id'
|
||||
'password-confirm' => 'same:password|required_with:password'
|
||||
], [
|
||||
'password-confirm.required_with' => 'Password confirmation required'
|
||||
]);
|
||||
@@ -143,8 +146,9 @@ class UserController extends Controller
|
||||
$user->fill($request->all());
|
||||
|
||||
// Role updates
|
||||
if ($this->currentUser->can('user-update') && $request->has('role')) {
|
||||
$user->attachRoleId($request->get('role'));
|
||||
if (userCan('users-manage') && $request->has('roles')) {
|
||||
$roles = $request->get('roles');
|
||||
$user->roles()->sync($roles);
|
||||
}
|
||||
|
||||
// Password updates
|
||||
@@ -154,12 +158,15 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
// External auth id updates
|
||||
if ($this->currentUser->can('user-update') && $request->has('external_auth_id')) {
|
||||
if ($this->currentUser->can('users-manage') && $request->has('external_auth_id')) {
|
||||
$user->external_auth_id = $request->get('external_auth_id');
|
||||
}
|
||||
|
||||
$user->save();
|
||||
return redirect('/users');
|
||||
session()->flash('success', 'User successfully updated');
|
||||
|
||||
$redirectUrl = userCan('users-manage') ? '/settings/users' : '/settings/users/' . $user->id;
|
||||
return redirect($redirectUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +176,7 @@ class UserController extends Controller
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$this->checkPermissionOr('user-delete', function () use ($id) {
|
||||
$this->checkPermissionOr('users-manage', function () use ($id) {
|
||||
return $this->currentUser->id == $id;
|
||||
});
|
||||
|
||||
@@ -186,7 +193,7 @@ class UserController extends Controller
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->preventAccessForDemoUsers();
|
||||
$this->checkPermissionOr('user-delete', function () use ($id) {
|
||||
$this->checkPermissionOr('users-manage', function () use ($id) {
|
||||
return $this->currentUser->id == $id;
|
||||
});
|
||||
|
||||
@@ -197,6 +204,25 @@ class UserController extends Controller
|
||||
}
|
||||
$this->userRepo->destroy($user);
|
||||
|
||||
return redirect('/users');
|
||||
return redirect('/settings/users');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the user profile page
|
||||
* @param $id
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showProfilePage($id)
|
||||
{
|
||||
$user = $this->userRepo->getById($id);
|
||||
$userActivity = $this->userRepo->getActivity($user);
|
||||
$recentlyCreated = $this->userRepo->getRecentlyCreated($user, 5, 0);
|
||||
$assetCounts = $this->userRepo->getAssetCounts($user);
|
||||
return view('users/profile', [
|
||||
'user' => $user,
|
||||
'activity' => $userActivity,
|
||||
'recentlyCreated' => $recentlyCreated,
|
||||
'assetCounts' => $assetCounts
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Authenticate
|
||||
return redirect()->guest('/register/confirm/awaiting');
|
||||
}
|
||||
|
||||
if ($this->auth->guest() && !Setting::get('app-public')) {
|
||||
if ($this->auth->guest() && !setting('app-public')) {
|
||||
if ($request->ajax()) {
|
||||
return response('Unauthorized.', 401);
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
// Authenticated routes...
|
||||
Route::group(['middleware' => 'auth'], function () {
|
||||
|
||||
Route::group(['prefix' => 'pages'], function() {
|
||||
Route::get('/recently-created', 'PageController@showRecentlyCreated');
|
||||
Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'books'], function () {
|
||||
|
||||
// Books
|
||||
@@ -14,21 +19,28 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
Route::delete('/{id}', 'BookController@destroy');
|
||||
Route::get('/{slug}/sort-item', 'BookController@getSortItem');
|
||||
Route::get('/{slug}', 'BookController@show');
|
||||
Route::get('/{bookSlug}/permissions', 'BookController@showRestrict');
|
||||
Route::put('/{bookSlug}/permissions', 'BookController@restrict');
|
||||
Route::get('/{slug}/delete', 'BookController@showDelete');
|
||||
Route::get('/{bookSlug}/sort', 'BookController@sort');
|
||||
Route::put('/{bookSlug}/sort', 'BookController@saveSort');
|
||||
|
||||
// Pages
|
||||
Route::get('/{bookSlug}/page/create', 'PageController@create');
|
||||
Route::post('/{bookSlug}/page', 'PageController@store');
|
||||
Route::get('/{bookSlug}/draft/{pageId}', 'PageController@editDraft');
|
||||
Route::post('/{bookSlug}/page/{pageId}', 'PageController@store');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/export/pdf', 'PageController@exportPdf');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/export/html', 'PageController@exportHtml');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageController@exportPlainText');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
|
||||
Route::get('/{bookSlug}/draft/{pageId}/delete', 'PageController@showDeleteDraft');
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@showRestrict');
|
||||
Route::put('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@restrict');
|
||||
Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
|
||||
Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy');
|
||||
Route::delete('/{bookSlug}/draft/{pageId}', 'PageController@destroyDraft');
|
||||
|
||||
// Revisions
|
||||
Route::get('/{bookSlug}/page/{pageSlug}/revisions', 'PageController@showRevisions');
|
||||
@@ -42,19 +54,15 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show');
|
||||
Route::put('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@update');
|
||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit');
|
||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showRestrict');
|
||||
Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@restrict');
|
||||
Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
|
||||
Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy');
|
||||
|
||||
});
|
||||
|
||||
// Users
|
||||
Route::get('/users', 'UserController@index');
|
||||
Route::get('/users/create', 'UserController@create');
|
||||
Route::get('/users/{id}/delete', 'UserController@delete');
|
||||
Route::post('/users/create', 'UserController@store');
|
||||
Route::get('/users/{id}', 'UserController@edit');
|
||||
Route::put('/users/{id}', 'UserController@update');
|
||||
Route::delete('/users/{id}', 'UserController@destroy');
|
||||
// User Profile routes
|
||||
Route::get('/user/{userId}', 'UserController@showProfilePage');
|
||||
|
||||
// Image routes
|
||||
Route::group(['prefix' => 'images'], function() {
|
||||
@@ -70,11 +78,19 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
Route::delete('/{imageId}', 'ImageController@destroy');
|
||||
});
|
||||
|
||||
// Ajax routes
|
||||
Route::put('/ajax/page/{id}/save-draft', 'PageController@saveDraft');
|
||||
Route::get('/ajax/page/{id}', 'PageController@getPageAjax');
|
||||
Route::delete('/ajax/page/{id}', 'PageController@ajaxDestroy');
|
||||
|
||||
// Links
|
||||
Route::get('/link/{id}', 'PageController@redirectFromLink');
|
||||
|
||||
// Search
|
||||
Route::get('/search/all', 'SearchController@searchAll');
|
||||
Route::get('/search/pages', 'SearchController@searchPages');
|
||||
Route::get('/search/books', 'SearchController@searchBooks');
|
||||
Route::get('/search/chapters', 'SearchController@searchChapters');
|
||||
Route::get('/search/book/{bookId}', 'SearchController@searchBook');
|
||||
|
||||
// Other Pages
|
||||
@@ -82,8 +98,28 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
Route::get('/home', 'HomeController@index');
|
||||
|
||||
// Settings
|
||||
Route::get('/settings', 'SettingController@index');
|
||||
Route::post('/settings', 'SettingController@update');
|
||||
Route::group(['prefix' => 'settings'], function() {
|
||||
Route::get('/', 'SettingController@index');
|
||||
Route::post('/', 'SettingController@update');
|
||||
|
||||
// Users
|
||||
Route::get('/users', 'UserController@index');
|
||||
Route::get('/users/create', 'UserController@create');
|
||||
Route::get('/users/{id}/delete', 'UserController@delete');
|
||||
Route::post('/users/create', 'UserController@store');
|
||||
Route::get('/users/{id}', 'UserController@edit');
|
||||
Route::put('/users/{id}', 'UserController@update');
|
||||
Route::delete('/users/{id}', 'UserController@destroy');
|
||||
|
||||
// Roles
|
||||
Route::get('/roles', 'PermissionController@listRoles');
|
||||
Route::get('/roles/new', 'PermissionController@createRole');
|
||||
Route::post('/roles/new', 'PermissionController@storeRole');
|
||||
Route::get('/roles/delete/{id}', 'PermissionController@showDeleteRole');
|
||||
Route::delete('/roles/delete/{id}', 'PermissionController@deleteRole');
|
||||
Route::get('/roles/{id}', 'PermissionController@editRole');
|
||||
Route::put('/roles/{id}', 'PermissionController@updateRole');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<?php
|
||||
<?php namespace BookStack;
|
||||
|
||||
namespace BookStack;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Images;
|
||||
|
||||
class Image extends Model
|
||||
class Image extends Ownable
|
||||
{
|
||||
use Ownable;
|
||||
|
||||
protected $fillable = ['name'];
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php namespace BookStack;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
trait Ownable
|
||||
abstract class Ownable extends Model
|
||||
{
|
||||
/**
|
||||
* Relation for the user that created this entity.
|
||||
@@ -20,4 +21,14 @@ trait Ownable
|
||||
{
|
||||
return $this->belongsTo('BookStack\User', 'updated_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
* @return string
|
||||
*/
|
||||
public static function getClassName()
|
||||
{
|
||||
return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,18 +34,21 @@ class Page extends Entity
|
||||
|
||||
public function revisions()
|
||||
{
|
||||
return $this->hasMany('BookStack\PageRevision')->orderBy('created_at', 'desc');
|
||||
return $this->hasMany('BookStack\PageRevision')->where('type', '=', 'version')->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
$bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
|
||||
return '/books/' . $bookSlug . '/page/' . $this->slug;
|
||||
$midText = $this->draft ? '/draft/' : '/page/';
|
||||
$idComponent = $this->draft ? $this->id : $this->slug;
|
||||
return '/books/' . $bookSlug . $midText . $idComponent;
|
||||
}
|
||||
|
||||
public function getExcerpt($length = 100)
|
||||
{
|
||||
return strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
|
||||
$text = strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
|
||||
return mb_convert_encoding($text, 'UTF-8');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack;
|
||||
<?php namespace BookStack;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
@@ -8,16 +6,28 @@ class PageRevision extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'html', 'text'];
|
||||
|
||||
/**
|
||||
* Get the user that created the page revision
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function createdBy()
|
||||
{
|
||||
return $this->belongsTo('BookStack\User', 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page this revision originates from.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function page()
|
||||
{
|
||||
return $this->belongsTo('BookStack\Page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url for this revision.
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->page->getUrl() . '/revisions/' . $this->id;
|
||||
|
||||
@@ -13,4 +13,14 @@ class Permission extends Model
|
||||
{
|
||||
return $this->belongsToMany('BookStack\Permissions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permission object by name.
|
||||
* @param $roleName
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getByName($name)
|
||||
{
|
||||
return static::where('name', '=', $name)->first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,17 @@ class CustomFacadeProvider extends ServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind('activity', function() {
|
||||
return new ActivityService($this->app->make('BookStack\Activity'));
|
||||
return new ActivityService(
|
||||
$this->app->make('BookStack\Activity'),
|
||||
$this->app->make('BookStack\Services\RestrictionService')
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind('views', function() {
|
||||
return new ViewService($this->app->make('BookStack\View'));
|
||||
return new ViewService(
|
||||
$this->app->make('BookStack\View'),
|
||||
$this->app->make('BookStack\Services\RestrictionService')
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind('setting', function() {
|
||||
@@ -41,6 +47,7 @@ class CustomFacadeProvider extends ServiceProvider
|
||||
$this->app->make('Illuminate\Contracts\Cache\Repository')
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind('images', function() {
|
||||
return new ImageService(
|
||||
$this->app->make('Intervention\Image\ImageManager'),
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use Illuminate\Support\Str;
|
||||
use BookStack\Book;
|
||||
use Views;
|
||||
|
||||
class BookRepo
|
||||
class BookRepo extends EntityRepo
|
||||
{
|
||||
|
||||
protected $book;
|
||||
protected $pageRepo;
|
||||
protected $chapterRepo;
|
||||
|
||||
/**
|
||||
* BookRepo constructor.
|
||||
* @param Book $book
|
||||
* @param PageRepo $pageRepo
|
||||
* @param PageRepo $pageRepo
|
||||
* @param ChapterRepo $chapterRepo
|
||||
*/
|
||||
public function __construct(Book $book, PageRepo $pageRepo, ChapterRepo $chapterRepo)
|
||||
public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo)
|
||||
{
|
||||
$this->book = $book;
|
||||
$this->pageRepo = $pageRepo;
|
||||
$this->chapterRepo = $chapterRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query for getting books.
|
||||
* Takes into account any restrictions.
|
||||
* @return mixed
|
||||
*/
|
||||
private function bookQuery()
|
||||
{
|
||||
return $this->restrictionService->enforceBookRestrictions($this->book, 'view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +39,7 @@ class BookRepo
|
||||
*/
|
||||
public function getById($id)
|
||||
{
|
||||
return $this->book->findOrFail($id);
|
||||
return $this->bookQuery()->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +49,9 @@ class BookRepo
|
||||
*/
|
||||
public function getAll($count = 10)
|
||||
{
|
||||
return $this->book->orderBy('name', 'asc')->take($count)->get();
|
||||
$bookQuery = $this->bookQuery()->orderBy('name', 'asc');
|
||||
if (!$count) return $bookQuery->get();
|
||||
return $bookQuery->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +61,8 @@ class BookRepo
|
||||
*/
|
||||
public function getAllPaginated($count = 10)
|
||||
{
|
||||
return $this->book->orderBy('name', 'asc')->paginate($count);
|
||||
return $this->bookQuery()
|
||||
->orderBy('name', 'asc')->paginate($count);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +73,7 @@ class BookRepo
|
||||
*/
|
||||
public function getLatest($count = 10)
|
||||
{
|
||||
return $this->book->orderBy('created_at', 'desc')->take($count)->get();
|
||||
return $this->bookQuery()->orderBy('created_at', 'desc')->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,11 +102,12 @@ class BookRepo
|
||||
* Get a book by slug
|
||||
* @param $slug
|
||||
* @return mixed
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function getBySlug($slug)
|
||||
{
|
||||
$book = $this->book->where('slug', '=', $slug)->first();
|
||||
if ($book === null) abort(404);
|
||||
$book = $this->bookQuery()->where('slug', '=', $slug)->first();
|
||||
if ($book === null) throw new NotFoundException('Book not found');
|
||||
return $book;
|
||||
}
|
||||
|
||||
@@ -107,7 +118,7 @@ class BookRepo
|
||||
*/
|
||||
public function exists($id)
|
||||
{
|
||||
return $this->book->where('id', '=', $id)->exists();
|
||||
return $this->bookQuery()->where('id', '=', $id)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,17 +128,7 @@ class BookRepo
|
||||
*/
|
||||
public function newFromInput($input)
|
||||
{
|
||||
return $this->book->fill($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the amount of books that have a specific slug.
|
||||
* @param $slug
|
||||
* @return mixed
|
||||
*/
|
||||
public function countBySlug($slug)
|
||||
{
|
||||
return $this->book->where('slug', '=', $slug)->count();
|
||||
return $this->book->newInstance($input);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,6 +145,7 @@ class BookRepo
|
||||
$this->chapterRepo->destroy($chapter);
|
||||
}
|
||||
$book->views()->delete();
|
||||
$book->restrictions()->delete();
|
||||
$book->delete();
|
||||
}
|
||||
|
||||
@@ -159,7 +161,7 @@ class BookRepo
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slug
|
||||
* @param string $slug
|
||||
* @param bool|false $currentId
|
||||
* @return bool
|
||||
*/
|
||||
@@ -175,7 +177,7 @@ class BookRepo
|
||||
/**
|
||||
* Provides a suitable slug for the given book name.
|
||||
* Ensures the returned slug is unique in the system.
|
||||
* @param string $name
|
||||
* @param string $name
|
||||
* @param bool|false $currentId
|
||||
* @return string
|
||||
*/
|
||||
@@ -200,30 +202,52 @@ class BookRepo
|
||||
*/
|
||||
public function getChildren(Book $book)
|
||||
{
|
||||
$pages = $book->pages()->where('chapter_id', '=', 0)->get();
|
||||
$chapters = $book->chapters()->with('pages')->get();
|
||||
$pageQuery = $book->pages()->where('chapter_id', '=', 0);
|
||||
$pageQuery = $this->restrictionService->enforcePageRestrictions($pageQuery, 'view');
|
||||
$pages = $pageQuery->get();
|
||||
|
||||
$chapterQuery = $book->chapters()->with(['pages' => function($query) {
|
||||
$this->restrictionService->enforcePageRestrictions($query, 'view');
|
||||
}]);
|
||||
$chapterQuery = $this->restrictionService->enforceChapterRestrictions($chapterQuery, 'view');
|
||||
$chapters = $chapterQuery->get();
|
||||
$children = $pages->merge($chapters);
|
||||
$bookSlug = $book->slug;
|
||||
|
||||
$children->each(function ($child) use ($bookSlug) {
|
||||
$child->setAttribute('bookSlug', $bookSlug);
|
||||
if ($child->isA('chapter')) {
|
||||
$child->pages->each(function ($page) use ($bookSlug) {
|
||||
$page->setAttribute('bookSlug', $bookSlug);
|
||||
});
|
||||
$child->pages = $child->pages->sortBy(function($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->draft) $score -= 100;
|
||||
return $score;
|
||||
});
|
||||
}
|
||||
});
|
||||
return $children->sortBy('priority');
|
||||
|
||||
// Sort items with drafts first then by priority.
|
||||
return $children->sortBy(function($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->isA('page') && $child->draft) $score -= 100;
|
||||
return $score;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get books by search term.
|
||||
* @param $term
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term)
|
||||
public function getBySearch($term, $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$books = $this->book->fullTextSearch(['name', 'description'], $terms);
|
||||
$terms = $this->prepareSearchTerms($term);
|
||||
$books = $this->restrictionService->enforceBookRestrictions($this->book->fullTextSearchQuery(['name', 'description'], $terms))
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
foreach ($books as $book) {
|
||||
//highlight
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
|
||||
|
||||
use Activity;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use Illuminate\Support\Str;
|
||||
use BookStack\Chapter;
|
||||
|
||||
class ChapterRepo
|
||||
class ChapterRepo extends EntityRepo
|
||||
{
|
||||
|
||||
protected $chapter;
|
||||
|
||||
/**
|
||||
* ChapterRepo constructor.
|
||||
* @param $chapter
|
||||
* Base query for getting chapters, Takes restrictions into account.
|
||||
* @return mixed
|
||||
*/
|
||||
public function __construct(Chapter $chapter)
|
||||
private function chapterQuery()
|
||||
{
|
||||
$this->chapter = $chapter;
|
||||
return $this->restrictionService->enforceChapterRestrictions($this->chapter, 'view');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,7 +24,7 @@ class ChapterRepo
|
||||
*/
|
||||
public function idExists($id)
|
||||
{
|
||||
return $this->chapter->where('id', '=', $id)->count() > 0;
|
||||
return $this->chapterQuery()->where('id', '=', $id)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +34,7 @@ class ChapterRepo
|
||||
*/
|
||||
public function getById($id)
|
||||
{
|
||||
return $this->chapter->findOrFail($id);
|
||||
return $this->chapterQuery()->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +43,7 @@ class ChapterRepo
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->chapter->all();
|
||||
return $this->chapterQuery()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,14 +51,30 @@ class ChapterRepo
|
||||
* @param $slug
|
||||
* @param $bookId
|
||||
* @return mixed
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function getBySlug($slug, $bookId)
|
||||
{
|
||||
$chapter = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
|
||||
if ($chapter === null) abort(404);
|
||||
$chapter = $this->chapterQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
|
||||
if ($chapter === null) throw new NotFoundException('Chapter not found');
|
||||
return $chapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the child items for a chapter
|
||||
* @param Chapter $chapter
|
||||
*/
|
||||
public function getChildren(Chapter $chapter)
|
||||
{
|
||||
$pages = $this->restrictionService->enforcePageRestrictions($chapter->pages())->get();
|
||||
// Sort items with drafts first then by priority.
|
||||
return $pages->sortBy(function($child, $key) {
|
||||
$score = $child->priority;
|
||||
if ($child->draft) $score -= 100;
|
||||
return $score;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new chapter from request input.
|
||||
* @param $input
|
||||
@@ -85,6 +99,7 @@ class ChapterRepo
|
||||
}
|
||||
Activity::removeEntity($chapter);
|
||||
$chapter->views()->delete();
|
||||
$chapter->restrictions()->delete();
|
||||
$chapter->delete();
|
||||
}
|
||||
|
||||
@@ -123,14 +138,17 @@ class ChapterRepo
|
||||
|
||||
/**
|
||||
* Get chapters by the given search term.
|
||||
* @param $term
|
||||
* @param string $term
|
||||
* @param array $whereTerms
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term, $whereTerms = [])
|
||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$chapters = $this->chapter->fullTextSearch(['name', 'description'], $terms, $whereTerms);
|
||||
$terms = $this->prepareSearchTerms($term);
|
||||
$chapters = $this->restrictionService->enforceChapterRestrictions($this->chapter->fullTextSearchQuery(['name', 'description'], $terms, $whereTerms))
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
foreach ($chapters as $chapter) {
|
||||
//highlight
|
||||
|
||||
177
app/Repos/EntityRepo.php
Normal file
177
app/Repos/EntityRepo.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
use BookStack\Book;
|
||||
use BookStack\Chapter;
|
||||
use BookStack\Entity;
|
||||
use BookStack\Page;
|
||||
use BookStack\Services\RestrictionService;
|
||||
use BookStack\User;
|
||||
|
||||
class EntityRepo
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Book $book
|
||||
*/
|
||||
public $book;
|
||||
|
||||
/**
|
||||
* @var Chapter
|
||||
*/
|
||||
public $chapter;
|
||||
|
||||
/**
|
||||
* @var Page
|
||||
*/
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* @var RestrictionService
|
||||
*/
|
||||
protected $restrictionService;
|
||||
|
||||
/**
|
||||
* EntityService constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->book = app(Book::class);
|
||||
$this->chapter = app(Chapter::class);
|
||||
$this->page = app(Page::class);
|
||||
$this->restrictionService = app(RestrictionService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest books added to the system.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param bool $additionalQuery
|
||||
* @return
|
||||
*/
|
||||
public function getRecentlyCreatedBooks($count = 20, $page = 0, $additionalQuery = false)
|
||||
{
|
||||
$query = $this->restrictionService->enforceBookRestrictions($this->book)
|
||||
->orderBy('created_at', 'desc');
|
||||
if ($additionalQuery !== false && is_callable($additionalQuery)) {
|
||||
$additionalQuery($query);
|
||||
}
|
||||
return $query->skip($page * $count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently updated books.
|
||||
* @param $count
|
||||
* @param int $page
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyUpdatedBooks($count = 20, $page = 0)
|
||||
{
|
||||
return $this->restrictionService->enforceBookRestrictions($this->book)
|
||||
->orderBy('updated_at', 'desc')->skip($page * $count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param bool $additionalQuery
|
||||
* @return
|
||||
*/
|
||||
public function getRecentlyCreatedPages($count = 20, $page = 0, $additionalQuery = false)
|
||||
{
|
||||
$query = $this->restrictionService->enforcePageRestrictions($this->page)
|
||||
->orderBy('created_at', 'desc')->where('draft', '=', false);
|
||||
if ($additionalQuery !== false && is_callable($additionalQuery)) {
|
||||
$additionalQuery($query);
|
||||
}
|
||||
return $query->skip($page * $count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest chapters added to the system.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param bool $additionalQuery
|
||||
* @return
|
||||
*/
|
||||
public function getRecentlyCreatedChapters($count = 20, $page = 0, $additionalQuery = false)
|
||||
{
|
||||
$query = $this->restrictionService->enforceChapterRestrictions($this->chapter)
|
||||
->orderBy('created_at', 'desc');
|
||||
if ($additionalQuery !== false && is_callable($additionalQuery)) {
|
||||
$additionalQuery($query);
|
||||
}
|
||||
return $query->skip($page * $count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recently updated pages.
|
||||
* @param $count
|
||||
* @param int $page
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyUpdatedPages($count = 20, $page = 0)
|
||||
{
|
||||
return $this->restrictionService->enforcePageRestrictions($this->page)
|
||||
->where('draft', '=', false)
|
||||
->orderBy('updated_at', 'desc')->skip($page * $count)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get draft pages owned by the current user.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
*/
|
||||
public function getUserDraftPages($count = 20, $page = 0)
|
||||
{
|
||||
$user = auth()->user();
|
||||
return $this->page->where('draft', '=', true)
|
||||
->where('created_by', '=', $user->id)
|
||||
->orderBy('updated_at', 'desc')
|
||||
->skip($count * $page)->take($count)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates entity restrictions from a request
|
||||
* @param $request
|
||||
* @param Entity $entity
|
||||
*/
|
||||
public function updateRestrictionsFromRequest($request, Entity $entity)
|
||||
{
|
||||
$entity->restricted = $request->has('restricted') && $request->get('restricted') === 'true';
|
||||
$entity->restrictions()->delete();
|
||||
if ($request->has('restrictions')) {
|
||||
foreach ($request->get('restrictions') as $roleId => $restrictions) {
|
||||
foreach ($restrictions as $action => $value) {
|
||||
$entity->restrictions()->create([
|
||||
'role_id' => $roleId,
|
||||
'action' => strtolower($action)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a string of search terms by turning
|
||||
* it into an array of terms.
|
||||
* Keeps quoted terms together.
|
||||
* @param $termString
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareSearchTerms($termString)
|
||||
{
|
||||
preg_match_all('/"(.*?)"/', $termString, $matches);
|
||||
if (count($matches[1]) > 0) {
|
||||
$terms = $matches[1];
|
||||
$termString = trim(preg_replace('/"(.*?)"/', '', $termString));
|
||||
} else {
|
||||
$terms = [];
|
||||
}
|
||||
if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString));
|
||||
return $terms;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use BookStack\Image;
|
||||
use BookStack\Services\ImageService;
|
||||
use BookStack\Services\RestrictionService;
|
||||
use Setting;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
@@ -11,16 +12,19 @@ class ImageRepo
|
||||
|
||||
protected $image;
|
||||
protected $imageService;
|
||||
protected $restictionService;
|
||||
|
||||
/**
|
||||
* ImageRepo constructor.
|
||||
* @param Image $image
|
||||
* @param Image $image
|
||||
* @param ImageService $imageService
|
||||
* @param RestrictionService $restrictionService
|
||||
*/
|
||||
public function __construct(Image $image, ImageService $imageService)
|
||||
public function __construct(Image $image, ImageService $imageService, RestrictionService $restrictionService)
|
||||
{
|
||||
$this->image = $image;
|
||||
$this->imageService = $imageService;
|
||||
$this->restictionService = $restrictionService;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +38,12 @@ class ImageRepo
|
||||
return $this->image->findOrFail($id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a load images paginated, filtered by image type.
|
||||
* @param string $type
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
* @param bool|int $userFilter
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
* @param bool|int $userFilter
|
||||
* @return array
|
||||
*/
|
||||
public function getPaginatedByType($type, $page = 0, $pageSize = 24, $userFilter = false)
|
||||
@@ -51,6 +54,7 @@ class ImageRepo
|
||||
$images = $images->where('created_by', '=', $userFilter);
|
||||
}
|
||||
|
||||
$images = $this->restictionService->filterRelatedPages($images, 'images', 'uploaded_to');
|
||||
$images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get();
|
||||
$hasMore = count($images) > $pageSize;
|
||||
|
||||
@@ -68,12 +72,13 @@ class ImageRepo
|
||||
/**
|
||||
* Save a new image into storage and return the new image.
|
||||
* @param UploadedFile $uploadFile
|
||||
* @param string $type
|
||||
* @param string $type
|
||||
* @param int $uploadedTo
|
||||
* @return Image
|
||||
*/
|
||||
public function saveNew(UploadedFile $uploadFile, $type)
|
||||
public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0)
|
||||
{
|
||||
$image = $this->imageService->saveNewFromUpload($uploadFile, $type);
|
||||
$image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo);
|
||||
$this->loadThumbs($image);
|
||||
return $image;
|
||||
}
|
||||
@@ -123,9 +128,9 @@ class ImageRepo
|
||||
* Checks the cache then storage to avoid creating / accessing the filesystem on every check.
|
||||
*
|
||||
* @param Image $image
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param bool $keepRatio
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param bool $keepRatio
|
||||
* @return string
|
||||
*/
|
||||
public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
|
||||
|
||||
@@ -1,59 +1,53 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
|
||||
use Activity;
|
||||
use BookStack\Book;
|
||||
use BookStack\Chapter;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use Carbon\Carbon;
|
||||
use DOMDocument;
|
||||
use Illuminate\Support\Str;
|
||||
use BookStack\Page;
|
||||
use BookStack\PageRevision;
|
||||
|
||||
class PageRepo
|
||||
class PageRepo extends EntityRepo
|
||||
{
|
||||
protected $page;
|
||||
|
||||
protected $pageRevision;
|
||||
|
||||
/**
|
||||
* PageRepo constructor.
|
||||
* @param Page $page
|
||||
* @param PageRevision $pageRevision
|
||||
*/
|
||||
public function __construct(Page $page, PageRevision $pageRevision)
|
||||
public function __construct(PageRevision $pageRevision)
|
||||
{
|
||||
$this->page = $page;
|
||||
$this->pageRevision = $pageRevision;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page id exists.
|
||||
* @param $id
|
||||
* @return bool
|
||||
* Base query for getting pages, Takes restrictions into account.
|
||||
* @param bool $allowDrafts
|
||||
* @return mixed
|
||||
*/
|
||||
public function idExists($id)
|
||||
private function pageQuery($allowDrafts = false)
|
||||
{
|
||||
return $this->page->where('page_id', '=', $id)->count() > 0;
|
||||
$query = $this->restrictionService->enforcePageRestrictions($this->page, 'view');
|
||||
if (!$allowDrafts) {
|
||||
$query = $query->where('draft', '=', false);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a page via a specific ID.
|
||||
* @param $id
|
||||
* @param bool $allowDrafts
|
||||
* @return mixed
|
||||
*/
|
||||
public function getById($id)
|
||||
public function getById($id, $allowDrafts = false)
|
||||
{
|
||||
return $this->page->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pages.
|
||||
* @return \Illuminate\Database\Eloquent\Collection|static[]
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->page->all();
|
||||
return $this->pageQuery($allowDrafts)->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,15 +55,37 @@ class PageRepo
|
||||
* @param $slug
|
||||
* @param $bookId
|
||||
* @return mixed
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function getBySlug($slug, $bookId)
|
||||
{
|
||||
$page = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
|
||||
if ($page === null) abort(404);
|
||||
$page = $this->pageQuery()->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
|
||||
if ($page === null) throw new NotFoundException('Page not found');
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through page revisions and retrieve
|
||||
* the last page in the current book that
|
||||
* has a slug equal to the one given.
|
||||
* @param $pageSlug
|
||||
* @param $bookSlug
|
||||
* @return null | Page
|
||||
*/
|
||||
public function findPageUsingOldSlug($pageSlug, $bookSlug)
|
||||
{
|
||||
$revision = $this->pageRevision->where('slug', '=', $pageSlug)
|
||||
->whereHas('page', function ($query) {
|
||||
$this->restrictionService->enforcePageRestrictions($query);
|
||||
})
|
||||
->where('type', '=', 'version')
|
||||
->where('book_slug', '=', $bookSlug)->orderBy('created_at', 'desc')
|
||||
->with('page')->first();
|
||||
return $revision !== null ? $revision->page : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new Page instance from the given input.
|
||||
* @param $input
|
||||
* @return Page
|
||||
*/
|
||||
@@ -94,8 +110,8 @@ class PageRepo
|
||||
* Save a new page into the system.
|
||||
* Input validation must be done beforehand.
|
||||
* @param array $input
|
||||
* @param Book $book
|
||||
* @param int $chapterId
|
||||
* @param Book $book
|
||||
* @param int $chapterId
|
||||
* @return Page
|
||||
*/
|
||||
public function saveNew(array $input, Book $book, $chapterId = null)
|
||||
@@ -114,6 +130,47 @@ class PageRepo
|
||||
return $page;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publish a draft page to make it a normal page.
|
||||
* Sets the slug and updates the content.
|
||||
* @param Page $draftPage
|
||||
* @param array $input
|
||||
* @return Page
|
||||
*/
|
||||
public function publishDraft(Page $draftPage, array $input)
|
||||
{
|
||||
$draftPage->fill($input);
|
||||
|
||||
$draftPage->slug = $this->findSuitableSlug($draftPage->name, $draftPage->book->id);
|
||||
$draftPage->html = $this->formatHtml($input['html']);
|
||||
$draftPage->text = strip_tags($draftPage->html);
|
||||
$draftPage->draft = false;
|
||||
|
||||
$draftPage->save();
|
||||
return $draftPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new draft page instance.
|
||||
* @param Book $book
|
||||
* @param Chapter|null $chapter
|
||||
* @return static
|
||||
*/
|
||||
public function getDraftPage(Book $book, $chapter)
|
||||
{
|
||||
$page = $this->page->newInstance();
|
||||
$page->name = 'New Page';
|
||||
$page->created_by = auth()->user()->id;
|
||||
$page->updated_by = auth()->user()->id;
|
||||
$page->draft = true;
|
||||
|
||||
if ($chapter) $page->chapter_id = $chapter->id;
|
||||
|
||||
$book->pages()->save($page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a page's html to be tagged correctly
|
||||
* within the system.
|
||||
@@ -122,24 +179,23 @@ class PageRepo
|
||||
*/
|
||||
protected function formatHtml($htmlText)
|
||||
{
|
||||
if($htmlText == '') return $htmlText;
|
||||
if ($htmlText == '') return $htmlText;
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML($htmlText);
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
|
||||
|
||||
$container = $doc->documentElement;
|
||||
$body = $container->childNodes->item(0);
|
||||
$childNodes = $body->childNodes;
|
||||
|
||||
// Ensure no duplicate ids are used
|
||||
$lastId = false;
|
||||
$idArray = [];
|
||||
|
||||
foreach ($childNodes as $index => $childNode) {
|
||||
/** @var \DOMElement $childNode */
|
||||
if (get_class($childNode) !== 'DOMElement') continue;
|
||||
|
||||
// Overwrite id if not a bookstack custom id
|
||||
// Overwrite id if not a BookStack custom id
|
||||
if ($childNode->hasAttribute('id')) {
|
||||
$id = $childNode->getAttribute('id');
|
||||
if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) {
|
||||
@@ -149,13 +205,18 @@ class PageRepo
|
||||
}
|
||||
|
||||
// Create an unique id for the element
|
||||
do {
|
||||
$id = 'bkmrk-' . substr(uniqid(), -5);
|
||||
} while ($id == $lastId);
|
||||
$lastId = $id;
|
||||
// Uses the content as a basis to ensure output is the same every time
|
||||
// the same content is passed through.
|
||||
$contentId = 'bkmrk-' . substr(strtolower(preg_replace('/\s+/', '-', trim($childNode->nodeValue))), 0, 20);
|
||||
$newId = urlencode($contentId);
|
||||
$loopIndex = 0;
|
||||
while (in_array($newId, $idArray)) {
|
||||
$newId = urlencode($contentId . '-' . $loopIndex);
|
||||
$loopIndex++;
|
||||
}
|
||||
|
||||
$childNode->setAttribute('id', $id);
|
||||
$idArray[] = $id;
|
||||
$childNode->setAttribute('id', $newId);
|
||||
$idArray[] = $newId;
|
||||
}
|
||||
|
||||
// Generate inner html as a string
|
||||
@@ -171,14 +232,17 @@ class PageRepo
|
||||
/**
|
||||
* Gets pages by a search term.
|
||||
* Highlights page content for showing in results.
|
||||
* @param string $term
|
||||
* @param string $term
|
||||
* @param array $whereTerms
|
||||
* @param int $count
|
||||
* @param array $paginationAppends
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBySearch($term, $whereTerms = [])
|
||||
public function getBySearch($term, $whereTerms = [], $count = 20, $paginationAppends = [])
|
||||
{
|
||||
$terms = explode(' ', $term);
|
||||
$pages = $this->page->fullTextSearch(['name', 'text'], $terms, $whereTerms);
|
||||
$terms = $this->prepareSearchTerms($term);
|
||||
$pages = $this->restrictionService->enforcePageRestrictions($this->page->fullTextSearchQuery(['name', 'text'], $terms, $whereTerms))
|
||||
->paginate($count)->appends($paginationAppends);
|
||||
|
||||
// Add highlights to page text.
|
||||
$words = join('|', explode(' ', preg_quote(trim($term), '/')));
|
||||
@@ -215,7 +279,7 @@ class PageRepo
|
||||
*/
|
||||
public function searchForImage($imageString)
|
||||
{
|
||||
$pages = $this->page->where('html', 'like', '%' . $imageString . '%')->get();
|
||||
$pages = $this->pageQuery()->where('html', 'like', '%' . $imageString . '%')->get();
|
||||
foreach ($pages as $page) {
|
||||
$page->url = $page->getUrl();
|
||||
$page->html = '';
|
||||
@@ -226,8 +290,8 @@ class PageRepo
|
||||
|
||||
/**
|
||||
* Updates a page with any fillable data and saves it into the database.
|
||||
* @param Page $page
|
||||
* @param int $book_id
|
||||
* @param Page $page
|
||||
* @param int $book_id
|
||||
* @param string $input
|
||||
* @return Page
|
||||
*/
|
||||
@@ -238,13 +302,22 @@ class PageRepo
|
||||
$this->saveRevision($page);
|
||||
}
|
||||
|
||||
// Prevent slug being updated if no name change
|
||||
if ($page->name !== $input['name']) {
|
||||
$page->slug = $this->findSuitableSlug($input['name'], $book_id, $page->id);
|
||||
}
|
||||
|
||||
// Update with new details
|
||||
$userId = auth()->user()->id;
|
||||
$page->fill($input);
|
||||
$page->slug = $this->findSuitableSlug($page->name, $book_id, $page->id);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->updated_by = auth()->user()->id;
|
||||
$page->updated_by = $userId;
|
||||
$page->save();
|
||||
|
||||
// Remove all update drafts for this user & page.
|
||||
$this->userUpdateDraftsQuery($page, $userId)->delete();
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
@@ -276,8 +349,11 @@ class PageRepo
|
||||
{
|
||||
$revision = $this->pageRevision->fill($page->toArray());
|
||||
$revision->page_id = $page->id;
|
||||
$revision->slug = $page->slug;
|
||||
$revision->book_slug = $page->book->slug;
|
||||
$revision->created_by = auth()->user()->id;
|
||||
$revision->created_at = $page->updated_at;
|
||||
$revision->type = 'version';
|
||||
$revision->save();
|
||||
// Clear old revisions
|
||||
if ($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) {
|
||||
@@ -287,6 +363,153 @@ class PageRepo
|
||||
return $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a page update draft.
|
||||
* @param Page $page
|
||||
* @param array $data
|
||||
* @return PageRevision
|
||||
*/
|
||||
public function saveUpdateDraft(Page $page, $data = [])
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
$drafts = $this->userUpdateDraftsQuery($page, $userId)->get();
|
||||
|
||||
if ($drafts->count() > 0) {
|
||||
$draft = $drafts->first();
|
||||
} else {
|
||||
$draft = $this->pageRevision->newInstance();
|
||||
$draft->page_id = $page->id;
|
||||
$draft->slug = $page->slug;
|
||||
$draft->book_slug = $page->book->slug;
|
||||
$draft->created_by = $userId;
|
||||
$draft->type = 'update_draft';
|
||||
}
|
||||
|
||||
$draft->fill($data);
|
||||
$draft->save();
|
||||
return $draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a draft page.
|
||||
* @param Page $page
|
||||
* @param array $data
|
||||
* @return Page
|
||||
*/
|
||||
public function updateDraftPage(Page $page, $data = [])
|
||||
{
|
||||
$page->fill($data);
|
||||
|
||||
if (isset($data['html'])) {
|
||||
$page->text = strip_tags($data['html']);
|
||||
}
|
||||
|
||||
$page->save();
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for getting user update drafts.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return mixed
|
||||
*/
|
||||
private function userUpdateDraftsQuery(Page $page, $userId)
|
||||
{
|
||||
return $this->pageRevision->where('created_by', '=', $userId)
|
||||
->where('type', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a user has a draft version of a particular page or not.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUserGotPageDraft(Page $page, $userId)
|
||||
{
|
||||
return $this->userUpdateDraftsQuery($page, $userId)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest updated draft revision for a particular page and user.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserPageDraft(Page $page, $userId)
|
||||
{
|
||||
return $this->userUpdateDraftsQuery($page, $userId)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification message that informs the user that they are editing a draft page.
|
||||
* @param PageRevision $draft
|
||||
* @return string
|
||||
*/
|
||||
public function getUserPageDraftMessage(PageRevision $draft)
|
||||
{
|
||||
$message = 'You are currently editing a draft that was last saved ' . $draft->updated_at->diffForHumans() . '.';
|
||||
if ($draft->page->updated_at->timestamp > $draft->updated_at->timestamp) {
|
||||
$message .= "\n This page has been updated by since that time. It is recommended that you discard this draft.";
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page is being actively editing.
|
||||
* Checks for edits since last page updated.
|
||||
* Passing in a minuted range will check for edits
|
||||
* within the last x minutes.
|
||||
* @param Page $page
|
||||
* @param null $minRange
|
||||
* @return bool
|
||||
*/
|
||||
public function isPageEditingActive(Page $page, $minRange = null)
|
||||
{
|
||||
$draftSearch = $this->activePageEditingQuery($page, $minRange);
|
||||
return $draftSearch->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a notification message concerning the editing activity on
|
||||
* a particular page.
|
||||
* @param Page $page
|
||||
* @param null $minRange
|
||||
* @return string
|
||||
*/
|
||||
public function getPageEditingActiveMessage(Page $page, $minRange = null)
|
||||
{
|
||||
$pageDraftEdits = $this->activePageEditingQuery($page, $minRange)->get();
|
||||
$userMessage = $pageDraftEdits->count() > 1 ? $pageDraftEdits->count() . ' users have' : $pageDraftEdits->first()->createdBy->name . ' has';
|
||||
$timeMessage = $minRange === null ? 'since the page was last updated' : 'in the last ' . $minRange . ' minutes';
|
||||
$message = '%s started editing this page %s. Take care not to overwrite each other\'s updates!';
|
||||
return sprintf($message, $userMessage, $timeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* A query to check for active update drafts on a particular page.
|
||||
* @param Page $page
|
||||
* @param null $minRange
|
||||
* @return mixed
|
||||
*/
|
||||
private function activePageEditingQuery(Page $page, $minRange = null)
|
||||
{
|
||||
$query = $this->pageRevision->where('type', '=', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->where('updated_at', '>', $page->updated_at)
|
||||
->where('created_by', '!=', auth()->user()->id)
|
||||
->with('createdBy');
|
||||
|
||||
if ($minRange !== null) {
|
||||
$query = $query->where('updated_at', '>=', Carbon::now()->subMinutes($minRange));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single revision via it's id.
|
||||
* @param $id
|
||||
@@ -314,7 +537,7 @@ class PageRepo
|
||||
/**
|
||||
* Changes the related book for the specified page.
|
||||
* Changes the book id of any relations to the page that store the book id.
|
||||
* @param int $bookId
|
||||
* @param int $bookId
|
||||
* @param Page $page
|
||||
* @return Page
|
||||
*/
|
||||
@@ -355,8 +578,26 @@ class PageRepo
|
||||
Activity::removeEntity($page);
|
||||
$page->views()->delete();
|
||||
$page->revisions()->delete();
|
||||
$page->restrictions()->delete();
|
||||
$page->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param $count
|
||||
*/
|
||||
public function getRecentlyCreatedPaginated($count = 20)
|
||||
{
|
||||
return $this->pageQuery()->orderBy('created_at', 'desc')->paginate($count);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Get the latest pages added to the system.
|
||||
* @param $count
|
||||
*/
|
||||
public function getRecentlyUpdatedPaginated($count = 20)
|
||||
{
|
||||
return $this->pageQuery()->orderBy('updated_at', 'desc')->paginate($count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
142
app/Repos/PermissionsRepo.php
Normal file
142
app/Repos/PermissionsRepo.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Permission;
|
||||
use BookStack\Role;
|
||||
use Setting;
|
||||
|
||||
class PermissionsRepo
|
||||
{
|
||||
|
||||
protected $permission;
|
||||
protected $role;
|
||||
|
||||
/**
|
||||
* PermissionsRepo constructor.
|
||||
* @param $permission
|
||||
* @param $role
|
||||
*/
|
||||
public function __construct(Permission $permission, Role $role)
|
||||
{
|
||||
$this->permission = $permission;
|
||||
$this->role = $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the user roles from the system.
|
||||
* @return \Illuminate\Database\Eloquent\Collection|static[]
|
||||
*/
|
||||
public function getAllRoles()
|
||||
{
|
||||
return $this->role->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the roles except for the provided one.
|
||||
* @param Role $role
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAllRolesExcept(Role $role)
|
||||
{
|
||||
return $this->role->where('id', '!=', $role->id)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a role via its ID.
|
||||
* @param $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRoleById($id)
|
||||
{
|
||||
return $this->role->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new role into the system.
|
||||
* @param array $roleData
|
||||
* @return Role
|
||||
*/
|
||||
public function saveNewRole($roleData)
|
||||
{
|
||||
$role = $this->role->newInstance($roleData);
|
||||
$role->name = str_replace(' ', '-', strtolower($roleData['display_name']));
|
||||
// Prevent duplicate names
|
||||
while ($this->role->where('name', '=', $role->name)->count() > 0) {
|
||||
$role->name .= strtolower(str_random(2));
|
||||
}
|
||||
$role->save();
|
||||
|
||||
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
||||
$this->assignRolePermissions($role, $permissions);
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing role.
|
||||
* Ensure Admin role always has all permissions.
|
||||
* @param $roleId
|
||||
* @param $roleData
|
||||
*/
|
||||
public function updateRole($roleId, $roleData)
|
||||
{
|
||||
$role = $this->role->findOrFail($roleId);
|
||||
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
||||
$this->assignRolePermissions($role, $permissions);
|
||||
|
||||
if ($role->name === 'admin') {
|
||||
$permissions = $this->permission->all()->pluck('id')->toArray();
|
||||
$role->permissions()->sync($permissions);
|
||||
}
|
||||
|
||||
$role->fill($roleData);
|
||||
$role->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign an list of permission names to an role.
|
||||
* @param Role $role
|
||||
* @param array $permissionNameArray
|
||||
*/
|
||||
public function assignRolePermissions(Role $role, $permissionNameArray = [])
|
||||
{
|
||||
$permissions = [];
|
||||
$permissionNameArray = array_values($permissionNameArray);
|
||||
if ($permissionNameArray && count($permissionNameArray) > 0) {
|
||||
$permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
|
||||
}
|
||||
$role->permissions()->sync($permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a role from the system.
|
||||
* Check it's not an admin role or set as default before deleting.
|
||||
* If an migration Role ID is specified the users assign to the current role
|
||||
* will be added to the role of the specified id.
|
||||
* @param $roleId
|
||||
* @param $migrateRoleId
|
||||
* @throws PermissionsException
|
||||
*/
|
||||
public function deleteRole($roleId, $migrateRoleId)
|
||||
{
|
||||
$role = $this->role->findOrFail($roleId);
|
||||
|
||||
// Prevent deleting admin role or default registration role.
|
||||
if ($role->name === 'admin') {
|
||||
throw new PermissionsException('The admin role cannot be deleted');
|
||||
} else if ($role->id == setting('registration-role')) {
|
||||
throw new PermissionsException('This role cannot be deleted while set as the default registration role.');
|
||||
}
|
||||
|
||||
if ($migrateRoleId) {
|
||||
$newRole = $this->role->find($migrateRoleId);
|
||||
if ($newRole) {
|
||||
$users = $role->users->pluck('id')->toArray();
|
||||
$newRole->users()->sync($users);
|
||||
}
|
||||
}
|
||||
|
||||
$role->delete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
|
||||
use BookStack\Role;
|
||||
use BookStack\User;
|
||||
use Setting;
|
||||
@@ -10,15 +9,19 @@ class UserRepo
|
||||
|
||||
protected $user;
|
||||
protected $role;
|
||||
protected $entityRepo;
|
||||
|
||||
/**
|
||||
* UserRepo constructor.
|
||||
* @param $user
|
||||
* @param User $user
|
||||
* @param Role $role
|
||||
* @param EntityRepo $entityRepo
|
||||
*/
|
||||
public function __construct(User $user, Role $role)
|
||||
public function __construct(User $user, Role $role, EntityRepo $entityRepo)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->role = $role;
|
||||
$this->entityRepo = $entityRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +42,15 @@ class UserRepo
|
||||
return $this->user->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the users with their permissions.
|
||||
* @return \Illuminate\Database\Eloquent\Builder|static
|
||||
*/
|
||||
public function getAllUsers()
|
||||
{
|
||||
return $this->user->with('roles', 'avatar')->orderBy('name', 'asc')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user and attaches a role to them.
|
||||
* @param array $data
|
||||
@@ -65,8 +77,8 @@ class UserRepo
|
||||
*/
|
||||
public function attachDefaultRole($user)
|
||||
{
|
||||
$roleId = Setting::get('registration-role');
|
||||
if ($roleId === false) $roleId = $this->role->getDefault()->id;
|
||||
$roleId = setting('registration-role');
|
||||
if ($roleId === false) $roleId = $this->role->first()->id;
|
||||
$user->attachRoleId($roleId);
|
||||
}
|
||||
|
||||
@@ -77,15 +89,10 @@ class UserRepo
|
||||
*/
|
||||
public function isOnlyAdmin(User $user)
|
||||
{
|
||||
if ($user->role->name != 'admin') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$adminRole = $this->role->where('name', '=', 'admin')->first();
|
||||
if (count($adminRole->users) > 1) {
|
||||
return false;
|
||||
}
|
||||
if (!$user->roles->pluck('name')->contains('admin')) return false;
|
||||
|
||||
$adminRole = $this->role->getRole('admin');
|
||||
if ($adminRole->users->count() > 1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,4 +119,62 @@ class UserRepo
|
||||
$user->socialAccounts()->delete();
|
||||
$user->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest activity for a user.
|
||||
* @param User $user
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
public function getActivity(User $user, $count = 20, $page = 0)
|
||||
{
|
||||
return \Activity::userActivity($user, $count, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recently created content for this given user.
|
||||
* @param User $user
|
||||
* @param int $count
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRecentlyCreated(User $user, $count = 20)
|
||||
{
|
||||
return [
|
||||
'pages' => $this->entityRepo->getRecentlyCreatedPages($count, 0, function ($query) use ($user) {
|
||||
$query->where('created_by', '=', $user->id);
|
||||
}),
|
||||
'chapters' => $this->entityRepo->getRecentlyCreatedChapters($count, 0, function ($query) use ($user) {
|
||||
$query->where('created_by', '=', $user->id);
|
||||
}),
|
||||
'books' => $this->entityRepo->getRecentlyCreatedBooks($count, 0, function ($query) use ($user) {
|
||||
$query->where('created_by', '=', $user->id);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get asset created counts for the give user.
|
||||
* @param User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getAssetCounts(User $user)
|
||||
{
|
||||
return [
|
||||
'pages' => $this->entityRepo->page->where('created_by', '=', $user->id)->count(),
|
||||
'chapters' => $this->entityRepo->chapter->where('created_by', '=', $user->id)->count(),
|
||||
'books' => $this->entityRepo->book->where('created_by', '=', $user->id)->count(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the roles which can be given restricted access to
|
||||
* other entities in the system.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRestrictableRoles()
|
||||
{
|
||||
return $this->role->where('name', '!=', 'admin')->get();
|
||||
}
|
||||
|
||||
}
|
||||
21
app/Restriction.php
Normal file
21
app/Restriction.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Restriction extends Model
|
||||
{
|
||||
|
||||
protected $fillable = ['role_id', 'action'];
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* Get all this restriction's attached entity.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function restrictable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
25
app/Role.php
25
app/Role.php
@@ -6,11 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Role extends Model
|
||||
{
|
||||
/**
|
||||
* Sets the default role name for newly registered users.
|
||||
* @var string
|
||||
*/
|
||||
protected static $default = 'viewer';
|
||||
|
||||
protected $fillable = ['display_name', 'description'];
|
||||
|
||||
/**
|
||||
* The roles that belong to the role.
|
||||
@@ -28,6 +25,15 @@ class Role extends Model
|
||||
return $this->belongsToMany('BookStack\Permission');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this role has a permission.
|
||||
* @param $permission
|
||||
*/
|
||||
public function hasPermission($permission)
|
||||
{
|
||||
return $this->permissions->pluck('name')->contains($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a permission to this role.
|
||||
* @param Permission $permission
|
||||
@@ -37,15 +43,6 @@ class Role extends Model
|
||||
$this->permissions()->attach($permission->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the default role.
|
||||
* @return Role
|
||||
*/
|
||||
public static function getDefault()
|
||||
{
|
||||
return static::getRole(static::$default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role object for the specified role.
|
||||
* @param $roleName
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php namespace BookStack\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use BookStack\Activity;
|
||||
use BookStack\Entity;
|
||||
use Session;
|
||||
@@ -9,14 +8,17 @@ class ActivityService
|
||||
{
|
||||
protected $activity;
|
||||
protected $user;
|
||||
protected $restrictionService;
|
||||
|
||||
/**
|
||||
* ActivityService constructor.
|
||||
* @param $activity
|
||||
* @param Activity $activity
|
||||
* @param RestrictionService $restrictionService
|
||||
*/
|
||||
public function __construct(Activity $activity)
|
||||
public function __construct(Activity $activity, RestrictionService $restrictionService)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
$this->restrictionService = $restrictionService;
|
||||
$this->user = auth()->user();
|
||||
}
|
||||
|
||||
@@ -24,25 +26,26 @@ class ActivityService
|
||||
* Add activity data to database.
|
||||
* @param Entity $entity
|
||||
* @param $activityKey
|
||||
* @param int $bookId
|
||||
* @param bool $extra
|
||||
* @param int $bookId
|
||||
* @param bool $extra
|
||||
*/
|
||||
public function add(Entity $entity, $activityKey, $bookId = 0, $extra = false)
|
||||
{
|
||||
$this->activity->user_id = $this->user->id;
|
||||
$this->activity->book_id = $bookId;
|
||||
$this->activity->key = strtolower($activityKey);
|
||||
$activity = $this->activity->newInstance();
|
||||
$activity->user_id = $this->user->id;
|
||||
$activity->book_id = $bookId;
|
||||
$activity->key = strtolower($activityKey);
|
||||
if ($extra !== false) {
|
||||
$this->activity->extra = $extra;
|
||||
$activity->extra = $extra;
|
||||
}
|
||||
$entity->activity()->save($this->activity);
|
||||
$entity->activity()->save($activity);
|
||||
$this->setNotification($activityKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a activity history with a message & without binding to a entitiy.
|
||||
* Adds a activity history with a message & without binding to a entity.
|
||||
* @param $activityKey
|
||||
* @param int $bookId
|
||||
* @param int $bookId
|
||||
* @param bool|false $extra
|
||||
*/
|
||||
public function addMessage($activityKey, $bookId = 0, $extra = false)
|
||||
@@ -85,20 +88,22 @@ class ActivityService
|
||||
*/
|
||||
public function latest($count = 20, $page = 0)
|
||||
{
|
||||
$activityList = $this->activity->orderBy('created_at', 'desc')
|
||||
->skip($count * $page)->take($count)->get();
|
||||
$activityList = $this->restrictionService
|
||||
->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
|
||||
->orderBy('created_at', 'desc')->skip($count * $page)->take($count)->get();
|
||||
|
||||
return $this->filterSimilar($activityList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest activity for an entitiy, Filtering out similar
|
||||
* Gets the latest activity for an entity, Filtering out similar
|
||||
* items to prevent a message activity list.
|
||||
* @param Entity $entity
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
function entityActivity($entity, $count = 20, $page = 0)
|
||||
public function entityActivity($entity, $count = 20, $page = 0)
|
||||
{
|
||||
$activity = $entity->hasMany('BookStack\Activity')->orderBy('created_at', 'desc')
|
||||
->skip($count * $page)->take($count)->get();
|
||||
@@ -107,15 +112,31 @@ class ActivityService
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out similar activity.
|
||||
* @param Activity[] $activity
|
||||
* Get latest activity for a user, Filtering out similar
|
||||
* items.
|
||||
* @param $user
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @return array
|
||||
*/
|
||||
protected function filterSimilar($activity)
|
||||
public function userActivity($user, $count = 20, $page = 0)
|
||||
{
|
||||
$activityList = $this->restrictionService
|
||||
->filterRestrictedEntityRelations($this->activity, 'activities', 'entity_id', 'entity_type')
|
||||
->orderBy('created_at', 'desc')->where('user_id', '=', $user->id)->skip($count * $page)->take($count)->get();
|
||||
return $this->filterSimilar($activityList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out similar activity.
|
||||
* @param Activity[] $activities
|
||||
* @return array
|
||||
*/
|
||||
protected function filterSimilar($activities)
|
||||
{
|
||||
$newActivity = [];
|
||||
$previousItem = false;
|
||||
foreach ($activity as $activityItem) {
|
||||
foreach ($activities as $activityItem) {
|
||||
if ($previousItem === false) {
|
||||
$previousItem = $activityItem;
|
||||
$newActivity[] = $activityItem;
|
||||
|
||||
@@ -45,7 +45,7 @@ class EmailConfirmationService
|
||||
'token' => $token,
|
||||
]);
|
||||
$this->mailer->send('emails/email-confirmation', ['token' => $token], function (Message $message) use ($user) {
|
||||
$appName = \Setting::get('app-name', 'BookStack');
|
||||
$appName = setting('app-name', 'BookStack');
|
||||
$message->to($user->email, $user->name)->subject('Confirm your email on ' . $appName . '.');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,14 +41,16 @@ class ImageService
|
||||
/**
|
||||
* Saves a new image from an upload.
|
||||
* @param UploadedFile $uploadedFile
|
||||
* @param string $type
|
||||
* @param string $type
|
||||
* @param int $uploadedTo
|
||||
* @return mixed
|
||||
* @throws ImageUploadException
|
||||
*/
|
||||
public function saveNewFromUpload(UploadedFile $uploadedFile, $type)
|
||||
public function saveNewFromUpload(UploadedFile $uploadedFile, $type, $uploadedTo = 0)
|
||||
{
|
||||
$imageName = $uploadedFile->getClientOriginalName();
|
||||
$imageData = file_get_contents($uploadedFile->getRealPath());
|
||||
return $this->saveNew($imageName, $imageData, $type);
|
||||
return $this->saveNew($imageName, $imageData, $type, $uploadedTo);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,13 +75,14 @@ class ImageService
|
||||
* @param string $imageName
|
||||
* @param string $imageData
|
||||
* @param string $type
|
||||
* @param int $uploadedTo
|
||||
* @return Image
|
||||
* @throws ImageUploadException
|
||||
*/
|
||||
private function saveNew($imageName, $imageData, $type)
|
||||
private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
$secureUploads = Setting::get('app-secure-images');
|
||||
$secureUploads = setting('app-secure-images');
|
||||
$imageName = str_replace(' ', '-', $imageName);
|
||||
|
||||
if ($secureUploads) $imageName = str_random(16) . '-' . $imageName;
|
||||
@@ -100,7 +103,8 @@ class ImageService
|
||||
'name' => $imageName,
|
||||
'path' => $fullPath,
|
||||
'url' => $this->getPublicUrl($fullPath),
|
||||
'type' => $type
|
||||
'type' => $type,
|
||||
'uploaded_to' => $uploadedTo
|
||||
];
|
||||
|
||||
if (auth()->user() && auth()->user()->id !== 0) {
|
||||
|
||||
326
app/Services/RestrictionService.php
Normal file
326
app/Services/RestrictionService.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php namespace BookStack\Services;
|
||||
|
||||
use BookStack\Entity;
|
||||
|
||||
class RestrictionService
|
||||
{
|
||||
|
||||
protected $userRoles;
|
||||
protected $isAdmin;
|
||||
protected $currentAction;
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* RestrictionService constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->currentUser = auth()->user();
|
||||
$this->userRoles = $this->currentUser ? $this->currentUser->roles->pluck('id') : [];
|
||||
$this->isAdmin = $this->currentUser ? $this->currentUser->hasRole('admin') : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an entity has a restriction set upon it.
|
||||
* @param Entity $entity
|
||||
* @param $action
|
||||
* @return bool
|
||||
*/
|
||||
public function checkIfEntityRestricted(Entity $entity, $action)
|
||||
{
|
||||
if ($this->isAdmin) return true;
|
||||
$this->currentAction = $action;
|
||||
$baseQuery = $entity->where('id', '=', $entity->id);
|
||||
if ($entity->isA('page')) {
|
||||
return $this->pageRestrictionQuery($baseQuery)->count() > 0;
|
||||
} elseif ($entity->isA('chapter')) {
|
||||
return $this->chapterRestrictionQuery($baseQuery)->count() > 0;
|
||||
} elseif ($entity->isA('book')) {
|
||||
return $this->bookRestrictionQuery($baseQuery)->count() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an entity has restrictions set on itself or its
|
||||
* parent tree.
|
||||
* @param Entity $entity
|
||||
* @param $action
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function checkIfRestrictionsSet(Entity $entity, $action)
|
||||
{
|
||||
$this->currentAction = $action;
|
||||
if ($entity->isA('page')) {
|
||||
return $entity->restricted || ($entity->chapter && $entity->chapter->restricted) || $entity->book->restricted;
|
||||
} elseif ($entity->isA('chapter')) {
|
||||
return $entity->restricted || $entity->book->restricted;
|
||||
} elseif ($entity->isA('book')) {
|
||||
return $entity->restricted;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add restrictions for a page query
|
||||
* @param $query
|
||||
* @param string $action
|
||||
* @return mixed
|
||||
*/
|
||||
public function enforcePageRestrictions($query, $action = 'view')
|
||||
{
|
||||
// Prevent drafts being visible to others.
|
||||
$query = $query->where(function ($query) {
|
||||
$query->where('draft', '=', false);
|
||||
if ($this->currentUser) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ($this->isAdmin) return $query;
|
||||
$this->currentAction = $action;
|
||||
return $this->pageRestrictionQuery($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for restricting pages.
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
private function pageRestrictionQuery($query)
|
||||
{
|
||||
return $query->where(function ($parentWhereQuery) {
|
||||
|
||||
$parentWhereQuery
|
||||
// (Book & chapter & page) or (Book & page & NO CHAPTER) unrestricted
|
||||
->where(function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$query->whereExists(function ($query) {
|
||||
$query->select('*')->from('chapters')
|
||||
->whereRaw('chapters.id=pages.chapter_id')
|
||||
->where('restricted', '=', false);
|
||||
})->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=pages.book_id')
|
||||
->where('restricted', '=', false);
|
||||
})->where('restricted', '=', false);
|
||||
})->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', false)->where('chapter_id', '=', 0)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=pages.book_id')
|
||||
->where('restricted', '=', false);
|
||||
});
|
||||
});
|
||||
})
|
||||
// Page unrestricted, Has no chapter & book has accepted restrictions
|
||||
->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', false)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('chapters')
|
||||
->whereRaw('chapters.id=pages.chapter_id');
|
||||
}, 'and', true)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=pages.book_id')
|
||||
->whereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'books', 'Book');
|
||||
});
|
||||
});
|
||||
})
|
||||
// Page unrestricted, Has an unrestricted chapter & book has accepted restrictions
|
||||
->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', false)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('chapters')
|
||||
->whereRaw('chapters.id=pages.chapter_id')->where('restricted', '=', false);
|
||||
})
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=pages.book_id')
|
||||
->whereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'books', 'Book');
|
||||
});
|
||||
});
|
||||
})
|
||||
// Page unrestricted, Has a chapter with accepted permissions
|
||||
->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', false)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('chapters')
|
||||
->whereRaw('chapters.id=pages.chapter_id')
|
||||
->where('restricted', '=', true)
|
||||
->whereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
|
||||
});
|
||||
});
|
||||
})
|
||||
// Page has accepted permissions
|
||||
->orWhereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'pages', 'Page');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add on permission restrictions to a chapter query.
|
||||
* @param $query
|
||||
* @param string $action
|
||||
* @return mixed
|
||||
*/
|
||||
public function enforceChapterRestrictions($query, $action = 'view')
|
||||
{
|
||||
if ($this->isAdmin) return $query;
|
||||
$this->currentAction = $action;
|
||||
return $this->chapterRestrictionQuery($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for restricting chapters.
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
private function chapterRestrictionQuery($query)
|
||||
{
|
||||
return $query->where(function ($parentWhereQuery) {
|
||||
|
||||
$parentWhereQuery
|
||||
// Book & chapter unrestricted
|
||||
->where(function ($query) {
|
||||
$query->where('restricted', '=', false)->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=chapters.book_id')
|
||||
->where('restricted', '=', false);
|
||||
});
|
||||
})
|
||||
// Chapter unrestricted & book has accepted restrictions
|
||||
->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', false)
|
||||
->whereExists(function ($query) {
|
||||
$query->select('*')->from('books')
|
||||
->whereRaw('books.id=chapters.book_id')
|
||||
->whereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'books', 'Book');
|
||||
});
|
||||
});
|
||||
})
|
||||
// Chapter has accepted permissions
|
||||
->orWhereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'chapters', 'Chapter');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add restrictions to a book query.
|
||||
* @param $query
|
||||
* @param string $action
|
||||
* @return mixed
|
||||
*/
|
||||
public function enforceBookRestrictions($query, $action = 'view')
|
||||
{
|
||||
if ($this->isAdmin) return $query;
|
||||
$this->currentAction = $action;
|
||||
return $this->bookRestrictionQuery($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for restricting books.
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
private function bookRestrictionQuery($query)
|
||||
{
|
||||
return $query->where(function ($parentWhereQuery) {
|
||||
$parentWhereQuery
|
||||
->where('restricted', '=', false)
|
||||
->orWhere(function ($query) {
|
||||
$query->where('restricted', '=', true)->whereExists(function ($query) {
|
||||
$this->checkRestrictionsQuery($query, 'books', 'Book');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter items that have entities set a a polymorphic relation.
|
||||
* @param $query
|
||||
* @param string $tableName
|
||||
* @param string $entityIdColumn
|
||||
* @param string $entityTypeColumn
|
||||
* @return mixed
|
||||
*/
|
||||
public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn)
|
||||
{
|
||||
if ($this->isAdmin) return $query;
|
||||
$this->currentAction = 'view';
|
||||
$tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
|
||||
return $query->where(function ($query) use ($tableDetails) {
|
||||
$query->where(function ($query) use (&$tableDetails) {
|
||||
$query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Page')
|
||||
->whereExists(function ($query) use (&$tableDetails) {
|
||||
$query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
|
||||
->where(function ($query) {
|
||||
$this->pageRestrictionQuery($query);
|
||||
});
|
||||
});
|
||||
})->orWhere(function ($query) use (&$tableDetails) {
|
||||
$query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Book')->whereExists(function ($query) use (&$tableDetails) {
|
||||
$query->select('*')->from('books')->whereRaw('books.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
|
||||
->where(function ($query) {
|
||||
$this->bookRestrictionQuery($query);
|
||||
});
|
||||
});
|
||||
})->orWhere(function ($query) use (&$tableDetails) {
|
||||
$query->where($tableDetails['entityTypeColumn'], '=', 'BookStack\Chapter')->whereExists(function ($query) use (&$tableDetails) {
|
||||
$query->select('*')->from('chapters')->whereRaw('chapters.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
|
||||
->where(function ($query) {
|
||||
$this->chapterRestrictionQuery($query);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters pages that are a direct relation to another item.
|
||||
* @param $query
|
||||
* @param $tableName
|
||||
* @param $entityIdColumn
|
||||
* @return mixed
|
||||
*/
|
||||
public function filterRelatedPages($query, $tableName, $entityIdColumn)
|
||||
{
|
||||
if ($this->isAdmin) return $query;
|
||||
$this->currentAction = 'view';
|
||||
$tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
|
||||
return $query->where(function ($query) use (&$tableDetails) {
|
||||
$query->where(function ($query) use (&$tableDetails) {
|
||||
$query->whereExists(function ($query) use (&$tableDetails) {
|
||||
$query->select('*')->from('pages')->whereRaw('pages.id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
|
||||
->where(function ($query) {
|
||||
$this->pageRestrictionQuery($query);
|
||||
});
|
||||
})->orWhere($tableDetails['entityIdColumn'], '=', 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The query to check the restrictions on an entity.
|
||||
* @param $query
|
||||
* @param $tableName
|
||||
* @param $modelName
|
||||
*/
|
||||
private function checkRestrictionsQuery($query, $tableName, $modelName)
|
||||
{
|
||||
$query->select('*')->from('restrictions')
|
||||
->whereRaw('restrictions.restrictable_id=' . $tableName . '.id')
|
||||
->where('restrictions.restrictable_type', '=', 'BookStack\\' . $modelName)
|
||||
->where('restrictions.action', '=', $this->currentAction)
|
||||
->whereIn('restrictions.role_id', $this->userRoles);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class SocialAuthService
|
||||
|
||||
// Otherwise let the user know this social account is not used by anyone.
|
||||
$message = 'This ' . $socialDriver . ' account is not linked to any users. Please attach it in your profile settings';
|
||||
if (\Setting::get('registration-enabled')) {
|
||||
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');
|
||||
|
||||
@@ -9,15 +9,18 @@ class ViewService
|
||||
|
||||
protected $view;
|
||||
protected $user;
|
||||
protected $restrictionService;
|
||||
|
||||
/**
|
||||
* ViewService constructor.
|
||||
* @param $view
|
||||
* @param View $view
|
||||
* @param RestrictionService $restrictionService
|
||||
*/
|
||||
public function __construct(View $view)
|
||||
public function __construct(View $view, RestrictionService $restrictionService)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->user = auth()->user();
|
||||
$this->restrictionService = $restrictionService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +30,7 @@ class ViewService
|
||||
*/
|
||||
public function add(Entity $entity)
|
||||
{
|
||||
if($this->user === null) return 0;
|
||||
if ($this->user === null) return 0;
|
||||
$view = $entity->views()->where('user_id', '=', $this->user->id)->first();
|
||||
// Add view if model exists
|
||||
if ($view) {
|
||||
@@ -47,18 +50,19 @@ class ViewService
|
||||
|
||||
/**
|
||||
* Get the entities with the most views.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param bool|false $filterModel
|
||||
*/
|
||||
public function getPopular($count = 10, $page = 0, $filterModel = false)
|
||||
{
|
||||
$skipCount = $count * $page;
|
||||
$query = $this->view->select('id', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
|
||||
$query = $this->restrictionService->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type')
|
||||
->select('id', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
|
||||
->groupBy('viewable_id', 'viewable_type')
|
||||
->orderBy('view_count', 'desc');
|
||||
|
||||
if($filterModel) $query->where('viewable_type', '=', get_class($filterModel));
|
||||
if ($filterModel) $query->where('viewable_type', '=', get_class($filterModel));
|
||||
|
||||
$views = $query->with('viewable')->skip($skipCount)->take($count)->get();
|
||||
$viewedEntities = $views->map(function ($item) {
|
||||
@@ -69,22 +73,24 @@ class ViewService
|
||||
|
||||
/**
|
||||
* Get all recently viewed entities for the current user.
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param int $count
|
||||
* @param int $page
|
||||
* @param Entity|bool $filterModel
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserRecentlyViewed($count = 10, $page = 0, $filterModel = false)
|
||||
{
|
||||
if($this->user === null) return collect();
|
||||
if ($this->user === null) return collect();
|
||||
$skipCount = $count * $page;
|
||||
$query = $this->view->where('user_id', '=', auth()->user()->id);
|
||||
$query = $this->restrictionService
|
||||
->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type');
|
||||
|
||||
if ($filterModel) $query->where('viewable_type', '=', get_class($filterModel));
|
||||
if ($filterModel) $query = $query->where('viewable_type', '=', get_class($filterModel));
|
||||
$query = $query->where('user_id', '=', auth()->user()->id);
|
||||
|
||||
$views = $query->with('viewable')->orderBy('updated_at', 'desc')->skip($skipCount)->take($count)->get();
|
||||
$viewedEntities = $views->map(function ($item) {
|
||||
return $item->viewable()->getResults();
|
||||
return $item->viewable;
|
||||
});
|
||||
return $viewedEntities;
|
||||
}
|
||||
|
||||
61
app/User.php
61
app/User.php
@@ -14,21 +14,18 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* The database table used by the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name', 'email', 'image_id'];
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'remember_token'];
|
||||
@@ -50,10 +47,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions and roles
|
||||
*/
|
||||
|
||||
/**
|
||||
* The roles that belong to the user.
|
||||
*/
|
||||
@@ -62,21 +55,30 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
return $this->belongsToMany('BookStack\Role');
|
||||
}
|
||||
|
||||
public function getRoleAttribute()
|
||||
/**
|
||||
* Check if the user has a role.
|
||||
* @param $role
|
||||
* @return mixed
|
||||
*/
|
||||
public function hasRole($role)
|
||||
{
|
||||
return $this->roles()->with('permissions')->first();
|
||||
return $this->roles->pluck('name')->contains($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the user's permissions from their role.
|
||||
* Get all permissions belonging to a the current user.
|
||||
* @param bool $cache
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
|
||||
*/
|
||||
private function loadPermissions()
|
||||
public function permissions($cache = true)
|
||||
{
|
||||
if (isset($this->permissions)) return;
|
||||
if(isset($this->permissions) && $cache) return $this->permissions;
|
||||
$this->load('roles.permissions');
|
||||
$permissions = $this->roles[0]->permissions;
|
||||
$permissionsArray = $permissions->pluck('name')->all();
|
||||
$this->permissions = $permissionsArray;
|
||||
$permissions = $this->roles->map(function($role) {
|
||||
return $role->permissions;
|
||||
})->flatten()->unique();
|
||||
$this->permissions = $permissions;
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,11 +88,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function can($permissionName)
|
||||
{
|
||||
if ($this->email == 'guest') {
|
||||
return false;
|
||||
}
|
||||
$this->loadPermissions();
|
||||
return array_search($permissionName, $this->permissions) !== false;
|
||||
if ($this->email === 'guest') return false;
|
||||
return $this->permissions()->pluck('name')->contains($permissionName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,12 +107,11 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function attachRoleId($id)
|
||||
{
|
||||
$this->roles()->sync([$id]);
|
||||
$this->roles()->attach($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the social account associated with this user.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function socialAccounts()
|
||||
@@ -138,8 +136,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Returns the user's avatar,
|
||||
* Uses Gravatar as the avatar service.
|
||||
*
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
@@ -164,6 +160,21 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function getEditUrl()
|
||||
{
|
||||
return '/users/' . $this->id;
|
||||
return '/settings/users/' . $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shortened version of the user's name.
|
||||
* @param int $chars
|
||||
* @return string
|
||||
*/
|
||||
public function getShortName($chars = 8)
|
||||
{
|
||||
if (strlen($this->name) <= $chars) return $this->name;
|
||||
|
||||
$splitName = explode(' ', $this->name);
|
||||
if (strlen($splitName[0]) <= $chars) return $splitName[0];
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
if (! function_exists('versioned_asset')) {
|
||||
if (!function_exists('versioned_asset')) {
|
||||
/**
|
||||
* Get the path to a versioned file.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $file
|
||||
* @return string
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
@@ -27,4 +27,48 @@ if (! function_exists('versioned_asset')) {
|
||||
|
||||
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user has a permission.
|
||||
* If an ownable element is passed in the permissions are checked against
|
||||
* that particular item.
|
||||
* @param $permission
|
||||
* @param \BookStack\Ownable $ownable
|
||||
* @return mixed
|
||||
*/
|
||||
function userCan($permission, \BookStack\Ownable $ownable = null)
|
||||
{
|
||||
if (!auth()->check()) return false;
|
||||
if ($ownable === null) {
|
||||
return auth()->user() && auth()->user()->can($permission);
|
||||
}
|
||||
|
||||
// Check permission on ownable item
|
||||
$permissionBaseName = strtolower($permission) . '-';
|
||||
$hasPermission = false;
|
||||
if (auth()->user()->can($permissionBaseName . 'all')) $hasPermission = true;
|
||||
if (auth()->user()->can($permissionBaseName . 'own') && $ownable->createdBy && $ownable->createdBy->id === auth()->user()->id) $hasPermission = true;
|
||||
|
||||
if (!$ownable instanceof \BookStack\Entity) return $hasPermission;
|
||||
|
||||
// Check restrictions on the entity
|
||||
$restrictionService = app('BookStack\Services\RestrictionService');
|
||||
$explodedPermission = explode('-', $permission);
|
||||
$action = end($explodedPermission);
|
||||
$hasAccess = $restrictionService->checkIfEntityRestricted($ownable, $action);
|
||||
$restrictionsSet = $restrictionService->checkIfRestrictionsSet($ownable, $action);
|
||||
return ($hasAccess && $restrictionsSet) || (!$restrictionsSet && $hasPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to access system settings.
|
||||
* @param $key
|
||||
* @param bool $default
|
||||
* @return mixed
|
||||
*/
|
||||
function setting($key, $default = false)
|
||||
{
|
||||
$settingService = app('BookStack\Services\SettingService');
|
||||
return $settingService->get($key, $default);
|
||||
}
|
||||
|
||||
189
composer.lock
generated
189
composer.lock
generated
@@ -9,16 +9,16 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.14.2",
|
||||
"version": "3.15.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2"
|
||||
"reference": "5e6078913293576de969703481994b77c380ca30"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2",
|
||||
"reference": "2970cb63e7b7b37dd8c07a4fa4e4e18a110ed4e2",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5e6078913293576de969703481994b77c380ca30",
|
||||
"reference": "5e6078913293576de969703481994b77c380ca30",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -40,7 +40,8 @@
|
||||
"ext-simplexml": "*",
|
||||
"ext-spl": "*",
|
||||
"nette/neon": "^2.3",
|
||||
"phpunit/phpunit": "~4.0|~5.0"
|
||||
"phpunit/phpunit": "~4.0|~5.0",
|
||||
"psr/cache": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
|
||||
@@ -84,7 +85,7 @@
|
||||
"s3",
|
||||
"sdk"
|
||||
],
|
||||
"time": "2016-01-28 21:33:18"
|
||||
"time": "2016-02-11 23:23:31"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
@@ -918,16 +919,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v5.2.12",
|
||||
"version": "v5.2.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7"
|
||||
"reference": "39e89553c124dce266da03ee3c0260bdd62f1848"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/6b6255ad7bfbdb721b8d00b09d52b146c5d363d7",
|
||||
"reference": "6b6255ad7bfbdb721b8d00b09d52b146c5d363d7",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/39e89553c124dce266da03ee3c0260bdd62f1848",
|
||||
"reference": "39e89553c124dce266da03ee3c0260bdd62f1848",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1042,7 +1043,7 @@
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2016-01-26 04:15:37"
|
||||
"time": "2016-02-15 17:46:58"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
@@ -1629,16 +1630,16 @@
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "1.1.6",
|
||||
"version": "v1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "e6f80ab77885151908d0ec743689ca700886e8b0"
|
||||
"reference": "b0e69d10852716b2ccbdff69c75c477637220790"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/e6f80ab77885151908d0ec743689ca700886e8b0",
|
||||
"reference": "e6f80ab77885151908d0ec743689ca700886e8b0",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/b0e69d10852716b2ccbdff69c75c477637220790",
|
||||
"reference": "b0e69d10852716b2ccbdff69c75c477637220790",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1673,7 +1674,7 @@
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2016-01-29 16:19:52"
|
||||
"time": "2016-02-06 03:52:05"
|
||||
},
|
||||
{
|
||||
"name": "phenx/php-font-lib",
|
||||
@@ -2024,16 +2025,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3"
|
||||
"reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
|
||||
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0",
|
||||
"reference": "5a02eaadaa285e2bb727eb6bbdfb8201fcd971b0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2080,20 +2081,20 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-22 10:39:06"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a"
|
||||
"reference": "29606049ced1ec715475f88d1bbe587252a3476e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/73612266ac709769effdbfc0762e5b07cfd2ac2a",
|
||||
"reference": "73612266ac709769effdbfc0762e5b07cfd2ac2a",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/29606049ced1ec715475f88d1bbe587252a3476e",
|
||||
"reference": "29606049ced1ec715475f88d1bbe587252a3476e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2137,20 +2138,20 @@
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:39:53"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf"
|
||||
"reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d36355e026905fa5229e1ed7b4e9eda2e67adfcf",
|
||||
"reference": "d36355e026905fa5229e1ed7b4e9eda2e67adfcf",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa",
|
||||
"reference": "4dd5df31a28c0f82b41cb1e1599b74b5dcdbdafa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2197,20 +2198,20 @@
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-10-30 23:35:59"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "8617895eb798b6bdb338321ce19453dc113e5675"
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/8617895eb798b6bdb338321ce19453dc113e5675",
|
||||
"reference": "8617895eb798b6bdb338321ce19453dc113e5675",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2246,20 +2247,20 @@
|
||||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 11:13:14"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5"
|
||||
"reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5",
|
||||
"reference": "939c8c28a5b1e4ab7317bc30c1f9aa881c4b06b5",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/9344a87ceedfc50354a39653e54257ee9aa6a77d",
|
||||
"reference": "9344a87ceedfc50354a39653e54257ee9aa6a77d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2298,20 +2299,20 @@
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-18 15:43:53"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db"
|
||||
"reference": "cec02604450481ac26710ca4249cc61b57b23942"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f7933e9f19e26e7baba7ec04735b466fedd3a6db",
|
||||
"reference": "f7933e9f19e26e7baba7ec04735b466fedd3a6db",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/cec02604450481ac26710ca4249cc61b57b23942",
|
||||
"reference": "cec02604450481ac26710ca4249cc61b57b23942",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2380,7 +2381,7 @@
|
||||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 16:46:13"
|
||||
"time": "2016-02-03 12:38:44"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
@@ -2551,16 +2552,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3"
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
|
||||
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/dfecef47506179db2501430e732adbf3793099c8",
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2596,20 +2597,20 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-23 11:04:02"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59"
|
||||
"reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59",
|
||||
"reference": "3b1bac52f42cb0f54df1a2dbabd55a1d214e2a59",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/4686baa55a835e1c1ede9b86ba02415c8c8d6166",
|
||||
"reference": "4686baa55a835e1c1ede9b86ba02415c8c8d6166",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2670,20 +2671,20 @@
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2015-12-23 08:00:11"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "dff0867826a7068d673801b7522f8e2634016ef9"
|
||||
"reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/dff0867826a7068d673801b7522f8e2634016ef9",
|
||||
"reference": "dff0867826a7068d673801b7522f8e2634016ef9",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91",
|
||||
"reference": "2de0b6f7ebe43cffd8a06996ebec6aab79ea9e91",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2734,20 +2735,20 @@
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 17:45:07"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "87db8700deb12ba2b65e858f656a1f885530bcb0"
|
||||
"reference": "24bb94807eff00db49374c37ebf56a0304e8aef3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/87db8700deb12ba2b65e858f656a1f885530bcb0",
|
||||
"reference": "87db8700deb12ba2b65e858f656a1f885530bcb0",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/24bb94807eff00db49374c37ebf56a0304e8aef3",
|
||||
"reference": "24bb94807eff00db49374c37ebf56a0304e8aef3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2797,7 +2798,7 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2015-12-05 11:13:14"
|
||||
"time": "2016-01-07 13:38:51"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
@@ -3182,22 +3183,24 @@
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.5.0",
|
||||
"version": "v1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
|
||||
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "~2.0",
|
||||
"sebastian/comparator": "~1.1"
|
||||
"sebastian/comparator": "~1.1",
|
||||
"sebastian/recursion-context": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "~2.0"
|
||||
@@ -3205,7 +3208,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4.x-dev"
|
||||
"dev-master": "1.5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -3238,7 +3241,7 @@
|
||||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2015-08-13 10:07:40"
|
||||
"time": "2016-02-15 07:46:21"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
@@ -3482,16 +3485,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "4.8.21",
|
||||
"version": "4.8.23",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c"
|
||||
"reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c",
|
||||
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e351261f9cd33daf205a131a1ba61c6d33bd483",
|
||||
"reference": "6e351261f9cd33daf205a131a1ba61c6d33bd483",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3550,7 +3553,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2015-12-12 07:45:58"
|
||||
"time": "2016-02-11 14:56:33"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
@@ -3981,16 +3984,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3"
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3",
|
||||
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4030,20 +4033,20 @@
|
||||
],
|
||||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-05 17:45:07"
|
||||
"time": "2016-01-27 05:14:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d"
|
||||
"reference": "b693a9650aa004576b593ff2e91ae749dc90123d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
|
||||
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b693a9650aa004576b593ff2e91ae749dc90123d",
|
||||
"reference": "b693a9650aa004576b593ff2e91ae749dc90123d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4086,20 +4089,20 @@
|
||||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:42:31"
|
||||
"time": "2016-01-25 09:56:57"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691"
|
||||
"reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691",
|
||||
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3cf0709d7fe936e97bee9e954382e449003f1d9a",
|
||||
"reference": "3cf0709d7fe936e97bee9e954382e449003f1d9a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4135,7 +4138,7 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-12-26 13:39:53"
|
||||
"time": "2016-02-02 13:44:19"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
<?php
|
||||
|
||||
// MEMCACHED - Split out configuration into an array
|
||||
if (env('CACHE_DRIVER') === 'memcached') {
|
||||
$memcachedServerKeys = ['host', 'port', 'weight'];
|
||||
$memcachedServers = explode(',', trim(env('MEMCACHED_SERVERS', '127.0.0.1:11211:100'), ','));
|
||||
foreach ($memcachedServers as $index => $memcachedServer) {
|
||||
$memcachedServerDetails = explode(':', $memcachedServer);
|
||||
$components = count($memcachedServerDetails);
|
||||
if ($components < 2) $memcachedServerDetails[] = '11211';
|
||||
if ($components < 3) $memcachedServerDetails[] = '100';
|
||||
$memcachedServers[$index] = array_combine($memcachedServerKeys, $memcachedServerDetails);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@@ -49,11 +62,7 @@ return [
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'servers' => [
|
||||
[
|
||||
'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100,
|
||||
],
|
||||
],
|
||||
'servers' => env('CACHE_DRIVER') === 'memcached' ? $memcachedServers : [],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
|
||||
@@ -17,6 +17,7 @@ $factory->define(BookStack\User::class, function ($faker) {
|
||||
'email' => $faker->email,
|
||||
'password' => str_random(10),
|
||||
'remember_token' => str_random(10),
|
||||
'email_confirmed' => 1
|
||||
];
|
||||
});
|
||||
|
||||
@@ -45,3 +46,10 @@ $factory->define(BookStack\Page::class, function ($faker) {
|
||||
'text' => strip_tags($html)
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(BookStack\Role::class, function ($faker) {
|
||||
return [
|
||||
'display_name' => $faker->sentence(3),
|
||||
'description' => $faker->sentence(10)
|
||||
];
|
||||
});
|
||||
@@ -18,7 +18,7 @@ class CreateUsersTable extends Migration
|
||||
$table->string('email')->unique();
|
||||
$table->string('password', 60);
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
\BookStack\User::forceCreate([
|
||||
|
||||
@@ -17,7 +17,7 @@ class CreateBooksTable extends Migration
|
||||
$table->string('name');
|
||||
$table->string('slug')->indexed();
|
||||
$table->text('description');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class CreatePagesTable extends Migration
|
||||
$table->longText('html');
|
||||
$table->longText('text');
|
||||
$table->integer('priority');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class CreateImagesTable extends Migration
|
||||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->string('url');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class CreateChaptersTable extends Migration
|
||||
$table->text('name');
|
||||
$table->text('description');
|
||||
$table->integer('priority');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class CreatePageRevisionsTable extends Migration
|
||||
$table->longText('html');
|
||||
$table->longText('text');
|
||||
$table->integer('created_by');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class CreateActivitiesTable extends Migration
|
||||
$table->integer('user_id');
|
||||
$table->integer('entity_id');
|
||||
$table->string('entity_type');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class AddRolesAndPermissions extends Migration
|
||||
$table->string('name')->unique();
|
||||
$table->string('display_name')->nullable();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
// Create table for associating roles to users (Many-to-Many)
|
||||
@@ -50,7 +50,7 @@ class AddRolesAndPermissions extends Migration
|
||||
$table->string('name')->unique();
|
||||
$table->string('display_name')->nullable();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
|
||||
// Create table for associating permissions to roles (Many-to-Many)
|
||||
|
||||
@@ -15,7 +15,7 @@ class CreateSettingsTable extends Migration
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->string('setting_key')->primary()->indexed();
|
||||
$table->text('value');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class CreateSocialAccountsTable extends Migration
|
||||
$table->string('driver')->index();
|
||||
$table->string('driver_id');
|
||||
$table->string('avatar');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class AddEmailConfirmationTable extends Migration
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('token')->index();
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class CreateViewsTable extends Migration
|
||||
$table->integer('viewable_id');
|
||||
$table->string('viewable_type');
|
||||
$table->integer('views');
|
||||
$table->timestamps();
|
||||
$table->nullableTimestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSlugToRevisions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->string('slug');
|
||||
$table->index('slug');
|
||||
$table->string('book_slug');
|
||||
$table->index('book_slug');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
$table->dropColumn('book_slug');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdatePermissionsAndRoles extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// Get roles with permissions we need to change
|
||||
$adminRole = \BookStack\Role::getRole('admin');
|
||||
$editorRole = \BookStack\Role::getRole('editor');
|
||||
|
||||
// Delete old permissions
|
||||
$permissions = \BookStack\Permission::all();
|
||||
$permissions->each(function ($permission) {
|
||||
$permission->delete();
|
||||
});
|
||||
|
||||
// Create & attach new admin permissions
|
||||
$permissionsToCreate = [
|
||||
'settings-manage' => 'Manage Settings',
|
||||
'users-manage' => 'Manage Users',
|
||||
'user-roles-manage' => 'Manage Roles & Permissions',
|
||||
'restrictions-manage-all' => 'Manage All Entity Restrictions',
|
||||
'restrictions-manage-own' => 'Manage Entity Restrictions On Own Content'
|
||||
];
|
||||
foreach ($permissionsToCreate as $name => $displayName) {
|
||||
$newPermission = new \BookStack\Permission();
|
||||
$newPermission->name = $name;
|
||||
$newPermission->display_name = $displayName;
|
||||
$newPermission->save();
|
||||
$adminRole->attachPermission($newPermission);
|
||||
}
|
||||
|
||||
// Create & attach new entity permissions
|
||||
$entities = ['Book', 'Page', 'Chapter', 'Image'];
|
||||
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
|
||||
foreach ($entities as $entity) {
|
||||
foreach ($ops as $op) {
|
||||
$newPermission = new \BookStack\Permission();
|
||||
$newPermission->name = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
|
||||
$newPermission->display_name = $op . ' ' . $entity . 's';
|
||||
$newPermission->save();
|
||||
$adminRole->attachPermission($newPermission);
|
||||
if ($editorRole !== null) $editorRole->attachPermission($newPermission);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// Get roles with permissions we need to change
|
||||
$adminRole = \BookStack\Role::getRole('admin');
|
||||
|
||||
// Delete old permissions
|
||||
$permissions = \BookStack\Permission::all();
|
||||
$permissions->each(function ($permission) {
|
||||
$permission->delete();
|
||||
});
|
||||
|
||||
// Create default CRUD permissions and allocate to admins and editors
|
||||
$entities = ['Book', 'Page', 'Chapter', 'Image'];
|
||||
$ops = ['Create', 'Update', 'Delete'];
|
||||
foreach ($entities as $entity) {
|
||||
foreach ($ops as $op) {
|
||||
$newPermission = new \BookStack\Permission();
|
||||
$newPermission->name = strtolower($entity) . '-' . strtolower($op);
|
||||
$newPermission->display_name = $op . ' ' . $entity . 's';
|
||||
$newPermission->save();
|
||||
$adminRole->attachPermission($newPermission);
|
||||
}
|
||||
}
|
||||
|
||||
// Create admin permissions
|
||||
$entities = ['Settings', 'User'];
|
||||
$ops = ['Create', 'Update', 'Delete'];
|
||||
foreach ($entities as $entity) {
|
||||
foreach ($ops as $op) {
|
||||
$newPermission = new \BookStack\Permission();
|
||||
$newPermission->name = strtolower($entity) . '-' . strtolower($op);
|
||||
$newPermission->display_name = $op . ' ' . $entity;
|
||||
$newPermission->save();
|
||||
$adminRole->attachPermission($newPermission);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddEntityAccessControls extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('images', function (Blueprint $table) {
|
||||
$table->integer('uploaded_to')->default(0);
|
||||
$table->index('uploaded_to');
|
||||
});
|
||||
|
||||
Schema::table('books', function (Blueprint $table) {
|
||||
$table->boolean('restricted')->default(false);
|
||||
$table->index('restricted');
|
||||
});
|
||||
|
||||
Schema::table('chapters', function (Blueprint $table) {
|
||||
$table->boolean('restricted')->default(false);
|
||||
$table->index('restricted');
|
||||
});
|
||||
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->boolean('restricted')->default(false);
|
||||
$table->index('restricted');
|
||||
});
|
||||
|
||||
Schema::create('restrictions', function(Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('restrictable_id');
|
||||
$table->string('restrictable_type');
|
||||
$table->integer('role_id');
|
||||
$table->string('action');
|
||||
$table->index('role_id');
|
||||
$table->index('action');
|
||||
$table->index(['restrictable_id', 'restrictable_type']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('images', function (Blueprint $table) {
|
||||
$table->dropColumn('uploaded_to');
|
||||
});
|
||||
|
||||
Schema::table('books', function (Blueprint $table) {
|
||||
$table->dropColumn('restricted');
|
||||
});
|
||||
|
||||
Schema::table('chapters', function (Blueprint $table) {
|
||||
$table->dropColumn('restricted');
|
||||
});
|
||||
|
||||
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->dropColumn('restricted');
|
||||
});
|
||||
|
||||
Schema::drop('restrictions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPageRevisionTypes extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->string('type')->default('version');
|
||||
$table->index('type');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('page_revisions', function (Blueprint $table) {
|
||||
$table->dropColumn('type');
|
||||
});
|
||||
}
|
||||
}
|
||||
32
database/migrations/2016_03_13_082138_add_page_drafts.php
Normal file
32
database/migrations/2016_03_13_082138_add_page_drafts.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPageDrafts extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('pages', function(Blueprint $table) {
|
||||
$table->boolean('draft')->default(false);
|
||||
$table->index('draft');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->dropColumn('draft');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class DummyContentSeeder extends Seeder
|
||||
public function run()
|
||||
{
|
||||
$user = factory(BookStack\User::class, 1)->create();
|
||||
$role = \BookStack\Role::getDefault();
|
||||
$role = \BookStack\Role::getRole('editor');
|
||||
$user->attachRole($role);
|
||||
|
||||
|
||||
|
||||
@@ -21,12 +21,18 @@
|
||||
</filter>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_DEBUG" value="false"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
<env name="DB_CONNECTION" value="mysql_testing"/>
|
||||
<env name="MAIL_PRETEND" value="true"/>
|
||||
<env name="MAIL_DRIVER" value="log"/>
|
||||
<env name="AUTH_METHOD" value="standard"/>
|
||||
<env name="DISABLE_EXTERNAL_SERVICES" value="false"/>
|
||||
<env name="LDAP_VERSION" value="3"/>
|
||||
<env name="GITHUB_APP_ID" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GITHUB_APP_SECRET" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GOOGLE_APP_ID" value="aaaaaaaaaaaaaa"/>
|
||||
<env name="GOOGLE_APP_SECRET" value="aaaaaaaaaaaaaa"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"css/styles.css": "css/styles.css?version=2748d88",
|
||||
"css/print-styles.css": "css/print-styles.css?version=2748d88",
|
||||
"js/common.js": "js/common.js?version=2748d88"
|
||||
"css/styles.css": "css/styles.css?version=93b1377",
|
||||
"css/print-styles.css": "css/print-styles.css?version=93b1377",
|
||||
"js/common.js": "js/common.js?version=93b1377"
|
||||
}
|
||||
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/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
4
public/libs/jq-color-picker/tiny-color-picker.min.js
vendored
Normal file
4
public/libs/jq-color-picker/tiny-color-picker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("advlist",function(e){function t(e,t){var n=[];return tinymce.each(t.split(/[ ,]/),function(e){n.push({text:e.replace(/\-/g," ").replace(/\b\w/g,function(e){return e.toUpperCase()}),data:"default"==e?"":e})}),n}function n(t,n){e.undoManager.transact(function(){var r,i=e.dom,o=e.selection;r=i.getParent(o.getNode(),"ol,ul"),r&&r.nodeName==t&&n!==!1||e.execCommand("UL"==t?"InsertUnorderedList":"InsertOrderedList"),n=n===!1?a[t]:n,a[t]=n,r=i.getParent(o.getNode(),"ol,ul"),r&&(i.setStyle(r,"listStyleType",n?n:null),r.removeAttribute("data-mce-style")),e.focus()})}function r(t){var n=e.dom.getStyle(e.dom.getParent(e.selection.getNode(),"ol,ul"),"listStyleType")||"";t.control.items().each(function(e){e.active(e.settings.data===n)})}var i,o,a={};i=t("OL",e.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),o=t("UL",e.getParam("advlist_bullet_styles","default,circle,disc,square")),e.addButton("numlist",{type:"splitbutton",tooltip:"Numbered list",menu:i,onshow:r,onselect:function(e){n("OL",e.control.settings.data)},onclick:function(){n("OL",!1)}}),e.addButton("bullist",{type:"splitbutton",tooltip:"Bullet list",menu:o,onshow:r,onselect:function(e){n("UL",e.control.settings.data)},onclick:function(){n("UL",!1)}})});
|
||||
tinymce.PluginManager.add("advlist",function(a){function b(a,b){var c=[];return tinymce.each(b.split(/[ ,]/),function(a){c.push({text:a.replace(/\-/g," ").replace(/\b\w/g,function(a){return a.toUpperCase()}),data:"default"==a?"":a})}),c}function c(b,c){a.undoManager.transact(function(){var d,e=a.dom,f=a.selection;d=e.getParent(f.getNode(),"ol,ul"),d&&d.nodeName==b&&c!==!1||a.execCommand("UL"==b?"InsertUnorderedList":"InsertOrderedList"),c=c===!1?g[b]:c,g[b]=c,d=e.getParent(f.getNode(),"ol,ul"),d&&(e.setStyle(d,"listStyleType",c?c:null),d.removeAttribute("data-mce-style")),a.focus()})}function d(b){var c=a.dom.getStyle(a.dom.getParent(a.selection.getNode(),"ol,ul"),"listStyleType")||"";b.control.items().each(function(a){a.active(a.settings.data===c)})}var e,f,g={};e=b("OL",a.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),f=b("UL",a.getParam("advlist_bullet_styles","default,circle,disc,square")),a.addButton("numlist",{type:"splitbutton",tooltip:"Numbered list",menu:e,onshow:d,onselect:function(a){c("OL",a.control.settings.data)},onclick:function(){c("OL",!1)}}),a.addButton("bullist",{type:"splitbutton",tooltip:"Bullet list",menu:f,onshow:d,onselect:function(a){c("UL",a.control.settings.data)},onclick:function(){c("UL",!1)}})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("anchor",function(e){function t(){var t=e.selection.getNode(),n="",r="A"==t.tagName&&""===e.dom.getAttrib(t,"href");r&&(n=t.name||t.id||""),e.windowManager.open({title:"Anchor",body:{type:"textbox",name:"name",size:40,label:"Name",value:n},onsubmit:function(n){var i=n.data.name;r?t.id=i:(e.selection.collapse(!0),e.execCommand("mceInsertContent",!1,e.dom.createHTML("a",{id:i})))}})}e.addCommand("mceAnchor",t),e.addButton("anchor",{icon:"anchor",tooltip:"Anchor",onclick:t,stateSelector:"a:not([href])"}),e.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",onclick:t})});
|
||||
tinymce.PluginManager.add("anchor",function(a){function b(){var b=a.selection.getNode(),c="",d="A"==b.tagName&&""===a.dom.getAttrib(b,"href");d&&(c=b.name||b.id||""),a.windowManager.open({title:"Anchor",body:{type:"textbox",name:"name",size:40,label:"Name",value:c},onsubmit:function(c){var e=c.data.name;d?b.id=e:(a.selection.collapse(!0),a.execCommand("mceInsertContent",!1,a.dom.createHTML("a",{id:e})))}})}a.addCommand("mceAnchor",b),a.addButton("anchor",{icon:"anchor",tooltip:"Anchor",onclick:b,stateSelector:"a:not([href])"}),a.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",onclick:b})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("autolink",function(e){function t(e){i(e,-1,"(",!0)}function n(e){i(e,0,"",!0)}function r(e){i(e,-1,"",!1)}function i(e,t,n){function r(e,t){if(0>t&&(t=0),3==e.nodeType){var n=e.data.length;t>n&&(t=n)}return t}function i(e,t){1!=e.nodeType||e.hasChildNodes()?a.setStart(e,r(e,t)):a.setStartBefore(e)}function o(e,t){1!=e.nodeType||e.hasChildNodes()?a.setEnd(e,r(e,t)):a.setEndAfter(e)}var a,s,l,c,u,d,f,p,m,h;if("A"!=e.selection.getNode().tagName){if(a=e.selection.getRng(!0).cloneRange(),a.startOffset<5){if(p=a.endContainer.previousSibling,!p){if(!a.endContainer.firstChild||!a.endContainer.firstChild.nextSibling)return;p=a.endContainer.firstChild.nextSibling}if(m=p.length,i(p,m),o(p,m),a.endOffset<5)return;s=a.endOffset,c=p}else{if(c=a.endContainer,3!=c.nodeType&&c.firstChild){for(;3!=c.nodeType&&c.firstChild;)c=c.firstChild;3==c.nodeType&&(i(c,0),o(c,c.nodeValue.length))}s=1==a.endOffset?2:a.endOffset-1-t}l=s;do i(c,s>=2?s-2:0),o(c,s>=1?s-1:0),s-=1,h=a.toString();while(" "!=h&&""!==h&&160!=h.charCodeAt(0)&&s-2>=0&&h!=n);a.toString()==n||160==a.toString().charCodeAt(0)?(i(c,s),o(c,l),s+=1):0===a.startOffset?(i(c,0),o(c,l)):(i(c,s),o(c,l)),d=a.toString(),"."==d.charAt(d.length-1)&&o(c,l-1),d=a.toString(),f=d.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i),f&&("www."==f[1]?f[1]="http://www.":/@$/.test(f[1])&&!/^mailto:/.test(f[1])&&(f[1]="mailto:"+f[1]),u=e.selection.getBookmark(),e.selection.setRng(a),e.execCommand("createlink",!1,f[1]+f[2]),e.selection.moveToBookmark(u),e.nodeChanged())}}var o;return e.on("keydown",function(t){return 13==t.keyCode?r(e):void 0}),tinymce.Env.ie?void e.on("focus",function(){if(!o){o=!0;try{e.execCommand("AutoUrlDetect",!1,!0)}catch(t){}}}):(e.on("keypress",function(n){return 41==n.keyCode?t(e):void 0}),void e.on("keyup",function(t){return 32==t.keyCode?n(e):void 0}))});
|
||||
tinymce.PluginManager.add("autolink",function(a){function b(a){e(a,-1,"(",!0)}function c(a){e(a,0,"",!0)}function d(a){e(a,-1,"",!1)}function e(a,b,c){function d(a,b){if(0>b&&(b=0),3==a.nodeType){var c=a.data.length;b>c&&(b=c)}return b}function e(a,b){1!=a.nodeType||a.hasChildNodes()?h.setStart(a,d(a,b)):h.setStartBefore(a)}function f(a,b){1!=a.nodeType||a.hasChildNodes()?h.setEnd(a,d(a,b)):h.setEndAfter(a)}var h,i,j,k,l,m,n,o,p,q;if("A"!=a.selection.getNode().tagName){if(h=a.selection.getRng(!0).cloneRange(),h.startOffset<5){if(o=h.endContainer.previousSibling,!o){if(!h.endContainer.firstChild||!h.endContainer.firstChild.nextSibling)return;o=h.endContainer.firstChild.nextSibling}if(p=o.length,e(o,p),f(o,p),h.endOffset<5)return;i=h.endOffset,k=o}else{if(k=h.endContainer,3!=k.nodeType&&k.firstChild){for(;3!=k.nodeType&&k.firstChild;)k=k.firstChild;3==k.nodeType&&(e(k,0),f(k,k.nodeValue.length))}i=1==h.endOffset?2:h.endOffset-1-b}j=i;do e(k,i>=2?i-2:0),f(k,i>=1?i-1:0),i-=1,q=h.toString();while(" "!=q&&""!==q&&160!=q.charCodeAt(0)&&i-2>=0&&q!=c);h.toString()==c||160==h.toString().charCodeAt(0)?(e(k,i),f(k,j),i+=1):0===h.startOffset?(e(k,0),f(k,j)):(e(k,i),f(k,j)),m=h.toString(),"."==m.charAt(m.length-1)&&f(k,j-1),m=h.toString(),n=m.match(g),n&&("www."==n[1]?n[1]="http://www.":/@$/.test(n[1])&&!/^mailto:/.test(n[1])&&(n[1]="mailto:"+n[1]),l=a.selection.getBookmark(),a.selection.setRng(h),a.execCommand("createlink",!1,n[1]+n[2]),a.selection.moveToBookmark(l),a.nodeChanged())}}var f,g=/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;return a.settings.autolink_pattern&&(g=a.settings.autolink_pattern),a.on("keydown",function(b){return 13==b.keyCode?d(a):void 0}),tinymce.Env.ie?void a.on("focus",function(){if(!f){f=!0;try{a.execCommand("AutoUrlDetect",!1,!0)}catch(b){}}}):(a.on("keypress",function(c){return 41==c.keyCode?b(a):void 0}),void a.on("keyup",function(b){return 32==b.keyCode?c(a):void 0}))});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("autoresize",function(e){function t(){return e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen()}function n(r){var a,s,l,c,u,d,f,p,m,h,g,v,y=tinymce.DOM;if(s=e.getDoc()){if(l=s.body,c=s.documentElement,u=i.autoresize_min_height,!l||r&&"setcontent"===r.type&&r.initial||t())return void(l&&c&&(l.style.overflowY="auto",c.style.overflowY="auto"));f=e.dom.getStyle(l,"margin-top",!0),p=e.dom.getStyle(l,"margin-bottom",!0),m=e.dom.getStyle(l,"padding-top",!0),h=e.dom.getStyle(l,"padding-bottom",!0),g=e.dom.getStyle(l,"border-top-width",!0),v=e.dom.getStyle(l,"border-bottom-width",!0),d=l.offsetHeight+parseInt(f,10)+parseInt(p,10)+parseInt(m,10)+parseInt(h,10)+parseInt(g,10)+parseInt(v,10),(isNaN(d)||0>=d)&&(d=tinymce.Env.ie?l.scrollHeight:tinymce.Env.webkit&&0===l.clientHeight?0:l.offsetHeight),d>i.autoresize_min_height&&(u=d),i.autoresize_max_height&&d>i.autoresize_max_height?(u=i.autoresize_max_height,l.style.overflowY="auto",c.style.overflowY="auto"):(l.style.overflowY="hidden",c.style.overflowY="hidden",l.scrollTop=0),u!==o&&(a=u-o,y.setStyle(e.iframeElement,"height",u+"px"),o=u,tinymce.isWebKit&&0>a&&n(r))}}function r(t,i,o){tinymce.util.Delay.setEditorTimeout(e,function(){n({}),t--?r(t,i,o):o&&o()},i)}var i=e.settings,o=0;e.settings.inline||(i.autoresize_min_height=parseInt(e.getParam("autoresize_min_height",e.getElement().offsetHeight),10),i.autoresize_max_height=parseInt(e.getParam("autoresize_max_height",0),10),e.on("init",function(){var t,n;t=e.getParam("autoresize_overflow_padding",1),n=e.getParam("autoresize_bottom_margin",50),t!==!1&&e.dom.setStyles(e.getBody(),{paddingLeft:t,paddingRight:t}),n!==!1&&e.dom.setStyles(e.getBody(),{paddingBottom:n})}),e.on("nodechange setcontent keyup FullscreenStateChanged",n),e.getParam("autoresize_on_init",!0)&&e.on("init",function(){r(20,100,function(){r(5,1e3)})}),e.addCommand("mceAutoResize",n))});
|
||||
tinymce.PluginManager.add("autoresize",function(a){function b(){return a.plugins.fullscreen&&a.plugins.fullscreen.isFullscreen()}function c(d){var g,h,i,j,k,l,m,n,o,p,q,r,s=tinymce.DOM;if(h=a.getDoc()){if(i=h.body,j=h.documentElement,k=e.autoresize_min_height,!i||d&&"setcontent"===d.type&&d.initial||b())return void(i&&j&&(i.style.overflowY="auto",j.style.overflowY="auto"));m=a.dom.getStyle(i,"margin-top",!0),n=a.dom.getStyle(i,"margin-bottom",!0),o=a.dom.getStyle(i,"padding-top",!0),p=a.dom.getStyle(i,"padding-bottom",!0),q=a.dom.getStyle(i,"border-top-width",!0),r=a.dom.getStyle(i,"border-bottom-width",!0),l=i.offsetHeight+parseInt(m,10)+parseInt(n,10)+parseInt(o,10)+parseInt(p,10)+parseInt(q,10)+parseInt(r,10),(isNaN(l)||0>=l)&&(l=tinymce.Env.ie?i.scrollHeight:tinymce.Env.webkit&&0===i.clientHeight?0:i.offsetHeight),l>e.autoresize_min_height&&(k=l),e.autoresize_max_height&&l>e.autoresize_max_height?(k=e.autoresize_max_height,i.style.overflowY="auto",j.style.overflowY="auto"):(i.style.overflowY="hidden",j.style.overflowY="hidden",i.scrollTop=0),k!==f&&(g=k-f,s.setStyle(a.iframeElement,"height",k+"px"),f=k,tinymce.isWebKit&&0>g&&c(d))}}function d(b,e,f){tinymce.util.Delay.setEditorTimeout(a,function(){c({}),b--?d(b,e,f):f&&f()},e)}var e=a.settings,f=0;a.settings.inline||(e.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight),10),e.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0),10),a.on("init",function(){var b,c;b=a.getParam("autoresize_overflow_padding",1),c=a.getParam("autoresize_bottom_margin",50),b!==!1&&a.dom.setStyles(a.getBody(),{paddingLeft:b,paddingRight:b}),c!==!1&&a.dom.setStyles(a.getBody(),{paddingBottom:c})}),a.on("nodechange setcontent keyup FullscreenStateChanged",c),a.getParam("autoresize_on_init",!0)&&a.on("init",function(){d(20,100,function(){d(5,1e3)})}),a.addCommand("mceAutoResize",c))});
|
||||
@@ -1 +1 @@
|
||||
tinymce._beforeUnloadHandler=function(){var e;return tinymce.each(tinymce.editors,function(t){t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&t.getParam("autosave_ask_before_unload",!0)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))}),e},tinymce.PluginManager.add("autosave",function(e){function t(e,t){var n={s:1e3,m:6e4};return e=/^(\d+)([ms]?)$/.exec(""+(e||t)),(e[2]?n[e[2]]:1)*parseInt(e,10)}function n(){var e=parseInt(p.getItem(u+"time"),10)||0;return(new Date).getTime()-e>f.autosave_retention?(r(!1),!1):!0}function r(t){p.removeItem(u+"draft"),p.removeItem(u+"time"),t!==!1&&e.fire("RemoveDraft")}function i(){!c()&&e.isDirty()&&(p.setItem(u+"draft",e.getContent({format:"raw",no_events:!0})),p.setItem(u+"time",(new Date).getTime()),e.fire("StoreDraft"))}function o(){n()&&(e.setContent(p.getItem(u+"draft"),{format:"raw"}),e.fire("RestoreDraft"))}function a(){d||(setInterval(function(){e.removed||i()},f.autosave_interval),d=!0)}function s(){var t=this;t.disabled(!n()),e.on("StoreDraft RestoreDraft RemoveDraft",function(){t.disabled(!n())}),a()}function l(){e.undoManager.beforeChange(),o(),r(),e.undoManager.add()}function c(t){var n=e.settings.forced_root_block;return t=tinymce.trim("undefined"==typeof t?e.getBody().innerHTML:t),""===t||new RegExp("^<"+n+"[^>]*>((\xa0| |[ ]|<br[^>]*>)+?|)</"+n+">|<br>$","i").test(t)}var u,d,f=e.settings,p=tinymce.util.LocalStorage;u=f.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",u=u.replace(/\{path\}/g,document.location.pathname),u=u.replace(/\{query\}/g,document.location.search),u=u.replace(/\{id\}/g,e.id),f.autosave_interval=t(f.autosave_interval,"30s"),f.autosave_retention=t(f.autosave_retention,"20m"),e.addButton("restoredraft",{title:"Restore last draft",onclick:l,onPostRender:s}),e.addMenuItem("restoredraft",{text:"Restore last draft",onclick:l,onPostRender:s,context:"file"}),e.settings.autosave_restore_when_empty!==!1&&(e.on("init",function(){n()&&c()&&o()}),e.on("saveContent",function(){r()})),window.onbeforeunload=tinymce._beforeUnloadHandler,this.hasDraft=n,this.storeDraft=i,this.restoreDraft=o,this.removeDraft=r,this.isEmpty=c});
|
||||
tinymce._beforeUnloadHandler=function(){var a;return tinymce.each(tinymce.editors,function(b){b.plugins.autosave&&b.plugins.autosave.storeDraft(),!a&&b.isDirty()&&b.getParam("autosave_ask_before_unload",!0)&&(a=b.translate("You have unsaved changes are you sure you want to navigate away?"))}),a},tinymce.PluginManager.add("autosave",function(a){function b(a,b){var c={s:1e3,m:6e4};return a=/^(\d+)([ms]?)$/.exec(""+(a||b)),(a[2]?c[a[2]]:1)*parseInt(a,10)}function c(){var a=parseInt(n.getItem(k+"time"),10)||0;return(new Date).getTime()-a>m.autosave_retention?(d(!1),!1):!0}function d(b){n.removeItem(k+"draft"),n.removeItem(k+"time"),b!==!1&&a.fire("RemoveDraft")}function e(){!j()&&a.isDirty()&&(n.setItem(k+"draft",a.getContent({format:"raw",no_events:!0})),n.setItem(k+"time",(new Date).getTime()),a.fire("StoreDraft"))}function f(){c()&&(a.setContent(n.getItem(k+"draft"),{format:"raw"}),a.fire("RestoreDraft"))}function g(){l||(setInterval(function(){a.removed||e()},m.autosave_interval),l=!0)}function h(){var b=this;b.disabled(!c()),a.on("StoreDraft RestoreDraft RemoveDraft",function(){b.disabled(!c())}),g()}function i(){a.undoManager.beforeChange(),f(),d(),a.undoManager.add()}function j(b){var c=a.settings.forced_root_block;return b=tinymce.trim("undefined"==typeof b?a.getBody().innerHTML:b),""===b||new RegExp("^<"+c+"[^>]*>((\xa0| |[ ]|<br[^>]*>)+?|)</"+c+">|<br>$","i").test(b)}var k,l,m=a.settings,n=tinymce.util.LocalStorage;k=m.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",k=k.replace(/\{path\}/g,document.location.pathname),k=k.replace(/\{query\}/g,document.location.search),k=k.replace(/\{id\}/g,a.id),m.autosave_interval=b(m.autosave_interval,"30s"),m.autosave_retention=b(m.autosave_retention,"20m"),a.addButton("restoredraft",{title:"Restore last draft",onclick:i,onPostRender:h}),a.addMenuItem("restoredraft",{text:"Restore last draft",onclick:i,onPostRender:h,context:"file"}),a.settings.autosave_restore_when_empty!==!1&&(a.on("init",function(){c()&&j()&&f()}),a.on("saveContent",function(){d()})),window.onbeforeunload=tinymce._beforeUnloadHandler,this.hasDraft=c,this.storeDraft=e,this.restoreDraft=f,this.removeDraft=d,this.isEmpty=j});
|
||||
@@ -1 +1 @@
|
||||
!function(){tinymce.create("tinymce.plugins.BBCodePlugin",{init:function(e){var t=this,n=e.getParam("bbcode_dialect","punbb").toLowerCase();e.on("beforeSetContent",function(e){e.content=t["_"+n+"_bbcode2html"](e.content)}),e.on("postProcess",function(e){e.set&&(e.content=t["_"+n+"_bbcode2html"](e.content)),e.get&&(e.content=t["_"+n+"_html2bbcode"](e.content))})},getInfo:function(){return{longname:"BBCode Plugin",author:"Ephox Corp",authorurl:"http://www.tinymce.com",infourl:"http://www.tinymce.com/wiki.php/Plugin:bbcode"}},_punbb_html2bbcode:function(e){function t(t,n){e=e.replace(t,n)}return e=tinymce.trim(e),t(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),t(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),t(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),t(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),t(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),t(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),t(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),t(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),t(/<font>(.*?)<\/font>/gi,"$1"),t(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),t(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),t(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),t(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),t(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),t(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),t(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),t(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),t(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),t(/<\/(strong|b)>/gi,"[/b]"),t(/<(strong|b)>/gi,"[b]"),t(/<\/(em|i)>/gi,"[/i]"),t(/<(em|i)>/gi,"[i]"),t(/<\/u>/gi,"[/u]"),t(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),t(/<u>/gi,"[u]"),t(/<blockquote[^>]*>/gi,"[quote]"),t(/<\/blockquote>/gi,"[/quote]"),t(/<br \/>/gi,"\n"),t(/<br\/>/gi,"\n"),t(/<br>/gi,"\n"),t(/<p>/gi,""),t(/<\/p>/gi,"\n"),t(/ |\u00a0/gi," "),t(/"/gi,'"'),t(/</gi,"<"),t(/>/gi,">"),t(/&/gi,"&"),e},_punbb_bbcode2html:function(e){function t(t,n){e=e.replace(t,n)}return e=tinymce.trim(e),t(/\n/gi,"<br />"),t(/\[b\]/gi,"<strong>"),t(/\[\/b\]/gi,"</strong>"),t(/\[i\]/gi,"<em>"),t(/\[\/i\]/gi,"</em>"),t(/\[u\]/gi,"<u>"),t(/\[\/u\]/gi,"</u>"),t(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),t(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),t(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),t(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),t(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span> '),t(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span> '),e}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}();
|
||||
!function(){tinymce.create("tinymce.plugins.BBCodePlugin",{init:function(a){var b=this,c=a.getParam("bbcode_dialect","punbb").toLowerCase();a.on("beforeSetContent",function(a){a.content=b["_"+c+"_bbcode2html"](a.content)}),a.on("postProcess",function(a){a.set&&(a.content=b["_"+c+"_bbcode2html"](a.content)),a.get&&(a.content=b["_"+c+"_html2bbcode"](a.content))})},getInfo:function(){return{longname:"BBCode Plugin",author:"Ephox Corp",authorurl:"http://www.tinymce.com",infourl:"http://www.tinymce.com/wiki.php/Plugin:bbcode"}},_punbb_html2bbcode:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),b(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),b(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),b(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),b(/<font>(.*?)<\/font>/gi,"$1"),b(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),b(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),b(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),b(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),b(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),b(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),b(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),b(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),b(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),b(/<\/(strong|b)>/gi,"[/b]"),b(/<(strong|b)>/gi,"[b]"),b(/<\/(em|i)>/gi,"[/i]"),b(/<(em|i)>/gi,"[i]"),b(/<\/u>/gi,"[/u]"),b(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),b(/<u>/gi,"[u]"),b(/<blockquote[^>]*>/gi,"[quote]"),b(/<\/blockquote>/gi,"[/quote]"),b(/<br \/>/gi,"\n"),b(/<br\/>/gi,"\n"),b(/<br>/gi,"\n"),b(/<p>/gi,""),b(/<\/p>/gi,"\n"),b(/ |\u00a0/gi," "),b(/"/gi,'"'),b(/</gi,"<"),b(/>/gi,">"),b(/&/gi,"&"),a},_punbb_bbcode2html:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/\n/gi,"<br />"),b(/\[b\]/gi,"<strong>"),b(/\[\/b\]/gi,"</strong>"),b(/\[i\]/gi,"<em>"),b(/\[\/i\]/gi,"</em>"),b(/\[u\]/gi,"<u>"),b(/\[\/u\]/gi,"</u>"),b(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),b(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),b(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),b(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),b(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span> '),b(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span> '),a}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}();
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("code",function(e){function t(){var t=e.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:e.getParam("code_dialog_width",600),minHeight:e.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(t){e.focus(),e.undoManager.transact(function(){e.setContent(t.data.code)}),e.selection.setCursorLocation(),e.nodeChanged()}});t.find("#code").value(e.getContent({source_view:!0}))}e.addCommand("mceCodeEditor",t),e.addButton("code",{icon:"code",tooltip:"Source code",onclick:t}),e.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:t})});
|
||||
tinymce.PluginManager.add("code",function(a){function b(){var b=a.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:a.getParam("code_dialog_width",600),minHeight:a.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(b){a.focus(),a.undoManager.transact(function(){a.setContent(b.data.code)}),a.selection.setCursorLocation(),a.nodeChanged()}});b.find("#code").value(a.getContent({source_view:!0}))}a.addCommand("mceCodeEditor",b),a.addButton("code",{icon:"code",tooltip:"Source code",onclick:b}),a.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:b})});
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("colorpicker",function(e){function t(t,n){function r(e){var t=new tinymce.util.Color(e),n=t.toRgb();o.fromJSON({r:n.r,g:n.g,b:n.b,hex:t.toHex().substr(1)}),i(t.toHex())}function i(e){o.find("#preview")[0].getEl().style.background=e}var o=e.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:n,onchange:function(){var e=this.rgb();o&&(o.find("#r").value(e.r),o.find("#g").value(e.g),o.find("#b").value(e.b),o.find("#hex").value(this.value().substr(1)),i(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var e,t,n=o.find("colorpicker")[0];return e=this.name(),t=this.value(),"hex"==e?(t="#"+t,r(t),void n.value(t)):(t={r:o.find("#r").value(),g:o.find("#g").value(),b:o.find("#b").value()},n.value(t),void r(t))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){t("#"+this.toJSON().hex)}});r(n)}e.settings.color_picker_callback||(e.settings.color_picker_callback=t)});
|
||||
tinymce.PluginManager.add("colorpicker",function(a){function b(b,c){function d(a){var b=new tinymce.util.Color(a),c=b.toRgb();f.fromJSON({r:c.r,g:c.g,b:c.b,hex:b.toHex().substr(1)}),e(b.toHex())}function e(a){f.find("#preview")[0].getEl().style.background=a}var f=a.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:c,onchange:function(){var a=this.rgb();f&&(f.find("#r").value(a.r),f.find("#g").value(a.g),f.find("#b").value(a.b),f.find("#hex").value(this.value().substr(1)),e(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var a,b,c=f.find("colorpicker")[0];return a=this.name(),b=this.value(),"hex"==a?(b="#"+b,d(b),void c.value(b)):(b={r:f.find("#r").value(),g:f.find("#g").value(),b:f.find("#b").value()},c.value(b),void d(b))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){b("#"+this.toJSON().hex)}});d(c)}a.settings.color_picker_callback||(a.settings.color_picker_callback=b)});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("contextmenu",function(e){var t,n=e.settings.contextmenu_never_use_native;e.on("contextmenu",function(r){var i,o=e.getDoc();if(!r.ctrlKey||n){if(r.preventDefault(),tinymce.Env.mac&&tinymce.Env.webkit&&2==r.button&&o.caretRangeFromPoint&&e.selection.setRng(o.caretRangeFromPoint(r.x,r.y)),i=e.settings.contextmenu||"link image inserttable | cell row column deletetable",t)t.show();else{var a=[];tinymce.each(i.split(/[ ,]/),function(t){var n=e.menuItems[t];"|"==t&&(n={text:t}),n&&(n.shortcut="",a.push(n))});for(var s=0;s<a.length;s++)"|"==a[s].text&&(0===s||s==a.length-1)&&a.splice(s,1);t=new tinymce.ui.Menu({items:a,context:"contextmenu",classes:"contextmenu"}).renderTo(),e.on("remove",function(){t.remove(),t=null})}var l={x:r.pageX,y:r.pageY};e.inline||(l=tinymce.DOM.getPos(e.getContentAreaContainer()),l.x+=r.clientX,l.y+=r.clientY),t.moveTo(l.x,l.y)}})});
|
||||
tinymce.PluginManager.add("contextmenu",function(a){var b,c=a.settings.contextmenu_never_use_native;a.on("contextmenu",function(d){var e,f=a.getDoc();if(!d.ctrlKey||c){if(d.preventDefault(),tinymce.Env.mac&&tinymce.Env.webkit&&2==d.button&&f.caretRangeFromPoint&&a.selection.setRng(f.caretRangeFromPoint(d.x,d.y)),e=a.settings.contextmenu||"link image inserttable | cell row column deletetable",b)b.show();else{var g=[];tinymce.each(e.split(/[ ,]/),function(b){var c=a.menuItems[b];"|"==b&&(c={text:b}),c&&(c.shortcut="",g.push(c))});for(var h=0;h<g.length;h++)"|"==g[h].text&&(0!==h&&h!=g.length-1||g.splice(h,1));b=new tinymce.ui.Menu({items:g,context:"contextmenu",classes:"contextmenu"}).renderTo(),a.on("remove",function(){b.remove(),b=null})}var i={x:d.pageX,y:d.pageY};a.inline||(i=tinymce.DOM.getPos(a.getContentAreaContainer()),i.x+=d.clientX,i.y+=d.clientY),b.moveTo(i.x,i.y)}})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("directionality",function(e){function t(t){var n,r=e.dom,i=e.selection.getSelectedBlocks();i.length&&(n=r.getAttrib(i[0],"dir"),tinymce.each(i,function(e){r.getParent(e.parentNode,"*[dir='"+t+"']",r.getRoot())||(n!=t?r.setAttrib(e,"dir",t):r.setAttrib(e,"dir",null))}),e.nodeChanged())}function n(e){var t=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(n){t.push(n+"[dir="+e+"]")}),t.join(",")}e.addCommand("mceDirectionLTR",function(){t("ltr")}),e.addCommand("mceDirectionRTL",function(){t("rtl")}),e.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:n("ltr")}),e.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:n("rtl")})});
|
||||
tinymce.PluginManager.add("directionality",function(a){function b(b){var c,d=a.dom,e=a.selection.getSelectedBlocks();e.length&&(c=d.getAttrib(e[0],"dir"),tinymce.each(e,function(a){d.getParent(a.parentNode,"*[dir='"+b+"']",d.getRoot())||(c!=b?d.setAttrib(a,"dir",b):d.setAttrib(a,"dir",null))}),a.nodeChanged())}function c(a){var b=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(c){b.push(c+"[dir="+a+"]")}),b.join(",")}a.addCommand("mceDirectionLTR",function(){b("ltr")}),a.addCommand("mceDirectionRTL",function(){b("rtl")}),a.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:c("ltr")}),a.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:c("rtl")})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("emoticons",function(e,t){function n(){var e;return e='<table role="list" class="mce-grid">',tinymce.each(r,function(n){e+="<tr>",tinymce.each(n,function(n){var r=t+"/img/smiley-"+n+".gif";e+='<td><a href="#" data-mce-url="'+r+'" data-mce-alt="'+n+'" tabindex="-1" role="option" aria-label="'+n+'"><img src="'+r+'" style="width: 18px; height: 18px" role="presentation" /></a></td>'}),e+="</tr>"}),e+="</table>"}var r=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];e.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:n,onclick:function(t){var n=e.dom.getParent(t.target,"a");n&&(e.insertContent('<img src="'+n.getAttribute("data-mce-url")+'" alt="'+n.getAttribute("data-mce-alt")+'" />'),this.hide())}},tooltip:"Emoticons"})});
|
||||
tinymce.PluginManager.add("emoticons",function(a,b){function c(){var a;return a='<table role="list" class="mce-grid">',tinymce.each(d,function(c){a+="<tr>",tinymce.each(c,function(c){var d=b+"/img/smiley-"+c+".gif";a+='<td><a href="#" data-mce-url="'+d+'" data-mce-alt="'+c+'" tabindex="-1" role="option" aria-label="'+c+'"><img src="'+d+'" style="width: 18px; height: 18px" role="presentation" /></a></td>'}),a+="</tr>"}),a+="</table>"}var d=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];a.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:c,onclick:function(b){var c=a.dom.getParent(b.target,"a");c&&(a.insertContent('<img src="'+c.getAttribute("data-mce-url")+'" alt="'+c.getAttribute("data-mce-alt")+'" />'),this.hide())}},tooltip:"Emoticons"})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("example",function(e,t){e.addButton("example",{text:"My button",icon:!1,onclick:function(){e.windowManager.open({title:"Example plugin",body:[{type:"textbox",name:"title",label:"Title"}],onsubmit:function(t){e.insertContent("Title: "+t.data.title)}})}}),e.addMenuItem("example",{text:"Example plugin",context:"tools",onclick:function(){e.windowManager.open({title:"TinyMCE site",url:t+"/dialog.html",width:600,height:400,buttons:[{text:"Insert",onclick:function(){var t=e.windowManager.getWindows()[0];e.insertContent(t.getContentWindow().document.getElementById("content").value),t.close()}},{text:"Close",onclick:"close"}]})}})});
|
||||
tinymce.PluginManager.add("example",function(a,b){a.addButton("example",{text:"My button",icon:!1,onclick:function(){a.windowManager.open({title:"Example plugin",body:[{type:"textbox",name:"title",label:"Title"}],onsubmit:function(b){a.insertContent("Title: "+b.data.title)}})}}),a.addMenuItem("example",{text:"Example plugin",context:"tools",onclick:function(){a.windowManager.open({title:"TinyMCE site",url:b+"/dialog.html",width:600,height:400,buttons:[{text:"Insert",onclick:function(){var b=a.windowManager.getWindows()[0];a.insertContent(b.getContentWindow().document.getElementById("content").value),b.close()}},{text:"Close",onclick:"close"}]})}})});
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("fullscreen",function(e){function t(){var e,t,n=window,r=document,i=r.body;return i.offsetWidth&&(e=i.offsetWidth,t=i.offsetHeight),n.innerWidth&&n.innerHeight&&(e=n.innerWidth,t=n.innerHeight),{w:e,h:t}}function n(){function n(){c.setStyle(f,"height",t().h-(d.clientHeight-f.clientHeight))}var u,d,f,p,m=document.body,h=document.documentElement;l=!l,d=e.getContainer(),u=d.style,f=e.getContentAreaContainer().firstChild,p=f.style,l?(r=p.width,i=p.height,p.width=p.height="100%",a=u.width,s=u.height,u.width=u.height="",c.addClass(m,"mce-fullscreen"),c.addClass(h,"mce-fullscreen"),c.addClass(d,"mce-fullscreen"),c.bind(window,"resize",n),n(),o=n):(p.width=r,p.height=i,a&&(u.width=a),s&&(u.height=s),c.removeClass(m,"mce-fullscreen"),c.removeClass(h,"mce-fullscreen"),c.removeClass(d,"mce-fullscreen"),c.unbind(window,"resize",o)),e.fire("FullscreenStateChanged",{state:l})}var r,i,o,a,s,l=!1,c=tinymce.DOM;return e.settings.inline?void 0:(e.on("init",function(){e.addShortcut("Meta+Alt+F","",n)}),e.on("remove",function(){o&&c.unbind(window,"resize",o)}),e.addCommand("mceFullScreen",n),e.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Meta+Alt+F",selectable:!0,onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})},context:"view"}),e.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Meta+Alt+F",onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})}}),{isFullscreen:function(){return l}})});
|
||||
tinymce.PluginManager.add("fullscreen",function(a){function b(){var a,b,c=window,d=document,e=d.body;return e.offsetWidth&&(a=e.offsetWidth,b=e.offsetHeight),c.innerWidth&&c.innerHeight&&(a=c.innerWidth,b=c.innerHeight),{w:a,h:b}}function c(){var a=tinymce.DOM.getViewPort();return{x:a.x,y:a.y}}function d(a){scrollTo(a.x,a.y)}function e(){function e(){m.setStyle(p,"height",b().h-(o.clientHeight-p.clientHeight))}var n,o,p,q,r=document.body,s=document.documentElement;l=!l,o=a.getContainer(),n=o.style,p=a.getContentAreaContainer().firstChild,q=p.style,l?(k=c(),f=q.width,g=q.height,q.width=q.height="100%",i=n.width,j=n.height,n.width=n.height="",m.addClass(r,"mce-fullscreen"),m.addClass(s,"mce-fullscreen"),m.addClass(o,"mce-fullscreen"),m.bind(window,"resize",e),e(),h=e):(q.width=f,q.height=g,i&&(n.width=i),j&&(n.height=j),m.removeClass(r,"mce-fullscreen"),m.removeClass(s,"mce-fullscreen"),m.removeClass(o,"mce-fullscreen"),m.unbind(window,"resize",h),d(k)),a.fire("FullscreenStateChanged",{state:l})}var f,g,h,i,j,k,l=!1,m=tinymce.DOM;return a.settings.inline?void 0:(a.on("init",function(){a.addShortcut("Meta+Alt+F","",e)}),a.on("remove",function(){h&&m.unbind(window,"resize",h)}),a.addCommand("mceFullScreen",e),a.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Meta+Alt+F",selectable:!0,onClick:function(){e(),a.focus()},onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})},context:"view"}),a.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Meta+Alt+F",onClick:e,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})}}),{isFullscreen:function(){return l}})});
|
||||
2
public/libs/tinymce/plugins/hr/plugin.min.js
vendored
2
public/libs/tinymce/plugins/hr/plugin.min.js
vendored
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("hr",function(e){e.addCommand("InsertHorizontalRule",function(){e.execCommand("mceInsertContent",!1,"<hr />")}),e.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),e.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})});
|
||||
tinymce.PluginManager.add("hr",function(a){a.addCommand("InsertHorizontalRule",function(){a.execCommand("mceInsertContent",!1,"<hr />")}),a.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),a.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("importcss",function(e){function t(t){var n=e.settings,r=n.skin!==!1?n.skin||"lightgray":!1;if(r){var i=n.skin_url;return i=i?e.documentBaseURI.toAbsolute(i):tinymce.baseURL+"/skins/"+r,t===i+"/content"+(e.inline?".inline":"")+".min.css"}return!1}function n(e){return"string"==typeof e?function(t){return-1!==t.indexOf(e)}:e instanceof RegExp?function(t){return e.test(t)}:e}function r(n,r){function i(e,n){var s,l=e.href;if(l&&r(l,n)&&!t(l)){a(e.imports,function(e){i(e,!0)});try{s=e.cssRules||e.rules}catch(c){}a(s,function(e){e.styleSheet?i(e.styleSheet,!0):e.selectorText&&a(e.selectorText.split(","),function(e){o.push(tinymce.trim(e))})})}}var o=[],s={};a(e.contentCSS,function(e){s[e]=!0}),r||(r=function(e,t){return t||s[e]});try{a(n.styleSheets,function(e){i(e)})}catch(l){}return o}function i(t){var n,r=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(r){var i=r[1],o=r[2].substr(1).split(".").join(" "),a=tinymce.makeMap("a,img");return r[1]?(n={title:t},e.schema.getTextBlockElements()[i]?n.block=i:e.schema.getBlockElements()[i]||a[i.toLowerCase()]?n.selector=i:n.inline=i):r[2]&&(n={inline:"span",title:t.substr(1),classes:o}),e.settings.importcss_merge_classes!==!1?n.classes=o:n.attributes={"class":o},n}}var o=this,a=tinymce.each;e.on("renderFormatsMenu",function(t){var s=e.settings,l={},c=s.importcss_selector_converter||i,u=n(s.importcss_selector_filter),d=t.control;e.settings.importcss_append||d.items().remove();var f=[];tinymce.each(s.importcss_groups,function(e){e=tinymce.extend({},e),e.filter=n(e.filter),f.push(e)}),a(r(t.doc||e.getDoc(),n(s.importcss_file_filter)),function(t){if(-1===t.indexOf(".mce-")&&!l[t]&&(!u||u(t))){var n,r=c.call(o,t);if(r){var i=r.name||tinymce.DOM.uniqueId();if(f)for(var a=0;a<f.length;a++)if(!f[a].filter||f[a].filter(t)){f[a].item||(f[a].item={text:f[a].title,menu:[]}),n=f[a].item.menu;break}e.formatter.register(i,r);var s=tinymce.extend({},d.settings.itemDefaults,{text:r.title,format:i});n?n.push(s):d.add(s)}l[t]=!0}}),a(f,function(e){d.add(e.item)}),t.control.renderNew()}),o.convertSelectorToFormat=i});
|
||||
tinymce.PluginManager.add("importcss",function(a){function b(a){var b=tinymce.Env.cacheSuffix;return"string"==typeof a&&(a=a.replace("?"+b,"").replace("&"+b,"")),a}function c(b){var c=a.settings,d=c.skin!==!1?c.skin||"lightgray":!1;if(d){var e=c.skin_url;return e=e?a.documentBaseURI.toAbsolute(e):tinymce.baseURL+"/skins/"+d,b===e+"/content"+(a.inline?".inline":"")+".min.css"}return!1}function d(a){return"string"==typeof a?function(b){return-1!==b.indexOf(a)}:a instanceof RegExp?function(b){return a.test(b)}:a}function e(d,e){function f(a,d){var i,j=a.href;if(j=b(j),j&&e(j,d)&&!c(j)){h(a.imports,function(a){f(a,!0)});try{i=a.cssRules||a.rules}catch(k){}h(i,function(a){a.styleSheet?f(a.styleSheet,!0):a.selectorText&&h(a.selectorText.split(","),function(a){g.push(tinymce.trim(a))})})}}var g=[],i={};h(a.contentCSS,function(a){i[a]=!0}),e||(e=function(a,b){return b||i[a]});try{h(d.styleSheets,function(a){f(a)})}catch(j){}return g}function f(b){var c,d=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(b);if(d){var e=d[1],f=d[2].substr(1).split(".").join(" "),g=tinymce.makeMap("a,img");return d[1]?(c={title:b},a.schema.getTextBlockElements()[e]?c.block=e:a.schema.getBlockElements()[e]||g[e.toLowerCase()]?c.selector=e:c.inline=e):d[2]&&(c={inline:"span",title:b.substr(1),classes:f}),a.settings.importcss_merge_classes!==!1?c.classes=f:c.attributes={"class":f},c}}var g=this,h=tinymce.each;a.on("renderFormatsMenu",function(b){var c=a.settings,i={},j=c.importcss_selector_converter||f,k=d(c.importcss_selector_filter),l=b.control;a.settings.importcss_append||l.items().remove();var m=[];tinymce.each(c.importcss_groups,function(a){a=tinymce.extend({},a),a.filter=d(a.filter),m.push(a)}),h(e(b.doc||a.getDoc(),d(c.importcss_file_filter)),function(b){if(-1===b.indexOf(".mce-")&&!i[b]&&(!k||k(b))){var c,d=j.call(g,b);if(d){var e=d.name||tinymce.DOM.uniqueId();if(m)for(var f=0;f<m.length;f++)if(!m[f].filter||m[f].filter(b)){m[f].item||(m[f].item={text:m[f].title,menu:[]}),c=m[f].item.menu;break}a.formatter.register(e,d);var h=tinymce.extend({},l.settings.itemDefaults,{text:d.title,format:e});c?c.push(h):l.add(h)}i[b]=!0}}),h(m,function(a){l.add(a.item)}),b.control.renderNew()}),g.convertSelectorToFormat=f});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("insertdatetime",function(e){function t(t,n){function r(e,t){if(e=""+e,e.length<t)for(var n=0;n<t-e.length;n++)e="0"+e;return e}return n=n||new Date,t=t.replace("%D","%m/%d/%Y"),t=t.replace("%r","%I:%M:%S %p"),t=t.replace("%Y",""+n.getFullYear()),t=t.replace("%y",""+n.getYear()),t=t.replace("%m",r(n.getMonth()+1,2)),t=t.replace("%d",r(n.getDate(),2)),t=t.replace("%H",""+r(n.getHours(),2)),t=t.replace("%M",""+r(n.getMinutes(),2)),t=t.replace("%S",""+r(n.getSeconds(),2)),t=t.replace("%I",""+((n.getHours()+11)%12+1)),t=t.replace("%p",""+(n.getHours()<12?"AM":"PM")),t=t.replace("%B",""+e.translate(l[n.getMonth()])),t=t.replace("%b",""+e.translate(s[n.getMonth()])),t=t.replace("%A",""+e.translate(a[n.getDay()])),t=t.replace("%a",""+e.translate(o[n.getDay()])),t=t.replace("%%","%")}function n(n){var r=t(n);if(e.settings.insertdatetime_element){var i;i=t(/%[HMSIp]/.test(n)?"%Y-%m-%dT%H:%M":"%Y-%m-%d"),r='<time datetime="'+i+'">'+r+"</time>";var o=e.dom.getParent(e.selection.getStart(),"time");if(o)return void e.dom.setOuterHTML(o,r)}e.insertContent(r)}var r,i,o="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),a="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),s="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),l="January February March April May June July August September October November December".split(" "),c=[];e.addCommand("mceInsertDate",function(){n(e.getParam("insertdatetime_dateformat",e.translate("%Y-%m-%d")))}),e.addCommand("mceInsertTime",function(){n(e.getParam("insertdatetime_timeformat",e.translate("%H:%M:%S")))}),e.addButton("insertdatetime",{type:"splitbutton",title:"Insert date/time",onclick:function(){n(r||i)},menu:c}),tinymce.each(e.settings.insertdatetime_formats||["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"],function(e){i||(i=e),c.push({text:t(e),onclick:function(){r=e,n(e)}})}),e.addMenuItem("insertdatetime",{icon:"date",text:"Insert date/time",menu:c,context:"insert"})});
|
||||
tinymce.PluginManager.add("insertdatetime",function(a){function b(b,c){function d(a,b){if(a=""+a,a.length<b)for(var c=0;c<b-a.length;c++)a="0"+a;return a}return c=c||new Date,b=b.replace("%D","%m/%d/%Y"),b=b.replace("%r","%I:%M:%S %p"),b=b.replace("%Y",""+c.getFullYear()),b=b.replace("%y",""+c.getYear()),b=b.replace("%m",d(c.getMonth()+1,2)),b=b.replace("%d",d(c.getDate(),2)),b=b.replace("%H",""+d(c.getHours(),2)),b=b.replace("%M",""+d(c.getMinutes(),2)),b=b.replace("%S",""+d(c.getSeconds(),2)),b=b.replace("%I",""+((c.getHours()+11)%12+1)),b=b.replace("%p",""+(c.getHours()<12?"AM":"PM")),b=b.replace("%B",""+a.translate(i[c.getMonth()])),b=b.replace("%b",""+a.translate(h[c.getMonth()])),b=b.replace("%A",""+a.translate(g[c.getDay()])),b=b.replace("%a",""+a.translate(f[c.getDay()])),b=b.replace("%%","%")}function c(c){var d=b(c);if(a.settings.insertdatetime_element){var e;e=b(/%[HMSIp]/.test(c)?"%Y-%m-%dT%H:%M":"%Y-%m-%d"),d='<time datetime="'+e+'">'+d+"</time>";var f=a.dom.getParent(a.selection.getStart(),"time");if(f)return void a.dom.setOuterHTML(f,d)}a.insertContent(d)}var d,e,f="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),g="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),h="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),i="January February March April May June July August September October November December".split(" "),j=[];a.addCommand("mceInsertDate",function(){c(a.getParam("insertdatetime_dateformat",a.translate("%Y-%m-%d")))}),a.addCommand("mceInsertTime",function(){c(a.getParam("insertdatetime_timeformat",a.translate("%H:%M:%S")))}),a.addButton("insertdatetime",{type:"splitbutton",title:"Insert date/time",onclick:function(){c(d||e)},menu:j}),tinymce.each(a.settings.insertdatetime_formats||["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"],function(a){e||(e=a),j.push({text:b(a),onclick:function(){d=a,c(a)}})}),a.addMenuItem("insertdatetime",{icon:"date",text:"Insert date/time",menu:j,context:"insert"})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("layer",function(e){function t(e){do if(e.className&&-1!=e.className.indexOf("mceItemLayer"))return e;while(e=e.parentNode)}function n(t){var n=e.dom;tinymce.each(n.select("div,p",t),function(e){/^(absolute|relative|fixed)$/i.test(e.style.position)&&(e.hasVisual?n.addClass(e,"mceItemVisualAid"):n.removeClass(e,"mceItemVisualAid"),n.addClass(e,"mceItemLayer"))})}function r(n){var r,i,o=[],a=t(e.selection.getNode()),s=-1,l=-1;for(i=[],tinymce.walk(e.getBody(),function(e){1==e.nodeType&&/^(absolute|relative|static)$/i.test(e.style.position)&&i.push(e)},"childNodes"),r=0;r<i.length;r++)o[r]=i[r].style.zIndex?parseInt(i[r].style.zIndex,10):0,0>s&&i[r]==a&&(s=r);if(0>n){for(r=0;r<o.length;r++)if(o[r]<o[s]){l=r;break}l>-1?(i[s].style.zIndex=o[l],i[l].style.zIndex=o[s]):o[s]>0&&(i[s].style.zIndex=o[s]-1)}else{for(r=0;r<o.length;r++)if(o[r]>o[s]){l=r;break}l>-1?(i[s].style.zIndex=o[l],i[l].style.zIndex=o[s]):i[s].style.zIndex=o[s]+1}e.execCommand("mceRepaint")}function i(){var t=e.dom,n=t.getPos(t.getParent(e.selection.getNode(),"*")),r=e.getBody();e.dom.add(r,"div",{style:{position:"absolute",left:n.x,top:n.y>20?n.y:20,width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},e.selection.getContent()||e.getLang("layer.content")),tinymce.Env.ie&&t.setHTML(r,r.innerHTML)}function o(){var n=t(e.selection.getNode());n||(n=e.dom.getParent(e.selection.getNode(),"DIV,P,IMG")),n&&("absolute"==n.style.position.toLowerCase()?(e.dom.setStyles(n,{position:"",left:"",top:"",width:"",height:""}),e.dom.removeClass(n,"mceItemVisualAid"),e.dom.removeClass(n,"mceItemLayer")):(n.style.left||(n.style.left="20px"),n.style.top||(n.style.top="20px"),n.style.width||(n.style.width=n.width?n.width+"px":"100px"),n.style.height||(n.style.height=n.height?n.height+"px":"100px"),n.style.position="absolute",e.dom.setAttrib(n,"data-mce-style",""),e.addVisual(e.getBody())),e.execCommand("mceRepaint"),e.nodeChanged())}e.addCommand("mceInsertLayer",i),e.addCommand("mceMoveForward",function(){r(1)}),e.addCommand("mceMoveBackward",function(){r(-1)}),e.addCommand("mceMakeAbsolute",function(){o()}),e.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"}),e.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"}),e.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"}),e.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"}),e.on("init",function(){tinymce.Env.ie&&e.getDoc().execCommand("2D-Position",!1,!0)}),e.on("mouseup",function(n){var r=t(n.target);r&&e.dom.setAttrib(r,"data-mce-style","")}),e.on("mousedown",function(n){var r,i=n.target,o=e.getDoc();tinymce.Env.gecko&&(t(i)?"on"!==o.designMode&&(o.designMode="on",i=o.body,r=i.parentNode,r.removeChild(i),r.appendChild(i)):"on"==o.designMode&&(o.designMode="off"))}),e.on("NodeChange",n)});
|
||||
tinymce.PluginManager.add("layer",function(a){function b(a){do if(a.className&&-1!=a.className.indexOf("mceItemLayer"))return a;while(a=a.parentNode)}function c(b){var c=a.dom;tinymce.each(c.select("div,p",b),function(a){/^(absolute|relative|fixed)$/i.test(a.style.position)&&(a.hasVisual?c.addClass(a,"mceItemVisualAid"):c.removeClass(a,"mceItemVisualAid"),c.addClass(a,"mceItemLayer"))})}function d(c){var d,e,f=[],g=b(a.selection.getNode()),h=-1,i=-1;for(e=[],tinymce.walk(a.getBody(),function(a){1==a.nodeType&&/^(absolute|relative|static)$/i.test(a.style.position)&&e.push(a)},"childNodes"),d=0;d<e.length;d++)f[d]=e[d].style.zIndex?parseInt(e[d].style.zIndex,10):0,0>h&&e[d]==g&&(h=d);if(0>c){for(d=0;d<f.length;d++)if(f[d]<f[h]){i=d;break}i>-1?(e[h].style.zIndex=f[i],e[i].style.zIndex=f[h]):f[h]>0&&(e[h].style.zIndex=f[h]-1)}else{for(d=0;d<f.length;d++)if(f[d]>f[h]){i=d;break}i>-1?(e[h].style.zIndex=f[i],e[i].style.zIndex=f[h]):e[h].style.zIndex=f[h]+1}a.execCommand("mceRepaint")}function e(){var b=a.dom,c=b.getPos(b.getParent(a.selection.getNode(),"*")),d=a.getBody();a.dom.add(d,"div",{style:{position:"absolute",left:c.x,top:c.y>20?c.y:20,width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},a.selection.getContent()||a.getLang("layer.content")),tinymce.Env.ie&&b.setHTML(d,d.innerHTML)}function f(){var c=b(a.selection.getNode());c||(c=a.dom.getParent(a.selection.getNode(),"DIV,P,IMG")),c&&("absolute"==c.style.position.toLowerCase()?(a.dom.setStyles(c,{position:"",left:"",top:"",width:"",height:""}),a.dom.removeClass(c,"mceItemVisualAid"),a.dom.removeClass(c,"mceItemLayer")):(c.style.left||(c.style.left="20px"),c.style.top||(c.style.top="20px"),c.style.width||(c.style.width=c.width?c.width+"px":"100px"),c.style.height||(c.style.height=c.height?c.height+"px":"100px"),c.style.position="absolute",a.dom.setAttrib(c,"data-mce-style",""),a.addVisual(a.getBody())),a.execCommand("mceRepaint"),a.nodeChanged())}a.addCommand("mceInsertLayer",e),a.addCommand("mceMoveForward",function(){d(1)}),a.addCommand("mceMoveBackward",function(){d(-1)}),a.addCommand("mceMakeAbsolute",function(){f()}),a.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"}),a.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"}),a.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"}),a.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"}),a.on("init",function(){tinymce.Env.ie&&a.getDoc().execCommand("2D-Position",!1,!0)}),a.on("mouseup",function(c){var d=b(c.target);d&&a.dom.setAttrib(d,"data-mce-style","")}),a.on("mousedown",function(c){var d,e=c.target,f=a.getDoc();tinymce.Env.gecko&&(b(e)?"on"!==f.designMode&&(f.designMode="on",e=f.body,d=e.parentNode,d.removeChild(e),d.appendChild(e)):"on"==f.designMode&&(f.designMode="off"))}),a.on("NodeChange",c)});
|
||||
@@ -1 +1 @@
|
||||
!function(e){e.on("AddEditor",function(e){e.editor.settings.inline_styles=!1}),e.PluginManager.add("legacyoutput",function(t,n,r){t.on("init",function(){var n="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",r=e.explode(t.settings.font_size_style_values),i=t.schema;t.formatter.register({alignleft:{selector:n,attributes:{align:"left"}},aligncenter:{selector:n,attributes:{align:"center"}},alignright:{selector:n,attributes:{align:"right"}},alignjustify:{selector:n,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:!0}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:!0}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(t){return e.inArray(r,t.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}}),e.each("b,i,u,strike".split(","),function(e){i.addValidElements(e+"[*]")}),i.getElementRule("font")||i.addValidElements("font[face|size|color|style]"),e.each(n.split(","),function(e){var t=i.getElementRule(e);t&&(t.attributes.align||(t.attributes.align={},t.attributesOrder.push("align")))})}),t.addButton("fontsizeselect",function(){var e=[],n="8pt=1 10pt=2 12pt=3 14pt=4 18pt=5 24pt=6 36pt=7",r=t.settings.fontsize_formats||n;return t.$.each(r.split(" "),function(t,n){var r=n,i=n,o=n.split("=");o.length>1&&(r=o[0],i=o[1]),e.push({text:r,value:i})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:e,fixedWidth:!0,onPostRender:function(){var e=this;t.on("NodeChange",function(){var n;n=t.dom.getParent(t.selection.getNode(),"font"),n?e.value(n.size):e.value("")})},onclick:function(e){e.control.settings.value&&t.execCommand("FontSize",!1,e.control.settings.value)}}}),t.addButton("fontselect",function(){function e(e){e=e.replace(/;$/,"").split(";");for(var t=e.length;t--;)e[t]=e[t].split("=");return e}var n="Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",i=[],o=e(t.settings.font_formats||n);return r.each(o,function(e,t){i.push({text:{raw:t[0]},value:t[1],textStyle:-1==t[1].indexOf("dings")?"font-family:"+t[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:i,fixedWidth:!0,onPostRender:function(){var e=this;t.on("NodeChange",function(){var n;n=t.dom.getParent(t.selection.getNode(),"font"),n?e.value(n.face):e.value("")})},onselect:function(e){e.control.settings.value&&t.execCommand("FontName",!1,e.control.settings.value)}}})})}(tinymce);
|
||||
!function(a){a.on("AddEditor",function(a){a.editor.settings.inline_styles=!1}),a.PluginManager.add("legacyoutput",function(b,c,d){b.on("init",function(){var c="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",d=a.explode(b.settings.font_size_style_values),e=b.schema;b.formatter.register({alignleft:{selector:c,attributes:{align:"left"}},aligncenter:{selector:c,attributes:{align:"center"}},alignright:{selector:c,attributes:{align:"right"}},alignjustify:{selector:c,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:!0}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:!0}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(b){return a.inArray(d,b.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}}),a.each("b,i,u,strike".split(","),function(a){e.addValidElements(a+"[*]")}),e.getElementRule("font")||e.addValidElements("font[face|size|color|style]"),a.each(c.split(","),function(a){var b=e.getElementRule(a);b&&(b.attributes.align||(b.attributes.align={},b.attributesOrder.push("align")))})}),b.addButton("fontsizeselect",function(){var a=[],c="8pt=1 10pt=2 12pt=3 14pt=4 18pt=5 24pt=6 36pt=7",d=b.settings.fontsize_formats||c;return b.$.each(d.split(" "),function(b,c){var d=c,e=c,f=c.split("=");f.length>1&&(d=f[0],e=f[1]),a.push({text:d,value:e})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:a,fixedWidth:!0,onPostRender:function(){var a=this;b.on("NodeChange",function(){var c;c=b.dom.getParent(b.selection.getNode(),"font"),c?a.value(c.size):a.value("")})},onclick:function(a){a.control.settings.value&&b.execCommand("FontSize",!1,a.control.settings.value)}}}),b.addButton("fontselect",function(){function a(a){a=a.replace(/;$/,"").split(";");for(var b=a.length;b--;)a[b]=a[b].split("=");return a}var c="Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",e=[],f=a(b.settings.font_formats||c);return d.each(f,function(a,b){e.push({text:{raw:b[0]},value:b[1],textStyle:-1==b[1].indexOf("dings")?"font-family:"+b[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:e,fixedWidth:!0,onPostRender:function(){var a=this;b.on("NodeChange",function(){var c;c=b.dom.getParent(b.selection.getNode(),"font"),c?a.value(c.face):a.value("")})},onselect:function(a){a.control.settings.value&&b.execCommand("FontName",!1,a.control.settings.value)}}})})}(tinymce);
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("link",function(e){function t(t){return function(){var n=e.settings.link_list;"string"==typeof n?tinymce.util.XHR.send({url:n,success:function(e){t(tinymce.util.JSON.parse(e))}}):"function"==typeof n?n(t):t(n)}}function n(e,t,n){function r(e,n){return n=n||[],tinymce.each(e,function(e){var i={text:e.text||e.title};e.menu?i.menu=r(e.menu):(i.value=e.value,t&&t(i)),n.push(i)}),n}return r(e,n||[])}function r(t){function r(e){var t=d.find("#text");(!t.value()||e.lastControl&&t.value()==e.lastControl.text())&&t.value(e.control.text()),d.find("#href").value(e.control.value())}function i(t){var n=[];return tinymce.each(e.dom.select("a:not([href])"),function(e){var r=e.name||e.id;r&&n.push({text:r,value:"#"+r,selected:-1!=t.indexOf("#"+r)})}),n.length?(n.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:n,onselect:r}):void 0}function o(){!u&&0===x.text.length&&f&&this.parent().parent().find("#text")[0].value(this.value())}function a(t){var n=t.meta||{};m&&m.value(e.convertURL(this.value(),"href")),tinymce.each(t.meta,function(e,t){d.find("#"+t).value(e)}),n.text||o.call(this)}function s(e){var t=C.getContent();if(/</.test(t)&&(!/^<a [^>]+>[^<]+<\/a>$/.test(t)||-1==t.indexOf("href=")))return!1;if(e){var n,r=e.childNodes;if(0===r.length)return!1;for(n=r.length-1;n>=0;n--)if(3!=r[n].nodeType)return!1}return!0}var l,c,u,d,f,p,m,h,g,v,y,b,x={},C=e.selection,w=e.dom;l=C.getNode(),c=w.getParent(l,"a[href]"),f=s(),x.text=u=c?c.innerText||c.textContent:C.getContent({format:"text"}),x.href=c?w.getAttrib(c,"href"):"",c?x.target=w.getAttrib(c,"target"):e.settings.default_link_target&&(x.target=e.settings.default_link_target),(b=w.getAttrib(c,"rel"))&&(x.rel=b),(b=w.getAttrib(c,"class"))&&(x["class"]=b),(b=w.getAttrib(c,"title"))&&(x.title=b),f&&(p={name:"text",type:"textbox",size:40,label:"Text to display",onchange:function(){x.text=this.value()}}),t&&(m={type:"listbox",label:"Link list",values:n(t,function(t){t.value=e.convertURL(t.value||t.url,"href")},[{text:"None",value:""}]),onselect:r,value:e.convertURL(x.href,"href"),onPostRender:function(){m=this}}),e.settings.target_list!==!1&&(e.settings.target_list||(e.settings.target_list=[{text:"None",value:""},{text:"New window",value:"_blank"}]),g={name:"target",type:"listbox",label:"Target",values:n(e.settings.target_list)}),e.settings.rel_list&&(h={name:"rel",type:"listbox",label:"Rel",values:n(e.settings.rel_list)}),e.settings.link_class_list&&(v={name:"class",type:"listbox",label:"Class",values:n(e.settings.link_class_list,function(t){t.value&&(t.textStyle=function(){return e.formatter.getCssText({inline:"a",classes:[t.value]})})})}),e.settings.link_title!==!1&&(y={name:"title",type:"textbox",label:"Title",value:x.title}),d=e.windowManager.open({title:"Insert link",data:x,body:[{name:"href",type:"filepicker",filetype:"file",size:40,autofocus:!0,label:"Url",onchange:a,onkeyup:o},p,y,i(x.href),m,h,g,v],onSubmit:function(t){function n(t,n){var r=e.selection.getRng();tinymce.util.Delay.setEditorTimeout(e,function(){e.windowManager.confirm(t,function(t){e.selection.setRng(r),n(t)})})}function r(){var t={href:i,target:x.target?x.target:null,rel:x.rel?x.rel:null,"class":x["class"]?x["class"]:null,title:x.title?x.title:null};c?(e.focus(),f&&x.text!=u&&("innerText"in c?c.innerText=x.text:c.textContent=x.text),w.setAttribs(c,t),C.select(c),e.undoManager.add()):f?e.insertContent(w.createHTML("a",t,w.encode(x.text))):e.execCommand("mceInsertLink",!1,t)}var i;return x=tinymce.extend(x,t.data),(i=x.href)?i.indexOf("@")>0&&-1==i.indexOf("//")&&-1==i.indexOf("mailto:")?void n("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(e){e&&(i="mailto:"+i),r()}):e.settings.link_assume_external_targets&&!/^\w+:/i.test(i)||!e.settings.link_assume_external_targets&&/^\s*www[\.|\d\.]/i.test(i)?void n("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(e){e&&(i="http://"+i),r()}):void r():void e.execCommand("unlink")}})}e.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Meta+K",onclick:t(r),stateSelector:"a[href]"}),e.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),e.addShortcut("Meta+K","",t(r)),e.addCommand("mceLink",t(r)),this.showDialog=r,e.addMenuItem("link",{icon:"link",text:"Insert/edit link",shortcut:"Meta+K",onclick:t(r),stateSelector:"a[href]",context:"insert",prependToContext:!0})});
|
||||
tinymce.PluginManager.add("link",function(a){function b(b){return function(){var c=a.settings.link_list;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):"function"==typeof c?c(b):b(c)}}function c(a,b,c){function d(a,c){return c=c||[],tinymce.each(a,function(a){var e={text:a.text||a.title};a.menu?e.menu=d(a.menu):(e.value=a.value,b&&b(e)),c.push(e)}),c}return d(a,c||[])}function d(b){function d(a){var b=l.find("#text");(!b.value()||a.lastControl&&b.value()==a.lastControl.text())&&b.value(a.control.text()),l.find("#href").value(a.control.value())}function e(b){var c=[];return tinymce.each(a.dom.select("a:not([href])"),function(a){var d=a.name||a.id;d&&c.push({text:d,value:"#"+d,selected:-1!=b.indexOf("#"+d)})}),c.length?(c.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:c,onselect:d}):void 0}function f(){!k&&0===u.text.length&&m&&this.parent().parent().find("#text")[0].value(this.value())}function g(b){var c=b.meta||{};o&&o.value(a.convertURL(this.value(),"href")),tinymce.each(b.meta,function(a,b){l.find("#"+b).value(a)}),c.text||f.call(this)}function h(a){var b=v.getContent();if(/</.test(b)&&(!/^<a [^>]+>[^<]+<\/a>$/.test(b)||-1==b.indexOf("href=")))return!1;if(a){var c,d=a.childNodes;if(0===d.length)return!1;for(c=d.length-1;c>=0;c--)if(3!=d[c].nodeType)return!1}return!0}var i,j,k,l,m,n,o,p,q,r,s,t,u={},v=a.selection,w=a.dom;i=v.getNode(),j=w.getParent(i,"a[href]"),m=h(),u.text=k=j?j.innerText||j.textContent:v.getContent({format:"text"}),u.href=j?w.getAttrib(j,"href"):"",j?u.target=w.getAttrib(j,"target"):a.settings.default_link_target&&(u.target=a.settings.default_link_target),(t=w.getAttrib(j,"rel"))&&(u.rel=t),(t=w.getAttrib(j,"class"))&&(u["class"]=t),(t=w.getAttrib(j,"title"))&&(u.title=t),m&&(n={name:"text",type:"textbox",size:40,label:"Text to display",onchange:function(){u.text=this.value()}}),b&&(o={type:"listbox",label:"Link list",values:c(b,function(b){b.value=a.convertURL(b.value||b.url,"href")},[{text:"None",value:""}]),onselect:d,value:a.convertURL(u.href,"href"),onPostRender:function(){o=this}}),a.settings.target_list!==!1&&(a.settings.target_list||(a.settings.target_list=[{text:"None",value:""},{text:"New window",value:"_blank"}]),q={name:"target",type:"listbox",label:"Target",values:c(a.settings.target_list)}),a.settings.rel_list&&(p={name:"rel",type:"listbox",label:"Rel",values:c(a.settings.rel_list)}),a.settings.link_class_list&&(r={name:"class",type:"listbox",label:"Class",values:c(a.settings.link_class_list,function(b){b.value&&(b.textStyle=function(){return a.formatter.getCssText({inline:"a",classes:[b.value]})})})}),a.settings.link_title!==!1&&(s={name:"title",type:"textbox",label:"Title",value:u.title}),l=a.windowManager.open({title:"Insert link",data:u,body:[{name:"href",type:"filepicker",filetype:"file",size:40,autofocus:!0,label:"Url",onchange:g,onkeyup:f},n,s,e(u.href),o,p,q,r],onSubmit:function(b){function c(b,c){var d=a.selection.getRng();tinymce.util.Delay.setEditorTimeout(a,function(){a.windowManager.confirm(b,function(b){a.selection.setRng(d),c(b)})})}function d(){var b={href:e,target:u.target?u.target:null,rel:u.rel?u.rel:null,"class":u["class"]?u["class"]:null,title:u.title?u.title:null};j?(a.focus(),m&&u.text!=k&&("innerText"in j?j.innerText=u.text:j.textContent=u.text),w.setAttribs(j,b),v.select(j),a.undoManager.add()):m?a.insertContent(w.createHTML("a",b,w.encode(u.text))):a.execCommand("mceInsertLink",!1,b)}var e;return u=tinymce.extend(u,b.data),(e=u.href)?e.indexOf("@")>0&&-1==e.indexOf("//")&&-1==e.indexOf("mailto:")?void c("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(a){a&&(e="mailto:"+e),d()}):a.settings.link_assume_external_targets&&!/^\w+:/i.test(e)||!a.settings.link_assume_external_targets&&/^\s*www[\.|\d\.]/i.test(e)?void c("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(a){a&&(e="http://"+e),d()}):void d():void a.execCommand("unlink")}})}a.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Meta+K",onclick:b(d),stateSelector:"a[href]"}),a.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),a.addShortcut("Meta+K","",b(d)),a.addCommand("mceLink",b(d)),this.showDialog=d,a.addMenuItem("link",{icon:"link",text:"Insert/edit link",shortcut:"Meta+K",onclick:b(d),stateSelector:"a[href]",context:"insert",prependToContext:!0})});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("nonbreaking",function(e){var t=e.getParam("nonbreaking_force_tab");if(e.addCommand("mceNonBreaking",function(){e.insertContent(e.plugins.visualchars&&e.plugins.visualchars.state?'<span class="mce-nbsp"> </span>':" "),e.dom.setAttrib(e.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),e.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),e.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),t){var n=+t>1?+t:3;e.on("keydown",function(t){if(9==t.keyCode){if(t.shiftKey)return;t.preventDefault();for(var r=0;n>r;r++)e.execCommand("mceNonBreaking")}})}});
|
||||
tinymce.PluginManager.add("nonbreaking",function(a){var b=a.getParam("nonbreaking_force_tab");if(a.addCommand("mceNonBreaking",function(){a.insertContent(a.plugins.visualchars&&a.plugins.visualchars.state?'<span class="mce-nbsp"> </span>':" "),a.dom.setAttrib(a.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),a.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),a.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),b){var c=+b>1?+b:3;a.on("keydown",function(b){if(9==b.keyCode){if(b.shiftKey)return;b.preventDefault();for(var d=0;c>d;d++)a.execCommand("mceNonBreaking")}})}});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("noneditable",function(e){function t(e){return function(t){return-1!==(" "+t.attr("class")+" ").indexOf(e)}}function n(t){function n(t){var n=arguments,r=n[n.length-2];return r>0&&'"'==a.charAt(r-1)?t:'<span class="'+s+'" data-mce-content="'+e.dom.encode(n[0])+'">'+e.dom.encode("string"==typeof n[1]?n[1]:n[0])+"</span>"}var r=o.length,a=t.content,s=tinymce.trim(i);if("raw"!=t.format){for(;r--;)a=a.replace(o[r],n);t.content=a}}var r,i,o,a="contenteditable";r=" "+tinymce.trim(e.getParam("noneditable_editable_class","mceEditable"))+" ",i=" "+tinymce.trim(e.getParam("noneditable_noneditable_class","mceNonEditable"))+" ";var s=t(r),l=t(i);o=e.getParam("noneditable_regexp"),o&&!o.length&&(o=[o]),e.on("PreInit",function(){o&&e.on("BeforeSetContent",n),e.parser.addAttributeFilter("class",function(e){for(var t,n=e.length;n--;)t=e[n],s(t)?t.attr(a,"true"):l(t)&&t.attr(a,"false")}),e.serializer.addAttributeFilter(a,function(e){for(var t,n=e.length;n--;)t=e[n],(s(t)||l(t))&&(o&&t.attr("data-mce-content")?(t.name="#text",t.type=3,t.raw=!0,t.value=t.attr("data-mce-content")):t.attr(a,null))})})});
|
||||
tinymce.PluginManager.add("noneditable",function(a){function b(a){return function(b){return-1!==(" "+b.attr("class")+" ").indexOf(a)}}function c(b){function c(b){var c=arguments,d=c[c.length-2];return d>0&&'"'==g.charAt(d-1)?b:'<span class="'+h+'" data-mce-content="'+a.dom.encode(c[0])+'">'+a.dom.encode("string"==typeof c[1]?c[1]:c[0])+"</span>"}var d=f.length,g=b.content,h=tinymce.trim(e);if("raw"!=b.format){for(;d--;)g=g.replace(f[d],c);b.content=g}}var d,e,f,g="contenteditable";d=" "+tinymce.trim(a.getParam("noneditable_editable_class","mceEditable"))+" ",e=" "+tinymce.trim(a.getParam("noneditable_noneditable_class","mceNonEditable"))+" ";var h=b(d),i=b(e);f=a.getParam("noneditable_regexp"),f&&!f.length&&(f=[f]),a.on("PreInit",function(){f&&a.on("BeforeSetContent",c),a.parser.addAttributeFilter("class",function(a){for(var b,c=a.length;c--;)b=a[c],h(b)?b.attr(g,"true"):i(b)&&b.attr(g,"false")}),a.serializer.addAttributeFilter(g,function(a){for(var b,c=a.length;c--;)b=a[c],(h(b)||i(b))&&(f&&b.attr("data-mce-content")?(b.name="#text",b.type=3,b.raw=!0,b.value=b.attr("data-mce-content")):b.attr(g,null))})})});
|
||||
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("pagebreak",function(e){var t="mce-pagebreak",n=e.getParam("pagebreak_separator","<!-- pagebreak -->"),r=new RegExp(n.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(e){return"\\"+e}),"gi"),i='<img src="'+tinymce.Env.transparentSrc+'" class="'+t+'" data-mce-resize="false" data-mce-placeholder />';e.addCommand("mcePageBreak",function(){e.settings.pagebreak_split_block?e.insertContent("<p>"+i+"</p>"):e.insertContent(i)}),e.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),e.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),e.on("ResolveName",function(n){"IMG"==n.target.nodeName&&e.dom.hasClass(n.target,t)&&(n.name="pagebreak")}),e.on("click",function(n){n=n.target,"IMG"===n.nodeName&&e.dom.hasClass(n,t)&&e.selection.select(n)}),e.on("BeforeSetContent",function(e){e.content=e.content.replace(r,i)}),e.on("PreInit",function(){e.serializer.addNodeFilter("img",function(t){for(var r,i,o=t.length;o--;)if(r=t[o],i=r.attr("class"),i&&-1!==i.indexOf("mce-pagebreak")){var a=r.parent;if(e.schema.getBlockElements()[a.name]&&e.settings.pagebreak_split_block){a.type=3,a.value=n,a.raw=!0,r.remove();continue}r.type=3,r.value=n,r.raw=!0}})})});
|
||||
tinymce.PluginManager.add("pagebreak",function(a){var b="mce-pagebreak",c=a.getParam("pagebreak_separator","<!-- pagebreak -->"),d=new RegExp(c.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(a){return"\\"+a}),"gi"),e='<img src="'+tinymce.Env.transparentSrc+'" class="'+b+'" data-mce-resize="false" data-mce-placeholder />';a.addCommand("mcePageBreak",function(){a.settings.pagebreak_split_block?a.insertContent("<p>"+e+"</p>"):a.insertContent(e)}),a.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),a.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),a.on("ResolveName",function(c){"IMG"==c.target.nodeName&&a.dom.hasClass(c.target,b)&&(c.name="pagebreak")}),a.on("click",function(c){c=c.target,"IMG"===c.nodeName&&a.dom.hasClass(c,b)&&a.selection.select(c)}),a.on("BeforeSetContent",function(a){a.content=a.content.replace(d,e)}),a.on("PreInit",function(){a.serializer.addNodeFilter("img",function(b){for(var d,e,f=b.length;f--;)if(d=b[f],e=d.attr("class"),e&&-1!==e.indexOf("mce-pagebreak")){var g=d.parent;if(a.schema.getBlockElements()[g.name]&&a.settings.pagebreak_split_block){g.type=3,g.value=c,g.raw=!0,d.remove();continue}d.type=3,d.value=c,d.raw=!0}})})});
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
tinymce.PluginManager.add("preview",function(e){var t=e.settings,n=!tinymce.Env.ie;e.addCommand("mcePreview",function(){e.windowManager.open({title:"Preview",width:parseInt(e.getParam("plugin_preview_width","650"),10),height:parseInt(e.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(n?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var r,i="";i+='<base href="'+e.documentBaseURI.getURI()+'">',tinymce.each(e.contentCSS,function(t){i+='<link type="text/css" rel="stylesheet" href="'+e.documentBaseURI.toAbsolute(t)+'">'});var o=t.body_id||"tinymce";-1!=o.indexOf("=")&&(o=e.getParam("body_id","","hash"),o=o[e.id]||o);var a=t.body_class||"";-1!=a.indexOf("=")&&(a=e.getParam("body_class","","hash"),a=a[e.id]||"");var s=e.settings.directionality?' dir="'+e.settings.directionality+'"':"";if(r="<!DOCTYPE html><html><head>"+i+'</head><body id="'+o+'" class="mce-content-body '+a+'"'+s+">"+e.getContent()+"</body></html>",n)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(r);else{var l=this.getEl("body").firstChild.contentWindow.document;l.open(),l.write(r),l.close()}}})}),e.addButton("preview",{title:"Preview",cmd:"mcePreview"}),e.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})});
|
||||
tinymce.PluginManager.add("preview",function(a){var b=a.settings,c=!tinymce.Env.ie;a.addCommand("mcePreview",function(){a.windowManager.open({title:"Preview",width:parseInt(a.getParam("plugin_preview_width","650"),10),height:parseInt(a.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(c?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var d,e="";e+='<base href="'+a.documentBaseURI.getURI()+'">',tinymce.each(a.contentCSS,function(b){e+='<link type="text/css" rel="stylesheet" href="'+a.documentBaseURI.toAbsolute(b)+'">'});var f=b.body_id||"tinymce";-1!=f.indexOf("=")&&(f=a.getParam("body_id","","hash"),f=f[a.id]||f);var g=b.body_class||"";-1!=g.indexOf("=")&&(g=a.getParam("body_class","","hash"),g=g[a.id]||"");var h=a.settings.directionality?' dir="'+a.settings.directionality+'"':"";if(d="<!DOCTYPE html><html><head>"+e+'</head><body id="'+f+'" class="mce-content-body '+g+'"'+h+">"+a.getContent()+"</body></html>",c)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(d);else{var i=this.getEl("body").firstChild.contentWindow.document;i.open(),i.write(d),i.close()}}})}),a.addButton("preview",{title:"Preview",cmd:"mcePreview"}),a.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user