Merge branch 'master' into release
This commit is contained in:
commit
a4c9a8491b
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UpgradeDatabaseEncoding extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'bookstack:db-utf8mb4 {--database= : The database connection to use.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate SQL commands to upgrade the database to UTF8mb4';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$connection = DB::getDefaultConnection();
|
||||
if ($this->option('database') !== null) {
|
||||
DB::setDefaultConnection($this->option('database'));
|
||||
}
|
||||
|
||||
$database = DB::getDatabaseName();
|
||||
$tables = DB::select('SHOW TABLES');
|
||||
$this->line('ALTER DATABASE `'.$database.'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
$this->line('USE `'.$database.'`;');
|
||||
$key = 'Tables_in_' . $database;
|
||||
foreach ($tables as $table) {
|
||||
$tableName = $table->$key;
|
||||
$this->line('ALTER TABLE `'.$tableName.'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
}
|
||||
|
||||
DB::setDefaultConnection($connection);
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ class Kernel extends ConsoleKernel
|
|||
Commands\ClearActivity::class,
|
||||
Commands\ClearRevisions::class,
|
||||
Commands\RegeneratePermissions::class,
|
||||
Commands\RegenerateSearch::class
|
||||
Commands\RegenerateSearch::class,
|
||||
Commands\UpgradeDatabaseEncoding::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -571,7 +571,7 @@ class EntityRepo
|
|||
|
||||
$draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
|
||||
$draftPage->html = $this->formatHtml($input['html']);
|
||||
$draftPage->text = strip_tags($draftPage->html);
|
||||
$draftPage->text = $this->pageToPlainText($draftPage->html);
|
||||
$draftPage->draft = false;
|
||||
$draftPage->revision_count = 1;
|
||||
|
||||
|
@ -713,6 +713,17 @@ class EntityRepo
|
|||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain text version of a page's content.
|
||||
* @param Page $page
|
||||
* @return string
|
||||
*/
|
||||
public function pageToPlainText(Page $page)
|
||||
{
|
||||
$html = $this->renderPage($page);
|
||||
return strip_tags($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new draft page instance.
|
||||
* @param Book $book
|
||||
|
@ -816,7 +827,7 @@ class EntityRepo
|
|||
$userId = user()->id;
|
||||
$page->fill($input);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
if (setting('app-editor') !== 'markdown') $page->markdown = '';
|
||||
$page->updated_by = $userId;
|
||||
$page->revision_count++;
|
||||
|
@ -933,7 +944,7 @@ class EntityRepo
|
|||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
$page->fill($revision->toArray());
|
||||
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
|
||||
$page->text = strip_tags($page->html);
|
||||
$page->text = $this->pageToPlainText($page->html);
|
||||
$page->updated_by = user()->id;
|
||||
$page->save();
|
||||
$this->searchService->indexEntity($page);
|
||||
|
@ -953,7 +964,7 @@ class EntityRepo
|
|||
if ($page->draft) {
|
||||
$page->fill($data);
|
||||
if (isset($data['html'])) {
|
||||
$page->text = strip_tags($data['html']);
|
||||
$page->text = $this->pageToPlainText($data['html']);
|
||||
}
|
||||
$page->save();
|
||||
return $page;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateDbEncodingToUt8mb4 extends Migration
|
||||
|
@ -13,16 +11,9 @@ class UpdateDbEncodingToUt8mb4 extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
$database = DB::getDatabaseName();
|
||||
$tables = DB::select('SHOW TABLES');
|
||||
$pdo = DB::getPdo();
|
||||
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
$pdo->exec('ALTER DATABASE `'.$database.'` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
|
||||
$key = 'Tables_in_' . $database;
|
||||
foreach ($tables as $table) {
|
||||
$tableName = $table->$key;
|
||||
$pdo->exec('ALTER TABLE `'.$tableName.'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
|
||||
}
|
||||
// Migration removed due to issues during live migration.
|
||||
// Instead you can run the command `artisan bookstack:db-utf8mb4`
|
||||
// which will generate out the SQL request to upgrade your DB to utf8mb4.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,15 +23,6 @@ class UpdateDbEncodingToUt8mb4 extends Migration
|
|||
*/
|
||||
public function down()
|
||||
{
|
||||
$database = DB::getDatabaseName();
|
||||
$tables = DB::select('SHOW TABLES');
|
||||
$pdo = DB::getPdo();
|
||||
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
$pdo->exec('ALTER DATABASE `'.$database.'` CHARACTER SET utf8 COLLATE utf8_unicode_ci');
|
||||
$key = 'Tables_in_' . $database;
|
||||
foreach ($tables as $table) {
|
||||
$tableName = $table->$key;
|
||||
$pdo->exec('ALTER TABLE `'.$tableName.'` CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci');
|
||||
}
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,7 +379,7 @@ module.exports = function (ngApp, events) {
|
|||
*/
|
||||
$scope.discardDraft = function () {
|
||||
let url = window.baseUrl('/ajax/page/' + pageId);
|
||||
$http.get(url).then((responseData) => {
|
||||
$http.get(url).then(responseData => {
|
||||
if (autoSave) $interval.cancel(autoSave);
|
||||
$scope.draftText = trans('entities.pages_editing_page');
|
||||
$scope.isUpdateDraft = false;
|
||||
|
|
|
@ -123,25 +123,31 @@ module.exports = function (ngApp, events) {
|
|||
restrict: 'A',
|
||||
link: function (scope, element, attrs) {
|
||||
const menu = element.find('ul');
|
||||
element.find('[dropdown-toggle]').on('click', function () {
|
||||
|
||||
function hide() {
|
||||
menu.hide();
|
||||
menu.removeClass('anim menuIn');
|
||||
}
|
||||
|
||||
function show() {
|
||||
menu.show().addClass('anim menuIn');
|
||||
element.mouseleave(hide);
|
||||
|
||||
// Focus on input if exist in dropdown and hide on enter press
|
||||
let inputs = menu.find('input');
|
||||
let hasInput = inputs.length > 0;
|
||||
if (hasInput) {
|
||||
inputs.first().focus();
|
||||
element.on('keypress', 'input', event => {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
menu.hide();
|
||||
menu.removeClass('anim menuIn');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
element.mouseleave(function () {
|
||||
menu.hide();
|
||||
menu.removeClass('anim menuIn');
|
||||
});
|
||||
if (inputs.length > 0) inputs.first().focus();
|
||||
}
|
||||
|
||||
// Hide menu on option click
|
||||
element.on('click', '> ul a', hide);
|
||||
// Show dropdown on toggle click.
|
||||
element.find('[dropdown-toggle]').on('click', show);
|
||||
// Hide menu on enter press in inputs
|
||||
element.on('keypress', 'input', event => {
|
||||
if (event.keyCode !== 13) return true;
|
||||
event.preventDefault();
|
||||
hide();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -142,7 +142,6 @@ form.search-box {
|
|||
color: #aaa;
|
||||
padding: 0 $-xs;
|
||||
}
|
||||
|
||||
.faded {
|
||||
a, button, span, span > div {
|
||||
color: #666;
|
||||
|
@ -178,6 +177,8 @@ form.search-box {
|
|||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.action-buttons .dropdown-container:last-child a {
|
||||
padding-right: 0;
|
||||
padding-left: $-s;
|
||||
|
@ -196,6 +197,25 @@ form.search-box {
|
|||
}
|
||||
}
|
||||
|
||||
@include smaller-than($m) {
|
||||
.breadcrumbs .text-button, .action-buttons .text-button {
|
||||
padding: $-s $-xs;
|
||||
}
|
||||
.action-buttons .dropdown-container:last-child a {
|
||||
padding-left: $-xs;
|
||||
}
|
||||
.breadcrumbs .text-button {
|
||||
font-size: 0;
|
||||
}
|
||||
.breadcrumbs a i {
|
||||
font-size: $fs-m;
|
||||
padding-right: 0;
|
||||
}
|
||||
.breadcrumbs span.sep {
|
||||
padding: 0 $-xxs;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
text-align: center;
|
||||
a, .tab-item {
|
||||
|
|
|
@ -152,6 +152,14 @@ pre {
|
|||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
pre {
|
||||
padding-left: 12px;
|
||||
}
|
||||
pre:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
display: block;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<div class="faded-small toolbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 faded">
|
||||
<div class="col-sm-6 col-xs-1 faded">
|
||||
@include('books._breadcrumbs', ['book' => $book])
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-6 col-xs-11">
|
||||
<div class="action-buttons faded">
|
||||
<span dropdown class="dropdown-container">
|
||||
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
|
||||
|
@ -50,7 +50,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="container" id="entity-dashboard" entity-id="{{ $book->id }}" entity-type="book">
|
||||
<div ng-non-bindable class="container" id="entity-dashboard" entity-id="{{ $book->id }}" entity-type="book">
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
|
||||
|
@ -112,7 +112,7 @@
|
|||
@endif
|
||||
|
||||
<div class="search-box">
|
||||
<form v-on:submit="searchBook">
|
||||
<form v-on:submit.prevent="searchBook">
|
||||
<input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
|
||||
<button type="submit"><i class="zmdi zmdi-search"></i></button>
|
||||
<button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<div class="faded-small toolbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 faded" ng-non-bindable>
|
||||
<div class="col-sm-6 col-xs-3 faded" ng-non-bindable>
|
||||
@include('chapters._breadcrumbs', ['chapter' => $chapter])
|
||||
</div>
|
||||
<div class="col-sm-4 faded">
|
||||
<div class="col-sm-6 col-xs-9 faded">
|
||||
<div class="action-buttons">
|
||||
<span dropdown class="dropdown-container">
|
||||
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
|
||||
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="container" id="entity-dashboard" entity-id="{{ $chapter->id }}" entity-type="chapter">
|
||||
<div class="container" id="entity-dashboard" ng-non-bindable entity-id="{{ $chapter->id }}" entity-type="chapter">
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<h1>{{ $chapter->name }}</h1>
|
||||
|
@ -116,7 +116,7 @@
|
|||
@endif
|
||||
|
||||
<div class="search-box">
|
||||
<form v-on:submit="searchBook">
|
||||
<form v-on:submit.prevent="searchBook">
|
||||
<input v-model="searchTerm" v-on:change="checkSearchForm()" type="text" name="term" placeholder="{{ trans('entities.chapters_search_this') }}">
|
||||
<button type="submit"><i class="zmdi zmdi-search"></i></button>
|
||||
<button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<div class="faded-small toolbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 faded">
|
||||
<div class="col-sm-8 col-xs-5 faded">
|
||||
@include('pages._breadcrumbs', ['page' => $page])
|
||||
</div>
|
||||
<div class="col-sm-6 faded">
|
||||
<div class="col-sm-4 col-xs-7 faded">
|
||||
<div class="action-buttons">
|
||||
<span dropdown class="dropdown-container">
|
||||
<div dropdown-toggle class="text-button text-primary"><i class="zmdi zmdi-open-in-new"></i>{{ trans('entities.export') }}</div>
|
||||
|
|
Loading…
Reference in New Issue