From: Dan Brown Date: Thu, 29 Sep 2016 08:32:40 +0000 (+0100) Subject: Merge branch 'diff' of git://github.com/younes0/BookStack into younes0-diff X-Git-Tag: v0.13.0~1^2~14 X-Git-Url: http://source.bookstackapp.com/bookstack/commitdiff_plain/fff5bbcee458992443e3732fbcbbbe34f765fcc3?hp=c279c6e2af60274871a5e2d3082e8cae444ece18 Merge branch 'diff' of git://github.com/younes0/BookStack into younes0-diff --- diff --git a/.env.example b/.env.example index 5661cda22..e44a46ef3 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,10 @@ APP_ENV=production APP_DEBUG=false APP_KEY=SomeRandomString +# The below url has to be set if using social auth options +# or if you are not using BookStack at the root path of your domain. +# APP_URL=http://bookstack.dev + # Database details DB_HOST=localhost DB_DATABASE=database_database @@ -42,8 +46,6 @@ GITHUB_APP_ID=false GITHUB_APP_SECRET=false GOOGLE_APP_ID=false GOOGLE_APP_SECRET=false -# URL used for social login redirects, NO TRAILING SLASH -APP_URL=http://bookstack.dev # External services such as Gravatar DISABLE_EXTERNAL_SERVICES=false diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..4f9f4c480 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +### For Feature Requests +Desired Feature: + +### For Bug Reports +PHP Version: + +MySQL Version: + +Expected Behavior: + +Actual Behavior: diff --git a/.gitignore b/.gitignore index 69f0e2909..7417bbdd8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ Homestead.yaml /public/bower /storage/images _ide_helper.php -/storage/debugbar \ No newline at end of file +/storage/debugbar +.phpstorm.meta.php \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 83e9e10f5..e2eb5f511 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,6 @@ php: cache: directories: - - vendor - - node_modules - $HOME/.composer/cache addons: @@ -17,19 +15,17 @@ addons: - mysql-client-core-5.6 - mysql-client-5.6 -before_install: - - npm install -g npm@latest - before_script: - mysql -u root -e 'create database `bookstack-test`;' - composer config -g github-oauth.github.com $GITHUB_ACCESS_TOKEN - phpenv config-rm xdebug.ini - composer self-update + - composer dump-autoload --no-interaction - composer install --prefer-dist --no-interaction - - npm install - - ./node_modules/.bin/gulp + - php artisan clear-compiled -n + - php artisan optimize -n - php artisan migrate --force -n --database=mysql_testing - php artisan db:seed --force -n --class=DummyContentSeeder --database=mysql_testing script: - - vendor/bin/phpunit \ No newline at end of file + - phpunit \ No newline at end of file diff --git a/app/Book.php b/app/Book.php index 919af80a5..aa2dee9c0 100644 --- a/app/Book.php +++ b/app/Book.php @@ -7,11 +7,15 @@ class Book extends Entity /** * Get the url for this book. + * @param string|bool $path * @return string */ - public function getUrl() + public function getUrl($path = false) { - return '/books/' . $this->slug; + if ($path !== false) { + return baseUrl('/books/' . $this->slug . '/' . trim($path, '/')); + } + return baseUrl('/books/' . $this->slug); } /* diff --git a/app/Chapter.php b/app/Chapter.php index 08faef68e..8f0453172 100644 --- a/app/Chapter.php +++ b/app/Chapter.php @@ -25,12 +25,16 @@ class Chapter extends Entity /** * Get the url of this chapter. + * @param string|bool $path * @return string */ - public function getUrl() + public function getUrl($path = false) { $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug; - return '/books/' . $bookSlug. '/chapter/' . $this->slug; + if ($path !== false) { + return baseUrl('/books/' . $bookSlug. '/chapter/' . $this->slug . '/' . trim($path, '/')); + } + return baseUrl('/books/' . $bookSlug. '/chapter/' . $this->slug); } /** diff --git a/app/EmailConfirmation.php b/app/EmailConfirmation.php deleted file mode 100644 index e77b754bb..000000000 --- a/app/EmailConfirmation.php +++ /dev/null @@ -1,16 +0,0 @@ -belongsTo(User::class); - } - -} diff --git a/app/Entity.php b/app/Entity.php index 1342c2997..8a15b5aa4 100644 --- a/app/Entity.php +++ b/app/Entity.php @@ -160,43 +160,49 @@ class Entity extends Ownable public function fullTextSearchQuery($fieldsToSearch, $terms, $wheres = []) { $exactTerms = []; - if (count($terms) === 0) { - $search = $this; - $orderBy = 'updated_at'; - } else { - 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; + $fuzzyTerms = []; + $search = static::newQuery(); + + foreach ($terms as $key => $term) { + $term = htmlentities($term, ENT_QUOTES); + $term = preg_replace('/[+\-><\(\)~*\"@]+/', ' ', $term); + if (preg_match('/".*?"/', $term) || is_numeric($term)) { + $term = str_replace('"', '', $term); + $exactTerms[] = '%' . $term . '%'; + } else { + $term = '' . $term . '*'; + if ($term !== '*') $fuzzyTerms[] = $term; } - $termString = implode(' ', $terms); + } + + $isFuzzy = count($exactTerms) === 0 || count($fuzzyTerms) > 0; + + // Perform fulltext search if relevant terms exist. + if ($isFuzzy) { + $termString = implode(' ', $fuzzyTerms); $fields = implode(',', $fieldsToSearch); - $search = static::selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]); + $search = $search->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); - } + // 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); } - }); - } - $orderBy = 'title_relevance'; - }; + } + }); + } + + $orderBy = $isFuzzy ? 'title_relevance' : 'updated_at'; // Add additional where terms foreach ($wheres as $whereTerm) { $search->where($whereTerm[0], $whereTerm[1], $whereTerm[2]); } + // Load in relations if ($this->isA('page')) { $search = $search->with('book', 'chapter', 'createdBy', 'updatedBy'); @@ -206,5 +212,5 @@ class Entity extends Ownable return $search->orderBy($orderBy, 'desc'); } - + } diff --git a/app/Events/Event.php b/app/Events/Event.php deleted file mode 100644 index dfe173828..000000000 --- a/app/Events/Event.php +++ /dev/null @@ -1,8 +0,0 @@ -message); - return response()->redirectTo($e->redirectLocation); + if ($this->isExceptionType($e, NotifyException::class)) { + session()->flash('error', $this->getOriginalMessage($e)); + return redirect($e->redirectLocation); } // Handle pretty exceptions which will show a friendly application-fitting page // Which will include the basic message to point the user roughly to the cause. - if (($e instanceof PrettyException || $e->getPrevious() instanceof PrettyException) && !config('app.debug')) { - $message = ($e instanceof PrettyException) ? $e->getMessage() : $e->getPrevious()->getMessage(); + if ($this->isExceptionType($e, PrettyException::class) && !config('app.debug')) { + $message = $this->getOriginalMessage($e); $code = ($e->getCode() === 0) ? 500 : $e->getCode(); return response()->view('errors/' . $code, ['message' => $message], $code); } return parent::render($request, $e); } + + /** + * Check the exception chain to compare against the original exception type. + * @param Exception $e + * @param $type + * @return bool + */ + protected function isExceptionType(Exception $e, $type) { + do { + if (is_a($e, $type)) return true; + } while ($e = $e->getPrevious()); + return false; + } + + /** + * Get original exception message. + * @param Exception $e + * @return string + */ + protected function getOriginalMessage(Exception $e) { + do { + $message = $e->getMessage(); + } while ($e = $e->getPrevious()); + return $message; + } + + /** + * Convert an authentication exception into an unauthenticated response. + * + * @param \Illuminate\Http\Request $request + * @param \Illuminate\Auth\AuthenticationException $exception + * @return \Illuminate\Http\Response + */ + protected function unauthenticated($request, AuthenticationException $exception) + { + if ($request->expectsJson()) { + return response()->json(['error' => 'Unauthenticated.'], 401); + } + + return redirect()->guest('login'); + } } diff --git a/app/Exceptions/PrettyException.php b/app/Exceptions/PrettyException.php index d92acf831..889252006 100644 --- a/app/Exceptions/PrettyException.php +++ b/app/Exceptions/PrettyException.php @@ -1,5 +1,3 @@ middleware('guest'); + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php new file mode 100644 index 000000000..0de4a8282 --- /dev/null +++ b/app/Http/Controllers/Auth/LoginController.php @@ -0,0 +1,123 @@ +middleware('guest', ['only' => ['getLogin', 'postLogin']]); + $this->socialAuthService = $socialAuthService; + $this->userRepo = $userRepo; + $this->redirectPath = baseUrl('/'); + $this->redirectAfterLogout = baseUrl('/login'); + parent::__construct(); + } + + public function username() + { + return config('auth.method') === 'standard' ? 'email' : 'username'; + } + + /** + * 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 Authenticatable $user + * @return \Illuminate\Http\RedirectResponse + * @throws AuthException + */ + protected function authenticated(Request $request, Authenticatable $user) + { + // Explicitly log them out for now if they do no exist. + if (!$user->exists) auth()->logout($user); + + if (!$user->exists && $user->email === null && !$request->has('email')) { + $request->flash(); + session()->flash('request-email', true); + return redirect('/login'); + } + + if (!$user->exists && $user->email === null && $request->has('email')) { + $user->email = $request->get('email'); + } + + if (!$user->exists) { + + // Check for users with same email already + $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0; + if ($alreadyUser) { + throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.'); + } + + $user->save(); + $this->userRepo->attachDefaultRole($user); + auth()->login($user); + } + + $path = session()->pull('url.intended', '/'); + $path = baseUrl($path, true); + return redirect($path); + } + + /** + * Show the application login form. + * @return \Illuminate\Http\Response + */ + public function getLogin() + { + $socialDrivers = $this->socialAuthService->getActiveDrivers(); + $authMethod = config('auth.method'); + return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]); + } + + /** + * Redirect to the relevant social site. + * @param $socialDriver + * @return \Symfony\Component\HttpFoundation\RedirectResponse + */ + public function getSocialLogin($socialDriver) + { + session()->put('social-callback', 'login'); + return $this->socialAuthService->startLogIn($socialDriver); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/RegisterController.php similarity index 67% rename from app/Http/Controllers/Auth/AuthController.php rename to app/Http/Controllers/Auth/RegisterController.php index beb191d62..6bba6de04 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -2,63 +2,67 @@ namespace BookStack\Http\Controllers\Auth; -use BookStack\Exceptions\AuthException; -use BookStack\Exceptions\PrettyException; -use Illuminate\Contracts\Auth\Authenticatable; -use Illuminate\Http\Request; -use BookStack\Exceptions\SocialSignInException; +use BookStack\Exceptions\ConfirmationEmailException; use BookStack\Exceptions\UserRegistrationException; use BookStack\Repos\UserRepo; use BookStack\Services\EmailConfirmationService; use BookStack\Services\SocialAuthService; -use BookStack\SocialAccount; +use BookStack\User; +use Exception; +use Illuminate\Http\Request; +use Illuminate\Http\Response; use Validator; use BookStack\Http\Controllers\Controller; -use Illuminate\Foundation\Auth\ThrottlesLogins; -use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; +use Illuminate\Foundation\Auth\RegistersUsers; -class AuthController extends Controller +class RegisterController extends Controller { /* |-------------------------------------------------------------------------- - | Registration & Login Controller + | Register Controller |-------------------------------------------------------------------------- | - | This controller handles the registration of new users, as well as the - | authentication of existing users. By default, this controller uses - | a simple trait to add these behaviors. Why don't you explore it? + | This controller handles the registration of new users as well as their + | validation and creation. By default this controller uses a trait to + | provide this functionality without requiring any additional code. | */ - use AuthenticatesAndRegistersUsers, ThrottlesLogins; - - protected $redirectPath = '/'; - protected $redirectAfterLogout = '/login'; - protected $username = 'email'; - + use RegistersUsers; protected $socialAuthService; protected $emailConfirmationService; protected $userRepo; /** - * Create a new authentication controller instance. + * Where to redirect users after login / registration. + * + * @var string + */ + protected $redirectTo = '/'; + protected $redirectPath = '/'; + + /** + * Create a new controller instance. + * * @param SocialAuthService $socialAuthService * @param EmailConfirmationService $emailConfirmationService * @param UserRepo $userRepo */ public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo) { - $this->middleware('guest', ['only' => ['getLogin', 'postLogin', 'getRegister', 'postRegister']]); + $this->middleware('guest'); $this->socialAuthService = $socialAuthService; $this->emailConfirmationService = $emailConfirmationService; $this->userRepo = $userRepo; - $this->username = config('auth.method') === 'standard' ? 'email' : 'username'; + $this->redirectTo = baseUrl('/'); + $this->redirectPath = baseUrl('/'); parent::__construct(); } /** * Get a validator for an incoming registration request. + * * @param array $data * @return \Illuminate\Contracts\Validation\Validator */ @@ -71,6 +75,10 @@ class AuthController extends Controller ]); } + /** + * Check whether or not registrations are allowed in the app settings. + * @throws UserRegistrationException + */ protected function checkRegistrationAllowed() { if (!setting('registration-enabled')) { @@ -80,7 +88,7 @@ class AuthController extends Controller /** * Show the application registration form. - * @return \Illuminate\Http\Response + * @return Response */ public function getRegister() { @@ -91,9 +99,10 @@ class AuthController extends Controller /** * Handle a registration request for the application. - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @param Request|\Illuminate\Http\Request $request + * @return Response * @throws UserRegistrationException + * @throws \Illuminate\Foundation\Validation\ValidationException */ public function postRegister(Request $request) { @@ -110,64 +119,18 @@ class AuthController extends Controller return $this->registerUser($userData); } - /** - * 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 Authenticatable $user - * @return \Illuminate\Http\RedirectResponse - * @throws AuthException + * Create a new user instance after a valid registration. + * @param array $data + * @return User */ - protected function authenticated(Request $request, Authenticatable $user) + protected function create(array $data) { - // Explicitly log them out for now if they do no exist. - if (!$user->exists) auth()->logout($user); - - if (!$user->exists && $user->email === null && !$request->has('email')) { - $request->flash(); - session()->flash('request-email', true); - return redirect('/login'); - } - - if (!$user->exists && $user->email === null && $request->has('email')) { - $user->email = $request->get('email'); - } - - if (!$user->exists) { - - // Check for users with same email already - $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0; - if ($alreadyUser) { - throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.'); - } - - $user->save(); - $this->userRepo->attachDefaultRole($user); - auth()->login($user); - } - - return redirect()->intended($this->redirectPath()); - } - - /** - * Register a new user after a registration callback. - * @param $socialDriver - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - * @throws UserRegistrationException - */ - protected function socialRegisterCallback($socialDriver) - { - $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver); - $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser); - - // Create an array of the user data to create a new user instance - $userData = [ - 'name' => $socialUser->getName(), - 'email' => $socialUser->getEmail(), - 'password' => str_random(30) - ]; - return $this->registerUser($userData, $socialAccount); + return User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + ]); } /** @@ -176,7 +139,7 @@ class AuthController extends Controller * @param bool|false|SocialAccount $socialAccount * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @throws UserRegistrationException - * @throws \BookStack\Exceptions\ConfirmationEmailException + * @throws ConfirmationEmailException */ protected function registerUser(array $userData, $socialAccount = false) { @@ -195,7 +158,13 @@ class AuthController extends Controller if (setting('registration-confirmation') || setting('registration-restrict')) { $newUser->save(); - $this->emailConfirmationService->sendConfirmation($newUser); + + try { + $this->emailConfirmationService->sendConfirmation($newUser); + } catch (Exception $e) { + session()->flash('error', trans('auth.email_confirm_send_error')); + } + return redirect('/register/confirm'); } @@ -213,18 +182,6 @@ class AuthController extends Controller return view('auth/register-confirm'); } - /** - * View the confirmation email as a standard web page. - * @param $token - * @return \Illuminate\View\View - * @throws UserRegistrationException - */ - public function viewConfirmEmail($token) - { - $confirmation = $this->emailConfirmationService->getEmailConfirmationFromToken($token); - return view('emails/email-confirmation', ['token' => $confirmation->token]); - } - /** * Confirms an email via a token and logs the user into the system. * @param $token @@ -237,8 +194,8 @@ class AuthController extends Controller $user = $confirmation->user; $user->email_confirmed = true; $user->save(); - auth()->login($confirmation->user); - session()->flash('success', 'Your email has been confirmed!'); + auth()->login($user); + session()->flash('success', trans('auth.email_confirm_success')); $this->emailConfirmationService->deleteConfirmationsByUser($user); return redirect($this->redirectPath); } @@ -264,31 +221,17 @@ class AuthController extends Controller 'email' => 'required|email|exists:users,email' ]); $user = $this->userRepo->getByEmail($request->get('email')); - $this->emailConfirmationService->sendConfirmation($user); - session()->flash('success', 'Confirmation email resent, Please check your inbox.'); - return redirect('/register/confirm'); - } - /** - * Show the application login form. - * @return \Illuminate\Http\Response - */ - public function getLogin() - { - $socialDrivers = $this->socialAuthService->getActiveDrivers(); - $authMethod = config('auth.method'); - return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]); - } + try { + $this->emailConfirmationService->sendConfirmation($user); + } catch (Exception $e) { + session()->flash('error', trans('auth.email_confirm_send_error')); + return redirect('/register/confirm'); + } - /** - * Redirect to the relevant social site. - * @param $socialDriver - * @return \Symfony\Component\HttpFoundation\RedirectResponse - */ - public function getSocialLogin($socialDriver) - { - session()->put('social-callback', 'login'); - return $this->socialAuthService->startLogIn($socialDriver); + $this->emailConfirmationService->sendConfirmation($user); + session()->flash('success', trans('auth.email_confirm_resent')); + return redirect('/register/confirm'); } /** @@ -334,4 +277,25 @@ class AuthController extends Controller return $this->socialAuthService->detachSocialAccount($socialDriver); } -} + /** + * Register a new user after a registration callback. + * @param $socialDriver + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws UserRegistrationException + */ + protected function socialRegisterCallback($socialDriver) + { + $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver); + $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser); + + // Create an array of the user data to create a new user instance + $userData = [ + 'name' => $socialUser->getName(), + 'email' => $socialUser->getEmail(), + 'password' => str_random(30) + ]; + return $this->registerUser($userData, $socialAccount); + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php similarity index 82% rename from app/Http/Controllers/Auth/PasswordController.php rename to app/Http/Controllers/Auth/ResetPasswordController.php index 038e444d2..656b8cc42 100644 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -5,7 +5,7 @@ namespace BookStack\Http\Controllers\Auth; use BookStack\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ResetsPasswords; -class PasswordController extends Controller +class ResetPasswordController extends Controller { /* |-------------------------------------------------------------------------- @@ -20,13 +20,14 @@ class PasswordController extends Controller use ResetsPasswords; - protected $redirectTo = '/'; - /** - * Create a new password controller instance. + * Create a new controller instance. + * + * @return void */ public function __construct() { $this->middleware('guest'); + parent::__construct(); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index 5b97fbdaf..8ada59433 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -3,7 +3,6 @@ use Activity; use BookStack\Repos\UserRepo; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use BookStack\Http\Requests; use BookStack\Repos\BookRepo; use BookStack\Repos\ChapterRepo; @@ -180,21 +179,31 @@ class BookController extends Controller return redirect($book->getUrl()); } - $sortedBooks = []; // Sort pages and chapters + $sortedBooks = []; + $updatedModels = collect(); $sortMap = json_decode($request->get('sort-tree')); $defaultBookId = $book->id; - foreach ($sortMap as $index => $bookChild) { - $id = $bookChild->id; + + // Loop through contents of provided map and update entities accordingly + foreach ($sortMap as $bookChild) { + $priority = $bookChild->sort; + $id = intval($bookChild->id); $isPage = $bookChild->type == 'page'; - $bookId = $this->bookRepo->exists($bookChild->book) ? $bookChild->book : $defaultBookId; + $bookId = $this->bookRepo->exists($bookChild->book) ? intval($bookChild->book) : $defaultBookId; + $chapterId = ($isPage && $bookChild->parentChapter === false) ? 0 : intval($bookChild->parentChapter); $model = $isPage ? $this->pageRepo->getById($id) : $this->chapterRepo->getById($id); - $isPage ? $this->pageRepo->changeBook($bookId, $model) : $this->chapterRepo->changeBook($bookId, $model); - $model->priority = $index; - if ($isPage) { - $model->chapter_id = ($bookChild->parentChapter === false) ? 0 : $bookChild->parentChapter; + + // Update models only if there's a change in parent chain or ordering. + if ($model->priority !== $priority || $model->book_id !== $bookId || ($isPage && $model->chapter_id !== $chapterId)) { + $isPage ? $this->pageRepo->changeBook($bookId, $model) : $this->chapterRepo->changeBook($bookId, $model); + $model->priority = $priority; + if ($isPage) $model->chapter_id = $chapterId; + $model->save(); + $updatedModels->push($model); } - $model->save(); + + // Store involved books to be sorted later if (!in_array($bookId, $sortedBooks)) { $sortedBooks[] = $bookId; } @@ -203,10 +212,12 @@ class BookController extends Controller // Add activity for books foreach ($sortedBooks as $bookId) { $updatedBook = $this->bookRepo->getById($bookId); - $this->bookRepo->updateBookPermissions($updatedBook); Activity::add($updatedBook, 'book_sort', $updatedBook->id); } + // Update permissions on changed models + $this->bookRepo->buildJointPermissions($updatedModels); + return redirect($book->getUrl()); } diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php index 3c9050bf6..03ec2c110 100644 --- a/app/Http/Controllers/ChapterController.php +++ b/app/Http/Controllers/ChapterController.php @@ -204,7 +204,7 @@ class ChapterController extends Controller return redirect()->back(); } - $this->chapterRepo->changeBook($parent->id, $chapter); + $this->chapterRepo->changeBook($parent->id, $chapter, true); Activity::add($chapter, 'chapter_move', $chapter->book->id); session()->flash('success', sprintf('Chapter moved to "%s"', $parent->name)); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 26eeb3002..43292d941 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -30,17 +30,22 @@ abstract class Controller extends BaseController */ public function __construct() { - // Get a user instance for the current user - $user = auth()->user(); - if (!$user) $user = User::getDefault(); + $this->middleware(function ($request, $next) { - // Share variables with views - view()->share('signedIn', auth()->check()); - view()->share('currentUser', $user); + // Get a user instance for the current user + $user = auth()->user(); + if (!$user) $user = User::getDefault(); - // Share variables with controllers - $this->currentUser = $user; - $this->signedIn = auth()->check(); + // Share variables with views + view()->share('signedIn', auth()->check()); + view()->share('currentUser', $user); + + // Share variables with controllers + $this->currentUser = $user; + $this->signedIn = auth()->check(); + + return $next($request); + }); } /** diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index fc71292e0..6081d1b27 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -43,7 +43,7 @@ class PageController extends Controller /** * Show the form for creating a new page. - * @param $bookSlug + * @param string $bookSlug * @param bool $chapterSlug * @return Response * @internal param bool $pageSlug @@ -62,8 +62,8 @@ class PageController extends Controller /** * Show form to continue editing a draft page. - * @param $bookSlug - * @param $pageId + * @param string $bookSlug + * @param int $pageId * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function editDraft($bookSlug, $pageId) @@ -113,8 +113,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 + * @param string $bookSlug + * @param string $pageSlug * @return Response */ public function show($bookSlug, $pageSlug) @@ -132,14 +132,17 @@ class PageController extends Controller $this->checkOwnablePermission('page-view', $page); $sidebarTree = $this->bookRepo->getChildren($book); + $pageNav = $this->pageRepo->getPageNav($page); + Views::add($page); $this->setPageTitle($page->getShortName()); - return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]); + return view('pages/show', ['page' => $page, 'book' => $book, + 'current' => $page, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav]); } /** * Get page from an ajax request. - * @param $pageId + * @param int $pageId * @return \Illuminate\Http\JsonResponse */ public function getPageAjax($pageId) @@ -150,8 +153,8 @@ class PageController extends Controller /** * Show the form for editing the specified page. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return Response */ public function edit($bookSlug, $pageSlug) @@ -186,8 +189,8 @@ class PageController extends Controller /** * Update the specified page in storage. * @param Request $request - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return Response */ public function update(Request $request, $bookSlug, $pageSlug) @@ -206,7 +209,7 @@ class PageController extends Controller /** * Save a draft update as a revision. * @param Request $request - * @param $pageId + * @param int $pageId * @return \Illuminate\Http\JsonResponse */ public function saveDraft(Request $request, $pageId) @@ -231,7 +234,7 @@ class PageController extends Controller /** * Redirect from a special link url which * uses the page id rather than the name. - * @param $pageId + * @param int $pageId * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function redirectFromLink($pageId) @@ -242,8 +245,8 @@ class PageController extends Controller /** * Show the deletion page for the specified page. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\View\View */ public function showDelete($bookSlug, $pageSlug) @@ -258,8 +261,8 @@ class PageController extends Controller /** * Show the deletion page for the specified page. - * @param $bookSlug - * @param $pageId + * @param string $bookSlug + * @param int $pageId * @return \Illuminate\View\View * @throws NotFoundException */ @@ -274,8 +277,8 @@ class PageController extends Controller /** * Remove the specified page from storage. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return Response * @internal param int $id */ @@ -292,8 +295,8 @@ class PageController extends Controller /** * Remove the specified draft page from storage. - * @param $bookSlug - * @param $pageId + * @param string $bookSlug + * @param int $pageId * @return Response * @throws NotFoundException */ @@ -309,8 +312,8 @@ class PageController extends Controller /** * Shows the last revisions for this page. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\View\View */ public function showRevisions($bookSlug, $pageSlug) @@ -323,9 +326,9 @@ class PageController extends Controller /** * Shows a preview of a single revision - * @param $bookSlug - * @param $pageSlug - * @param $revisionId + * @param string $bookSlug + * @param string $pageSlug + * @param int $revisionId * @return \Illuminate\View\View */ public function showRevision($bookSlug, $pageSlug, $revisionId) @@ -349,9 +352,9 @@ class PageController extends Controller /** * Restores a page using the content of the specified revision. - * @param $bookSlug - * @param $pageSlug - * @param $revisionId + * @param string $bookSlug + * @param string $pageSlug + * @param int $revisionId * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function restoreRevision($bookSlug, $pageSlug, $revisionId) @@ -367,8 +370,8 @@ class PageController extends Controller /** * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper. * https://github.com/barryvdh/laravel-dompdf - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\Http\Response */ public function exportPdf($bookSlug, $pageSlug) @@ -384,8 +387,8 @@ class PageController extends Controller /** * Export a page to a self-contained HTML file. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\Http\Response */ public function exportHtml($bookSlug, $pageSlug) @@ -401,8 +404,8 @@ class PageController extends Controller /** * Export a page to a simple plaintext .txt file. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\Http\Response */ public function exportPlainText($bookSlug, $pageSlug) @@ -422,7 +425,7 @@ class PageController extends Controller */ public function showRecentlyCreated() { - $pages = $this->pageRepo->getRecentlyCreatedPaginated(20); + $pages = $this->pageRepo->getRecentlyCreatedPaginated(20)->setPath(baseUrl('/pages/recently-created')); return view('pages/detailed-listing', [ 'title' => 'Recently Created Pages', 'pages' => $pages @@ -435,7 +438,7 @@ class PageController extends Controller */ public function showRecentlyUpdated() { - $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20); + $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20)->setPath(baseUrl('/pages/recently-updated')); return view('pages/detailed-listing', [ 'title' => 'Recently Updated Pages', 'pages' => $pages @@ -444,8 +447,8 @@ class PageController extends Controller /** * Show the Restrictions view. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function showRestrict($bookSlug, $pageSlug) @@ -462,8 +465,8 @@ class PageController extends Controller /** * Show the view to choose a new parent to move a page into. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @return mixed * @throws NotFoundException */ @@ -480,8 +483,8 @@ class PageController extends Controller /** * Does the action of moving the location of a page - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @param Request $request * @return mixed * @throws NotFoundException @@ -523,8 +526,8 @@ class PageController extends Controller /** * Set the permissions for this page. - * @param $bookSlug - * @param $pageSlug + * @param string $bookSlug + * @param string $pageSlug * @param Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 053d9ebd5..4c56516dc 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -3,6 +3,7 @@ namespace BookStack\Http\Controllers; use BookStack\Activity; +use Exception; use Illuminate\Http\Request; use Illuminate\Http\Response; @@ -100,9 +101,14 @@ class UserController extends Controller // Get avatar from gravatar and save if (!config('services.disable_services')) { - $avatar = \Images::saveUserGravatar($user); - $user->avatar()->associate($avatar); - $user->save(); + try { + $avatar = \Images::saveUserGravatar($user); + $user->avatar()->associate($avatar); + $user->save(); + } catch (Exception $e) { + \Log::error('Failed to save user gravatar image'); + } + } return redirect('/settings/users'); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index a1f2a581f..f1d95f5c0 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -9,15 +9,32 @@ class Kernel extends HttpKernel /** * The application's global HTTP middleware stack. * + * These middleware are run during every request to your application. + * * @var array */ protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, - \BookStack\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \BookStack\Http\Middleware\VerifyCsrfToken::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array + */ + protected $middlewareGroups = [ + 'web' => [ + \BookStack\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \BookStack\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + 'api' => [ + 'throttle:60,1', + 'bindings', + ], ]; /** @@ -26,6 +43,7 @@ class Kernel extends HttpKernel * @var array */ protected $routeMiddleware = [ + 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'auth' => \BookStack\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class, diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 599f40c84..8461ed0ba 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -33,14 +33,14 @@ class Authenticate public function handle($request, Closure $next) { if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) { - return redirect()->guest('/register/confirm/awaiting'); + return redirect(baseUrl('/register/confirm/awaiting')); } if ($this->auth->guest() && !setting('app-public')) { if ($request->ajax()) { return response('Unauthorized.', 401); } else { - return redirect()->guest('/login'); + return redirect()->guest(baseUrl('/login')); } } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index ab8ce4d3a..2b3c64695 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -34,7 +34,8 @@ class RedirectIfAuthenticated */ public function handle($request, Closure $next) { - if ($this->auth->check()) { + $requireConfirmation = setting('registration-confirmation'); + if ($this->auth->check() && (!$requireConfirmation || ($requireConfirmation && $this->auth->user()->email_confirmed))) { return redirect('/'); } diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php deleted file mode 100644 index 780af746b..000000000 --- a/app/Jobs/Job.php +++ /dev/null @@ -1,21 +0,0 @@ -token = $token; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + $appName = ['appName' => setting('app-name')]; + return (new MailMessage) + ->subject(trans('auth.email_confirm_subject', $appName)) + ->greeting(trans('auth.email_confirm_greeting', $appName)) + ->line(trans('auth.email_confirm_text')) + ->action(trans('auth.email_confirm_action'), baseUrl('/register/confirm/' . $this->token)); + } + +} diff --git a/app/Notifications/ResetPassword.php b/app/Notifications/ResetPassword.php new file mode 100644 index 000000000..646030a10 --- /dev/null +++ b/app/Notifications/ResetPassword.php @@ -0,0 +1,50 @@ +token = $token; + } + + /** + * Get the notification's channels. + * + * @param mixed $notifiable + * @return array|string + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Build the mail representation of the notification. + * + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail() + { + return (new MailMessage) + ->line('You are receiving this email because we received a password reset request for your account.') + ->action('Reset Password', baseUrl('password/reset/' . $this->token)) + ->line('If you did not request a password reset, no further action is required.'); + } +} diff --git a/app/Page.php b/app/Page.php index c6978d34b..1961a4f7f 100644 --- a/app/Page.php +++ b/app/Page.php @@ -56,14 +56,20 @@ class Page extends Entity /** * Get the url for this page. + * @param string|bool $path * @return string */ - public function getUrl() + public function getUrl($path = false) { $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug; $midText = $this->draft ? '/draft/' : '/page/'; $idComponent = $this->draft ? $this->id : $this->slug; - return '/books/' . $bookSlug . $midText . $idComponent; + + if ($path !== false) { + return baseUrl('/books/' . $bookSlug . $midText . $idComponent . '/' . trim($path, '/')); + } + + return baseUrl('/books/' . $bookSlug . $midText . $idComponent); } /** diff --git a/app/PageRevision.php b/app/PageRevision.php index 1baf0f8e5..e5721f5aa 100644 --- a/app/PageRevision.php +++ b/app/PageRevision.php @@ -3,7 +3,7 @@ class PageRevision extends Model { - protected $fillable = ['name', 'html', 'text', 'markdown']; + protected $fillable = ['name', 'html', 'text', 'markdown', 'summary']; /** * Get the user that created the page revision diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f214c9141..4665bf6c7 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -1,10 +1,6 @@ -id === (int) $userId; +// }); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index f754b87a9..3802f20c0 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -21,13 +21,10 @@ class EventServiceProvider extends ServiceProvider /** * Register any other events for your application. * - * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ - public function boot(DispatcherContract $events) + public function boot() { - parent::boot($events); - - // + parent::boot(); } } diff --git a/app/Providers/PaginationServiceProvider.php b/app/Providers/PaginationServiceProvider.php new file mode 100644 index 000000000..ec41267a4 --- /dev/null +++ b/app/Providers/PaginationServiceProvider.php @@ -0,0 +1,35 @@ +app['view']; + }); + + Paginator::currentPathResolver(function () { + return baseUrl($this->app['request']->path()); + }); + + Paginator::currentPageResolver(function ($pageName = 'page') { + $page = $this->app['request']->input($pageName); + + if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { + return $page; + } + + return 1; + }); + } +} \ No newline at end of file diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 2d9cd3b85..88ab23526 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -4,6 +4,7 @@ namespace BookStack\Providers; use Illuminate\Routing\Router; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; +use Route; class RouteServiceProvider extends ServiceProvider { @@ -19,26 +20,54 @@ class RouteServiceProvider extends ServiceProvider /** * Define your route model bindings, pattern filters, etc. * - * @param \Illuminate\Routing\Router $router * @return void */ - public function boot(Router $router) + public function boot() { - // - - parent::boot($router); + parent::boot(); } /** * Define the routes for the application. * - * @param \Illuminate\Routing\Router $router * @return void */ - public function map(Router $router) + public function map() + { + $this->mapWebRoutes(); +// $this->mapApiRoutes(); + } + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes() + { + Route::group([ + 'middleware' => 'web', + 'namespace' => $this->namespace, + ], function ($router) { + require base_path('routes/web.php'); + }); + } + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() { - $router->group(['namespace' => $this->namespace], function ($router) { - require app_path('Http/routes.php'); + Route::group([ + 'middleware' => 'api', + 'namespace' => $this->namespace, + 'prefix' => 'api', + ], function ($router) { + require base_path('routes/api.php'); }); } } diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php index a11ed2763..fdc4dd8d4 100644 --- a/app/Repos/BookRepo.php +++ b/app/Repos/BookRepo.php @@ -2,6 +2,7 @@ use Alpha\B; use BookStack\Exceptions\NotFoundException; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Str; use BookStack\Book; use Views; @@ -173,15 +174,6 @@ class BookRepo extends EntityRepo $book->delete(); } - /** - * Alias method to update the book jointPermissions in the PermissionService. - * @param Book $book - */ - public function updateBookPermissions(Book $book) - { - $this->permissionService->buildJointPermissionsForEntity($book); - } - /** * Get the next child element priority. * @param Book $book @@ -216,12 +208,10 @@ class BookRepo extends EntityRepo */ public function findSuitableSlug($name, $currentId = false) { - $originalSlug = Str::slug($name); - $slug = $originalSlug; - $count = 2; + $slug = Str::slug($name); + if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5); while ($this->doesSlugExist($slug, $currentId)) { - $slug = $originalSlug . '-' . $count; - $count++; + $slug .= '-' . substr(md5(rand(1, 500)), 0, 3); } return $slug; } @@ -229,7 +219,7 @@ class BookRepo extends EntityRepo /** * Get all child objects of a book. * Returns a sorted collection of Pages and Chapters. - * Loads the bookslug onto child elements to prevent access database access for getting the slug. + * Loads the book slug onto child elements to prevent access database access for getting the slug. * @param Book $book * @param bool $filterDrafts * @return mixed @@ -245,7 +235,7 @@ class BookRepo extends EntityRepo $pages = $pageQuery->get(); - $chapterQuery = $book->chapters()->with(['pages' => function($query) use ($filterDrafts) { + $chapterQuery = $book->chapters()->with(['pages' => function ($query) use ($filterDrafts) { $this->permissionService->enforcePageRestrictions($query, 'view'); if ($filterDrafts) $query->where('draft', '=', false); }]); @@ -263,7 +253,7 @@ class BookRepo extends EntityRepo $child->pages->each(function ($page) use ($bookSlug) { $page->setAttribute('bookSlug', $bookSlug); }); - $child->pages = $child->pages->sortBy(function($child, $key) { + $child->pages = $child->pages->sortBy(function ($child, $key) { $score = $child->priority; if ($child->draft) $score -= 100; return $score; @@ -272,7 +262,7 @@ class BookRepo extends EntityRepo }); // Sort items with drafts first then by priority. - return $children->sortBy(function($child, $key) { + return $children->sortBy(function ($child, $key) { $score = $child->priority; if ($child->isA('page') && $child->draft) $score -= 100; return $score; diff --git a/app/Repos/ChapterRepo.php b/app/Repos/ChapterRepo.php index 3c518bde9..c12a9f0f2 100644 --- a/app/Repos/ChapterRepo.php +++ b/app/Repos/ChapterRepo.php @@ -81,7 +81,7 @@ class ChapterRepo extends EntityRepo { $pages = $this->permissionService->enforcePageRestrictions($chapter->pages())->get(); // Sort items with drafts first then by priority. - return $pages->sortBy(function($child, $key) { + return $pages->sortBy(function ($child, $key) { $score = $child->priority; if ($child->draft) $score -= 100; return $score; @@ -151,6 +151,7 @@ class ChapterRepo extends EntityRepo public function findSuitableSlug($name, $bookId, $currentId = false) { $slug = Str::slug($name); + if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5); while ($this->doesSlugExist($slug, $bookId, $currentId)) { $slug .= '-' . substr(md5(rand(1, 500)), 0, 3); } @@ -194,11 +195,12 @@ class ChapterRepo extends EntityRepo /** * Changes the book relation of this chapter. - * @param $bookId + * @param $bookId * @param Chapter $chapter + * @param bool $rebuildPermissions * @return Chapter */ - public function changeBook($bookId, Chapter $chapter) + public function changeBook($bookId, Chapter $chapter, $rebuildPermissions = false) { $chapter->book_id = $bookId; // Update related activity @@ -212,9 +214,12 @@ class ChapterRepo extends EntityRepo foreach ($chapter->pages as $page) { $this->pageRepo->changeBook($bookId, $page); } - // Update permissions - $chapter->load('book'); - $this->permissionService->buildJointPermissionsForEntity($chapter->book); + + // Update permissions if applicable + if ($rebuildPermissions) { + $chapter->load('book'); + $this->permissionService->buildJointPermissionsForEntity($chapter->book); + } return $chapter; } diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php index 012a64967..c94601738 100644 --- a/app/Repos/EntityRepo.php +++ b/app/Repos/EntityRepo.php @@ -6,6 +6,7 @@ use BookStack\Entity; use BookStack\Page; use BookStack\Services\PermissionService; use BookStack\User; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; class EntityRepo @@ -168,15 +169,16 @@ class EntityRepo * @param $termString * @return array */ - protected function prepareSearchTerms($termString) + public function prepareSearchTerms($termString) { $termString = $this->cleanSearchTermString($termString); - preg_match_all('/"(.*?)"/', $termString, $matches); + preg_match_all('/(".*?")/', $termString, $matches); + $terms = []; if (count($matches[1]) > 0) { - $terms = $matches[1]; + foreach ($matches[1] as $match) { + $terms[] = $match; + } $termString = trim(preg_replace('/"(.*?)"/', '', $termString)); - } else { - $terms = []; } if (!empty($termString)) $terms = array_merge($terms, explode(' ', $termString)); return $terms; @@ -259,6 +261,15 @@ class EntityRepo return $query; } + /** + * Alias method to update the book jointPermissions in the PermissionService. + * @param Collection $collection collection on entities + */ + public function buildJointPermissions(Collection $collection) + { + $this->permissionService->buildJointPermissionsForEntities($collection); + } + } diff --git a/app/Repos/ImageRepo.php b/app/Repos/ImageRepo.php index 916ebd3e1..435b8bbd7 100644 --- a/app/Repos/ImageRepo.php +++ b/app/Repos/ImageRepo.php @@ -13,7 +13,7 @@ class ImageRepo protected $image; protected $imageService; - protected $restictionService; + protected $restrictionService; protected $page; /** @@ -27,7 +27,7 @@ class ImageRepo { $this->image = $image; $this->imageService = $imageService; - $this->restictionService = $permissionService; + $this->restrictionService = $permissionService; $this->page = $page; } @@ -52,7 +52,7 @@ class ImageRepo */ private function returnPaginated($query, $page = 0, $pageSize = 24) { - $images = $this->restictionService->filterRelatedPages($query, 'images', 'uploaded_to'); + $images = $this->restrictionService->filterRelatedPages($query, 'images', 'uploaded_to'); $images = $images->orderBy('created_at', 'desc')->skip($pageSize * $page)->take($pageSize + 1)->get(); $hasMore = count($images) > $pageSize; diff --git a/app/Repos/PageRepo.php b/app/Repos/PageRepo.php index de050e1c7..c64da1267 100644 --- a/app/Repos/PageRepo.php +++ b/app/Repos/PageRepo.php @@ -7,6 +7,7 @@ use BookStack\Entity; use BookStack\Exceptions\NotFoundException; use Carbon\Carbon; use DOMDocument; +use DOMXPath; use Illuminate\Support\Str; use BookStack\Page; use BookStack\PageRevision; @@ -110,31 +111,6 @@ class PageRepo extends EntityRepo return $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId)->count(); } - /** - * Save a new page into the system. - * Input validation must be done beforehand. - * @param array $input - * @param Book $book - * @param int $chapterId - * @return Page - */ - public function saveNew(array $input, Book $book, $chapterId = null) - { - $page = $this->newFromInput($input); - $page->slug = $this->findSuitableSlug($page->name, $book->id); - - if ($chapterId) $page->chapter_id = $chapterId; - - $page->html = $this->formatHtml($input['html']); - $page->text = strip_tags($page->html); - $page->created_by = auth()->user()->id; - $page->updated_by = auth()->user()->id; - - $book->pages()->save($page); - return $page; - } - - /** * Publish a draft page to make it a normal page. * Sets the slug and updates the content. @@ -147,7 +123,7 @@ class PageRepo extends EntityRepo $draftPage->fill($input); // Save page tags if present - if(isset($input['tags'])) { + if (isset($input['tags'])) { $this->tagRepo->saveTagsToEntity($draftPage, $input['tags']); } @@ -157,6 +133,8 @@ class PageRepo extends EntityRepo $draftPage->draft = false; $draftPage->save(); + $this->saveRevision($draftPage, 'Initial Publish'); + return $draftPage; } @@ -181,6 +159,35 @@ class PageRepo extends EntityRepo return $page; } + /** + * Parse te headers on the page to get a navigation menu + * @param Page $page + * @return array + */ + public function getPageNav(Page $page) + { + if ($page->html == '') return null; + libxml_use_internal_errors(true); + $doc = new DOMDocument(); + $doc->loadHTML(mb_convert_encoding($page->html, 'HTML-ENTITIES', 'UTF-8')); + $xPath = new DOMXPath($doc); + $headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6"); + + if (is_null($headers)) return null; + + $tree = []; + foreach ($headers as $header) { + $text = $header->nodeValue; + $tree[] = [ + 'nodeName' => strtolower($header->nodeName), + 'level' => intval(str_replace('h', '', $header->nodeName)), + 'link' => '#' . $header->getAttribute('id'), + 'text' => strlen($text) > 30 ? substr($text, 0, 27) . '...' : $text + ]; + } + return $tree; + } + /** * Formats a page's html to be tagged correctly * within the system. @@ -308,10 +315,9 @@ class PageRepo extends EntityRepo */ public function updatePage(Page $page, $book_id, $input) { - // Save a revision before updating - if ($page->html !== $input['html'] || $page->name !== $input['name']) { - $this->saveRevision($page); - } + // Hold the old details to compare later + $oldHtml = $page->html; + $oldName = $page->name; // Prevent slug being updated if no name change if ($page->name !== $input['name']) { @@ -319,7 +325,7 @@ class PageRepo extends EntityRepo } // Save page tags if present - if(isset($input['tags'])) { + if (isset($input['tags'])) { $this->tagRepo->saveTagsToEntity($page, $input['tags']); } @@ -335,6 +341,11 @@ class PageRepo extends EntityRepo // Remove all update drafts for this user & page. $this->userUpdateDraftsQuery($page, $userId)->delete(); + // Save a revision after updating + if ($oldHtml !== $input['html'] || $oldName !== $input['name'] || $input['summary'] !== null) { + $this->saveRevision($page, $input['summary']); + } + return $page; } @@ -360,11 +371,12 @@ class PageRepo extends EntityRepo /** * Saves a page revision into the system. * @param Page $page + * @param null|string $summary * @return $this */ - public function saveRevision(Page $page) + public function saveRevision(Page $page, $summary = null) { - $revision = $this->pageRevision->fill($page->toArray()); + $revision = $this->pageRevision->newInstance($page->toArray()); if (setting('app-editor') !== 'markdown') $revision->markdown = ''; $revision->page_id = $page->id; $revision->slug = $page->slug; @@ -372,12 +384,15 @@ class PageRepo extends EntityRepo $revision->created_by = auth()->user()->id; $revision->created_at = $page->updated_at; $revision->type = 'version'; + $revision->summary = $summary; $revision->save(); + // Clear old revisions if ($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) { $this->pageRevision->where('page_id', '=', $page->id) ->orderBy('created_at', 'desc')->skip(50)->take(5)->delete(); } + return $revision; } @@ -405,7 +420,7 @@ class PageRepo extends EntityRepo $draft->fill($data); if (setting('app-editor') !== 'markdown') $draft->markdown = ''; - + $draft->save(); return $draft; } @@ -591,14 +606,15 @@ class PageRepo extends EntityRepo /** * Gets a suitable slug for the resource - * @param $name - * @param $bookId + * @param string $name + * @param int $bookId * @param bool|false $currentId * @return string */ public function findSuitableSlug($name, $bookId, $currentId = false) { $slug = Str::slug($name); + if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5); while ($this->doesSlugExist($slug, $bookId, $currentId)) { $slug .= '-' . substr(md5(rand(1, 500)), 0, 3); } diff --git a/app/Repos/UserRepo.php b/app/Repos/UserRepo.php index 0926f6304..127db9fb5 100644 --- a/app/Repos/UserRepo.php +++ b/app/Repos/UserRepo.php @@ -2,6 +2,7 @@ use BookStack\Role; use BookStack\User; +use Exception; use Setting; class UserRepo @@ -84,9 +85,14 @@ class UserRepo // Get avatar from gravatar and save if (!config('services.disable_services')) { - $avatar = \Images::saveUserGravatar($user); - $user->avatar()->associate($avatar); - $user->save(); + try { + $avatar = \Images::saveUserGravatar($user); + $user->avatar()->associate($avatar); + $user->save(); + } catch (Exception $e) { + $user->save(); + \Log::error('Failed to save user gravatar image'); + } } return $user; diff --git a/app/Services/EmailConfirmationService.php b/app/Services/EmailConfirmationService.php index c3096c654..d4ec1e976 100644 --- a/app/Services/EmailConfirmationService.php +++ b/app/Services/EmailConfirmationService.php @@ -1,30 +1,27 @@ mailer = $mailer; - $this->emailConfirmation = $emailConfirmation; + $this->db = $db; + $this->users = $users; } /** @@ -38,16 +35,28 @@ class EmailConfirmationService if ($user->email_confirmed) { throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login'); } + $this->deleteConfirmationsByUser($user); + $token = $this->createEmailConfirmation($user); + + $user->notify(new ConfirmEmail($token)); + } + + /** + * Creates a new email confirmation in the database and returns the token. + * @param User $user + * @return string + */ + public function createEmailConfirmation(User $user) + { $token = $this->getToken(); - $this->emailConfirmation->create([ + $this->db->table('email_confirmations')->insert([ 'user_id' => $user->id, - 'token' => $token, + 'token' => $token, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now() ]); - $this->mailer->send('emails/email-confirmation', ['token' => $token], function (Message $message) use ($user) { - $appName = setting('app-name', 'BookStack'); - $message->to($user->email, $user->name)->subject('Confirm your email on ' . $appName . '.'); - }); + return $token; } /** @@ -59,22 +68,24 @@ class EmailConfirmationService */ public function getEmailConfirmationFromToken($token) { - $emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first(); - // If not found + $emailConfirmation = $this->db->table('email_confirmations')->where('token', '=', $token)->first(); + + // If not found show error if ($emailConfirmation === null) { throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register'); } // If more than a day old - if (Carbon::now()->subDay()->gt($emailConfirmation->created_at)) { - $this->sendConfirmation($emailConfirmation->user); + if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) { + $user = $this->users->getById($emailConfirmation->user_id); + $this->sendConfirmation($user); throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm'); } + $emailConfirmation->user = $this->users->getById($emailConfirmation->user_id); return $emailConfirmation; } - /** * Delete all email confirmations that belong to a user. * @param User $user @@ -82,7 +93,7 @@ class EmailConfirmationService */ public function deleteConfirmationsByUser(User $user) { - return $this->emailConfirmation->where('user_id', '=', $user->id)->delete(); + return $this->db->table('email_confirmations')->where('user_id', '=', $user->id)->delete(); } /** @@ -92,7 +103,7 @@ class EmailConfirmationService protected function getToken() { $token = str_random(24); - while ($this->emailConfirmation->where('token', '=', $token)->exists()) { + while ($this->db->table('email_confirmations')->where('token', '=', $token)->exists()) { $token = str_random(25); } return $token; diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php index 0497681e9..14084d320 100644 --- a/app/Services/ExportService.php +++ b/app/Services/ExportService.php @@ -48,11 +48,13 @@ class ExportService foreach ($imageTagsOutput[0] as $index => $imgMatch) { $oldImgString = $imgMatch; $srcString = $imageTagsOutput[2][$index]; - if (strpos(trim($srcString), 'http') !== 0) { - $pathString = public_path($srcString); + $isLocal = strpos(trim($srcString), 'http') !== 0; + if ($isLocal) { + $pathString = public_path(trim($srcString, '/')); } else { $pathString = $srcString; } + if ($isLocal && !file_exists($pathString)) continue; $imageContent = file_get_contents($pathString); $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent); $newImageString = str_replace($srcString, $imageEncoded, $oldImgString); diff --git a/app/Services/ImageService.php b/app/Services/ImageService.php index dd965c90f..aa1375487 100644 --- a/app/Services/ImageService.php +++ b/app/Services/ImageService.php @@ -95,6 +95,7 @@ class ImageService try { $storage->put($fullPath, $imageData); + $storage->setVisibility($fullPath, 'public'); } catch (Exception $e) { throw new ImageUploadException('Image Path ' . $fullPath . ' is not writable by the server.'); } @@ -167,6 +168,7 @@ class ImageService $thumbData = (string)$thumb->encode(); $storage->put($thumbFilePath, $thumbData); + $storage->setVisibility($thumbFilePath, 'public'); $this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 72); return $this->getPublicUrl($thumbFilePath); @@ -211,7 +213,7 @@ class ImageService public function saveUserGravatar(User $user, $size = 500) { $emailHash = md5(strtolower(trim($user->email))); - $url = 'http://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon'; + $url = 'https://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon'; $imageName = str_replace(' ', '-', $user->name . '-gravatar.png'); $image = $this->saveNewFromUrl($url, 'user', $imageName); $image->created_by = $user->id; @@ -257,16 +259,22 @@ class ImageService $storageUrl = config('filesystems.url'); // Get the standard public s3 url if s3 is set as storage type + // Uses the nice, short URL if bucket name has no periods in otherwise the longer + // region-based url will be used to prevent http issues. if ($storageUrl == false && config('filesystems.default') === 's3') { $storageDetails = config('filesystems.disks.s3'); - $storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket']; + if (strpos($storageDetails['bucket'], '.') === false) { + $storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com'; + } else { + $storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket']; + } } $this->storageUrl = $storageUrl; } - return ($this->storageUrl == false ? '' : rtrim($this->storageUrl, '/')) . $filePath; + return ($this->storageUrl == false ? rtrim(baseUrl(''), '/') : rtrim($this->storageUrl, '/')) . $filePath; } -} \ No newline at end of file +} diff --git a/app/Services/PermissionService.php b/app/Services/PermissionService.php index 0fffe60f2..341a69edb 100644 --- a/app/Services/PermissionService.php +++ b/app/Services/PermissionService.php @@ -8,15 +8,16 @@ use BookStack\Ownable; use BookStack\Page; use BookStack\Role; use BookStack\User; -use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; class PermissionService { - protected $userRoles; - protected $isAdmin; protected $currentAction; - protected $currentUser; + protected $isAdminUser; + protected $userRoles = false; + protected $currentUserModel = false; public $book; public $chapter; @@ -25,6 +26,8 @@ class PermissionService protected $jointPermission; protected $role; + protected $entityCache; + /** * PermissionService constructor. * @param JointPermission $jointPermission @@ -35,12 +38,6 @@ class PermissionService */ public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role) { - $this->currentUser = auth()->user(); - $userSet = $this->currentUser !== null; - $this->userRoles = false; - $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false; - if (!$userSet) $this->currentUser = new User(); - $this->jointPermission = $jointPermission; $this->role = $role; $this->book = $book; @@ -48,6 +45,57 @@ class PermissionService $this->page = $page; } + /** + * Prepare the local entity cache and ensure it's empty + */ + protected function readyEntityCache() + { + $this->entityCache = [ + 'books' => collect(), + 'chapters' => collect() + ]; + } + + /** + * Get a book via ID, Checks local cache + * @param $bookId + * @return Book + */ + protected function getBook($bookId) + { + if (isset($this->entityCache['books']) && $this->entityCache['books']->has($bookId)) { + return $this->entityCache['books']->get($bookId); + } + + $book = $this->book->find($bookId); + if ($book === null) $book = false; + if (isset($this->entityCache['books'])) { + $this->entityCache['books']->put($bookId, $book); + } + + return $book; + } + + /** + * Get a chapter via ID, Checks local cache + * @param $chapterId + * @return Book + */ + protected function getChapter($chapterId) + { + if (isset($this->entityCache['chapters']) && $this->entityCache['chapters']->has($chapterId)) { + return $this->entityCache['chapters']->get($chapterId); + } + + $chapter = $this->chapter->find($chapterId); + if ($chapter === null) $chapter = false; + if (isset($this->entityCache['chapters'])) { + $this->entityCache['chapters']->put($chapterId, $chapter); + } + + return $chapter; + } + /** * Get the roles for the current user; * @return array|bool @@ -64,7 +112,7 @@ class PermissionService } - foreach ($this->currentUser->roles as $role) { + foreach ($this->currentUser()->roles as $role) { $roles[] = $role->id; } return $roles; @@ -76,6 +124,7 @@ class PermissionService public function buildJointPermissions() { $this->jointPermission->truncate(); + $this->readyEntityCache(); // Get all roles (Should be the most limited dimension) $roles = $this->role->with('permissions')->get(); @@ -97,7 +146,7 @@ class PermissionService } /** - * Create the entity jointPermissions for a particular entity. + * Rebuild the entity jointPermissions for a particular entity. * @param Entity $entity */ public function buildJointPermissionsForEntity(Entity $entity) @@ -116,6 +165,17 @@ class PermissionService $this->createManyJointPermissions($entities, $roles); } + /** + * Rebuild the entity jointPermissions for a collection of entities. + * @param Collection $entities + */ + public function buildJointPermissionsForEntities(Collection $entities) + { + $roles = $this->role->with('jointPermissions')->get(); + $this->deleteManyJointPermissionsForEntities($entities); + $this->createManyJointPermissions($entities, $roles); + } + /** * Build the entity jointPermissions for a particular role. * @param Role $role @@ -177,9 +237,14 @@ class PermissionService */ protected function deleteManyJointPermissionsForEntities($entities) { + $query = $this->jointPermission->newQuery(); foreach ($entities as $entity) { - $entity->jointPermissions()->delete(); + $query->orWhere(function($query) use ($entity) { + $query->where('entity_id', '=', $entity->id) + ->where('entity_type', '=', $entity->getMorphClass()); + }); } + $query->delete(); } /** @@ -189,6 +254,7 @@ class PermissionService */ protected function createManyJointPermissions($entities, $roles) { + $this->readyEntityCache(); $jointPermissions = []; foreach ($entities as $entity) { foreach ($roles as $role) { @@ -248,8 +314,9 @@ class PermissionService } elseif ($entity->isA('chapter')) { if (!$entity->restricted) { - $hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $restrictionAction); - $hasPermissiveAccessToBook = !$entity->book->restricted; + $book = $this->getBook($entity->book_id); + $hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction); + $hasPermissiveAccessToBook = !$book->restricted; return $this->createJointPermissionDataArray($entity, $role, $action, ($hasExplicitAccessToBook || ($roleHasPermission && $hasPermissiveAccessToBook)), ($hasExplicitAccessToBook || ($roleHasPermissionOwn && $hasPermissiveAccessToBook))); @@ -261,11 +328,14 @@ class PermissionService } elseif ($entity->isA('page')) { if (!$entity->restricted) { - $hasExplicitAccessToBook = $entity->book->hasActiveRestriction($role->id, $restrictionAction); - $hasPermissiveAccessToBook = !$entity->book->restricted; - $hasExplicitAccessToChapter = $entity->chapter && $entity->chapter->hasActiveRestriction($role->id, $restrictionAction); - $hasPermissiveAccessToChapter = $entity->chapter && !$entity->chapter->restricted; - $acknowledgeChapter = ($entity->chapter && $entity->chapter->restricted); + $book = $this->getBook($entity->book_id); + $hasExplicitAccessToBook = $book->hasActiveRestriction($role->id, $restrictionAction); + $hasPermissiveAccessToBook = !$book->restricted; + + $chapter = $this->getChapter($entity->chapter_id); + $hasExplicitAccessToChapter = $chapter && $chapter->hasActiveRestriction($role->id, $restrictionAction); + $hasPermissiveAccessToChapter = $chapter && !$chapter->restricted; + $acknowledgeChapter = ($chapter && $chapter->restricted); $hasExplicitAccessToParents = $acknowledgeChapter ? $hasExplicitAccessToChapter : $hasExplicitAccessToBook; $hasPermissiveAccessToParents = $acknowledgeChapter ? $hasPermissiveAccessToChapter : $hasPermissiveAccessToBook; @@ -314,7 +384,11 @@ class PermissionService */ public function checkOwnableUserAccess(Ownable $ownable, $permission) { - if ($this->isAdmin) return true; + if ($this->isAdmin()) { + $this->clean(); + return true; + } + $explodedPermission = explode('-', $permission); $baseQuery = $ownable->where('id', '=', $ownable->id); @@ -325,10 +399,10 @@ class PermissionService // Handle non entity specific jointPermissions if (in_array($explodedPermission[0], $nonJointPermissions)) { - $allPermission = $this->currentUser && $this->currentUser->can($permission . '-all'); - $ownPermission = $this->currentUser && $this->currentUser->can($permission . '-own'); + $allPermission = $this->currentUser() && $this->currentUser()->can($permission . '-all'); + $ownPermission = $this->currentUser() && $this->currentUser()->can($permission . '-own'); $this->currentAction = 'view'; - $isOwner = $this->currentUser && $this->currentUser->id === $ownable->created_by; + $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->created_by; return ($allPermission || ($isOwner && $ownPermission)); } @@ -338,7 +412,9 @@ class PermissionService } - return $this->entityRestrictionQuery($baseQuery)->count() > 0; + $q = $this->entityRestrictionQuery($baseQuery)->count() > 0; + $this->clean(); + return $q; } /** @@ -368,7 +444,7 @@ class PermissionService */ protected function entityRestrictionQuery($query) { - return $query->where(function ($parentQuery) { + $q = $query->where(function ($parentQuery) { $parentQuery->whereHas('jointPermissions', function ($permissionQuery) { $permissionQuery->whereIn('role_id', $this->getRoles()) ->where('action', '=', $this->currentAction) @@ -376,11 +452,13 @@ class PermissionService $query->where('has_permission', '=', true) ->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); }); + $this->clean(); + return $q; } /** @@ -394,9 +472,9 @@ class PermissionService // Prevent drafts being visible to others. $query = $query->where(function ($query) { $query->where('draft', '=', false); - if ($this->currentUser) { + if ($this->currentUser()) { $query->orWhere(function ($query) { - $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); + $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id); }); } }); @@ -434,7 +512,10 @@ class PermissionService */ public function enforceEntityRestrictions($query, $action = 'view') { - if ($this->isAdmin) return $query; + if ($this->isAdmin()) { + $this->clean(); + return $query; + } $this->currentAction = $action; return $this->entityRestrictionQuery($query); } @@ -449,11 +530,15 @@ class PermissionService */ public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn) { - if ($this->isAdmin) return $query; + if ($this->isAdmin()) { + $this->clean(); + return $query; + } + $this->currentAction = 'view'; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn]; - return $query->where(function ($query) use ($tableDetails) { + $q = $query->where(function ($query) use ($tableDetails) { $query->whereExists(function ($permissionQuery) use (&$tableDetails) { $permissionQuery->select('id')->from('joint_permissions') ->whereRaw('joint_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) @@ -463,12 +548,12 @@ class PermissionService ->where(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); }); - + return $q; } /** @@ -480,11 +565,15 @@ class PermissionService */ public function filterRelatedPages($query, $tableName, $entityIdColumn) { - if ($this->isAdmin) return $query; + if ($this->isAdmin()) { + $this->clean(); + return $query; + } + $this->currentAction = 'view'; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn]; - return $query->where(function ($query) use ($tableDetails) { + $q = $query->where(function ($query) use ($tableDetails) { $query->where(function ($query) use (&$tableDetails) { $query->whereExists(function ($permissionQuery) use (&$tableDetails) { $permissionQuery->select('id')->from('joint_permissions') @@ -495,12 +584,50 @@ class PermissionService ->where(function ($query) { $query->where('has_permission', '=', true)->orWhere(function ($query) { $query->where('has_permission_own', '=', true) - ->where('created_by', '=', $this->currentUser->id); + ->where('created_by', '=', $this->currentUser()->id); }); }); }); })->orWhere($tableDetails['entityIdColumn'], '=', 0); }); + $this->clean(); + return $q; + } + + /** + * Check if the current user is an admin. + * @return bool + */ + private function isAdmin() + { + if ($this->isAdminUser === null) { + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false; + } + + return $this->isAdminUser; + } + + /** + * Get the current user + * @return User + */ + private function currentUser() + { + if ($this->currentUserModel === false) { + $this->currentUserModel = auth()->user() ? auth()->user() : new User(); + } + + return $this->currentUserModel; + } + + /** + * Clean the cached user elements. + */ + private function clean() + { + $this->currentUserModel = false; + $this->userRoles = false; + $this->isAdminUser = null; } } \ No newline at end of file diff --git a/app/Services/SocialAuthService.php b/app/Services/SocialAuthService.php index ba3479349..b28a97ea4 100644 --- a/app/Services/SocialAuthService.php +++ b/app/Services/SocialAuthService.php @@ -113,20 +113,20 @@ class SocialAuthService if ($isLoggedIn && $socialAccount === null) { $this->fillSocialAccount($socialDriver, $socialUser); $currentUser->socialAccounts()->save($this->socialAccount); - \Session::flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.'); + session()->flash('success', title_case($socialDriver) . ' account was successfully attached to your profile.'); return redirect($currentUser->getEditUrl()); } // When a user is logged in and the social account exists and is already linked to the current user. if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id === $currentUser->id) { - \Session::flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.'); + session()->flash('error', 'This ' . title_case($socialDriver) . ' account is already attached to your profile.'); return redirect($currentUser->getEditUrl()); } // When a user is logged in, A social account exists but the users do not match. // Change the user that the social account is assigned to. if ($isLoggedIn && $socialAccount !== null && $socialAccount->user->id != $currentUser->id) { - \Session::flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.'); + session()->flash('success', 'This ' . title_case($socialDriver) . ' account is already used by another user.'); return redirect($currentUser->getEditUrl()); } @@ -135,6 +135,7 @@ class SocialAuthService if (setting('registration-enabled')) { $message .= ' or, If you do not yet have an account, You can register an account using the ' . $socialDriver . ' option'; } + throw new SocialSignInException($message . '.', '/login'); } @@ -157,7 +158,7 @@ class SocialAuthService $driver = trim(strtolower($socialDriver)); if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found'); - if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured; + if (!$this->checkDriverConfigured($driver)) throw new SocialDriverNotConfigured("Your {$driver} social settings are not configured correctly."); return $driver; } @@ -214,7 +215,7 @@ class SocialAuthService { session(); auth()->user()->socialAccounts()->where('driver', '=', $socialDriver)->delete(); - \Session::flash('success', $socialDriver . ' account successfully detached'); + session()->flash('success', title_case($socialDriver) . ' account successfully detached'); return redirect(auth()->user()->getEditUrl()); } diff --git a/app/User.php b/app/User.php index 74aec7e3a..8c39d81be 100644 --- a/app/User.php +++ b/app/User.php @@ -1,13 +1,15 @@ image_id === 0 || $this->image_id === '0' || $this->image_id === null) return '/user_avatar.png'; - return $this->avatar->getThumb($size, $size, false); + if ($this->image_id === 0 || $this->image_id === '0' || $this->image_id === null) return baseUrl('/user_avatar.png'); + return baseUrl($this->avatar->getThumb($size, $size, false)); } /** @@ -157,7 +159,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon */ public function getEditUrl() { - return '/settings/users/' . $this->id; + return baseUrl('/settings/users/' . $this->id); + } + + /** + * Get the url that links to this user's profile. + * @return mixed + */ + public function getProfileUrl() + { + return baseUrl('/user/' . $this->id); } /** @@ -174,4 +185,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return ''; } + + /** + * Send the password reset notification. + * @param string $token + * @return void + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new ResetPassword($token)); + } } diff --git a/app/helpers.php b/app/helpers.php index 42e4c1894..dd835fbf6 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -2,33 +2,38 @@ use BookStack\Ownable; -if (!function_exists('versioned_asset')) { - /** - * Get the path to a versioned file. - * - * @param string $file - * @return string - * - * @throws \InvalidArgumentException - */ - function versioned_asset($file) - { - static $manifest = null; - - if (is_null($manifest)) { - $manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true); - } +/** + * Get the path to a versioned file. + * + * @param string $file + * @return string + * @throws Exception + */ +function versioned_asset($file = '') +{ + // Don't require css and JS assets for testing + if (config('app.env') === 'testing') return ''; - if (isset($manifest[$file])) { - return '/' . $manifest[$file]; - } + static $manifest = null; + $manifestPath = 'build/manifest.json'; - if (file_exists(public_path($file))) { - return '/' . $file; + if (is_null($manifest) && file_exists($manifestPath)) { + $manifest = json_decode(file_get_contents(public_path($manifestPath)), true); + } else if (!file_exists($manifestPath)) { + if (config('app.env') !== 'production') { + $path = public_path($manifestPath); + $error = "No {$path} file found, Ensure you have built the css/js assets using gulp."; + } else { + $error = "No {$manifestPath} file found, Ensure you are using the release version of BookStack"; } + throw new \Exception($error); + } - throw new InvalidArgumentException("File {$file} not defined in asset manifest."); + if (isset($manifest[$file])) { + return baseUrl($manifest[$file]); } + + throw new InvalidArgumentException("File {$file} not defined in asset manifest."); } /** @@ -58,10 +63,58 @@ function userCan($permission, Ownable $ownable = null) */ function setting($key, $default = false) { - $settingService = app('BookStack\Services\SettingService'); + $settingService = app(\BookStack\Services\SettingService::class); return $settingService->get($key, $default); } +/** + * Helper to create url's relative to the applications root path. + * @param string $path + * @param bool $forceAppDomain + * @return string + */ +function baseUrl($path, $forceAppDomain = false) +{ + $isFullUrl = strpos($path, 'http') === 0; + if ($isFullUrl && !$forceAppDomain) return $path; + $path = trim($path, '/'); + + // Remove non-specified domain if forced and we have a domain + if ($isFullUrl && $forceAppDomain) { + $explodedPath = explode('/', $path); + $path = implode('/', array_splice($explodedPath, 3)); + } + + // Return normal url path if not specified in config + if (config('app.url') === '') { + return url($path); + } + + return rtrim(config('app.url'), '/') . '/' . $path; +} + +/** + * Get an instance of the redirector. + * Overrides the default laravel redirect helper. + * Ensures it redirects even when the app is in a subdirectory. + * + * @param string|null $to + * @param int $status + * @param array $headers + * @param bool $secure + * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse + */ +function redirect($to = null, $status = 302, $headers = [], $secure = null) +{ + if (is_null($to)) { + return app('redirect'); + } + + $to = baseUrl($to); + + return app('redirect')->to($to, $status, $headers, $secure); +} + /** * Generate a url with multiple parameters for sorting purposes. * Works out the logic to set the correct sorting direction @@ -91,5 +144,5 @@ function sortUrl($path, $data, $overrideData = []) if (count($queryStringSections) === 0) return $path; - return $path . '?' . implode('&', $queryStringSections); + return baseUrl($path . '?' . implode('&', $queryStringSections)); } \ No newline at end of file diff --git a/bootstrap/autoload.php b/bootstrap/autoload.php index 383013796..29f66ace4 100644 --- a/bootstrap/autoload.php +++ b/bootstrap/autoload.php @@ -14,6 +14,7 @@ define('LARAVEL_START', microtime(true)); | */ +require __DIR__.'/../app/helpers.php'; require __DIR__.'/../vendor/autoload.php'; /* diff --git a/composer.json b/composer.json index d1f99a8f3..7d4b5e62b 100644 --- a/composer.json +++ b/composer.json @@ -5,26 +5,24 @@ "license": "MIT", "type": "project", "require": { - "php": ">=5.5.9", + "php": ">=5.6.4", + "laravel/framework": "^5.3.4", "ext-tidy": "*", - - "laravel/framework": "5.2.*", "intervention/image": "^2.3", "laravel/socialite": "^2.0", "barryvdh/laravel-ide-helper": "^2.1", - "barryvdh/laravel-debugbar": "^2.0", + "barryvdh/laravel-debugbar": "^2.2.3", "league/flysystem-aws-s3-v3": "^1.0", - "barryvdh/laravel-dompdf": "0.6.*", - "predis/predis": "^1.0", + "barryvdh/laravel-dompdf": "^0.7", + "predis/predis": "^1.1", "gathercontent/htmldiff": "^0.2.1" }, "require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.*", - "phpunit/phpunit": "~4.0", - "phpspec/phpspec": "~2.1", - "symfony/dom-crawler": "~3.0", - "symfony/css-selector": "~3.0" + "phpunit/phpunit": "~5.0", + "symfony/css-selector": "3.1.*", + "symfony/dom-crawler": "3.1.*" }, "autoload": { "classmap": [ @@ -32,10 +30,7 @@ ], "psr-4": { "BookStack\\": "app/" - }, - "files": [ - "app/helpers.php" - ] + } }, "autoload-dev": { "classmap": [ @@ -43,21 +38,19 @@ ] }, "scripts": { + "post-root-package-install": [ + "php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "php artisan key:generate" + ], "post-install-cmd": [ - "php artisan clear-compiled", + "Illuminate\\Foundation\\ComposerScripts::postInstall", "php artisan optimize" ], - "pre-update-cmd": [ - "php artisan clear-compiled" - ], "post-update-cmd": [ + "Illuminate\\Foundation\\ComposerScripts::postUpdate", "php artisan optimize" - ], - "post-root-package-install": [ - "php -r \"copy('.env.example', '.env');\"" - ], - "post-create-project-cmd": [ - "php artisan key:generate" ] }, "config": { diff --git a/composer.lock b/composer.lock index 98fb86ce5..74a090288 100644 --- a/composer.lock +++ b/composer.lock @@ -4,25 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "097beaeb7ce90384b824d65d9dd36520", - "content-hash": "233ce3d93ee0c9b005c263ff1449ae0f", + "hash": "3124d900cfe857392a94de479f3ff6d4", + "content-hash": "a968767a73f77e66e865c276cf76eedf", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.18.25", + "version": "3.19.11", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "37a8ce927a69de3d821c21b64674a2b3b9d1d247" + "reference": "19bac3bdd7988cbf7f89d5ce8e2748d774e2cde8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/37a8ce927a69de3d821c21b64674a2b3b9d1d247", - "reference": "37a8ce927a69de3d821c21b64674a2b3b9d1d247", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/19bac3bdd7988cbf7f89d5ce8e2748d774e2cde8", + "reference": "19bac3bdd7988cbf7f89d5ce8e2748d774e2cde8", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "~5.3|~6.0.1|~6.1", + "guzzlehttp/guzzle": "^5.3.1|^6.2.1", "guzzlehttp/promises": "~1.0", "guzzlehttp/psr7": "~1.3.1", "mtdowling/jmespath.php": "~2.2", @@ -85,32 +85,32 @@ "s3", "sdk" ], - "time": "2016-07-05 19:25:06" + "time": "2016-09-27 19:38:36" }, { "name": "barryvdh/laravel-debugbar", - "version": "v2.2.2", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "c291e58d0a13953e0f68d99182ee77ebc693edc0" + "reference": "0c87981df959c7c1943abe227baf607c92f204f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/c291e58d0a13953e0f68d99182ee77ebc693edc0", - "reference": "c291e58d0a13953e0f68d99182ee77ebc693edc0", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/0c87981df959c7c1943abe227baf607c92f204f9", + "reference": "0c87981df959c7c1943abe227baf607c92f204f9", "shasum": "" }, "require": { - "illuminate/support": "5.1.*|5.2.*", - "maximebf/debugbar": "~1.11.0", + "illuminate/support": "5.1.*|5.2.*|5.3.*", + "maximebf/debugbar": "~1.13.0", "php": ">=5.5.9", "symfony/finder": "~2.7|~3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -139,31 +139,31 @@ "profiler", "webprofiler" ], - "time": "2016-05-11 13:54:43" + "time": "2016-09-15 14:05:56" }, { "name": "barryvdh/laravel-dompdf", - "version": "v0.6.1", + "version": "v0.7.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-dompdf.git", - "reference": "b606788108833f7765801dca35455fb23ce9f869" + "reference": "9b8bd179262ad6b200a11edfe7b53516afcfc42a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/b606788108833f7765801dca35455fb23ce9f869", - "reference": "b606788108833f7765801dca35455fb23ce9f869", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/9b8bd179262ad6b200a11edfe7b53516afcfc42a", + "reference": "9b8bd179262ad6b200a11edfe7b53516afcfc42a", "shasum": "" }, "require": { - "dompdf/dompdf": "0.6.*", - "illuminate/support": "5.0.x|5.1.x|5.2.x", - "php": ">=5.4.0" + "dompdf/dompdf": "^0.7", + "illuminate/support": "5.1.x|5.2.x|5.3.x", + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.7-dev" } }, "autoload": { @@ -187,7 +187,7 @@ "laravel", "pdf" ], - "time": "2015-12-21 19:51:22" + "time": "2016-08-17 08:17:33" }, { "name": "barryvdh/laravel-ide-helper", @@ -511,30 +511,46 @@ }, { "name": "dompdf/dompdf", - "version": "v0.6.2", + "version": "v0.7.0", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "cc06008f75262510ee135b8cbb14e333a309f651" + "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/cc06008f75262510ee135b8cbb14e333a309f651", - "reference": "cc06008f75262510ee135b8cbb14e333a309f651", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/5c98652b1a5beb7e3cc8ec35419b2828dd63ab14", + "reference": "5c98652b1a5beb7e3cc8ec35419b2828dd63ab14", "shasum": "" }, "require": { - "phenx/php-font-lib": "0.2.*" + "ext-dom": "*", + "ext-gd": "*", + "ext-mbstring": "*", + "phenx/php-font-lib": "0.4.*", + "phenx/php-svg-lib": "0.1.*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.7-dev" + } + }, "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, "classmap": [ - "include/" + "lib/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL" + "LGPL-2.1" ], "authors": [ { @@ -544,11 +560,15 @@ { "name": "Brian Sweeney", "email": "eclecticgeek@gmail.com" + }, + { + "name": "Gabriel Bull", + "email": "me@gabrielbull.com" } ], "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", "homepage": "https://github.com/dompdf/dompdf", - "time": "2015-12-07 04:07:13" + "time": "2016-05-11 00:36:29" }, { "name": "gathercontent/htmldiff", @@ -599,122 +619,29 @@ "description": "Compare two HTML strings", "time": "2015-04-15 15:39:46" }, - { - "name": "guzzle/guzzle", - "version": "v3.8.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba", - "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.3", - "symfony/event-dispatcher": ">=2.1" - }, - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - "require-dev": { - "doctrine/cache": "*", - "monolog/monolog": "1.*", - "phpunit/phpunit": "3.7.*", - "psr/log": "1.0.*", - "symfony/class-loader": "*", - "zendframework/zend-cache": "<2.3", - "zendframework/zend-log": "<2.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.8-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle": "src/", - "Guzzle\\Tests": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "abandoned": "guzzlehttp/guzzle", - "time": "2014-01-28 22:29:15" - }, { "name": "guzzlehttp/guzzle", - "version": "6.2.0", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d094e337976dff9d8e2424e8485872194e768662" + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d094e337976dff9d8e2424e8485872194e768662", - "reference": "d094e337976dff9d8e2424e8485872194e768662", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/3f808fba627f2c5b69e2501217bf31af349c1427", + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427", "shasum": "" }, "require": { - "guzzlehttp/promises": "~1.0", - "guzzlehttp/psr7": "~1.1", - "php": ">=5.5.0" + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.3.1", + "php": ">=5.5" }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "~4.0", - "psr/log": "~1.0" + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" }, "type": "library", "extra": { @@ -752,7 +679,7 @@ "rest", "web service" ], - "time": "2016-03-21 20:02:09" + "time": "2016-07-15 17:22:37" }, { "name": "guzzlehttp/promises", @@ -865,16 +792,16 @@ }, { "name": "intervention/image", - "version": "2.3.7", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "22088b04728a039bd1fc32f7e79a89a118b78698" + "reference": "4064a980324f6c3bfa2bd981dfb247afa705ec3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/22088b04728a039bd1fc32f7e79a89a118b78698", - "reference": "22088b04728a039bd1fc32f7e79a89a118b78698", + "url": "https://api.github.com/repos/Intervention/image/zipball/4064a980324f6c3bfa2bd981dfb247afa705ec3c", + "reference": "4064a980324f6c3bfa2bd981dfb247afa705ec3c", "shasum": "" }, "require": { @@ -923,7 +850,7 @@ "thumbnail", "watermark" ], - "time": "2016-04-26 14:08:40" + "time": "2016-09-01 17:04:03" }, { "name": "jakub-onderka/php-console-color", @@ -1072,16 +999,16 @@ }, { "name": "laravel/framework", - "version": "v5.2.39", + "version": "v5.3.11", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a" + "reference": "ca48001b95a0543fb39fcd7219de960bbc03eaa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/c2a77050269b4e03bd9a735a9f24e573a7598b8a", - "reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a", + "url": "https://api.github.com/repos/laravel/framework/zipball/ca48001b95a0543fb39fcd7219de960bbc03eaa5", + "reference": "ca48001b95a0543fb39fcd7219de960bbc03eaa5", "shasum": "" }, "require": { @@ -1094,20 +1021,20 @@ "monolog/monolog": "~1.11", "mtdowling/cron-expression": "~1.0", "nesbot/carbon": "~1.20", - "paragonie/random_compat": "~1.4", - "php": ">=5.5.9", + "paragonie/random_compat": "~1.4|~2.0", + "php": ">=5.6.4", "psy/psysh": "0.7.*", + "ramsey/uuid": "~3.0", "swiftmailer/swiftmailer": "~5.1", - "symfony/console": "2.8.*|3.0.*", - "symfony/debug": "2.8.*|3.0.*", - "symfony/finder": "2.8.*|3.0.*", - "symfony/http-foundation": "2.8.*|3.0.*", - "symfony/http-kernel": "2.8.*|3.0.*", - "symfony/polyfill-php56": "~1.0", - "symfony/process": "2.8.*|3.0.*", - "symfony/routing": "2.8.*|3.0.*", - "symfony/translation": "2.8.*|3.0.*", - "symfony/var-dumper": "2.8.*|3.0.*", + "symfony/console": "3.1.*", + "symfony/debug": "3.1.*", + "symfony/finder": "3.1.*", + "symfony/http-foundation": "3.1.*", + "symfony/http-kernel": "3.1.*", + "symfony/process": "3.1.*", + "symfony/routing": "3.1.*", + "symfony/translation": "3.1.*", + "symfony/var-dumper": "3.1.*", "vlucas/phpdotenv": "~2.2" }, "replace": { @@ -1129,6 +1056,7 @@ "illuminate/http": "self.version", "illuminate/log": "self.version", "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", "illuminate/pagination": "self.version", "illuminate/pipeline": "self.version", "illuminate/queue": "self.version", @@ -1138,16 +1066,17 @@ "illuminate/support": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", - "illuminate/view": "self.version" + "illuminate/view": "self.version", + "tightenco/collect": "self.version" }, "require-dev": { "aws/aws-sdk-php": "~3.0", "mockery/mockery": "~0.9.4", "pda/pheanstalk": "~3.0", - "phpunit/phpunit": "~4.1", + "phpunit/phpunit": "~5.4", "predis/predis": "~1.0", - "symfony/css-selector": "2.8.*|3.0.*", - "symfony/dom-crawler": "2.8.*|3.0.*" + "symfony/css-selector": "3.1.*", + "symfony/dom-crawler": "3.1.*" }, "suggest": { "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).", @@ -1159,20 +1088,17 @@ "pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).", "predis/predis": "Required to use the redis cache and queue drivers (~1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", - "symfony/css-selector": "Required to use some of the crawler integration testing tools (2.8.*|3.0.*).", - "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (2.8.*|3.0.*).", + "symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).", + "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).", "symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.2-dev" + "dev-master": "5.3-dev" } }, "autoload": { - "classmap": [ - "src/Illuminate/Queue/IlluminateQueueClosure.php" - ], "files": [ "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" @@ -1188,16 +1114,16 @@ "authors": [ { "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" + "email": "taylor@laravel.com" } ], "description": "The Laravel Framework.", - "homepage": "http://laravel.com", + "homepage": "https://laravel.com", "keywords": [ "framework", "laravel" ], - "time": "2016-06-17 19:25:12" + "time": "2016-09-28 02:15:37" }, { "name": "laravel/socialite", @@ -1255,16 +1181,16 @@ }, { "name": "league/flysystem", - "version": "1.0.24", + "version": "1.0.27", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "9aca859a303fdca30370f42b8c611d9cf0dedf4b" + "reference": "50e2045ed70a7e75a5e30bc3662904f3b67af8a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9aca859a303fdca30370f42b8c611d9cf0dedf4b", - "reference": "9aca859a303fdca30370f42b8c611d9cf0dedf4b", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/50e2045ed70a7e75a5e30bc3662904f3b67af8a9", + "reference": "50e2045ed70a7e75a5e30bc3662904f3b67af8a9", "shasum": "" }, "require": { @@ -1277,7 +1203,7 @@ "ext-fileinfo": "*", "mockery/mockery": "~0.9", "phpspec/phpspec": "^2.2", - "phpunit/phpunit": "~4.8 || ~5.0" + "phpunit/phpunit": "~4.8" }, "suggest": { "ext-fileinfo": "Required for MimeType", @@ -1334,7 +1260,7 @@ "sftp", "storage" ], - "time": "2016-06-03 19:11:39" + "time": "2016-08-10 08:55:11" }, { "name": "league/flysystem-aws-s3-v3", @@ -1385,26 +1311,26 @@ }, { "name": "league/oauth1-client", - "version": "1.6.1", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth1-client.git", - "reference": "cef3ceda13c78f89c323e4d5e6301c0eb7cea422" + "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/cef3ceda13c78f89c323e4d5e6301c0eb7cea422", - "reference": "cef3ceda13c78f89c323e4d5e6301c0eb7cea422", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/fca5f160650cb74d23fc11aa570dd61f86dcf647", + "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647", "shasum": "" }, "require": { - "guzzle/guzzle": "3.*", - "php": ">=5.3.0" + "guzzlehttp/guzzle": "^6.0", + "php": ">=5.5.0" }, "require-dev": { - "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "mockery/mockery": "^0.9", + "phpunit/phpunit": "^4.0", + "squizlabs/php_codesniffer": "^2.0" }, "type": "library", "extra": { @@ -1444,20 +1370,20 @@ "tumblr", "twitter" ], - "time": "2015-10-23 04:02:07" + "time": "2016-08-17 00:36:58" }, { "name": "maximebf/debugbar", - "version": "v1.11.1", + "version": "v1.13.0", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "d9302891c1f0a0ac5a4f66725163a00537c6359f" + "reference": "5f49a5ed6cfde81d31d89378806670d77462526e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/d9302891c1f0a0ac5a4f66725163a00537c6359f", - "reference": "d9302891c1f0a0ac5a4f66725163a00537c6359f", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/5f49a5ed6cfde81d31d89378806670d77462526e", + "reference": "5f49a5ed6cfde81d31d89378806670d77462526e", "shasum": "" }, "require": { @@ -1476,7 +1402,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -1505,20 +1431,20 @@ "debug", "debugbar" ], - "time": "2016-01-22 12:22:23" + "time": "2016-09-15 14:01:59" }, { "name": "monolog/monolog", - "version": "1.20.0", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037" + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/55841909e2bcde01b5318c35f2b74f8ecc86e037", - "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", "shasum": "" }, "require": { @@ -1583,7 +1509,7 @@ "logging", "psr-3" ], - "time": "2016-07-02 14:02:10" + "time": "2016-07-29 03:23:52" }, { "name": "mtdowling/cron-expression", @@ -1733,16 +1659,16 @@ }, { "name": "nikic/php-parser", - "version": "v2.1.0", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "47b254ea51f1d6d5dc04b9b299e88346bf2369e3" + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/47b254ea51f1d6d5dc04b9b299e88346bf2369e3", - "reference": "47b254ea51f1d6d5dc04b9b299e88346bf2369e3", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4dd659edadffdc2143e4753df655d866dbfeedf0", + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0", "shasum": "" }, "require": { @@ -1780,20 +1706,20 @@ "parser", "php" ], - "time": "2016-04-19 13:41:41" + "time": "2016-09-16 12:04:44" }, { "name": "paragonie/random_compat", - "version": "v1.4.1", + "version": "v2.0.2", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774" + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf", + "reference": "088c04e2f261c33bed6ca5245491cfca69195ccf", "shasum": "" }, "require": { @@ -1828,31 +1754,31 @@ "pseudorandom", "random" ], - "time": "2016-03-18 20:34:03" + "time": "2016-04-03 06:00:07" }, { "name": "phenx/php-font-lib", - "version": "0.2.2", + "version": "0.4", "source": { "type": "git", "url": "https://github.com/PhenX/php-font-lib.git", - "reference": "c30c7fc00a6b0d863e9bb4c5d5dd015298b2dc82" + "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/c30c7fc00a6b0d863e9bb4c5d5dd015298b2dc82", - "reference": "c30c7fc00a6b0d863e9bb4c5d5dd015298b2dc82", + "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/b8af0cacdc3cbf1e41a586fcb78f506f4121a088", + "reference": "b8af0cacdc3cbf1e41a586fcb78f506f4121a088", "shasum": "" }, "type": "library", "autoload": { - "classmap": [ - "classes/" - ] + "psr-0": { + "FontLib\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL" + "LGPL-3.0" ], "authors": [ { @@ -1862,7 +1788,41 @@ ], "description": "A library to read, parse, export and make subsets of different types of font files.", "homepage": "https://github.com/PhenX/php-font-lib", - "time": "2014-02-01 15:22:28" + "time": "2015-05-06 20:02:39" + }, + { + "name": "phenx/php-svg-lib", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/PhenX/php-svg-lib.git", + "reference": "b419766515b3426c6da74b0e29e93d71c4f17099" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/b419766515b3426c6da74b0e29e93d71c4f17099", + "reference": "b419766515b3426c6da74b0e29e93d71c4f17099", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Svg\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "time": "2015-05-06 18:49:49" }, { "name": "predis/predis", @@ -1916,16 +1876,16 @@ }, { "name": "psr/http-message", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { @@ -1953,6 +1913,7 @@ } ], "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", @@ -1961,26 +1922,34 @@ "request", "response" ], - "time": "2015-05-04 20:22:00" + "time": "2016-08-06 14:39:51" }, { "name": "psr/log", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "reference": "5277094ed527a1c4477177d102fe4c53551953e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/log/zipball/5277094ed527a1c4477177d102fe4c53551953e0", + "reference": "5277094ed527a1c4477177d102fe4c53551953e0", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1994,12 +1963,13 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2016-09-19 16:02:08" }, { "name": "psy/psysh", @@ -2073,25 +2043,105 @@ ], "time": "2016-03-09 05:03:14" }, + { + "name": "ramsey/uuid", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "a6d15c8618ea3951fd54d34e326b68d3d0bc0786" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/a6d15c8618ea3951fd54d34e326b68d3d0bc0786", + "reference": "a6d15c8618ea3951fd54d34e326b68d3d0bc0786", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "^1.0|^2.0", + "php": ">=5.4" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "apigen/apigen": "^4.1", + "codeception/aspect-mock": "1.0.0", + "goaop/framework": "1.0.0-alpha.2", + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "mockery/mockery": "^0.9.4", + "moontoast/math": "^1.1", + "phpunit/phpunit": "^4.7|>=5.0 <5.4", + "satooshi/php-coveralls": "^0.6.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator", + "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator", + "ircmaxell/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + }, + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "time": "2016-08-02 18:39:32" + }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.2", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "d8db871a54619458a805229a057ea2af33c753e8" + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8", - "reference": "d8db871a54619458a805229a057ea2af33c753e8", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "~0.9.1,<0.9.4" + "mockery/mockery": "~0.9.1" }, "type": "library", "extra": { @@ -2124,20 +2174,20 @@ "mail", "mailer" ], - "time": "2016-05-01 08:45:47" + "time": "2016-07-08 11:51:25" }, { "name": "symfony/class-loader", - "version": "v3.1.2", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9" + "reference": "2d0ba77c46ecc96a6641009a98f72632216811ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9", - "reference": "0d0ac77c336eb73f35bebdf3e1f3695ac741bbc9", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/2d0ba77c46ecc96a6641009a98f72632216811ba", + "reference": "2d0ba77c46ecc96a6641009a98f72632216811ba", "shasum": "" }, "require": { @@ -2180,20 +2230,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:41:56" + "time": "2016-08-23 13:39:15" }, { "name": "symfony/console", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f" + "reference": "8ea494c34f0f772c3954b5fbe00bffc5a435e563" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a7abb7153f6d1da47f87ec50274844e246b09d9f", - "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f", + "url": "https://api.github.com/repos/symfony/console/zipball/8ea494c34f0f772c3954b5fbe00bffc5a435e563", + "reference": "8ea494c34f0f772c3954b5fbe00bffc5a435e563", "shasum": "" }, "require": { @@ -2213,7 +2263,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2240,20 +2290,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-06-29 07:02:21" + "time": "2016-08-19 06:48:39" }, { "name": "symfony/debug", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2" + "reference": "34f6ac18c2974ca5fce68adf419ee7d15def6f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c54bc3539c3b87e86799533801e8ae0e971d78c2", - "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2", + "url": "https://api.github.com/repos/symfony/debug/zipball/34f6ac18c2974ca5fce68adf419ee7d15def6f11", + "reference": "34f6ac18c2974ca5fce68adf419ee7d15def6f11", "shasum": "" }, "require": { @@ -2270,7 +2320,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2297,20 +2347,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:40:00" + "time": "2016-08-23 13:39:15" }, { "name": "symfony/event-dispatcher", - "version": "v3.1.2", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5" + "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7f9839ede2070f53e7e2f0849b9bd14748c434c5", - "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c0c00c80b3a69132c4e55c3e7db32b4a387615e5", + "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5", "shasum": "" }, "require": { @@ -2357,20 +2407,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:41:56" + "time": "2016-07-19 10:45:57" }, { "name": "symfony/finder", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9" + "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9", - "reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9", + "url": "https://api.github.com/repos/symfony/finder/zipball/e568ef1784f447a0e54dcb6f6de30b9747b0f577", + "reference": "e568ef1784f447a0e54dcb6f6de30b9747b0f577", "shasum": "" }, "require": { @@ -2379,7 +2429,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2406,20 +2456,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:40:00" + "time": "2016-08-26 12:04:02" }, { "name": "symfony/http-foundation", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "1341139f906d295baa4f4abd55293d07e25a065a" + "reference": "63592e00fd90632b57ee50220a1ddb29b6bf3bb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1341139f906d295baa4f4abd55293d07e25a065a", - "reference": "1341139f906d295baa4f4abd55293d07e25a065a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/63592e00fd90632b57ee50220a1ddb29b6bf3bb4", + "reference": "63592e00fd90632b57ee50220a1ddb29b6bf3bb4", "shasum": "" }, "require": { @@ -2432,7 +2482,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2459,20 +2509,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2016-06-29 07:02:21" + "time": "2016-08-22 12:11:19" }, { "name": "symfony/http-kernel", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb" + "reference": "aeda215d6b01f119508c090d2a09ebb5b0bc61f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/177b63b2d50b63fa6d82ea41359ed9928cc7a1fb", - "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aeda215d6b01f119508c090d2a09ebb5b0bc61f3", + "reference": "aeda215d6b01f119508c090d2a09ebb5b0bc61f3", "shasum": "" }, "require": { @@ -2514,7 +2564,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2541,7 +2591,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2016-06-30 16:30:17" + "time": "2016-09-03 15:28:24" }, { "name": "symfony/polyfill-mbstring", @@ -2712,16 +2762,16 @@ }, { "name": "symfony/process", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d7cde1f9d94d87060204f863779389b61c382eeb" + "reference": "e64e93041c80e77197ace5ab9385dedb5a143697" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d7cde1f9d94d87060204f863779389b61c382eeb", - "reference": "d7cde1f9d94d87060204f863779389b61c382eeb", + "url": "https://api.github.com/repos/symfony/process/zipball/e64e93041c80e77197ace5ab9385dedb5a143697", + "reference": "e64e93041c80e77197ace5ab9385dedb5a143697", "shasum": "" }, "require": { @@ -2730,7 +2780,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2757,20 +2807,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:40:00" + "time": "2016-08-16 14:58:24" }, { "name": "symfony/routing", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9038984bd9c05ab07280121e9e10f61a7231457b" + "reference": "8edf62498a1a4c57ba317664a4b698339c10cdf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9038984bd9c05ab07280121e9e10f61a7231457b", - "reference": "9038984bd9c05ab07280121e9e10f61a7231457b", + "url": "https://api.github.com/repos/symfony/routing/zipball/8edf62498a1a4c57ba317664a4b698339c10cdf6", + "reference": "8edf62498a1a4c57ba317664a4b698339c10cdf6", "shasum": "" }, "require": { @@ -2799,7 +2849,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2832,20 +2882,20 @@ "uri", "url" ], - "time": "2016-06-29 05:40:00" + "time": "2016-08-16 14:58:24" }, { "name": "symfony/translation", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8" + "reference": "a35edc277513c9bc0f063ca174c36b346f974528" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/6bf844e1ee3c820c012386c10427a5c67bbefec8", - "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8", + "url": "https://api.github.com/repos/symfony/translation/zipball/a35edc277513c9bc0f063ca174c36b346f974528", + "reference": "a35edc277513c9bc0f063ca174c36b346f974528", "shasum": "" }, "require": { @@ -2869,7 +2919,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2896,20 +2946,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:40:00" + "time": "2016-08-05 08:37:39" }, { "name": "symfony/var-dumper", - "version": "v3.0.8", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2f046e9a9d571f22cc8b26783564876713b06579" + "reference": "62ee73706c421654a4c840028954510277f7dfc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2f046e9a9d571f22cc8b26783564876713b06579", - "reference": "2f046e9a9d571f22cc8b26783564876713b06579", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/62ee73706c421654a4c840028954510277f7dfc8", + "reference": "62ee73706c421654a4c840028954510277f7dfc8", "shasum": "" }, "require": { @@ -2925,7 +2975,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2959,20 +3009,20 @@ "debug", "dump" ], - "time": "2016-06-29 05:40:00" + "time": "2016-08-31 09:05:42" }, { "name": "vlucas/phpdotenv", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "9ca5644c536654e9509b9d257f53c58630eb2a6a" + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/9ca5644c536654e9509b9d257f53c58630eb2a6a", - "reference": "9ca5644c536654e9509b9d257f53c58630eb2a6a", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", "shasum": "" }, "require": { @@ -2984,7 +3034,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -3009,7 +3059,7 @@ "env", "environment" ], - "time": "2016-06-14 14:14:52" + "time": "2016-09-01 10:05:43" } ], "packages-dev": [ @@ -3160,50 +3210,6 @@ ], "time": "2015-05-11 14:41:42" }, - { - "name": "icap/html-diff", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/iCAPLyon1/HtmlDiff.git", - "reference": "f58ddb196292ae585a2efb7692653d015ecc436f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/iCAPLyon1/HtmlDiff/zipball/f58ddb196292ae585a2efb7692653d015ecc436f", - "reference": "f58ddb196292ae585a2efb7692653d015ecc436f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Icap\\HtmlDiff\\": "src/Icap/HtmlDiff" - } - }, - "notification-url": "https://packagist.org/downloads/", - "authors": [ - { - "name": "Panagiotis Tsavdaris", - "email": "ptsavdar@gmail.com" - }, - { - "name": "ICAP development team", - "homepage": "https://github.com/iCAPLyon1" - } - ], - "description": "A PHP5 library that diffs (compares) HTML files.", - "keywords": [ - "daisy diff", - "html diff" - ], - "time": "2016-02-17 16:35:09" - }, { "name": "mockery/mockery", "version": "0.9.5", @@ -3269,6 +3275,48 @@ ], "time": "2016-05-22 21:52:33" }, + { + "name": "myclabs/deep-copy", + "version": "1.5.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "doctrine/collections": "1.*", + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "homepage": "https://github.com/myclabs/DeepCopy", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2016-09-16 13:37:59" + }, { "name": "phpdocumentor/reflection-common", "version": "1.0", @@ -3415,118 +3463,6 @@ ], "time": "2016-06-10 07:14:17" }, - { - "name": "phpspec/php-diff", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/phpspec/php-diff.git", - "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/php-diff/zipball/30e103d19519fe678ae64a60d77884ef3d71b28a", - "reference": "30e103d19519fe678ae64a60d77884ef3d71b28a", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Diff": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Chris Boulton", - "homepage": "http://github.com/chrisboulton", - "role": "Original developer" - } - ], - "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", - "time": "2013-11-01 13:02:21" - }, - { - "name": "phpspec/phpspec", - "version": "2.5.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/phpspec.git", - "reference": "385ecb015e97c13818074f1517928b24d4a26067" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/phpspec/zipball/385ecb015e97c13818074f1517928b24d4a26067", - "reference": "385ecb015e97c13818074f1517928b24d4a26067", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.1", - "ext-tokenizer": "*", - "php": ">=5.3.3", - "phpspec/php-diff": "~1.0.0", - "phpspec/prophecy": "~1.4", - "sebastian/exporter": "~1.0", - "symfony/console": "~2.3|~3.0", - "symfony/event-dispatcher": "~2.1|~3.0", - "symfony/finder": "~2.1|~3.0", - "symfony/process": "^2.6|~3.0", - "symfony/yaml": "~2.1|~3.0" - }, - "require-dev": { - "behat/behat": "^3.0.11", - "bossa/phpspec2-expect": "~1.0", - "phpunit/phpunit": "~4.4", - "symfony/filesystem": "~2.1|~3.0" - }, - "suggest": { - "phpspec/nyan-formatters": "~1.0 – Adds Nyan formatters" - }, - "bin": [ - "bin/phpspec" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev" - } - }, - "autoload": { - "psr-0": { - "PhpSpec": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "homepage": "http://marcelloduarte.net/" - } - ], - "description": "Specification-oriented BDD framework for PHP 5.3+", - "homepage": "http://phpspec.net/", - "keywords": [ - "BDD", - "SpecBDD", - "TDD", - "spec", - "specification", - "testing", - "tests" - ], - "time": "2016-03-20 20:34:32" - }, { "name": "phpspec/prophecy", "version": "v1.6.1", @@ -3591,39 +3527,40 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.2.4", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3", + "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3", "shasum": "" }, "require": { - "php": ">=5.3.3", + "php": "^5.6 || ^7.0", "phpunit/php-file-iterator": "~1.3", "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" + "phpunit/php-token-stream": "^1.4.2", + "sebastian/code-unit-reverse-lookup": "~1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "~1.0|~2.0" }, "require-dev": { "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" + "phpunit/phpunit": "^5.4" }, "suggest": { "ext-dom": "*", - "ext-xdebug": ">=2.2.1", + "ext-xdebug": ">=2.4.0", "ext-xmlwriter": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -3649,7 +3586,7 @@ "testing", "xunit" ], - "time": "2015-10-06 15:47:00" + "time": "2016-07-26 14:39:29" }, { "name": "phpunit/php-file-iterator", @@ -3834,40 +3771,51 @@ }, { "name": "phpunit/phpunit", - "version": "4.8.26", + "version": "5.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" + "reference": "a57126dc681b08289fef6ac96a48e30656f84350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a57126dc681b08289fef6ac96a48e30656f84350", + "reference": "a57126dc681b08289fef6ac96a48e30656f84350", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", + "phpunit/php-code-coverage": "^4.0.1", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", + "phpunit/phpunit-mock-objects": "^3.2", "sebastian/comparator": "~1.1", "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", + "sebastian/environment": "^1.3 || ^2.0", "sebastian/exporter": "~1.2", "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", + "sebastian/object-enumerator": "~1.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "~1.0|~2.0", "symfony/yaml": "~2.1|~3.0" }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" + }, + "require-dev": { + "ext-pdo": "*" + }, "suggest": { + "ext-tidy": "*", + "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, "bin": [ @@ -3876,7 +3824,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.8.x-dev" + "dev-master": "5.5.x-dev" } }, "autoload": { @@ -3902,30 +3850,33 @@ "testing", "xunit" ], - "time": "2016-05-17 03:09:28" + "time": "2016-09-21 14:40:13" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", + "version": "3.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + "reference": "546898a2c0c356ef2891b39dd7d07f5d82c8ed0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/546898a2c0c356ef2891b39dd7d07f5d82c8ed0a", + "reference": "546898a2c0c356ef2891b39dd7d07f5d82c8ed0a", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^5.4" }, "suggest": { "ext-soap": "*" @@ -3933,7 +3884,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "3.2.x-dev" } }, "autoload": { @@ -3958,7 +3909,52 @@ "mock", "xunit" ], - "time": "2015-10-02 06:51:40" + "time": "2016-09-06 16:07:45" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2016-02-13 06:45:14" }, { "name": "sebastian/comparator", @@ -4078,23 +4074,23 @@ }, { "name": "sebastian/environment", - "version": "1.3.7", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^4.8 || ^5.0" }, "type": "library", "extra": { @@ -4124,7 +4120,7 @@ "environment", "hhvm" ], - "time": "2016-05-17 03:18:57" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", @@ -4244,6 +4240,52 @@ ], "time": "2015-10-12 03:26:01" }, + { + "name": "sebastian/object-enumerator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "d4ca2fb70344987502567bc50081c03e6192fb26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26", + "reference": "d4ca2fb70344987502567bc50081c03e6192fb26", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2016-01-28 13:25:10" + }, { "name": "sebastian/recursion-context", "version": "1.0.2", @@ -4297,21 +4339,71 @@ "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "time": "2015-11-11 19:50:13" }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28 20:34:47" + }, { "name": "sebastian/version", - "version": "1.0.6", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", + "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", "shasum": "" }, + "require": { + "php": ">=5.6" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -4330,11 +4422,11 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" + "time": "2016-02-04 12:56:52" }, { "name": "symfony/css-selector", - "version": "v3.1.2", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -4387,16 +4479,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.1.2", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0" + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0", - "reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/bb7395e8b1db3654de82b9f35d019958276de4d7", + "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7", "shasum": "" }, "require": { @@ -4439,20 +4531,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:41:56" + "time": "2016-08-05 08:37:39" }, { "name": "symfony/yaml", - "version": "v3.1.2", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de" + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de", - "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f291ed25eb1435bddbe8a96caaef16469c2a092d", + "reference": "f291ed25eb1435bddbe8a96caaef16469c2a092d", "shasum": "" }, "require": { @@ -4488,32 +4580,33 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-06-29 05:41:56" + "time": "2016-09-02 02:12:52" }, { "name": "webmozart/assert", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bb2d123231c095735130cc8f6d31385a44c7b308", + "reference": "bb2d123231c095735130cc8f6d31385a44c7b308", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3|^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -4537,7 +4630,7 @@ "check", "validate" ], - "time": "2015-08-24 13:29:44" + "time": "2016-08-09 15:02:57" } ], "aliases": [], @@ -4546,7 +4639,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.5.9" + "php": ">=5.6.4", + "ext-tidy": "*" }, "platform-dev": [] } diff --git a/config/app.php b/config/app.php index d305af3c0..a5b0d2fe0 100644 --- a/config/app.php +++ b/config/app.php @@ -31,7 +31,7 @@ return [ | */ - 'url' => env('APP_URL', 'http://localhost'), + 'url' => env('APP_URL', '') === 'http://bookstack.dev' ? '' : env('APP_URL', ''), /* |-------------------------------------------------------------------------- @@ -130,7 +130,6 @@ return [ Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, @@ -139,6 +138,7 @@ return [ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, + Illuminate\Notifications\NotificationServiceProvider::class, Laravel\Socialite\SocialiteServiceProvider::class, /** @@ -153,8 +153,11 @@ return [ /* * Application Service Providers... */ + BookStack\Providers\PaginationServiceProvider::class, + BookStack\Providers\AuthServiceProvider::class, BookStack\Providers\AppServiceProvider::class, + BookStack\Providers\BroadcastServiceProvider::class, BookStack\Providers\EventServiceProvider::class, BookStack\Providers\RouteServiceProvider::class, BookStack\Providers\CustomFacadeProvider::class, @@ -193,6 +196,7 @@ return [ 'Lang' => Illuminate\Support\Facades\Lang::class, 'Log' => Illuminate\Support\Facades\Log::class, 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Notification' => Illuminate\Support\Facades\Notification::class, 'Password' => Illuminate\Support\Facades\Password::class, 'Queue' => Illuminate\Support\Facades\Queue::class, 'Redirect' => Illuminate\Support\Facades\Redirect::class, diff --git a/config/setting-defaults.php b/config/setting-defaults.php index 6a55a0dc3..5482c1331 100644 --- a/config/setting-defaults.php +++ b/config/setting-defaults.php @@ -5,8 +5,10 @@ */ return [ - 'app-editor' => 'wysiwyg', - 'app-color' => '#0288D1', + 'app-name' => 'BookStack', + 'app-name-header' => true, + 'app-editor' => 'wysiwyg', + 'app-color' => '#0288D1', 'app-color-light' => 'rgba(21, 101, 192, 0.15)' ]; \ No newline at end of file diff --git a/database/migrations/2015_08_29_105422_add_roles_and_permissions.php b/database/migrations/2015_08_29_105422_add_roles_and_permissions.php index 763a33fec..47a77e29f 100644 --- a/database/migrations/2015_08_29_105422_add_roles_and_permissions.php +++ b/database/migrations/2015_08_29_105422_add_roles_and_permissions.php @@ -129,7 +129,7 @@ class AddRolesAndPermissions extends Migration // Set all current users as admins // (At this point only the initially create user should be an admin) - $users = DB::table('users')->get(); + $users = DB::table('users')->get()->all(); foreach ($users as $user) { DB::table('role_user')->insert([ 'role_id' => $adminId, diff --git a/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php b/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php new file mode 100644 index 000000000..c618877ef --- /dev/null +++ b/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php @@ -0,0 +1,31 @@ +string('summary')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('page_revisions', function ($table) { + $table->dropColumn('summary'); + }); + } +} diff --git a/package.json b/package.json index c387d1317..fde090beb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "babel-runtime": "^5.8.29", "bootstrap-sass": "^3.0.0", "dropzone": "^4.0.1", - "laravel-elixir": "^3.4.0", + "laravel-elixir": "^5.0.0", "marked": "^0.3.5", "moment": "^2.12.0", "zeroclipboard": "^2.2.0" diff --git a/phpspec.yml b/phpspec.yml deleted file mode 100644 index 58f1d982e..000000000 --- a/phpspec.yml +++ /dev/null @@ -1,5 +0,0 @@ -suites: - main: - namespace: BookStack - psr4_prefix: BookStack - src_path: app \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 2150a5aa3..a2b26d413 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -28,7 +28,7 @@ - + diff --git a/public/libs/tinymce/plugins/advlist/plugin.min.js b/public/libs/tinymce/plugins/advlist/plugin.min.js index 1e1c66804..ee90c67bd 100644 --- a/public/libs/tinymce/plugins/advlist/plugin.min.js +++ b/public/libs/tinymce/plugins/advlist/plugin.min.js @@ -1 +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)}})}); \ No newline at end of file +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;if(d=e.getParent(f.getNode(),"ol,ul"),!d||d.nodeName!=b||c===!1){var h={"list-style-type":c?c:""};a.execCommand("UL"==b?"InsertUnorderedList":"InsertOrderedList",!1,h)}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)}})}); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/charmap/plugin.min.js b/public/libs/tinymce/plugins/charmap/plugin.min.js index 9fd639ef9..9bf1e9fcd 100644 --- a/public/libs/tinymce/plugins/charmap/plugin.min.js +++ b/public/libs/tinymce/plugins/charmap/plugin.min.js @@ -1 +1 @@ -tinymce.PluginManager.add("charmap",function(a){function b(){return[["160","no-break space"],["173","soft hyphen"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["221","Y - acute"],["376","Y - diaeresis"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"]]}function c(a){return tinymce.util.Tools.grep(a,function(a){return i(a)&&2==a.length})}function d(a){return i(a)?[].concat(c(a)):"function"==typeof a?a():[]}function e(b){var c=a.settings;return c.charmap&&(b=d(c.charmap)),c.charmap_append?[].concat(b).concat(d(c.charmap_append)):b}function f(){return e(b())}function g(b){a.fire("insertCustomChar",{chr:b}).chr,a.execCommand("mceInsertContent",!1,b)}function h(){function b(a){for(;a;){if("TD"==a.nodeName)return a;a=a.parentNode}}var c,d,e,h;c='';var i=f(),j=Math.min(i.length,25),k=Math.ceil(i.length/j);for(e=0;k>e;e++){for(c+="",d=0;j>d;d++){var l=e*j+d;if(l
'+(m?String.fromCharCode(parseInt(m[0],10)):" ")+"
"}else c+="
"}c+="";var n={type:"container",html:c,onclick:function(a){var c=a.target;/^(TD|DIV)$/.test(c.nodeName)&&b(c).firstChild&&(g(tinymce.trim(c.innerText||c.textContent)),a.ctrlKey||h.close())},onmouseover:function(a){var c=b(a.target);c&&c.firstChild?(h.find("#preview").text(c.firstChild.firstChild.data),h.find("#previewTitle").text(c.title)):(h.find("#preview").text(" "),h.find("#previewTitle").text(" "))}};h=a.windowManager.open({title:"Special character",spacing:10,padding:10,items:[n,{type:"container",layout:"flex",direction:"column",align:"center",spacing:5,minWidth:160,minHeight:160,items:[{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:140,minHeight:80},{type:"label",name:"previewTitle",text:" ",style:"text-align: center",border:1,minWidth:140,minHeight:80}]}],buttons:[{text:"Close",onclick:function(){h.close()}}]})}var i=tinymce.util.Tools.isArray;return a.addCommand("mceShowCharmap",h),a.addButton("charmap",{icon:"charmap",tooltip:"Special character",cmd:"mceShowCharmap"}),a.addMenuItem("charmap",{icon:"charmap",text:"Special character",cmd:"mceShowCharmap",context:"insert"}),{getCharMap:f,insertChar:g}}); \ No newline at end of file +tinymce.PluginManager.add("charmap",function(a){function b(){return[["160","no-break space"],["173","soft hyphen"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["256","A - macron"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["274","E - macron"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["298","I - macron"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["332","O - macron"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["362","U - macron"],["221","Y - acute"],["376","Y - diaeresis"],["562","Y - macron"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["257","a - macron"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["275","e - macron"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["299","i - macron"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["333","o macron"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["363","u - macron"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["563","y - macron"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"]]}function c(a){return tinymce.util.Tools.grep(a,function(a){return i(a)&&2==a.length})}function d(a){return i(a)?[].concat(c(a)):"function"==typeof a?a():[]}function e(b){var c=a.settings;return c.charmap&&(b=d(c.charmap)),c.charmap_append?[].concat(b).concat(d(c.charmap_append)):b}function f(){return e(b())}function g(b){a.fire("insertCustomChar",{chr:b}).chr,a.execCommand("mceInsertContent",!1,b)}function h(){function b(a){for(;a;){if("TD"==a.nodeName)return a;a=a.parentNode}}var c,d,e,h;c='';var i=f(),j=Math.min(i.length,25),k=Math.ceil(i.length/j);for(e=0;k>e;e++){for(c+="",d=0;j>d;d++){var l=e*j+d;if(l
'+(m?String.fromCharCode(parseInt(m[0],10)):" ")+"
"}else c+="
"}c+="";var n={type:"container",html:c,onclick:function(a){var c=a.target;/^(TD|DIV)$/.test(c.nodeName)&&b(c).firstChild&&(g(tinymce.trim(c.innerText||c.textContent)),a.ctrlKey||h.close())},onmouseover:function(a){var c=b(a.target);c&&c.firstChild?(h.find("#preview").text(c.firstChild.firstChild.data),h.find("#previewTitle").text(c.title)):(h.find("#preview").text(" "),h.find("#previewTitle").text(" "))}};h=a.windowManager.open({title:"Special character",spacing:10,padding:10,items:[n,{type:"container",layout:"flex",direction:"column",align:"center",spacing:5,minWidth:160,minHeight:160,items:[{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:140,minHeight:80},{type:"label",name:"previewTitle",text:" ",style:"text-align: center",border:1,minWidth:140,minHeight:80}]}],buttons:[{text:"Close",onclick:function(){h.close()}}]})}var i=tinymce.util.Tools.isArray;return a.addCommand("mceShowCharmap",h),a.addButton("charmap",{icon:"charmap",tooltip:"Special character",cmd:"mceShowCharmap"}),a.addMenuItem("charmap",{icon:"charmap",text:"Special character",cmd:"mceShowCharmap",context:"insert"}),{getCharMap:f,insertChar:g}}); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/codesample/plugin.min.js b/public/libs/tinymce/plugins/codesample/plugin.min.js index 574fe6b9c..6cdbf9b96 100644 --- a/public/libs/tinymce/plugins/codesample/plugin.min.js +++ b/public/libs/tinymce/plugins/codesample/plugin.min.js @@ -1 +1 @@ -!function(a,b){"use strict";function c(a,b){for(var c,d=[],f=0;fa.length)break a;if(!(q instanceof e)){k.lastIndex=0;var r=k.exec(q);if(r){m&&(n=r[1].length);var s=r.index-1+n,r=r[0].slice(n),t=r.length,u=s+t,v=q.slice(0,s+1),w=q.slice(u+1),x=[p,1];v&&x.push(v);var y=new e(h,l?c.tokenize(r,l):r,o);x.push(y),w&&x.push(w),Array.prototype.splice.apply(f,x)}}}}}return f},hooks:{all:{},add:function(a,b){var d=c.hooks.all;d[a]=d[a]||[],d[a].push(b)},run:function(a,b){var d=c.hooks.all[a];if(d&&d.length)for(var e,f=0;e=d[f++];)e(b)}}},d=c.Token=function(a,b,c){this.type=a,this.content=b,this.alias=c};return d.stringify=function(a,b,e){if("string"==typeof a)return a;if("Array"===c.util.type(a))return a.map(function(c){return d.stringify(c,b,a)}).join("");var f={type:a.type,content:d.stringify(a.content,b,e),tag:"span",classes:["token",a.type],attributes:{},language:b,parent:e};if("comment"==f.type&&(f.attributes.spellcheck="true"),a.alias){var g="Array"===c.util.type(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(f.classes,g)}c.hooks.run("wrap",f);var h="";for(var i in f.attributes)h+=(h?" ":"")+i+'="'+(f.attributes[i]||"")+'"';return"<"+f.tag+' class="'+f.classes.join(" ")+'" '+h+">"+f.content+""},b.document?void 0:b.addEventListener?(b.addEventListener("message",function(a){var d=JSON.parse(a.data),e=d.language,f=d.code,g=d.immediateClose;b.postMessage(c.highlight(f,c.languages[e],e)),g&&b.close()},!1),b.Prism):b.Prism}();return"undefined"!=typeof module&&module.exports&&(module.exports=c),"undefined"!=typeof global&&(global.Prism=c),c.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),c.languages.xml=c.languages.markup,c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,c.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},c.languages.css.atrule.inside.rest=c.util.clone(c.languages.css),c.languages.markup&&(c.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/i,inside:{tag:{pattern:/|<\/style>/i,inside:c.languages.markup.tag.inside},rest:c.languages.css},alias:"language-css"}}),c.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:c.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:c.languages.css}},alias:"language-css"}},c.languages.markup.tag)),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),c.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),c.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/i,inside:{tag:{pattern:/|<\/script>/i,inside:c.languages.markup.tag.inside},rest:c.languages.javascript},alias:"language-javascript"}}),c.languages.js=c.languages.javascript,c.languages.c=c.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),c.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0}}}}),delete c.languages.c["class-name"],delete c.languages.c["boolean"],c.languages.csharp=c.languages.extend("clike",{keyword:/\b(abstract|as|async|await|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|void|volatile|while|add|alias|ascending|async|await|descending|dynamic|from|get|global|group|into|join|let|orderby|partial|remove|select|set|value|var|where|yield)\b/,string:[/@("|')(\1\1|\\\1|\\?(?!\1)[\s\S])*\1/,/("|')(\\?.)*?\1/],number:/\b-?(0x[\da-f]+|\d*\.?\d+)\b/i}),c.languages.insertBefore("csharp","keyword",{preprocessor:{pattern:/(^\s*)#.*/m,lookbehind:!0}}),c.languages.cpp=c.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),c.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}}),c.languages.java=c.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,lookbehind:!0}}),c.languages.php=c.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z0-9_]{2,}\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/,lookbehind:!0}}),c.languages.insertBefore("php","class-name",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),c.languages.insertBefore("php","keyword",{delimiter:/\?>|<\?(?:php)?/i,variable:/\$\w+\b/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),c.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}}),c.languages.markup&&(c.hooks.add("before-highlight",function(a){"php"===a.language&&(a.tokenStack=[],a.backupCode=a.code,a.code=a.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>)/gi,function(b){return a.tokenStack.push(b),"{{{PHP"+a.tokenStack.length+"}}}"}))}),c.hooks.add("before-insert",function(a){"php"===a.language&&(a.code=a.backupCode,delete a.backupCode)}),c.hooks.add("after-highlight",function(a){if("php"===a.language){for(var b,d=0;b=a.tokenStack[d];d++)a.highlightedCode=a.highlightedCode.replace("{{{PHP"+(d+1)+"}}}",c.highlight(b,a.grammar,"php").replace(/\$/g,"$$$$"));a.element.innerHTML=a.highlightedCode}}),c.hooks.add("wrap",function(a){"php"===a.language&&"markup"===a.type&&(a.content=a.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g,'$1'))}),c.languages.insertBefore("php","comment",{markup:{pattern:/<[^?]\/?(.*?)>/,inside:c.languages.markup},php:/\{\{\{PHP[0-9]+\}\}\}/})),c.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:/"""[\s\S]+?"""|'''[\s\S]+?'''|("|')(?:\\?.)*?\1/,"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)[a-z0-9_]+/i,lookbehind:!0},keyword:/\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/,"boolean":/\b(?:True|False)\b/,number:/\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,punctuation:/[{}[\];(),.:]/},function(a){a.languages.ruby=a.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var b={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:a.util.clone(a.languages.ruby)}};a.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:b}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Fload|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,inside:{interpolation:b}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,inside:{interpolation:b}}]}(c),c}),d("tinymce/codesampleplugin/Utils",[],function(){function a(a){return a&&"PRE"==a.nodeName&&-1!==a.className.indexOf("language-")}function b(a){return function(b,c){return a(c)}}return{isCodeSample:a,trimArg:b}}),d("tinymce/codesampleplugin/Dialog",["tinymce/dom/DOMUtils","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Prism"],function(a,b,c){function d(a,b,d){a.undoManager.transact(function(){var f=e(a);d=h.encode(d),f?(a.dom.setAttrib(f,"class","language-"+b),f.innerHTML=d,c.highlightElement(f),a.selection.select(f)):(a.insertContent('
'+d+"
"),a.selection.select(a.$("#__new").removeAttr("id")[0]))})}function e(a){var c=a.selection.getNode();return b.isCodeSample(c)?c:null}function f(a){var b=e(a);return b?b.textContent:""}function g(a){var b,c=e(a);return c?(b=c.className.match(/language-(\w+)/),b?b[1]:""):""}var h=a.DOM,i=[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}];return{open:function(a){a.windowManager.open({title:"Insert/Edit code sample",minWidth:Math.min(h.getViewPort().w,800),minHeight:Math.min(h.getViewPort().h,650),layout:"fit",body:[{type:"listbox",name:"language",label:"Language",maxWidth:200,value:g(a),values:i},{type:"textbox",name:"code",multiline:!0,spellcheck:!1,ariaLabel:"Code view",flex:1,style:"direction: ltr; text-align: left",classes:"monospace",value:f(a)}],onSubmit:function(b){d(a,b.data.language,b.data.code)}})}}}),d("tinymce/codesampleplugin/Plugin",["tinymce/Env","tinymce/PluginManager","tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Utils"],function(a,b,c,d,e){var f,g=e.trimArg;b.add("codesample",function(b,h){function i(){var a;f||(f=!0,a=b.dom.create("link",{rel:"stylesheet",href:h+"/css/prism.css"}),b.getDoc().getElementsByTagName("head")[0].appendChild(a))}var j=b.$;a.ceFalse&&(b.on("PreProcess",function(a){j("pre[contenteditable=false]",a.node).filter(g(e.isCodeSample)).each(function(a,b){var c=j(b),d=b.textContent;c.attr("class",j.trim(c.attr("class"))),c.removeAttr("contentEditable"),c.empty().append(j("").each(function(){this.textContent=d}))})}),b.on("SetContent",function(){var a=j("pre").filter(g(e.isCodeSample)).filter(function(a,b){return"false"!==b.contentEditable});a.length&&b.undoManager.transact(function(){a.each(function(a,d){j(d).find("br").each(function(a,c){c.parentNode.replaceChild(b.getDoc().createTextNode("\n"),c)}),d.contentEditable=!1,d.innerHTML=b.dom.encode(d.textContent),c.highlightElement(d),d.className=j.trim(d.className)})})}),b.addCommand("codesample",function(){d.open(b)}),b.addButton("codesample",{cmd:"codesample",title:"Insert/Edit code sample"}),b.on("init",i))})}),f(["tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Plugin"])}(this); \ No newline at end of file +!function(a,b){"use strict";function c(a,b){for(var c,d=[],f=0;fa.length)break a;if(!(q instanceof e)){k.lastIndex=0;var r=k.exec(q);if(r){m&&(n=r[1].length);var s=r.index-1+n,r=r[0].slice(n),t=r.length,u=s+t,v=q.slice(0,s+1),w=q.slice(u+1),x=[p,1];v&&x.push(v);var y=new e(h,l?c.tokenize(r,l):r,o);x.push(y),w&&x.push(w),Array.prototype.splice.apply(f,x)}}}}}return f},hooks:{all:{},add:function(a,b){var d=c.hooks.all;d[a]=d[a]||[],d[a].push(b)},run:function(a,b){var d=c.hooks.all[a];if(d&&d.length)for(var e,f=0;e=d[f++];)e(b)}}},d=c.Token=function(a,b,c){this.type=a,this.content=b,this.alias=c};return d.stringify=function(a,b,e){if("string"==typeof a)return a;if("Array"===c.util.type(a))return a.map(function(c){return d.stringify(c,b,a)}).join("");var f={type:a.type,content:d.stringify(a.content,b,e),tag:"span",classes:["token",a.type],attributes:{},language:b,parent:e};if("comment"==f.type&&(f.attributes.spellcheck="true"),a.alias){var g="Array"===c.util.type(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(f.classes,g)}c.hooks.run("wrap",f);var h="";for(var i in f.attributes)h+=(h?" ":"")+i+'="'+(f.attributes[i]||"")+'"';return"<"+f.tag+' class="'+f.classes.join(" ")+'" '+h+">"+f.content+""},b.document?void 0:b.addEventListener?(b.addEventListener("message",function(a){var d=JSON.parse(a.data),e=d.language,f=d.code,g=d.immediateClose;b.postMessage(c.highlight(f,c.languages[e],e)),g&&b.close()},!1),b.Prism):b.Prism}();return"undefined"!=typeof module&&module.exports&&(module.exports=c),"undefined"!=typeof global&&(global.Prism=c),c.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),c.languages.xml=c.languages.markup,c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,c.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},c.languages.css.atrule.inside.rest=c.util.clone(c.languages.css),c.languages.markup&&(c.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/i,inside:{tag:{pattern:/|<\/style>/i,inside:c.languages.markup.tag.inside},rest:c.languages.css},alias:"language-css"}}),c.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:c.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:c.languages.css}},alias:"language-css"}},c.languages.markup.tag)),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),c.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),c.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/i,inside:{tag:{pattern:/|<\/script>/i,inside:c.languages.markup.tag.inside},rest:c.languages.javascript},alias:"language-javascript"}}),c.languages.js=c.languages.javascript,c.languages.c=c.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),c.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0}}}}),delete c.languages.c["class-name"],delete c.languages.c["boolean"],c.languages.csharp=c.languages.extend("clike",{keyword:/\b(abstract|as|async|await|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|void|volatile|while|add|alias|ascending|async|await|descending|dynamic|from|get|global|group|into|join|let|orderby|partial|remove|select|set|value|var|where|yield)\b/,string:[/@("|')(\1\1|\\\1|\\?(?!\1)[\s\S])*\1/,/("|')(\\?.)*?\1/],number:/\b-?(0x[\da-f]+|\d*\.?\d+)\b/i}),c.languages.insertBefore("csharp","keyword",{preprocessor:{pattern:/(^\s*)#.*/m,lookbehind:!0}}),c.languages.cpp=c.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),c.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}}),c.languages.java=c.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,lookbehind:!0}}),c.languages.php=c.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z0-9_]{2,}\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/,lookbehind:!0}}),c.languages.insertBefore("php","class-name",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),c.languages.insertBefore("php","keyword",{delimiter:/\?>|<\?(?:php)?/i,variable:/\$\w+\b/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),c.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}}),c.languages.markup&&(c.hooks.add("before-highlight",function(a){"php"===a.language&&(a.tokenStack=[],a.backupCode=a.code,a.code=a.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>)/gi,function(b){return a.tokenStack.push(b),"{{{PHP"+a.tokenStack.length+"}}}"}))}),c.hooks.add("before-insert",function(a){"php"===a.language&&(a.code=a.backupCode,delete a.backupCode)}),c.hooks.add("after-highlight",function(a){if("php"===a.language){for(var b,d=0;b=a.tokenStack[d];d++)a.highlightedCode=a.highlightedCode.replace("{{{PHP"+(d+1)+"}}}",c.highlight(b,a.grammar,"php").replace(/\$/g,"$$$$"));a.element.innerHTML=a.highlightedCode}}),c.hooks.add("wrap",function(a){"php"===a.language&&"markup"===a.type&&(a.content=a.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g,'$1'))}),c.languages.insertBefore("php","comment",{markup:{pattern:/<[^?]\/?(.*?)>/,inside:c.languages.markup},php:/\{\{\{PHP[0-9]+\}\}\}/})),c.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:/"""[\s\S]+?"""|'''[\s\S]+?'''|("|')(?:\\?.)*?\1/,"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)[a-z0-9_]+/i,lookbehind:!0},keyword:/\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/,"boolean":/\b(?:True|False)\b/,number:/\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,punctuation:/[{}[\];(),.:]/},function(a){a.languages.ruby=a.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var b={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:a.util.clone(a.languages.ruby)}};a.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:b}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Fload|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,inside:{interpolation:b}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,inside:{interpolation:b}}]}(c),c}),d("tinymce/codesampleplugin/Utils",[],function(){function a(a){return a&&"PRE"==a.nodeName&&-1!==a.className.indexOf("language-")}function b(a){return function(b,c){return a(c)}}return{isCodeSample:a,trimArg:b}}),d("tinymce/codesampleplugin/Dialog",["tinymce/dom/DOMUtils","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Prism"],function(a,b,c){function d(a){var b=[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}],c=a.settings.codesample_languages;return c?c:b}function e(a,b,d){a.undoManager.transact(function(){var e=f(a);d=i.encode(d),e?(a.dom.setAttrib(e,"class","language-"+b),e.innerHTML=d,c.highlightElement(e),a.selection.select(e)):(a.insertContent('
'+d+"
"),a.selection.select(a.$("#__new").removeAttr("id")[0]))})}function f(a){var c=a.selection.getNode();return b.isCodeSample(c)?c:null}function g(a){var b=f(a);return b?b.textContent:""}function h(a){var b,c=f(a);return c?(b=c.className.match(/language-(\w+)/),b?b[1]:""):""}var i=a.DOM;return{open:function(a){a.windowManager.open({title:"Insert/Edit code sample",minWidth:Math.min(i.getViewPort().w,a.getParam("codesample_dialog_width",800)),minHeight:Math.min(i.getViewPort().h,a.getParam("codesample_dialog_height",650)),layout:"flex",direction:"column",align:"stretch",body:[{type:"listbox",name:"language",label:"Language",maxWidth:200,value:h(a),values:d(a)},{type:"textbox",name:"code",multiline:!0,spellcheck:!1,ariaLabel:"Code view",flex:1,style:"direction: ltr; text-align: left",classes:"monospace",value:g(a),autofocus:!0}],onSubmit:function(b){e(a,b.data.language,b.data.code)}})}}}),d("tinymce/codesampleplugin/Plugin",["tinymce/Env","tinymce/PluginManager","tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Utils"],function(a,b,c,d,e){var f,g=e.trimArg;b.add("codesample",function(b,h){function i(){var a;b.inline&&f||!b.inline&&j||(b.inline?f=!0:j=!0,a=b.dom.create("link",{rel:"stylesheet",href:h+"/css/prism.css"}),b.getDoc().getElementsByTagName("head")[0].appendChild(a))}var j,k=b.$;a.ceFalse&&(b.on("PreProcess",function(a){k("pre[contenteditable=false]",a.node).filter(g(e.isCodeSample)).each(function(a,b){var c=k(b),d=b.textContent;c.attr("class",k.trim(c.attr("class"))),c.removeAttr("contentEditable"),c.empty().append(k("").each(function(){this.textContent=d}))})}),b.on("SetContent",function(){var a=k("pre").filter(g(e.isCodeSample)).filter(function(a,b){return"false"!==b.contentEditable});a.length&&b.undoManager.transact(function(){a.each(function(a,d){k(d).find("br").each(function(a,c){c.parentNode.replaceChild(b.getDoc().createTextNode("\n"),c)}),d.contentEditable=!1,d.innerHTML=b.dom.encode(d.textContent),c.highlightElement(d),d.className=k.trim(d.className)})})}),b.addCommand("codesample",function(){d.open(b)}),b.addButton("codesample",{cmd:"codesample",title:"Insert/Edit code sample"}),b.on("init",i))})}),f(["tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Plugin"])}(this); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/contextmenu/plugin.min.js b/public/libs/tinymce/plugins/contextmenu/plugin.min.js index c25e98b23..13cd3d212 100644 --- a/public/libs/tinymce/plugins/contextmenu/plugin.min.js +++ b/public/libs/tinymce/plugins/contextmenu/plugin.min.js @@ -1 +1 @@ -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;hi;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;c>f;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};h("1",tinymce.PluginManager),h("2",tinymce.Env),h("3",tinymce.util.Promise),h("4",tinymce.util.URI),h("5",tinymce.util.Tools),h("6",tinymce.util.Delay),g("j",[],function(){function a(a,b){return c(document.createElement("canvas"),a,b)}function b(a){return a.getContext("2d")}function c(a,b,c){return a.width=b,a.height=c,a}return{create:a,resize:c,get2dContext:b}}),g("k",[],function(){function a(a){return a.naturalWidth||a.width}function b(a){return a.naturalHeight||a.height}return{getWidth:a,getHeight:b}}),g("l",[],function(){function a(a,b){return function(){a.apply(b,arguments)}}function b(b){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof b)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],h(b,a(d,this),a(e,this))}function c(a){var b=this;return null===this._state?void this._deferreds.push(a):void i(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function d(b){try{if(b===this)throw new TypeError("A promise cannot be resolved with itself.");if(b&&("object"==typeof b||"function"==typeof b)){var c=b.then;if("function"==typeof c)return void h(a(c,b),a(d,this),a(e,this))}this._state=!0,this._value=b,f.call(this)}catch(g){e.call(this,g)}}function e(a){this._state=!1,this._value=a,f.call(this)}function f(){for(var a=0,b=this._deferreds.length;b>a;a++)c.call(this,this._deferreds[a]);this._deferreds=null}function g(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function h(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}if(window.Promise)return window.Promise;var i=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return b.prototype["catch"]=function(a){return this.then(null,a)},b.prototype.then=function(a,d){var e=this;return new b(function(b,f){c.call(e,new g(a,d,b,f))})},b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&j(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},b}),g("m",[],function(){function a(a){var b=document.createElement("a");return b.href=a,b.pathname}function b(b){var c=a(b).split("."),d=c[c.length-1],e={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png"};return d&&(d=d.toLowerCase()),e[d]}return{guessMimeType:b}}),g("c",["l","j","m","k"],function(a,b,c,d){function e(b){return new a(function(a){function c(){b.removeEventListener("load",c),a(b)}b.complete?a(b):b.addEventListener("load",c)})}function f(a){return e(a).then(function(a){var c,e;return e=b.create(d.getWidth(a),d.getHeight(a)),c=b.get2dContext(e),c.drawImage(a,0,0),e})}function g(a){return e(a).then(function(a){var b=a.src;return 0===b.indexOf("blob:")?i(b):0===b.indexOf("data:")?j(b):f(a).then(function(a){return j(a.toDataURL(c.guessMimeType(b)))})})}function h(b){return new a(function(a){function c(){d.removeEventListener("load",c),a(d)}var d=new Image;d.addEventListener("load",c),d.src=URL.createObjectURL(b),d.complete&&c()})}function i(b){return new a(function(a){var c=new XMLHttpRequest;c.open("GET",b,!0),c.responseType="blob",c.onload=function(){200==this.status&&a(this.response)},c.send()})}function j(b){return new a(function(a){var c,d,e,f,g,h;if(b=b.split(","),f=/data:([^;]+)/.exec(b[0]),f&&(g=f[1]),c=atob(b[1]),window.WebKitBlobBuilder){for(h=new WebKitBlobBuilder,d=new ArrayBuffer(c.length),e=0;ec?a=c:b>a&&(a=b),a}function b(){return[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1]}function c(a,b){var c,d,e,f,g=[],h=new Array(10);for(c=0;5>c;c++){for(d=0;5>d;d++)g[d]=b[d+5*c];for(d=0;5>d;d++){for(f=0,e=0;5>e;e++)f+=a[d+5*e]*g[e];h[d+5*c]=f}}return h}function d(b,c){return c=a(c,0,1),b.map(function(b,d){return d%6===0?b=1-(1-b)*c:b*=c,a(b,0,1)})}function e(b,d){var e;return d=a(d,-1,1),d*=100,0>d?e=127+d/100*127:(e=d%1,e=0===e?l[d]:l[Math.floor(d)]*(1-e)+l[Math.floor(d)+1]*e,e=127*e+127),c(b,[e/127,0,0,0,.5*(127-e),0,e/127,0,0,.5*(127-e),0,0,e/127,0,.5*(127-e),0,0,0,1,0,0,0,0,0,1])}function f(b,d){var e,f,g,h;return d=a(d,-1,1),e=1+(d>0?3*d:d),f=.3086,g=.6094,h=.082,c(b,[f*(1-e)+e,g*(1-e),h*(1-e),0,0,f*(1-e),g*(1-e)+e,h*(1-e),0,0,f*(1-e),g*(1-e),h*(1-e)+e,0,0,0,0,0,1,0,0,0,0,0,1])}function g(b,d){var e,f,g,h,i;return d=a(d,-180,180)/180*Math.PI,e=Math.cos(d),f=Math.sin(d),g=.213,h=.715,i=.072,c(b,[g+e*(1-g)+f*-g,h+e*-h+f*-h,i+e*-i+f*(1-i),0,0,g+e*-g+.143*f,h+e*(1-h)+.14*f,i+e*-i+f*-.283,0,0,g+e*-g+f*-(1-g),h+e*-h+f*h,i+e*(1-i)+f*i,0,0,0,0,0,1,0,0,0,0,0,1])}function h(b,d){return d=a(255*d,-255,255),c(b,[1,0,0,0,d,0,1,0,0,d,0,0,1,0,d,0,0,0,1,0,0,0,0,0,1])}function i(b,d,e,f){return d=a(d,0,2),e=a(e,0,2),f=a(f,0,2),c(b,[d,0,0,0,0,0,e,0,0,0,0,0,f,0,0,0,0,0,1,0,0,0,0,0,1])}function j(b,e){return e=a(e,0,1),c(b,d([.393,.769,.189,0,0,.349,.686,.168,0,0,.272,.534,.131,0,0,0,0,0,1,0,0,0,0,0,1],e))}function k(b,e){return e=a(e,0,1),c(b,d([.33,.34,.33,0,0,.33,.34,.33,0,0,.33,.34,.33,0,0,0,0,0,1,0,0,0,0,0,1],e))}var l=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10];return{identity:b,adjust:d,multiply:c,adjustContrast:e,adjustBrightness:h,adjustSaturation:f,adjustHue:g,adjustColors:i,adjustSepia:j,adjustGrayscale:k}}),g("a",["j","k","c","n"],function(a,b,c,d){function e(d,e){return c.blobToImage(d).then(function(d){function f(a,b){var c,d,e,f,g,h=a.data,i=b[0],j=b[1],k=b[2],l=b[3],m=b[4],n=b[5],o=b[6],p=b[7],q=b[8],r=b[9],s=b[10],t=b[11],u=b[12],v=b[13],w=b[14],x=b[15],y=b[16],z=b[17],A=b[18],B=b[19];for(g=0;gc?a=c:b>a&&(a=b),a}var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;for(g=Math.round(Math.sqrt(c.length)),h=Math.floor(g/2),e=a.data,f=b.data,t=a.width,u=a.height,j=0;u>j;j++)for(i=0;t>i;i++){for(k=l=m=0,o=0;g>o;o++)for(n=0;g>n;n++)p=d(i+n-h,0,t-1),q=d(j+o-h,0,u-1),r=4*(q*t+p),s=c[o*g+n],k+=e[r]*s,l+=e[r+1]*s,m+=e[r+2]*s;r=4*(j*t+i),f[r]=d(k,0,255),f[r+1]=d(l,0,255),f[r+2]=d(m,0,255)}return b}var g,h,i=a.create(b.getWidth(d),b.getHeight(d)),j=a.get2dContext(i);return j.drawImage(d,0,0),k(d),g=j.getImageData(0,0,i.width,i.height),h=j.getImageData(0,0,i.width,i.height),h=f(g,h,e),j.putImageData(h,0,0),c.canvasToBlob(i)})}function g(d){return function(e,f){return c.blobToImage(e).then(function(e){function g(a,b){var c,d=a.data;for(c=0;ce?360+e:e,90!=e&&270!=e||b.resize(g,g.height,g.width),90!=e&&180!=e||(j=g.width),270!=e&&180!=e||(k=g.height),i.translate(j,k),i.rotate(e*Math.PI/180),i.drawImage(f,0,0),h(f),a.canvasToBlob(g,d.type)})}function e(d,e){return a.blobToImage(d).then(function(d){var f=b.create(c.getWidth(d),c.getHeight(d)),g=b.get2dContext(f);return"v"==e?(g.scale(1,-1),g.drawImage(d,0,-f.height)):(g.scale(-1,1),g.drawImage(d,-f.width,0)),h(d),a.canvasToBlob(f)})}function f(c,d,e,f,g){return a.blobToImage(c).then(function(c){var i=b.create(f,g),j=b.get2dContext(i);return j.drawImage(c,-d,-e),h(c),a.canvasToBlob(i)})}function g(c,d,e){return a.blobToImage(c).then(function(f){var g=b.create(d,e),i=b.get2dContext(g);return i.drawImage(f,0,0,d,e),h(f),a.canvasToBlob(g,c.type)})}var h=a.revokeImageUrl;return{rotate:d,flip:e,crop:f,resize:g}}),g("7",["a","b"],function(a,b){var c=function(b){return a.invert(b)},d=function(b){return a.sharpen(b)},e=function(b){return a.emboss(b)},f=function(b,c){return a.gamma(b,c)},g=function(b,c){return a.exposure(b,c)},h=function(b,c,d,e){return a.colorize(b,c,d,e)},i=function(b,c){return a.brightness(b,c)},j=function(b,c){return a.hue(b,c)},k=function(b,c){return a.saturate(b,c)},l=function(b,c){return a.contrast(b,c)},m=function(b,c){return a.grayscale(b,c)},n=function(b,c){return a.sepia(b,c)},o=function(a,c){return b.flip(a,c)},p=function(a,c,d,e,f){return b.crop(a,c,d,e,f)},q=function(a,c,d){return b.resize(a,c,d)},r=function(a,c){return b.rotate(a,c)};return{invert:c,sharpen:d,emboss:e,brightness:i,hue:j,saturate:k,contrast:l,grayscale:m,sepia:n,colorize:h,gamma:f,exposure:g,flip:o,crop:p,resize:q,rotate:r}}),g("8",["c"],function(a){var b=function(b){return a.blobToImage(b)},c=function(b){return a.imageToBlob(b)},d=function(b){return a.blobToDataUri(b)},e=function(b){return a.blobToBase64(b)};return{blobToImage:b,imageToBlob:c,blobToDataUri:d,blobToBase64:e}}),h("d",tinymce.dom.DOMUtils),h("e",tinymce.ui.Factory),h("f",tinymce.ui.Form),h("g",tinymce.ui.Container),h("o",tinymce.ui.Control),h("p",tinymce.ui.DragHelper),h("q",tinymce.geom.Rect),h("s",tinymce.dom.DomQuery),h("t",tinymce.util.Observable),g("r",["s","p","q","5","t"],function(a,b,c,d,e){var f=0;return function(g,h,i,j){function k(a,b){return{x:b.x+a.x,y:b.y+a.y,w:b.w,h:b.h}}function l(a,b){return{x:b.x-a.x,y:b.y-a.y,w:b.w,h:b.h}}function m(){return l(i,g)}function n(){function e(a){var d;return new b(A,{document:j.ownerDocument,handle:A+"-"+a.name,start:function(){d=g},drag:function(b){var e,f,h,j,k;e=d.x,f=d.y,h=d.w,j=d.h,e+=b.deltaX*a.deltaX,f+=b.deltaY*a.deltaY,h+=b.deltaX*a.deltaW,j+=b.deltaY*a.deltaH,20>h&&(h=20),20>j&&(j=20),k=g=c.clamp({x:e,y:f,w:h,h:j},i,"move"==a.name),k=l(i,k),v.fire("updateRect",{rect:k}),s(k)}})}a('
').appendTo(j),d.each(y,function(b){a("#"+A,j).append('