mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-02-18 03:09:40 +03:00
Compare commits
340 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22df25a480 | ||
|
|
8b30c7f02e | ||
|
|
03eb63ec77 | ||
|
|
3ed5426315 | ||
|
|
90bf13c1ab | ||
|
|
d17eb0f54c | ||
|
|
ac7e3977de | ||
|
|
d7edc389a6 | ||
|
|
56d5af1336 | ||
|
|
06cf175b08 | ||
|
|
b65abd25e0 | ||
|
|
a5e49f642b | ||
|
|
91444e83fd | ||
|
|
6063ac4a11 | ||
|
|
02fd1c48ed | ||
|
|
6ee35f55cc | ||
|
|
261e57fc4e | ||
|
|
bc1302a8d8 | ||
|
|
eeb2b8cbe5 | ||
|
|
b167ae795e | ||
|
|
6ebe8bf619 | ||
|
|
009af9736e | ||
|
|
7668a999a2 | ||
|
|
ed88c623d6 | ||
|
|
873b1099f8 | ||
|
|
6a54733f2b | ||
|
|
7a5bd23909 | ||
|
|
6bb7b5465f | ||
|
|
0b967d84ad | ||
|
|
2261308415 | ||
|
|
7b5edb4d62 | ||
|
|
8378f06889 | ||
|
|
10dc851697 | ||
|
|
757cdddc7c | ||
|
|
65579214e2 | ||
|
|
d89440d198 | ||
|
|
08e58bab79 | ||
|
|
d29b177c84 | ||
|
|
151d72e42c | ||
|
|
711ba258f1 | ||
|
|
df4d4f30f1 | ||
|
|
f094837709 | ||
|
|
e27cbb9dce | ||
|
|
bdba25b6f2 | ||
|
|
6b2581de63 | ||
|
|
1031c61d0c | ||
|
|
46fc0e5026 | ||
|
|
332f678ed0 | ||
|
|
df95e99680 | ||
|
|
5a6d544db7 | ||
|
|
0d5d77d8ab | ||
|
|
db51cee2d8 | ||
|
|
a988438946 | ||
|
|
3bf7cac030 | ||
|
|
79c3a07e9a | ||
|
|
16117d329c | ||
|
|
9758872baf | ||
|
|
e90da18ada | ||
|
|
a08d80e1cc | ||
|
|
b711bc6816 | ||
|
|
247e6dba85 | ||
|
|
2b3d6e4e4a | ||
|
|
6b1980c4f3 | ||
|
|
9ba29770e1 | ||
|
|
3d375fae55 | ||
|
|
c99a50de2c | ||
|
|
1a32b25b5e | ||
|
|
481aa5b5b0 | ||
|
|
c943eb4d0d | ||
|
|
aca6de49b0 | ||
|
|
5fd04fa470 | ||
|
|
87339e4cd0 | ||
|
|
a9eb058dad | ||
|
|
61fad6a665 | ||
|
|
fa4bee2d98 | ||
|
|
ce63260fa6 | ||
|
|
a3557d5bb2 | ||
|
|
9ca22976c3 | ||
|
|
9e2934fe17 | ||
|
|
2259263214 | ||
|
|
762cf5f183 | ||
|
|
07175f2b3e | ||
|
|
6258175922 | ||
|
|
15736777a0 | ||
|
|
0c4ddf16a5 | ||
|
|
74a5e3113e | ||
|
|
df0a982433 | ||
|
|
212f924ffa | ||
|
|
09936566dd | ||
|
|
9469c04eab | ||
|
|
941bb73a68 | ||
|
|
8e652f5d8f | ||
|
|
eec1b21928 | ||
|
|
1baeb7bec9 | ||
|
|
61475ca0a3 | ||
|
|
244c5a3ebb | ||
|
|
39e7ac1c15 | ||
|
|
ab7f5def04 | ||
|
|
75915e8a94 | ||
|
|
9bde0ae4ea | ||
|
|
cd7e727f8c | ||
|
|
2329a5cedf | ||
|
|
d1a4ff9308 | ||
|
|
9a3bc27ef4 | ||
|
|
f8315fb9c4 | ||
|
|
bb62dee5a2 | ||
|
|
8acc188e16 | ||
|
|
874386ceab | ||
|
|
9dfbea8bf9 | ||
|
|
c1627a1468 | ||
|
|
576a59a693 | ||
|
|
fec6c65b78 | ||
|
|
f8c046d182 | ||
|
|
9ca2184f09 | ||
|
|
fd449582bd | ||
|
|
621142a46e | ||
|
|
0275d2ad58 | ||
|
|
41f56e659d | ||
|
|
5034f21394 | ||
|
|
e02fcbe983 | ||
|
|
1c88d21abf | ||
|
|
c1a1bc0135 | ||
|
|
fea5630ea4 | ||
|
|
e3f2bde26d | ||
|
|
756ee0b172 | ||
|
|
c81b63b56f | ||
|
|
70ee28ee13 | ||
|
|
1c9ecc3edd | ||
|
|
9bd5d6a422 | ||
|
|
1e41ccbc7a | ||
|
|
6200948eec | ||
|
|
0a402e3c63 | ||
|
|
55759bd22a | ||
|
|
d8e1f52ddd | ||
|
|
baea92b206 | ||
|
|
cfc05c1b22 | ||
|
|
ebf78d49a8 | ||
|
|
4cb4c9e568 | ||
|
|
36f524a354 | ||
|
|
b60d2190ac | ||
|
|
2f8b8c580d | ||
|
|
5187d3fa78 | ||
|
|
9c07741972 | ||
|
|
8fcbe44d3e | ||
|
|
7f902e41c7 | ||
|
|
3966fb1df6 | ||
|
|
830614fb19 | ||
|
|
76ae5c7398 | ||
|
|
769935f99e | ||
|
|
6920d6eef1 | ||
|
|
b5cd3bff3c | ||
|
|
ac07cb41b6 | ||
|
|
e8fa58f201 | ||
|
|
1f6994b62c | ||
|
|
47d82a1ac2 | ||
|
|
703d579561 | ||
|
|
ed375bfaf7 | ||
|
|
3da8c01c1f | ||
|
|
295f520f21 | ||
|
|
fba7ae923d | ||
|
|
28a9bd514f | ||
|
|
666c86b108 | ||
|
|
194293664f | ||
|
|
039ee5d06c | ||
|
|
afc66b3c3d | ||
|
|
a04b31866d | ||
|
|
db3dde98ef | ||
|
|
0d6a6b5b63 | ||
|
|
4df3267521 | ||
|
|
d3e4a1a6f9 | ||
|
|
b023699f1b | ||
|
|
f338dbe3f8 | ||
|
|
ab07f7df6c | ||
|
|
a59d73de7b | ||
|
|
1ac7618bb1 | ||
|
|
2a069880cd | ||
|
|
5e5928a8a6 | ||
|
|
d6e87420c3 | ||
|
|
79cfd39fde | ||
|
|
e9831a7507 | ||
|
|
0c802d1f86 | ||
|
|
b7a96c6466 | ||
|
|
9126da6299 | ||
|
|
fea139d8e7 | ||
|
|
ac7a8a8e1e | ||
|
|
bbaa2f4cda | ||
|
|
9d61eecd81 | ||
|
|
21247e10d0 | ||
|
|
c1fc06ae34 | ||
|
|
a0eb3d1079 | ||
|
|
164aea3a3a | ||
|
|
ec83f83017 | ||
|
|
5cd08ab2f5 | ||
|
|
072f6b103e | ||
|
|
b4dcde252b | ||
|
|
0b2c3c1aa7 | ||
|
|
0dc9d0bed7 | ||
|
|
a2a2e37797 | ||
|
|
3d9819f97c | ||
|
|
b2b4a24d7c | ||
|
|
7557d6d619 | ||
|
|
57eeb9b0a3 | ||
|
|
813c7d5902 | ||
|
|
4b645a82c7 | ||
|
|
d599b77b6f | ||
|
|
f200b4183d | ||
|
|
33642c20ec | ||
|
|
26e93dc8c1 | ||
|
|
a4c9a8491b | ||
|
|
2704962277 | ||
|
|
6bcd89acf7 | ||
|
|
433cb9b3b2 | ||
|
|
7f43372dd4 | ||
|
|
b12e2ceada | ||
|
|
bc067e9ad4 | ||
|
|
245294fbc5 | ||
|
|
f38bc75ab4 | ||
|
|
3407900abb | ||
|
|
684c20c4ea | ||
|
|
6ef522df7e | ||
|
|
3b771f2976 | ||
|
|
afc56c12fe | ||
|
|
5eeed03dcd | ||
|
|
0d98b4ce5e | ||
|
|
ae2ec43a82 | ||
|
|
265ed34ffd | ||
|
|
711dcb4a48 | ||
|
|
3079a9f4de | ||
|
|
a7d2cfdee2 | ||
|
|
a149e87ca7 | ||
|
|
854fd52a27 | ||
|
|
3d808ac75f | ||
|
|
39b924f158 | ||
|
|
a488ef6b00 | ||
|
|
6d66c38c12 | ||
|
|
922964ecf2 | ||
|
|
0c70416b5c | ||
|
|
770f30c3a8 | ||
|
|
b4044e6c3a | ||
|
|
9872767f20 | ||
|
|
dd4d2f4696 | ||
|
|
e5dc0e6bb8 | ||
|
|
85fbe820c4 | ||
|
|
832f8eaa94 | ||
|
|
3435dcc91e | ||
|
|
1ed74b8598 | ||
|
|
fd36978c13 | ||
|
|
1278a0b818 | ||
|
|
6a6516ddd5 | ||
|
|
67bc7007aa | ||
|
|
1fe8f13503 | ||
|
|
8f3adcda5d | ||
|
|
9e0c931573 | ||
|
|
21a8df78ee | ||
|
|
7f8351e044 | ||
|
|
afc1ecafe9 | ||
|
|
ab6ff5fda2 | ||
|
|
b0ba1a43a9 | ||
|
|
e919cab3d1 | ||
|
|
f37509062e | ||
|
|
24ee78ccd8 | ||
|
|
d37b398e79 | ||
|
|
7a724f9134 | ||
|
|
f3b2e0fb91 | ||
|
|
844976c85b | ||
|
|
f0d914abbf | ||
|
|
70ee636d87 | ||
|
|
b35f6dbb03 | ||
|
|
2ea7e10923 | ||
|
|
e7f8188ee5 | ||
|
|
0ed3023b42 | ||
|
|
3fd61a3600 | ||
|
|
a663fc8aa8 | ||
|
|
d84315fff8 | ||
|
|
144a6e469d | ||
|
|
c5f11e4516 | ||
|
|
16a09e8ff6 | ||
|
|
f51db4b9f6 | ||
|
|
6ad24a6bee | ||
|
|
5b736c3b36 | ||
|
|
cc553cc93d | ||
|
|
e88a06291e | ||
|
|
6eccb3d5b9 | ||
|
|
026de8c5ca | ||
|
|
e10d4b91cf | ||
|
|
d089eaf754 | ||
|
|
bb2d85965f | ||
|
|
d99fd1fd65 | ||
|
|
947c58f227 | ||
|
|
bce5fdd5cd | ||
|
|
fdf139edb2 | ||
|
|
af72f0d490 | ||
|
|
8924618d12 | ||
|
|
6557fbb666 | ||
|
|
574ee820a9 | ||
|
|
7d02f77e67 | ||
|
|
fd50efb503 | ||
|
|
9dbd7fa618 | ||
|
|
8dab31b87a | ||
|
|
e155c52256 | ||
|
|
c76e7c706c | ||
|
|
552943c033 | ||
|
|
4efe3b41da | ||
|
|
218376a41c | ||
|
|
e647ec22b1 | ||
|
|
38fe756725 | ||
|
|
652a67ad65 | ||
|
|
5bd9da6054 | ||
|
|
7c6fe8c4e2 | ||
|
|
689d1eb082 | ||
|
|
06d75e1804 | ||
|
|
9558f84b97 | ||
|
|
2fd421b115 | ||
|
|
6ff440e677 | ||
|
|
0bda5554dd | ||
|
|
860d4d4be5 | ||
|
|
9a97995f18 | ||
|
|
1a1e71cd60 | ||
|
|
34802ff8a6 | ||
|
|
0ff5aad9c0 | ||
|
|
03e5d61798 | ||
|
|
4f231d1bf0 | ||
|
|
8b82753218 | ||
|
|
3368fe42d8 | ||
|
|
c3ea0d333e | ||
|
|
d447355a61 | ||
|
|
8e2437498f | ||
|
|
9de85283cd | ||
|
|
b3d4c199ae | ||
|
|
4e71a5a47b | ||
|
|
410e967eb1 | ||
|
|
388f2f40dc | ||
|
|
148350009c | ||
|
|
70991fc1e5 | ||
|
|
e5c4e0ac86 | ||
|
|
397db04428 | ||
|
|
cd6572b61a | ||
|
|
581881d0ca | ||
|
|
d2efc2f47f | ||
|
|
4cc73657a1 |
@@ -46,6 +46,9 @@ GITHUB_APP_ID=false
|
||||
GITHUB_APP_SECRET=false
|
||||
GOOGLE_APP_ID=false
|
||||
GOOGLE_APP_SECRET=false
|
||||
OKTA_BASE_URL=false
|
||||
OKTA_KEY=false
|
||||
OKTA_SECRET=false
|
||||
|
||||
# External services such as Gravatar
|
||||
DISABLE_EXTERNAL_SERVICES=false
|
||||
|
||||
12
.github/ISSUE_TEMPLATE.md
vendored
12
.github/ISSUE_TEMPLATE.md
vendored
@@ -4,10 +4,18 @@ Desired Feature:
|
||||
|
||||
### For Bug Reports
|
||||
|
||||
* BookStack Version:
|
||||
* BookStack Version *(Found in settings, Please don't put 'latest')*:
|
||||
* PHP Version:
|
||||
* MySQL Version:
|
||||
|
||||
##### Expected Behavior
|
||||
|
||||
##### Actual Behavior
|
||||
|
||||
|
||||
##### Current Behavior
|
||||
|
||||
|
||||
|
||||
##### Steps to Reproduce
|
||||
|
||||
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -2,22 +2,22 @@
|
||||
/node_modules
|
||||
Homestead.yaml
|
||||
.env
|
||||
/public/dist
|
||||
.idea
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/public/dist
|
||||
/public/plugins
|
||||
/public/css/*.map
|
||||
/public/js/*.map
|
||||
/public/bower
|
||||
/public/build/
|
||||
/storage/images
|
||||
_ide_helper.php
|
||||
/storage/debugbar
|
||||
.phpstorm.meta.php
|
||||
yarn.lock
|
||||
/bin
|
||||
nbproject
|
||||
.buildpath
|
||||
|
||||
.project
|
||||
|
||||
.settings/org.eclipse.wst.common.project.facet.core.xml
|
||||
|
||||
.settings/org.eclipse.php.core.prefs
|
||||
.settings/
|
||||
|
||||
@@ -2,7 +2,8 @@ dist: trusty
|
||||
sudo: false
|
||||
language: php
|
||||
php:
|
||||
- 7.0
|
||||
- 7.0.20
|
||||
- 7.1.9
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -14,7 +15,6 @@ before_script:
|
||||
- mysql -u root -e "GRANT ALL ON \`bookstack-test\`.* TO 'bookstack-test'@'localhost';"
|
||||
- mysql -u root -e "FLUSH PRIVILEGES;"
|
||||
- phpenv config-rm xdebug.ini
|
||||
- composer dump-autoload --no-interaction
|
||||
- composer install --prefer-dist --no-interaction
|
||||
- php artisan clear-compiled -n
|
||||
- php artisan optimize -n
|
||||
@@ -25,4 +25,4 @@ after_failure:
|
||||
- cat storage/logs/laravel.log
|
||||
|
||||
script:
|
||||
- phpunit
|
||||
- phpunit
|
||||
|
||||
29
app/Book.php
29
app/Book.php
@@ -3,7 +3,7 @@
|
||||
class Book extends Entity
|
||||
{
|
||||
|
||||
protected $fillable = ['name', 'description'];
|
||||
protected $fillable = ['name', 'description', 'image_id'];
|
||||
|
||||
/**
|
||||
* Get the url for this book.
|
||||
@@ -18,6 +18,33 @@ class Book extends Entity
|
||||
return baseUrl('/books/' . urlencode($this->slug));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns book cover image, if book cover not exists return default cover image.
|
||||
* @param int $width - Width of the image
|
||||
* @param int $height - Height of the image
|
||||
* @return string
|
||||
*/
|
||||
public function getBookCover($width = 440, $height = 250)
|
||||
{
|
||||
$default = baseUrl('/book_default_cover.png');
|
||||
if (!$this->image_id) return $default;
|
||||
|
||||
try {
|
||||
$cover = $this->cover ? baseUrl($this->cover->getThumb($width, $height, false)) : $default;
|
||||
} catch (\Exception $err) {
|
||||
$cover = $default;
|
||||
}
|
||||
return $cover;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cover image of the book
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function cover()
|
||||
{
|
||||
return $this->belongsTo(Image::class, 'image_id');
|
||||
}
|
||||
/*
|
||||
* Get the edit url for this book.
|
||||
* @return string
|
||||
|
||||
43
app/Comment.php
Normal file
43
app/Comment.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php namespace BookStack;
|
||||
|
||||
class Comment extends Ownable
|
||||
{
|
||||
protected $fillable = ['text', 'html', 'parent_id'];
|
||||
protected $appends = ['created', 'updated'];
|
||||
|
||||
/**
|
||||
* Get the entity that this comment belongs to
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function entity()
|
||||
{
|
||||
return $this->morphTo('entity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a comment has been updated since creation.
|
||||
* @return bool
|
||||
*/
|
||||
public function isUpdated()
|
||||
{
|
||||
return $this->updated_at->timestamp > $this->created_at->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get created date as a relative diff.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCreatedAttribute()
|
||||
{
|
||||
return $this->created_at->diffForHumans();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updated date as a relative diff.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUpdatedAttribute()
|
||||
{
|
||||
return $this->updated_at->diffForHumans();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ class RegenerateSearch extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
protected $description = 'Re-index all content for searching';
|
||||
|
||||
protected $searchService;
|
||||
|
||||
|
||||
57
app/Console/Commands/UpgradeDatabaseEncoding.php
Normal file
57
app/Console/Commands/UpgradeDatabaseEncoding.php
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,7 @@ class Kernel extends ConsoleKernel
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\ClearViews::class,
|
||||
Commands\ClearActivity::class,
|
||||
Commands\ClearRevisions::class,
|
||||
Commands\RegeneratePermissions::class,
|
||||
Commands\RegenerateSearch::class
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -28,4 +24,14 @@ class Kernel extends ConsoleKernel
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php namespace BookStack;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
class Entity extends Ownable
|
||||
{
|
||||
|
||||
@@ -65,6 +67,17 @@ class Entity extends Ownable
|
||||
return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comments for an entity
|
||||
* @param bool $orderByCreated
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function comments($orderByCreated = true)
|
||||
{
|
||||
$query = $this->morphMany(Comment::class, 'entity');
|
||||
return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the related search terms.
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
|
||||
|
||||
@@ -26,10 +26,10 @@ class Handler extends ExceptionHandler
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return mixed
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
{
|
||||
@@ -103,4 +103,16 @@ class Handler extends ExceptionHandler
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a validation exception into a JSON response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Validation\ValidationException $exception
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function invalidJson($request, ValidationException $exception)
|
||||
{
|
||||
return response()->json($exception->errors(), $exception->status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,13 +72,13 @@ class LoginController extends Controller
|
||||
// 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')) {
|
||||
if (!$user->exists && $user->email === null && !$request->filled('email')) {
|
||||
$request->flash();
|
||||
session()->flash('request-email', true);
|
||||
return redirect('/login');
|
||||
}
|
||||
|
||||
if (!$user->exists && $user->email === null && $request->has('email')) {
|
||||
if (!$user->exists && $user->email === null && $request->filled('email')) {
|
||||
$user->email = $request->get('email');
|
||||
}
|
||||
|
||||
@@ -102,12 +102,21 @@ class LoginController extends Controller
|
||||
|
||||
/**
|
||||
* Show the application login form.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getLogin()
|
||||
public function getLogin(Request $request)
|
||||
{
|
||||
$socialDrivers = $this->socialAuthService->getActiveDrivers();
|
||||
$authMethod = config('auth.method');
|
||||
|
||||
if ($request->has('email')) {
|
||||
session()->flashInput([
|
||||
'email' => $request->get('email'),
|
||||
'password' => (config('app.env') === 'demo') ? $request->get('password', '') : ''
|
||||
]);
|
||||
}
|
||||
|
||||
return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -52,7 +53,7 @@ class RegisterController extends Controller
|
||||
*/
|
||||
public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
|
||||
{
|
||||
$this->middleware('guest')->except(['socialCallback', 'detachSocialAccount']);
|
||||
$this->middleware('guest')->only(['getRegister', 'postRegister', 'socialRegister']);
|
||||
$this->socialAuthService = $socialAuthService;
|
||||
$this->emailConfirmationService = $emailConfirmationService;
|
||||
$this->userRepo = $userRepo;
|
||||
@@ -103,7 +104,7 @@ class RegisterController extends Controller
|
||||
* @param Request|\Illuminate\Http\Request $request
|
||||
* @return Response
|
||||
* @throws UserRegistrationException
|
||||
* @throws \Illuminate\Foundation\Validation\ValidationException
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function postRegister(Request $request)
|
||||
{
|
||||
@@ -230,7 +231,6 @@ class RegisterController extends Controller
|
||||
return redirect('/register/confirm');
|
||||
}
|
||||
|
||||
$this->emailConfirmationService->sendConfirmation($user);
|
||||
session()->flash('success', trans('auth.email_confirm_resent'));
|
||||
return redirect('/register/confirm');
|
||||
}
|
||||
@@ -250,21 +250,30 @@ class RegisterController extends Controller
|
||||
/**
|
||||
* The callback for social login services.
|
||||
* @param $socialDriver
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws SocialSignInException
|
||||
* @throws UserRegistrationException
|
||||
* @throws \BookStack\Exceptions\SocialDriverNotConfigured
|
||||
* @throws ConfirmationEmailException
|
||||
*/
|
||||
public function socialCallback($socialDriver)
|
||||
public function socialCallback($socialDriver, Request $request)
|
||||
{
|
||||
if (session()->has('social-callback')) {
|
||||
$action = session()->pull('social-callback');
|
||||
if ($action == 'login') {
|
||||
return $this->socialAuthService->handleLoginCallback($socialDriver);
|
||||
} elseif ($action == 'register') {
|
||||
return $this->socialRegisterCallback($socialDriver);
|
||||
}
|
||||
} else {
|
||||
if (!session()->has('social-callback')) {
|
||||
throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
|
||||
}
|
||||
|
||||
// Check request for error information
|
||||
if ($request->has('error') && $request->has('error_description')) {
|
||||
throw new SocialSignInException(trans('errors.social_login_bad_response', [
|
||||
'socialAccount' => $socialDriver,
|
||||
'error' => $request->get('error_description'),
|
||||
]), '/login');
|
||||
}
|
||||
|
||||
$action = session()->pull('social-callback');
|
||||
if ($action == 'login') return $this->socialAuthService->handleLoginCallback($socialDriver);
|
||||
if ($action == 'register') return $this->socialRegisterCallback($socialDriver);
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
@@ -282,7 +291,9 @@ class RegisterController extends Controller
|
||||
* Register a new user after a registration callback.
|
||||
* @param $socialDriver
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws ConfirmationEmailException
|
||||
* @throws UserRegistrationException
|
||||
* @throws \BookStack\Exceptions\SocialDriverNotConfigured
|
||||
*/
|
||||
protected function socialRegisterCallback($socialDriver)
|
||||
{
|
||||
|
||||
@@ -36,11 +36,19 @@ class BookController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$books = $this->entityRepo->getAllPaginated('book', 10);
|
||||
$books = $this->entityRepo->getAllPaginated('book', 20);
|
||||
$recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
|
||||
$popular = $this->entityRepo->getPopular('book', 4, 0);
|
||||
$this->setPageTitle('Books');
|
||||
return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular]);
|
||||
$new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
|
||||
$booksViewType = setting()->getUser($this->currentUser, 'books_view_type', 'list');
|
||||
$this->setPageTitle(trans('entities.books'));
|
||||
return view('books/index', [
|
||||
'books' => $books,
|
||||
'recents' => $recents,
|
||||
'popular' => $popular,
|
||||
'new' => $new,
|
||||
'booksViewType' => $booksViewType
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +92,12 @@ class BookController extends Controller
|
||||
$bookChildren = $this->entityRepo->getBookChildren($book);
|
||||
Views::add($book);
|
||||
$this->setPageTitle($book->getShortName());
|
||||
return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
|
||||
return view('books/show', [
|
||||
'book' => $book,
|
||||
'current' => $book,
|
||||
'bookChildren' => $bookChildren,
|
||||
'activity' => Activity::entityActivity($book, 20, 0)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,9 +127,9 @@ class BookController extends Controller
|
||||
'name' => 'required|string|max:255',
|
||||
'description' => 'string|max:1000'
|
||||
]);
|
||||
$book = $this->entityRepo->updateFromInput('book', $book, $request->all());
|
||||
Activity::add($book, 'book_update', $book->id);
|
||||
return redirect($book->getUrl());
|
||||
$book = $this->entityRepo->updateFromInput('book', $book, $request->all());
|
||||
Activity::add($book, 'book_update', $book->id);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +185,7 @@ class BookController extends Controller
|
||||
$this->checkOwnablePermission('book-update', $book);
|
||||
|
||||
// Return if no map sent
|
||||
if (!$request->has('sort-tree')) {
|
||||
if (!$request->filled('sort-tree')) {
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
|
||||
93
app/Http/Controllers/CommentController.php
Normal file
93
app/Http/Controllers/CommentController.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Repos\CommentRepo;
|
||||
use BookStack\Repos\EntityRepo;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
protected $entityRepo;
|
||||
protected $commentRepo;
|
||||
|
||||
/**
|
||||
* CommentController constructor.
|
||||
* @param EntityRepo $entityRepo
|
||||
* @param CommentRepo $commentRepo
|
||||
*/
|
||||
public function __construct(EntityRepo $entityRepo, CommentRepo $commentRepo)
|
||||
{
|
||||
$this->entityRepo = $entityRepo;
|
||||
$this->commentRepo = $commentRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new comment for a Page
|
||||
* @param Request $request
|
||||
* @param integer $pageId
|
||||
* @param null|integer $commentId
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function savePageComment(Request $request, $pageId, $commentId = null)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'text' => 'required|string',
|
||||
'html' => 'required|string',
|
||||
]);
|
||||
|
||||
try {
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return response('Not found', 404);
|
||||
}
|
||||
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
|
||||
// Prevent adding comments to draft pages
|
||||
if ($page->draft) {
|
||||
return $this->jsonError(trans('errors.cannot_add_comment_to_draft'), 400);
|
||||
}
|
||||
|
||||
// Create a new comment.
|
||||
$this->checkPermission('comment-create-all');
|
||||
$comment = $this->commentRepo->create($page, $request->only(['html', 'text', 'parent_id']));
|
||||
Activity::add($page, 'commented_on', $page->book->id);
|
||||
return view('comments/comment', ['comment' => $comment]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing comment.
|
||||
* @param Request $request
|
||||
* @param integer $commentId
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function update(Request $request, $commentId)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'text' => 'required|string',
|
||||
'html' => 'required|string',
|
||||
]);
|
||||
|
||||
$comment = $this->commentRepo->getById($commentId);
|
||||
$this->checkOwnablePermission('page-view', $comment->entity);
|
||||
$this->checkOwnablePermission('comment-update', $comment);
|
||||
|
||||
$comment = $this->commentRepo->update($comment, $request->only(['html', 'text']));
|
||||
return view('comments/comment', ['comment' => $comment]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a comment from the system.
|
||||
* @param integer $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$comment = $this->commentRepo->getById($id);
|
||||
$this->checkOwnablePermission('comment-delete', $comment);
|
||||
$this->commentRepo->delete($comment);
|
||||
return response()->json(['message' => trans('entities.comment_deleted')]);
|
||||
}
|
||||
}
|
||||
@@ -29,21 +29,32 @@ class HomeController extends Controller
|
||||
$activity = Activity::latest(10);
|
||||
$draftPages = $this->signedIn ? $this->entityRepo->getUserDraftPages(6) : [];
|
||||
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 10*$recentFactor);
|
||||
$recentlyCreatedPages = $this->entityRepo->getRecentlyCreated('page', 5);
|
||||
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 5);
|
||||
return view('home', [
|
||||
$recents = $this->signedIn ? Views::getUserRecentlyViewed(12*$recentFactor, 0) : $this->entityRepo->getRecentlyCreated('book', 12*$recentFactor);
|
||||
$recentlyUpdatedPages = $this->entityRepo->getRecentlyUpdated('page', 12);
|
||||
|
||||
// Custom homepage
|
||||
$customHomepage = false;
|
||||
$homepageSetting = setting('app-homepage');
|
||||
if ($homepageSetting) {
|
||||
$id = intval(explode(':', $homepageSetting)[0]);
|
||||
$customHomepage = $this->entityRepo->getById('page', $id, false, true);
|
||||
$this->entityRepo->renderPage($customHomepage, true);
|
||||
}
|
||||
|
||||
$view = $customHomepage ? 'home-custom' : 'home';
|
||||
return view($view, [
|
||||
'activity' => $activity,
|
||||
'recents' => $recents,
|
||||
'recentlyCreatedPages' => $recentlyCreatedPages,
|
||||
'recentlyUpdatedPages' => $recentlyUpdatedPages,
|
||||
'draftPages' => $draftPages
|
||||
'draftPages' => $draftPages,
|
||||
'customHomepage' => $customHomepage
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a js representation of the current translations
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getTranslations() {
|
||||
$locale = app()->getLocale();
|
||||
@@ -76,4 +87,13 @@ class HomeController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom head HTML, Used in ajax calls to show in editor.
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function customHeadContent()
|
||||
{
|
||||
return view('partials/custom-head-content');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class ImageController extends Controller
|
||||
$imageUpload = $request->file('file');
|
||||
|
||||
try {
|
||||
$uploadedTo = $request->has('uploaded_to') ? $request->get('uploaded_to') : 0;
|
||||
$uploadedTo = $request->filled('uploaded_to') ? $request->get('uploaded_to') : 0;
|
||||
$image = $this->imageRepo->saveNew($imageUpload, $type, $uploadedTo);
|
||||
} catch (ImageUploadException $e) {
|
||||
return response($e->getMessage(), 500);
|
||||
@@ -162,7 +162,7 @@ class ImageController extends Controller
|
||||
$this->checkOwnablePermission('image-delete', $image);
|
||||
|
||||
// Check if this image is used on any pages
|
||||
$isForced = ($request->has('force') && ($request->get('force') === 'true') || $request->get('force') === true);
|
||||
$isForced = in_array($request->get('force', ''), [true, 'true']);
|
||||
if (!$isForced) {
|
||||
$pageSearch = $entityRepo->searchForImage($image->url);
|
||||
if ($pageSearch !== false) {
|
||||
|
||||
@@ -158,16 +158,25 @@ class PageController extends Controller
|
||||
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
|
||||
$pageContent = $this->entityRepo->renderPage($page);
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$sidebarTree = $this->entityRepo->getBookChildren($page->book);
|
||||
$pageNav = $this->entityRepo->getPageNav($pageContent);
|
||||
|
||||
$pageNav = $this->entityRepo->getPageNav($page->html);
|
||||
|
||||
// check if the comment's are enabled
|
||||
$commentsEnabled = !setting('app-disable-comments');
|
||||
if ($commentsEnabled) {
|
||||
$page->load(['comments.createdBy']);
|
||||
}
|
||||
|
||||
Views::add($page);
|
||||
$this->setPageTitle($page->getShortName());
|
||||
return view('pages/show', [
|
||||
'page' => $page,'book' => $page->book,
|
||||
'current' => $page, 'sidebarTree' => $sidebarTree,
|
||||
'pageNav' => $pageNav, 'pageContent' => $pageContent]);
|
||||
'current' => $page,
|
||||
'sidebarTree' => $sidebarTree,
|
||||
'commentsEnabled' => $commentsEnabled,
|
||||
'pageNav' => $pageNav
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,9 +332,10 @@ class PageController extends Controller
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$book = $page->book;
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
$this->entityRepo->destroyPage($page);
|
||||
|
||||
Activity::addMessage('page_delete', $book->id, $page->name);
|
||||
session()->flash('success', trans('entities.pages_delete_success'));
|
||||
$this->entityRepo->destroyPage($page);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
@@ -376,10 +386,11 @@ class PageController extends Controller
|
||||
|
||||
$page->fill($revision->toArray());
|
||||
$this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
|
||||
|
||||
|
||||
return view('pages/revision', [
|
||||
'page' => $page,
|
||||
'book' => $page->book,
|
||||
'revision' => $revision
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -409,6 +420,7 @@ class PageController extends Controller
|
||||
'page' => $page,
|
||||
'book' => $page->book,
|
||||
'diff' => $diff,
|
||||
'revision' => $revision
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -438,6 +450,7 @@ class PageController extends Controller
|
||||
public function exportPdf($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$pdfContent = $this->exportService->pageToPdf($page);
|
||||
return response()->make($pdfContent, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
@@ -454,6 +467,7 @@ class PageController extends Controller
|
||||
public function exportHtml($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$containedHtml = $this->exportService->pageToContainedHtml($page);
|
||||
return response()->make($containedHtml, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
|
||||
@@ -36,7 +36,7 @@ class SearchController extends Controller
|
||||
$searchTerm = $request->get('term');
|
||||
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
|
||||
|
||||
$page = $request->has('page') && is_int(intval($request->get('page'))) ? intval($request->get('page')) : 1;
|
||||
$page = intval($request->get('page', '0')) ?: 1;
|
||||
$nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
|
||||
|
||||
$results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
|
||||
@@ -88,8 +88,8 @@ class SearchController extends Controller
|
||||
*/
|
||||
public function searchEntitiesAjax(Request $request)
|
||||
{
|
||||
$entityTypes = $request->has('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']);
|
||||
$searchTerm = ($request->has('term') && trim($request->get('term')) !== '') ? $request->get('term') : false;
|
||||
$entityTypes = $request->filled('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']);
|
||||
$searchTerm = $request->get('term', false);
|
||||
|
||||
// Search for entities otherwise show most popular
|
||||
if ($searchTerm !== false) {
|
||||
|
||||
@@ -37,7 +37,7 @@ class TagController extends Controller
|
||||
*/
|
||||
public function getNameSuggestions(Request $request)
|
||||
{
|
||||
$searchTerm = $request->has('search') ? $request->get('search') : false;
|
||||
$searchTerm = $request->get('search', false);
|
||||
$suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
|
||||
return response()->json($suggestions);
|
||||
}
|
||||
@@ -49,8 +49,8 @@ class TagController extends Controller
|
||||
*/
|
||||
public function getValueSuggestions(Request $request)
|
||||
{
|
||||
$searchTerm = $request->has('search') ? $request->get('search') : false;
|
||||
$tagName = $request->has('name') ? $request->get('name') : false;
|
||||
$searchTerm = $request->get('search', false);
|
||||
$tagName = $request->get('name', false);
|
||||
$suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
|
||||
return response()->json($suggestions);
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ class UserController extends Controller
|
||||
{
|
||||
$this->checkPermission('users-manage');
|
||||
$listDetails = [
|
||||
'order' => $request->has('order') ? $request->get('order') : 'asc',
|
||||
'search' => $request->has('search') ? $request->get('search') : '',
|
||||
'sort' => $request->has('sort') ? $request->get('sort') : 'name',
|
||||
'order' => $request->get('order', 'asc'),
|
||||
'search' => $request->get('search', ''),
|
||||
'sort' => $request->get('sort', 'name'),
|
||||
];
|
||||
$users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
|
||||
$this->setPageTitle(trans('settings.users'));
|
||||
@@ -88,7 +88,7 @@ class UserController extends Controller
|
||||
|
||||
$user->save();
|
||||
|
||||
if ($request->has('roles')) {
|
||||
if ($request->filled('roles')) {
|
||||
$roles = $request->get('roles');
|
||||
$user->roles()->sync($roles);
|
||||
}
|
||||
@@ -155,24 +155,24 @@ class UserController extends Controller
|
||||
$user->fill($request->all());
|
||||
|
||||
// Role updates
|
||||
if (userCan('users-manage') && $request->has('roles')) {
|
||||
if (userCan('users-manage') && $request->filled('roles')) {
|
||||
$roles = $request->get('roles');
|
||||
$user->roles()->sync($roles);
|
||||
}
|
||||
|
||||
// Password updates
|
||||
if ($request->has('password') && $request->get('password') != '') {
|
||||
if ($request->filled('password')) {
|
||||
$password = $request->get('password');
|
||||
$user->password = bcrypt($password);
|
||||
}
|
||||
|
||||
// External auth id updates
|
||||
if ($this->currentUser->can('users-manage') && $request->has('external_auth_id')) {
|
||||
if ($this->currentUser->can('users-manage') && $request->filled('external_auth_id')) {
|
||||
$user->external_auth_id = $request->get('external_auth_id');
|
||||
}
|
||||
|
||||
// Save an user-specific settings
|
||||
if ($request->has('setting')) {
|
||||
if ($request->filled('setting')) {
|
||||
foreach ($request->get('setting') as $key => $value) {
|
||||
setting()->putUser($user, $key, $value);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ class Kernel extends HttpKernel
|
||||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\BookStack\Http\Middleware\TrimStrings::class,
|
||||
\BookStack\Http\Middleware\TrustProxies::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,8 @@ class Kernel extends HttpKernel
|
||||
'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,
|
||||
\BookStack\Http\Middleware\Localization::class
|
||||
@@ -42,10 +45,11 @@ 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,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'perm' => \BookStack\Http\Middleware\PermissionMiddleware::class
|
||||
];
|
||||
}
|
||||
|
||||
@@ -30,8 +30,11 @@ class Authenticate
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) {
|
||||
return redirect(baseUrl('/register/confirm/awaiting'));
|
||||
if ($this->auth->check()) {
|
||||
$requireConfirmation = (setting('registration-confirmation') || setting('registration-restrict'));
|
||||
if ($requireConfirmation && !$this->auth->user()->email_confirmed) {
|
||||
return redirect('/register/confirm/awaiting');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->auth->guest() && !setting('app-public')) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace BookStack\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends BaseEncrypter
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
|
||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
'password-confirm',
|
||||
];
|
||||
}
|
||||
47
app/Http/Middleware/TrustProxies.php
Normal file
47
app/Http/Middleware/TrustProxies.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The current proxy header mappings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [
|
||||
Request::HEADER_FORWARDED => 'FORWARDED',
|
||||
Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
|
||||
Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
|
||||
Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
|
||||
Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
|
||||
];
|
||||
|
||||
/**
|
||||
* Handle the request, Set the correct user-configured proxy information.
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$setProxies = config('app.proxies');
|
||||
if ($setProxies !== '**' && $setProxies !== '*' && $setProxies !== '') {
|
||||
$setProxies = explode(',', $setProxies);
|
||||
}
|
||||
$this->proxies = $setProxies;
|
||||
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace BookStack\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends BaseVerifier
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
|
||||
@@ -47,4 +47,16 @@ class PageRevision extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows checking of the exact class, Used to check entity type.
|
||||
* Included here to align with entities in similar use cases.
|
||||
* (Yup, Bit of an awkward hack)
|
||||
* @param $type
|
||||
* @return bool
|
||||
*/
|
||||
public static function isA($type)
|
||||
{
|
||||
return $type === 'revision';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ class EventServiceProvider extends ServiceProvider
|
||||
protected $listen = [
|
||||
SocialiteWasCalled::class => [
|
||||
'SocialiteProviders\Slack\SlackExtendSocialite@handle',
|
||||
'SocialiteProviders\Azure\AzureExtendSocialite@handle',
|
||||
'SocialiteProviders\Okta\OktaExtendSocialite@handle',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
87
app/Repos/CommentRepo.php
Normal file
87
app/Repos/CommentRepo.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php namespace BookStack\Repos;
|
||||
|
||||
use BookStack\Comment;
|
||||
use BookStack\Entity;
|
||||
|
||||
/**
|
||||
* Class CommentRepo
|
||||
* @package BookStack\Repos
|
||||
*/
|
||||
class CommentRepo {
|
||||
|
||||
/**
|
||||
* @var Comment $comment
|
||||
*/
|
||||
protected $comment;
|
||||
|
||||
/**
|
||||
* CommentRepo constructor.
|
||||
* @param Comment $comment
|
||||
*/
|
||||
public function __construct(Comment $comment)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a comment by ID.
|
||||
* @param $id
|
||||
* @return Comment|\Illuminate\Database\Eloquent\Model
|
||||
*/
|
||||
public function getById($id)
|
||||
{
|
||||
return $this->comment->newQuery()->findOrFail($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new comment on an entity.
|
||||
* @param Entity $entity
|
||||
* @param array $data
|
||||
* @return Comment
|
||||
*/
|
||||
public function create (Entity $entity, $data = [])
|
||||
{
|
||||
$userId = user()->id;
|
||||
$comment = $this->comment->newInstance($data);
|
||||
$comment->created_by = $userId;
|
||||
$comment->updated_by = $userId;
|
||||
$comment->local_id = $this->getNextLocalId($entity);
|
||||
$entity->comments()->save($comment);
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing comment.
|
||||
* @param Comment $comment
|
||||
* @param array $input
|
||||
* @return mixed
|
||||
*/
|
||||
public function update($comment, $input)
|
||||
{
|
||||
$comment->updated_by = user()->id;
|
||||
$comment->update($input);
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a comment from the system.
|
||||
* @param Comment $comment
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete($comment)
|
||||
{
|
||||
return $comment->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next local ID relative to the linked entity.
|
||||
* @param Entity $entity
|
||||
* @return int
|
||||
*/
|
||||
protected function getNextLocalId(Entity $entity)
|
||||
{
|
||||
$comments = $entity->comments(false)->orderBy('local_id', 'desc')->first();
|
||||
if ($comments === null) return 1;
|
||||
return $comments->local_id + 1;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use BookStack\Book;
|
||||
use BookStack\Chapter;
|
||||
use BookStack\Entity;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Exceptions\NotifyException;
|
||||
use BookStack\Page;
|
||||
use BookStack\PageRevision;
|
||||
use BookStack\Services\AttachmentService;
|
||||
@@ -137,10 +138,15 @@ class EntityRepo
|
||||
* @param string $type
|
||||
* @param integer $id
|
||||
* @param bool $allowDrafts
|
||||
* @param bool $ignorePermissions
|
||||
* @return Entity
|
||||
*/
|
||||
public function getById($type, $id, $allowDrafts = false)
|
||||
public function getById($type, $id, $allowDrafts = false, $ignorePermissions = false)
|
||||
{
|
||||
if ($ignorePermissions) {
|
||||
$entity = $this->getEntity($type);
|
||||
return $entity->newQuery()->find($id);
|
||||
}
|
||||
return $this->entityQuery($type, $allowDrafts)->find($id);
|
||||
}
|
||||
|
||||
@@ -436,9 +442,10 @@ class EntityRepo
|
||||
*/
|
||||
public function updateEntityPermissionsFromRequest($request, Entity $entity)
|
||||
{
|
||||
$entity->restricted = $request->has('restricted') && $request->get('restricted') === 'true';
|
||||
$entity->restricted = $request->get('restricted', '') === 'true';
|
||||
$entity->permissions()->delete();
|
||||
if ($request->has('restrictions')) {
|
||||
|
||||
if ($request->filled('restrictions')) {
|
||||
foreach ($request->get('restrictions') as $roleId => $restrictions) {
|
||||
foreach ($restrictions as $action => $value) {
|
||||
$entity->permissions()->create([
|
||||
@@ -448,6 +455,7 @@ class EntityRepo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$entity->save();
|
||||
$this->permissionService->buildJointPermissionsForEntity($entity);
|
||||
}
|
||||
@@ -547,8 +555,9 @@ class EntityRepo
|
||||
*/
|
||||
protected function nameToSlug($name)
|
||||
{
|
||||
$slug = str_replace(' ', '-', strtolower($name));
|
||||
$slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', $slug);
|
||||
$slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', mb_strtolower($name));
|
||||
$slug = preg_replace('/\s{2,}/', ' ', $slug);
|
||||
$slug = str_replace(' ', '-', $slug);
|
||||
if ($slug === "") $slug = substr(md5(rand(1, 500)), 0, 5);
|
||||
return $slug;
|
||||
}
|
||||
@@ -571,7 +580,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);
|
||||
$draftPage->draft = false;
|
||||
$draftPage->revision_count = 1;
|
||||
|
||||
@@ -671,9 +680,10 @@ class EntityRepo
|
||||
/**
|
||||
* Render the page for viewing, Parsing and performing features such as page transclusion.
|
||||
* @param Page $page
|
||||
* @param bool $ignorePermissions
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function renderPage(Page $page)
|
||||
public function renderPage(Page $page, $ignorePermissions = false)
|
||||
{
|
||||
$content = $page->html;
|
||||
$matches = [];
|
||||
@@ -685,19 +695,19 @@ class EntityRepo
|
||||
$pageId = intval($splitInclude[0]);
|
||||
if (is_nan($pageId)) continue;
|
||||
|
||||
$page = $this->getById('page', $pageId);
|
||||
if ($page === null) {
|
||||
$matchedPage = $this->getById('page', $pageId, false, $ignorePermissions);
|
||||
if ($matchedPage === null) {
|
||||
$content = str_replace($matches[0][$index], '', $content);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($splitInclude) === 1) {
|
||||
$content = str_replace($matches[0][$index], $page->html, $content);
|
||||
$content = str_replace($matches[0][$index], $matchedPage->html, $content);
|
||||
continue;
|
||||
}
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding('<body>'.$page->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
|
||||
$doc->loadHTML(mb_convert_encoding('<body>'.$matchedPage->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
|
||||
$matchingElem = $doc->getElementById($splitInclude[1]);
|
||||
if ($matchingElem === null) {
|
||||
$content = str_replace($matches[0][$index], '', $content);
|
||||
@@ -713,6 +723,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 +837,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 +954,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);
|
||||
$page->updated_by = user()->id;
|
||||
$page->save();
|
||||
$this->searchService->indexEntity($page);
|
||||
@@ -953,7 +974,7 @@ class EntityRepo
|
||||
if ($page->draft) {
|
||||
$page->fill($data);
|
||||
if (isset($data['html'])) {
|
||||
$page->text = strip_tags($data['html']);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
}
|
||||
$page->save();
|
||||
return $page;
|
||||
@@ -1056,6 +1077,7 @@ class EntityRepo
|
||||
/**
|
||||
* Destroy a given page along with its dependencies.
|
||||
* @param Page $page
|
||||
* @throws NotifyException
|
||||
*/
|
||||
public function destroyPage(Page $page)
|
||||
{
|
||||
@@ -1067,6 +1089,12 @@ class EntityRepo
|
||||
$this->permissionService->deleteJointPermissionsForEntity($page);
|
||||
$this->searchService->deleteEntityTerms($page);
|
||||
|
||||
// Check if set as custom homepage
|
||||
$customHome = setting('app-homepage', '0:');
|
||||
if (intval($page->id) === intval(explode(':', $customHome)[0])) {
|
||||
throw new NotifyException(trans('errors.page_custom_home_deletion'), $page->getUrl());
|
||||
}
|
||||
|
||||
// Delete Attached Files
|
||||
$attachmentService = app(AttachmentService::class);
|
||||
foreach ($page->attachments as $attachment) {
|
||||
|
||||
@@ -33,6 +33,7 @@ class TagRepo
|
||||
* @param $entityType
|
||||
* @param $entityId
|
||||
* @param string $action
|
||||
* @return \Illuminate\Database\Eloquent\Model|null|static
|
||||
*/
|
||||
public function getEntity($entityType, $entityId, $action = 'view')
|
||||
{
|
||||
|
||||
@@ -27,9 +27,9 @@ class ExportService
|
||||
*/
|
||||
public function pageToContainedHtml(Page $page)
|
||||
{
|
||||
$this->entityRepo->renderPage($page);
|
||||
$pageHtml = view('pages/export', [
|
||||
'page' => $page,
|
||||
'pageContent' => $this->entityRepo->renderPage($page)
|
||||
'page' => $page
|
||||
])->render();
|
||||
return $this->containHtml($pageHtml);
|
||||
}
|
||||
@@ -74,9 +74,9 @@ class ExportService
|
||||
*/
|
||||
public function pageToPdf(Page $page)
|
||||
{
|
||||
$this->entityRepo->renderPage($page);
|
||||
$html = view('pages/pdf', [
|
||||
'page' => $page,
|
||||
'pageContent' => $this->entityRepo->renderPage($page)
|
||||
'page' => $page
|
||||
])->render();
|
||||
return $this->htmlToPdf($html);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class ExportService
|
||||
$pdf = \SnappyPDF::loadHTML($containedHtml);
|
||||
$pdf->setOption('print-media-type', true);
|
||||
} else {
|
||||
$pdf = \PDF::loadHTML($containedHtml);
|
||||
$pdf = \DomPDF::loadHTML($containedHtml);
|
||||
}
|
||||
return $pdf->output();
|
||||
}
|
||||
@@ -136,6 +136,7 @@ class ExportService
|
||||
* Bundle of the contents of a html file to be self-contained.
|
||||
* @param $htmlContent
|
||||
* @return mixed|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function containHtml($htmlContent)
|
||||
{
|
||||
@@ -153,9 +154,27 @@ class ExportService
|
||||
} else {
|
||||
$pathString = $srcString;
|
||||
}
|
||||
|
||||
// Attempt to find local files even if url not absolute
|
||||
$base = baseUrl('/');
|
||||
if (strpos($srcString, $base) === 0) {
|
||||
$isLocal = true;
|
||||
$relString = str_replace($base, '', $srcString);
|
||||
$pathString = public_path(trim($relString, '/'));
|
||||
}
|
||||
|
||||
if ($isLocal && !file_exists($pathString)) continue;
|
||||
try {
|
||||
$imageContent = file_get_contents($pathString);
|
||||
if ($isLocal) {
|
||||
$imageContent = file_get_contents($pathString);
|
||||
} else {
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [CURLOPT_URL => $pathString, CURLOPT_RETURNTRANSFER => 1, CURLOPT_CONNECTTIMEOUT => 5]);
|
||||
$imageContent = curl_exec($ch);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
if ($err) throw new \Exception("Image fetch failed, Received error: " . $err);
|
||||
}
|
||||
$imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent);
|
||||
$newImageString = str_replace($srcString, $imageEncoded, $oldImgString);
|
||||
} catch (\ErrorException $e) {
|
||||
|
||||
@@ -42,6 +42,8 @@ class LdapService
|
||||
$userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]);
|
||||
$baseDn = $this->config['base_dn'];
|
||||
$emailAttr = $this->config['email_attribute'];
|
||||
$followReferrals = $this->config['follow_referrals'] ? 1 : 0;
|
||||
$this->ldap->setOption($ldapConnection, LDAP_OPT_REFERRALS, $followReferrals);
|
||||
$users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', $emailAttr]);
|
||||
if ($users['count'] === 0) return null;
|
||||
|
||||
|
||||
@@ -205,12 +205,17 @@ class PermissionService
|
||||
}
|
||||
|
||||
$entities[] = $entity->book;
|
||||
if ($entity->isA('page') && $entity->chapter_id) $entities[] = $entity->chapter;
|
||||
|
||||
if ($entity->isA('page') && $entity->chapter_id) {
|
||||
$entities[] = $entity->chapter;
|
||||
}
|
||||
|
||||
if ($entity->isA('chapter')) {
|
||||
foreach ($entity->pages as $page) {
|
||||
$entities[] = $page;
|
||||
}
|
||||
}
|
||||
|
||||
$this->deleteManyJointPermissionsForEntities($entities);
|
||||
$this->buildJointPermissionsForEntities(collect($entities));
|
||||
}
|
||||
@@ -468,7 +473,7 @@ class PermissionService
|
||||
$action = end($explodedPermission);
|
||||
$this->currentAction = $action;
|
||||
|
||||
$nonJointPermissions = ['restrictions', 'image', 'attachment'];
|
||||
$nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
|
||||
|
||||
// Handle non entity specific jointPermissions
|
||||
if (in_array($explodedPermission[0], $nonJointPermissions)) {
|
||||
|
||||
@@ -92,7 +92,7 @@ class SearchService
|
||||
return [
|
||||
'total' => $total,
|
||||
'count' => count($results),
|
||||
'results' => $results->sortByDesc('score')
|
||||
'results' => $results->sortByDesc('score')->values()
|
||||
];
|
||||
}
|
||||
|
||||
@@ -382,11 +382,13 @@ class SearchService
|
||||
protected function generateTermArrayFromText($text, $scoreAdjustment = 1)
|
||||
{
|
||||
$tokenMap = []; // {TextToken => OccurrenceCount}
|
||||
$splitText = explode(' ', $text);
|
||||
foreach ($splitText as $token) {
|
||||
if ($token === '') continue;
|
||||
$splitChars = " \n\t.,!?:;()[]{}<>`'\"";
|
||||
$token = strtok($text, $splitChars);
|
||||
|
||||
while ($token !== false) {
|
||||
if (!isset($tokenMap[$token])) $tokenMap[$token] = 0;
|
||||
$tokenMap[$token]++;
|
||||
$token = strtok($splitChars);
|
||||
}
|
||||
|
||||
$terms = [];
|
||||
@@ -479,4 +481,23 @@ class SearchService
|
||||
});
|
||||
}
|
||||
|
||||
protected function filterSortBy(\Illuminate\Database\Eloquent\Builder $query, Entity $model, $input)
|
||||
{
|
||||
$functionName = camel_case('sort_by_' . $input);
|
||||
if (method_exists($this, $functionName)) $this->$functionName($query, $model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorting filter options
|
||||
*/
|
||||
|
||||
protected function sortByLastCommented(\Illuminate\Database\Eloquent\Builder $query, Entity $model)
|
||||
{
|
||||
$commentsTable = $this->db->getTablePrefix() . 'comments';
|
||||
$morphClass = str_replace('\\', '\\\\', $model->getMorphClass());
|
||||
$commentQuery = $this->db->raw('(SELECT c1.entity_id, c1.entity_type, c1.created_at as last_commented FROM '.$commentsTable.' c1 LEFT JOIN '.$commentsTable.' c2 ON (c1.entity_id = c2.entity_id AND c1.entity_type = c2.entity_type AND c1.created_at < c2.created_at) WHERE c1.entity_type = \''. $morphClass .'\' AND c2.created_at IS NULL) as comments');
|
||||
|
||||
$query->join($commentQuery, $model->getTable() . '.id', '=', 'comments.entity_id')->orderBy('last_commented', 'desc');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php namespace BookStack\Services;
|
||||
|
||||
use BookStack\Http\Requests\Request;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Laravel\Socialite\Contracts\Factory as Socialite;
|
||||
use BookStack\Exceptions\SocialDriverNotConfigured;
|
||||
use BookStack\Exceptions\SocialSignInException;
|
||||
@@ -14,7 +16,7 @@ class SocialAuthService
|
||||
protected $socialite;
|
||||
protected $socialAccount;
|
||||
|
||||
protected $validSocialDrivers = ['google', 'github', 'facebook', 'slack', 'twitter'];
|
||||
protected $validSocialDrivers = ['google', 'github', 'facebook', 'slack', 'twitter', 'azure', 'okta'];
|
||||
|
||||
/**
|
||||
* SocialAuthService constructor.
|
||||
@@ -91,7 +93,6 @@ class SocialAuthService
|
||||
public function handleLoginCallback($socialDriver)
|
||||
{
|
||||
$driver = $this->validateDriver($socialDriver);
|
||||
|
||||
// Get user details from social driver
|
||||
$socialUser = $this->socialite->driver($driver)->user();
|
||||
$socialId = $socialUser->getId();
|
||||
@@ -104,7 +105,8 @@ class SocialAuthService
|
||||
// When a user is not logged in and a matching SocialAccount exists,
|
||||
// Simply log the user into the application.
|
||||
if (!$isLoggedIn && $socialAccount !== null) {
|
||||
return $this->logUserIn($socialAccount->user);
|
||||
auth()->login($socialAccount->user);
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
// When a user is logged in but the social account does not exist,
|
||||
@@ -134,14 +136,7 @@ class SocialAuthService
|
||||
$message .= trans('errors.social_account_register_instructions', ['socialAccount' => title_case($socialDriver)]);
|
||||
}
|
||||
|
||||
throw new SocialSignInException($message . '.', '/login');
|
||||
}
|
||||
|
||||
|
||||
private function logUserIn($user)
|
||||
{
|
||||
auth()->login($user);
|
||||
return redirect('/');
|
||||
throw new SocialSignInException($message, '/login');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,7 @@ class ViewService
|
||||
$query->whereIn('viewable_type', $filterModel);
|
||||
} else if ($filterModel) {
|
||||
$query->where('viewable_type', '=', get_class($filterModel));
|
||||
};
|
||||
}
|
||||
|
||||
return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
|
||||
}
|
||||
|
||||
16
artisan
16
artisan
@@ -1,19 +1,19 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
| Initialize The App
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
| We need to get things going before we start up the app.
|
||||
| The init file loads everything in, in the correct order.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/bootstrap/autoload.php';
|
||||
require __DIR__.'/bootstrap/init.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
@@ -40,7 +40,7 @@ $status = $kernel->handle(
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running. We will fire off the shutdown events
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
@@ -48,4 +48,4 @@ $status = $kernel->handle(
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
|
||||
exit($status);
|
||||
exit($status);
|
||||
@@ -1,6 +1,15 @@
|
||||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Load Our Own Helpers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This custom function loads any helpers, before the Laravel Framework
|
||||
| is built so we can override any helpers as we please.
|
||||
|
|
||||
*/
|
||||
require __DIR__.'/../app/helpers.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -13,23 +22,4 @@ define('LARAVEL_START', microtime(true));
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/../app/helpers.php';
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include The Compiled Class File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| To dramatically increase your application's performance, you may use a
|
||||
| compiled class file which contains all of the classes commonly used
|
||||
| by a request. The Artisan "optimize" is used to create this file.
|
||||
|
|
||||
*/
|
||||
|
||||
$compiledPath = __DIR__.'/cache/compiled.php';
|
||||
|
||||
if (file_exists($compiledPath)) {
|
||||
require $compiledPath;
|
||||
}
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
@@ -1,31 +1,35 @@
|
||||
{
|
||||
"name": "ssddanbrown/bookstack",
|
||||
"name": "bookstackapp/bookstack",
|
||||
"description": "BookStack documentation platform",
|
||||
"keywords": ["BookStack", "Documentation"],
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=5.6.4",
|
||||
"laravel/framework": "5.4.*",
|
||||
"php": ">=7.0.0",
|
||||
"laravel/framework": "5.5.*",
|
||||
"fideloper/proxy": "~3.3",
|
||||
"ext-tidy": "*",
|
||||
"intervention/image": "^2.3",
|
||||
"intervention/image": "^2.4",
|
||||
"laravel/socialite": "^3.0",
|
||||
"barryvdh/laravel-ide-helper": "^2.2.3",
|
||||
"barryvdh/laravel-debugbar": "^2.3.2",
|
||||
"league/flysystem-aws-s3-v3": "^1.0",
|
||||
"barryvdh/laravel-dompdf": "^0.8",
|
||||
"barryvdh/laravel-dompdf": "^0.8.1",
|
||||
"predis/predis": "^1.1",
|
||||
"gathercontent/htmldiff": "^0.2.1",
|
||||
"barryvdh/laravel-snappy": "^0.3.1",
|
||||
"laravel/browser-kit-testing": "^1.0",
|
||||
"socialiteproviders/slack": "^3.0"
|
||||
"barryvdh/laravel-snappy": "^0.4.0",
|
||||
"socialiteproviders/slack": "^3.0",
|
||||
"socialiteproviders/microsoft-azure": "^3.0",
|
||||
"socialiteproviders/okta": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"filp/whoops": "~2.0",
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"mockery/mockery": "0.9.*",
|
||||
"phpunit/phpunit": "~5.0",
|
||||
"mockery/mockery": "~1.0",
|
||||
"phpunit/phpunit": "~6.0",
|
||||
"symfony/css-selector": "3.1.*",
|
||||
"symfony/dom-crawler": "3.1.*"
|
||||
"symfony/dom-crawler": "3.1.*",
|
||||
"laravel/browser-kit-testing": "^2.0",
|
||||
"barryvdh/laravel-ide-helper": "^2.4.1",
|
||||
"barryvdh/laravel-debugbar": "^3.1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
@@ -56,14 +60,12 @@
|
||||
"php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postInstall",
|
||||
"php artisan optimize",
|
||||
"php artisan cache:clear",
|
||||
"php artisan view:clear"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
"php artisan optimize"
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover"
|
||||
],
|
||||
"refresh-test-database": [
|
||||
"php artisan migrate:refresh --database=mysql_testing",
|
||||
@@ -71,6 +73,10 @@
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"platform": {
|
||||
"php": "7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5864
composer.lock
generated
5864
composer.lock
generated
File diff suppressed because it is too large
Load Diff
6
config/app.php
Normal file → Executable file
6
config/app.php
Normal file → Executable file
@@ -58,7 +58,7 @@ return [
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LANG', 'en'),
|
||||
'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk', 'ja'],
|
||||
'locales' => ['en', 'de', 'es', 'es_AR', 'fr', 'nl', 'pt_BR', 'sk', 'ja', 'pl', 'it', 'ru'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -219,7 +219,7 @@ return [
|
||||
*/
|
||||
|
||||
'ImageTool' => Intervention\Image\Facades\Image::class,
|
||||
'PDF' => Barryvdh\DomPDF\Facade::class,
|
||||
'DomPDF' => Barryvdh\DomPDF\Facade::class,
|
||||
'SnappyPDF' => Barryvdh\Snappy\Facades\SnappyPdf::class,
|
||||
'Debugbar' => Barryvdh\Debugbar\Facade::class,
|
||||
|
||||
@@ -234,4 +234,6 @@ return [
|
||||
|
||||
],
|
||||
|
||||
'proxies' => env('APP_PROXIES', ''),
|
||||
|
||||
];
|
||||
|
||||
@@ -72,6 +72,22 @@ return [
|
||||
'name' => 'Twitter',
|
||||
],
|
||||
|
||||
'azure' => [
|
||||
'client_id' => env('AZURE_APP_ID', false),
|
||||
'client_secret' => env('AZURE_APP_SECRET', false),
|
||||
'tenant' => env('AZURE_TENANT', false),
|
||||
'redirect' => env('APP_URL') . '/login/service/azure/callback',
|
||||
'name' => 'Microsoft Azure',
|
||||
],
|
||||
|
||||
'okta' => [
|
||||
'client_id' => env('OKTA_APP_ID'),
|
||||
'client_secret' => env('OKTA_APP_SECRET'),
|
||||
'redirect' => env('APP_URL') . '/login/service/okta/callback',
|
||||
'base_url' => env('OKTA_BASE_URL'),
|
||||
'name' => 'Okta',
|
||||
],
|
||||
|
||||
'ldap' => [
|
||||
'server' => env('LDAP_SERVER', false),
|
||||
'dn' => env('LDAP_DN', false),
|
||||
@@ -80,6 +96,7 @@ return [
|
||||
'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
|
||||
'version' => env('LDAP_VERSION', false),
|
||||
'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
|
||||
'follow_referrals' => env('LDAP_FOLLOW_REFERRALS', false),
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -29,7 +29,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => 120,
|
||||
'lifetime' => env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
|
||||
@@ -70,4 +70,14 @@ $factory->define(BookStack\Image::class, function ($faker) {
|
||||
'type' => 'gallery',
|
||||
'uploaded_to' => 0
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(BookStack\Comment::class, function($faker) {
|
||||
$text = $faker->paragraph(1);
|
||||
$html = '<p>' . $text. '</p>';
|
||||
return [
|
||||
'html' => $html,
|
||||
'text' => $text,
|
||||
'parent_id' => null
|
||||
];
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCommentsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('comments', function (Blueprint $table) {
|
||||
$table->increments('id')->unsigned();
|
||||
$table->integer('entity_id')->unsigned();
|
||||
$table->string('entity_type');
|
||||
$table->longText('text')->nullable();
|
||||
$table->longText('html')->nullable();
|
||||
$table->integer('parent_id')->unsigned()->nullable();
|
||||
$table->integer('local_id')->unsigned()->nullable();
|
||||
$table->integer('created_by')->unsigned();
|
||||
$table->integer('updated_by')->unsigned()->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['entity_id', 'entity_type']);
|
||||
$table->index(['local_id']);
|
||||
|
||||
// Assign new comment permissions to admin role
|
||||
$adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
|
||||
// Create & attach new entity permissions
|
||||
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
|
||||
$entity = 'Comment';
|
||||
foreach ($ops as $op) {
|
||||
$permissionId = DB::table('role_permissions')->insertGetId([
|
||||
'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)),
|
||||
'display_name' => $op . ' ' . $entity . 's',
|
||||
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
|
||||
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
|
||||
]);
|
||||
DB::table('permission_role')->insert([
|
||||
'role_id' => $adminRoleId,
|
||||
'permission_id' => $permissionId
|
||||
]);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('comments');
|
||||
// Delete comment role permissions
|
||||
$ops = ['Create All', 'Create Own', 'Update All', 'Update Own', 'Delete All', 'Delete Own'];
|
||||
$entity = 'Comment';
|
||||
foreach ($ops as $op) {
|
||||
$permName = strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op));
|
||||
DB::table('role_permissions')->where('name', '=', $permName)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddCoverImageDisplay extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('books', function (Blueprint $table) {
|
||||
$table->integer('image_id')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('books', function (Blueprint $table) {
|
||||
$table->dropColumn('image_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11,29 +11,33 @@ class DummyContentSeeder extends Seeder
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$user = factory(\BookStack\User::class)->create();
|
||||
$role = \BookStack\Role::getRole('editor');
|
||||
$user->attachRole($role);
|
||||
// Create an editor user
|
||||
$editorUser = factory(\BookStack\User::class)->create();
|
||||
$editorRole = \BookStack\Role::getRole('editor');
|
||||
$editorUser->attachRole($editorRole);
|
||||
|
||||
// Create a viewer user
|
||||
$viewerUser = factory(\BookStack\User::class)->create();
|
||||
$role = \BookStack\Role::getRole('viewer');
|
||||
$viewerUser->attachRole($role);
|
||||
|
||||
factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
|
||||
->each(function($book) use ($user) {
|
||||
$chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
|
||||
->each(function($chapter) use ($user, $book){
|
||||
$pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $user->id, 'updated_by' => $user->id, 'book_id' => $book->id]);
|
||||
factory(\BookStack\Book::class, 20)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
|
||||
->each(function($book) use ($editorUser) {
|
||||
$chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id])
|
||||
->each(function($chapter) use ($editorUser, $book){
|
||||
$pages = factory(\BookStack\Page::class, 5)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'book_id' => $book->id]);
|
||||
$chapter->pages()->saveMany($pages);
|
||||
});
|
||||
$pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
|
||||
$pages = factory(\BookStack\Page::class, 3)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
|
||||
$book->chapters()->saveMany($chapters);
|
||||
$book->pages()->saveMany($pages);
|
||||
});
|
||||
|
||||
$largeBook = factory(\BookStack\Book::class)->create(['name' => 'Large book' . str_random(10), 'created_by' => $user->id, 'updated_by' => $user->id]);
|
||||
$pages = factory(\BookStack\Page::class, 200)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
|
||||
$chapters = factory(\BookStack\Chapter::class, 50)->make(['created_by' => $user->id, 'updated_by' => $user->id]);
|
||||
$largeBook = factory(\BookStack\Book::class)->create(['name' => 'Large book' . str_random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
|
||||
$pages = factory(\BookStack\Page::class, 200)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
|
||||
$chapters = factory(\BookStack\Chapter::class, 50)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
|
||||
$largeBook->pages()->saveMany($pages);
|
||||
$largeBook->chapters()->saveMany($chapters);
|
||||
|
||||
app(\BookStack\Services\PermissionService::class)->buildJointPermissions();
|
||||
app(\BookStack\Services\SearchService::class)->indexAllEntities();
|
||||
}
|
||||
|
||||
38
gulpfile.js
38
gulpfile.js
@@ -1,22 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
const argv = require('yargs').argv;
|
||||
const gulp = require('gulp'),
|
||||
plumber = require('gulp-plumber');
|
||||
|
||||
const autoprefixer = require('gulp-autoprefixer');
|
||||
const uglify = require('gulp-uglify');
|
||||
const minifycss = require('gulp-clean-css');
|
||||
const sass = require('gulp-sass');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
|
||||
const browserify = require("browserify");
|
||||
const source = require('vinyl-source-stream');
|
||||
const buffer = require('vinyl-buffer');
|
||||
const babelify = require("babelify");
|
||||
const watchify = require("watchify");
|
||||
const envify = require("envify");
|
||||
const uglify = require('gulp-uglify');
|
||||
|
||||
const gutil = require("gulp-util");
|
||||
const liveReload = require('gulp-livereload');
|
||||
|
||||
if (argv.production) process.env.NODE_ENV = 'production';
|
||||
let isProduction = argv.production || process.env.NODE_ENV === 'production';
|
||||
|
||||
gulp.task('styles', () => {
|
||||
let chain = gulp.src(['resources/assets/sass/**/*.scss'])
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(plumber({
|
||||
errorHandler: function (error) {
|
||||
console.log(error.message);
|
||||
@@ -24,31 +33,41 @@ gulp.task('styles', () => {
|
||||
}}))
|
||||
.pipe(sass())
|
||||
.pipe(autoprefixer('last 2 versions'));
|
||||
if (argv.production) chain = chain.pipe(minifycss());
|
||||
return chain.pipe(gulp.dest('public/css/'));
|
||||
if (isProduction) chain = chain.pipe(minifycss());
|
||||
chain = chain.pipe(sourcemaps.write());
|
||||
return chain.pipe(gulp.dest('public/css/')).pipe(liveReload());
|
||||
});
|
||||
|
||||
|
||||
function scriptTask(watch=false) {
|
||||
function scriptTask(watch = false) {
|
||||
|
||||
let props = {
|
||||
basedir: 'resources/assets/js',
|
||||
debug: true,
|
||||
entries: ['global.js']
|
||||
entries: ['global.js'],
|
||||
fast: !isProduction,
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
};
|
||||
|
||||
let bundler = watch ? watchify(browserify(props), { poll: true }) : browserify(props);
|
||||
bundler.transform(envify, {global: true}).transform(babelify, {presets: ['es2015']});
|
||||
|
||||
if (isProduction) {
|
||||
bundler.transform(envify, {global: true}).transform(babelify, {presets: ['es2015']});
|
||||
}
|
||||
|
||||
function rebundle() {
|
||||
let stream = bundler.bundle();
|
||||
stream = stream.pipe(source('common.js'));
|
||||
if (argv.production) stream = stream.pipe(buffer()).pipe(uglify());
|
||||
return stream.pipe(gulp.dest('public/js/'));
|
||||
if (isProduction) stream = stream.pipe(buffer()).pipe(uglify());
|
||||
return stream.pipe(gulp.dest('public/js/')).pipe(liveReload());
|
||||
}
|
||||
|
||||
bundler.on('update', function() {
|
||||
rebundle();
|
||||
gutil.log('Rebundle...');
|
||||
gutil.log('Rebundling assets...');
|
||||
});
|
||||
|
||||
bundler.on('log', gutil.log);
|
||||
return rebundle();
|
||||
}
|
||||
@@ -57,6 +76,7 @@ gulp.task('scripts', () => {scriptTask(false)});
|
||||
gulp.task('scripts-watch', () => {scriptTask(true)});
|
||||
|
||||
gulp.task('default', ['styles', 'scripts-watch'], () => {
|
||||
liveReload.listen();
|
||||
gulp.watch("resources/assets/sass/**/*.scss", ['styles']);
|
||||
});
|
||||
|
||||
|
||||
5977
package-lock.json
generated
Normal file
5977
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -4,7 +4,8 @@
|
||||
"build": "gulp build",
|
||||
"production": "gulp build --production",
|
||||
"dev": "gulp",
|
||||
"watch": "gulp"
|
||||
"watch": "gulp",
|
||||
"permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babelify": "^7.3.0",
|
||||
@@ -13,6 +14,7 @@
|
||||
"gulp": "3.9.1",
|
||||
"gulp-autoprefixer": "3.1.1",
|
||||
"gulp-clean-css": "^3.0.4",
|
||||
"gulp-livereload": "^3.8.1",
|
||||
"gulp-minify-css": "1.2.4",
|
||||
"gulp-plumber": "1.1.0",
|
||||
"gulp-sass": "3.1.0",
|
||||
@@ -23,21 +25,19 @@
|
||||
"yargs": "^7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"angular": "^1.5.5",
|
||||
"angular-animate": "^1.5.5",
|
||||
"angular-resource": "^1.5.5",
|
||||
"angular-sanitize": "^1.5.5",
|
||||
"angular-ui-sortable": "^0.17.0",
|
||||
"axios": "^0.16.1",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"clipboard": "^1.5.16",
|
||||
"clipboard": "^1.7.1",
|
||||
"codemirror": "^5.26.0",
|
||||
"dropzone": "^4.0.1",
|
||||
"gulp-sourcemaps": "^2.6.1",
|
||||
"gulp-util": "^3.0.8",
|
||||
"markdown-it": "^8.3.1",
|
||||
"markdown-it-task-lists": "^2.0.0",
|
||||
"moment": "^2.12.0",
|
||||
"vue": "^2.2.6"
|
||||
"vue": "^2.2.6",
|
||||
"vuedraggable": "^2.14.1"
|
||||
},
|
||||
"browser": {
|
||||
"vue": "vue/dist/vue.common.js"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="bootstrap/autoload.php"
|
||||
bootstrap="bootstrap/init.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
|
||||
BIN
public/book_default_cover.png
Normal file
BIN
public/book_default_cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
3
public/css/export-styles.css
vendored
3
public/css/export-styles.css
vendored
File diff suppressed because one or more lines are too long
3
public/css/print-styles.css
vendored
3
public/css/print-styles.css
vendored
@@ -1 +1,2 @@
|
||||
header{display:none}body{font-size:12px}.faded-small{display:none}.page-content{margin:0 auto}.print-hidden{display:none}.print-full-width{width:100%;float:none;display:block}h2{font-size:2em;line-height:1;margin-top:.6em;margin-bottom:.3em}
|
||||
header{display:none}body{font-size:12px}.faded-small{display:none}.page-content{margin:0 auto}.print-hidden{display:none}.print-full-width{width:100%;float:none;display:block}h2{font-size:2em;line-height:1;margin-top:.6em;margin-bottom:.3em}
|
||||
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInByaW50LXN0eWxlcy5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQ0UsUUFBQSxLQUdGLEtBQ0UsVUFBQSxLQUdGLGFBQ0UsUUFBQSxLQUdGLGNBQ0UsT0FBQSxFQUFBLEtBR0YsY0FDRSxRQUFBLEtBR0Ysa0JBQ0UsTUFBQSxLQUNBLE1BQUEsS0FDQSxRQUFBLE1BR0YsR0FDRSxVQUFBLElBQ0EsWUFBQSxFQUNBLFdBQUEsS0FDQSxjQUFBIiwiZmlsZSI6InByaW50LXN0eWxlcy5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyJAaW1wb3J0IFwidmFyaWFibGVzXCI7XG5cbmhlYWRlciB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG5cbmJvZHkge1xuICBmb250LXNpemU6IDEycHg7XG59XG5cbi5mYWRlZC1zbWFsbCB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG5cbi5wYWdlLWNvbnRlbnQge1xuICBtYXJnaW46IDAgYXV0bztcbn1cblxuLnByaW50LWhpZGRlbiB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG5cbi5wcmludC1mdWxsLXdpZHRoIHtcbiAgd2lkdGg6IDEwMCU7XG4gIGZsb2F0OiBub25lO1xuICBkaXNwbGF5OiBibG9jaztcbn1cblxuaDIge1xuICBmb250LXNpemU6IDJlbTtcbiAgbGluZS1oZWlnaHQ6IDE7XG4gIG1hcmdpbi10b3A6IDAuNmVtO1xuICBtYXJnaW4tYm90dG9tOiAwLjNlbTtcbn0iXX0= */
|
||||
|
||||
3
public/css/styles.css
vendored
3
public/css/styles.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,22 +4,22 @@
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylorotwell@gmail.com>
|
||||
* @author Taylor Otwell <taylor@laravel.com>
|
||||
*/
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
| Initialize The App
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader for
|
||||
| our application. We just need to utilize it! We'll simply require it
|
||||
| into the script here so that we don't have to worry about manual
|
||||
| loading any of our classes later on. It feels nice to relax.
|
||||
| We need to get things going before we start up the app.
|
||||
| The init file loads everything in, in the correct order.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/../bootstrap/autoload.php';
|
||||
require __DIR__.'/../bootstrap/init.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -55,4 +55,4 @@ $response = $kernel->handle(
|
||||
|
||||
$response->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
||||
$kernel->terminate($request, $response);
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("3",tinymce.util.Tools.resolve),g("1",["3"],function(a){return a("tinymce.PluginManager")}),g("2",["3"],function(a){return a("tinymce.util.Tools")}),g("0",["1","2"],function(a,b){return a.add("advlist",function(a){function c(b){return a.$.contains(a.getBody(),b)}function d(a){return a&&/^(OL|UL|DL)$/.test(a.nodeName)&&c(a)}function e(a,c){var d=[];return c&&b.each(c.split(/[ ,]/),function(a){d.push({text:a.replace(/\-/g," ").replace(/\b\w/g,function(a){return a.toUpperCase()}),data:"default"==a?"":a})}),d}function f(c,d){a.undoManager.transact(function(){var e,f=a.dom,g=a.selection;if(e=f.getParent(g.getNode(),"ol,ul"),!e||e.nodeName!=c||d===!1){var h={"list-style-type":d?d:""};a.execCommand("UL"==c?"InsertUnorderedList":"InsertOrderedList",!1,h)}e=f.getParent(g.getNode(),"ol,ul"),e&&b.each(f.select("ol,ul",e).concat([e]),function(a){a.nodeName!==c&&d!==!1&&(a=f.rename(a,c)),f.setStyle(a,"listStyleType",d?d:null),a.removeAttribute("data-mce-style")}),a.focus()})}function g(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 h,i,j=function(a,c){var d=a.settings.plugins?a.settings.plugins:"";return b.inArray(d.split(/[ ,]/),c)!==-1};h=e("OL",a.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),i=e("UL",a.getParam("advlist_bullet_styles","default,circle,disc,square"));var k=function(c){return function(){var e=this;a.on("NodeChange",function(a){var f=b.grep(a.parents,d);e.active(f.length>0&&f[0].nodeName===c)})}};j(a,"lists")&&(a.addCommand("ApplyUnorderedListStyle",function(a,b){f("UL",b["list-style-type"])}),a.addCommand("ApplyOrderedListStyle",function(a,b){f("OL",b["list-style-type"])}),a.addButton("numlist",{type:h.length>0?"splitbutton":"button",tooltip:"Numbered list",menu:h,onPostRender:k("OL"),onshow:g,onselect:function(a){f("OL",a.control.settings.data)},onclick:function(){f("OL",!1)}}),a.addButton("bullist",{type:i.length>0?"splitbutton":"button",tooltip:"Bullet list",onPostRender:k("UL"),menu:i,onshow:g,onselect:function(a){f("UL",a.control.settings.data)},onclick:function(){f("UL",!1)}}))}),function(){}}),d("0")()}();
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("3",tinymce.util.Tools.resolve),g("1",["3"],function(a){return a("tinymce.PluginManager")}),g("2",["3"],function(a){return a("tinymce.util.Tools")}),g("0",["1","2"],function(a,b){return a.add("advlist",function(a){function c(b){return a.$.contains(a.getBody(),b)}function d(a){return a&&/^(OL|UL|DL)$/.test(a.nodeName)&&c(a)}function e(a,c){var d=[];return c&&b.each(c.split(/[ ,]/),function(a){d.push({text:a.replace(/\-/g," ").replace(/\b\w/g,function(a){return a.toUpperCase()}),data:"default"==a?"":a})}),d}function f(b,c){var d="UL"==b?"InsertUnorderedList":"InsertOrderedList";a.execCommand(d,!1,c===!1?null:{"list-style-type":c})}function g(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 h,i,j=function(a,c){var d=a.settings.plugins?a.settings.plugins:"";return b.inArray(d.split(/[ ,]/),c)!==-1};h=e("OL",a.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),i=e("UL",a.getParam("advlist_bullet_styles","default,circle,disc,square"));var k=function(c){return function(){var e=this;a.on("NodeChange",function(a){var f=b.grep(a.parents,d);e.active(f.length>0&&f[0].nodeName===c)})}};j(a,"lists")&&(a.addCommand("ApplyUnorderedListStyle",function(a,b){f("UL",b["list-style-type"])}),a.addCommand("ApplyOrderedListStyle",function(a,b){f("OL",b["list-style-type"])}),a.addButton("numlist",{type:h.length>0?"splitbutton":"button",tooltip:"Numbered list",menu:h,onPostRender:k("OL"),onshow:g,onselect:function(a){f("OL",a.control.settings.data)},onclick:function(){a.execCommand("InsertOrderedList")}}),a.addButton("bullist",{type:i.length>0?"splitbutton":"button",tooltip:"Bullet list",onPostRender:k("UL"),menu:i,onshow:g,onselect:function(a){f("UL",a.control.settings.data)},onclick:function(){a.execCommand("InsertUnorderedList")}}))}),function(){}}),d("0")()}();
|
||||
@@ -1 +1 @@
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("3",tinymce.util.Tools.resolve),g("1",["3"],function(a){return a("tinymce.Env")}),g("2",["3"],function(a){return a("tinymce.PluginManager")}),g("0",["1","2"],function(a,b){return b.add("autolink",function(b){function c(a){f(a,-1,"(",!0)}function d(a){f(a,0,"",!0)}function e(a){f(a,-1,"",!1)}function f(a,b,c){function d(a,b){if(b<0&&(b=0),3==a.nodeType){var c=a.data.length;b>c&&(b=c)}return b}function e(a,b){1!=a.nodeType||a.hasChildNodes()?g.setStart(a,d(a,b)):g.setStartBefore(a)}function f(a,b){1!=a.nodeType||a.hasChildNodes()?g.setEnd(a,d(a,b)):g.setEndAfter(a)}var g,i,j,k,l,m,n,o,p,q;if("A"!=a.selection.getNode().tagName){if(g=a.selection.getRng(!0).cloneRange(),g.startOffset<5){if(o=g.endContainer.previousSibling,!o){if(!g.endContainer.firstChild||!g.endContainer.firstChild.nextSibling)return;o=g.endContainer.firstChild.nextSibling}if(p=o.length,e(o,p),f(o,p),g.endOffset<5)return;i=g.endOffset,k=o}else{if(k=g.endContainer,3!=k.nodeType&&k.firstChild){for(;3!=k.nodeType&&k.firstChild;)k=k.firstChild;3==k.nodeType&&(e(k,0),f(k,k.nodeValue.length))}i=1==g.endOffset?2:g.endOffset-1-b}j=i;do e(k,i>=2?i-2:0),f(k,i>=1?i-1:0),i-=1,q=g.toString();while(" "!=q&&""!==q&&160!=q.charCodeAt(0)&&i-2>=0&&q!=c);g.toString()==c||160==g.toString().charCodeAt(0)?(e(k,i),f(k,j),i+=1):0===g.startOffset?(e(k,0),f(k,j)):(e(k,i),f(k,j)),m=g.toString(),"."==m.charAt(m.length-1)&&f(k,j-1),m=g.toString(),n=m.match(h),n&&("www."==n[1]?n[1]="http://www.":/@$/.test(n[1])&&!/^mailto:/.test(n[1])&&(n[1]="mailto:"+n[1]),l=a.selection.getBookmark(),a.selection.setRng(g),a.execCommand("createlink",!1,n[1]+n[2]),a.settings.default_link_target&&a.dom.setAttrib(a.selection.getNode(),"target",a.settings.default_link_target),a.selection.moveToBookmark(l),a.nodeChanged())}}var g,h=/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;return b.settings.autolink_pattern&&(h=b.settings.autolink_pattern),b.on("keydown",function(a){if(13==a.keyCode)return e(b)}),a.ie?void b.on("focus",function(){if(!g){g=!0;try{b.execCommand("AutoUrlDetect",!1,!0)}catch(a){}}}):(b.on("keypress",function(a){if(41==a.keyCode)return c(b)}),void b.on("keyup",function(a){if(32==a.keyCode)return d(b)}))}),function(){}}),d("0")()}();
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("3",tinymce.util.Tools.resolve),g("1",["3"],function(a){return a("tinymce.Env")}),g("2",["3"],function(a){return a("tinymce.PluginManager")}),g("0",["1","2"],function(a,b){var c=function(a,b){return a===b||" "===a||160===a.charCodeAt(0)};return b.add("autolink",function(b){function d(a){g(a,-1,"(",!0)}function e(a){g(a,0,"",!0)}function f(a){g(a,-1,"",!1)}function g(a,b,d){function e(a,b){if(b<0&&(b=0),3==a.nodeType){var c=a.data.length;b>c&&(b=c)}return b}function f(a,b){1!=a.nodeType||a.hasChildNodes()?h.setStart(a,e(a,b)):h.setStartBefore(a)}function g(a,b){1!=a.nodeType||a.hasChildNodes()?h.setEnd(a,e(a,b)):h.setEndAfter(a)}var h,j,k,l,m,n,o,p,q,r;if("A"!=a.selection.getNode().tagName){if(h=a.selection.getRng(!0).cloneRange(),h.startOffset<5){if(p=h.endContainer.previousSibling,!p){if(!h.endContainer.firstChild||!h.endContainer.firstChild.nextSibling)return;p=h.endContainer.firstChild.nextSibling}if(q=p.length,f(p,q),g(p,q),h.endOffset<5)return;j=h.endOffset,l=p}else{if(l=h.endContainer,3!=l.nodeType&&l.firstChild){for(;3!=l.nodeType&&l.firstChild;)l=l.firstChild;3==l.nodeType&&(f(l,0),g(l,l.nodeValue.length))}j=1==h.endOffset?2:h.endOffset-1-b}k=j;do f(l,j>=2?j-2:0),g(l,j>=1?j-1:0),j-=1,r=h.toString();while(" "!=r&&""!==r&&160!=r.charCodeAt(0)&&j-2>=0&&r!=d);c(h.toString(),d)?(f(l,j),g(l,k),j+=1):0===h.startOffset?(f(l,0),g(l,k)):(f(l,j),g(l,k)),n=h.toString(),"."==n.charAt(n.length-1)&&g(l,k-1),n=h.toString(),o=n.match(i),o&&("www."==o[1]?o[1]="http://www.":/@$/.test(o[1])&&!/^mailto:/.test(o[1])&&(o[1]="mailto:"+o[1]),m=a.selection.getBookmark(),a.selection.setRng(h),a.execCommand("createlink",!1,o[1]+o[2]),a.settings.default_link_target&&a.dom.setAttrib(a.selection.getNode(),"target",a.settings.default_link_target),a.selection.moveToBookmark(m),a.nodeChanged())}}var h,i=/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;return b.settings.autolink_pattern&&(i=b.settings.autolink_pattern),b.on("keydown",function(a){if(13==a.keyCode)return f(b)}),a.ie?void b.on("focus",function(){if(!h){h=!0;try{b.execCommand("AutoUrlDetect",!1,!0)}catch(a){}}}):(b.on("keypress",function(a){if(41==a.keyCode)return d(b)}),void b.on("keyup",function(a){if(32==a.keyCode)return e(b)}))}),function(){}}),d("0")()}();
|
||||
@@ -1 +1 @@
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("6",tinymce.util.Tools.resolve),g("1",["6"],function(a){return a("tinymce.EditorManager")}),g("2",["6"],function(a){return a("tinymce.PluginManager")}),g("3",["6"],function(a){return a("tinymce.util.LocalStorage")}),g("4",["6"],function(a){return a("tinymce.util.Tools")}),h("5",window),g("0",["1","2","3","4","5"],function(a,b,c,d,e){return a._beforeUnloadHandler=function(){var b;return d.each(a.editors,function(a){a.plugins.autosave&&a.plugins.autosave.storeDraft(),!b&&a.isDirty()&&a.getParam("autosave_ask_before_unload",!0)&&(b=a.translate("You have unsaved changes are you sure you want to navigate away?"))}),b},b.add("autosave",function(b){function f(a,b){var c={s:1e3,m:6e4};return a=/^(\d+)([ms]?)$/.exec(""+(a||b)),(a[2]?c[a[2]]:1)*parseInt(a,10)}function g(){var a=parseInt(c.getItem(o+"time"),10)||0;return!((new Date).getTime()-a>q.autosave_retention)||(h(!1),!1)}function h(a){c.removeItem(o+"draft"),c.removeItem(o+"time"),a!==!1&&b.fire("RemoveDraft")}function i(){!n()&&b.isDirty()&&(c.setItem(o+"draft",b.getContent({format:"raw",no_events:!0})),c.setItem(o+"time",(new Date).getTime()),b.fire("StoreDraft"))}function j(){g()&&(b.setContent(c.getItem(o+"draft"),{format:"raw"}),b.fire("RestoreDraft"))}function k(){p||(setInterval(function(){b.removed||i()},q.autosave_interval),p=!0)}function l(){var a=this;a.disabled(!g()),b.on("StoreDraft RestoreDraft RemoveDraft",function(){a.disabled(!g())}),k()}function m(){b.undoManager.beforeChange(),j(),h(),b.undoManager.add()}function n(a){var c=b.settings.forced_root_block;return a=d.trim("undefined"==typeof a?b.getBody().innerHTML:a),""===a||new RegExp("^<"+c+"[^>]*>((\xa0| |[ \t]|<br[^>]*>)+?|)</"+c+">|<br>$","i").test(a)}var o,p,q=b.settings;o=q.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",o=o.replace(/\{path\}/g,document.location.pathname),o=o.replace(/\{query\}/g,document.location.search),o=o.replace(/\{id\}/g,b.id),q.autosave_interval=f(q.autosave_interval,"30s"),q.autosave_retention=f(q.autosave_retention,"20m"),b.addButton("restoredraft",{title:"Restore last draft",onclick:m,onPostRender:l}),b.addMenuItem("restoredraft",{text:"Restore last draft",onclick:m,onPostRender:l,context:"file"}),b.settings.autosave_restore_when_empty!==!1&&(b.on("init",function(){g()&&n()&&j()}),b.on("saveContent",function(){h()})),e.onbeforeunload=a._beforeUnloadHandler,this.hasDraft=g,this.storeDraft=i,this.restoreDraft=j,this.removeDraft=h,this.isEmpty=n}),function(){}}),d("0")()}();
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("6",tinymce.util.Tools.resolve),g("1",["6"],function(a){return a("tinymce.EditorManager")}),g("2",["6"],function(a){return a("tinymce.PluginManager")}),g("3",["6"],function(a){return a("tinymce.util.LocalStorage")}),g("4",["6"],function(a){return a("tinymce.util.Tools")}),h("5",window),g("0",["1","2","3","4","5"],function(a,b,c,d,e){return a._beforeUnloadHandler=function(){var b;return d.each(a.get(),function(a){a.plugins.autosave&&a.plugins.autosave.storeDraft(),!b&&a.isDirty()&&a.getParam("autosave_ask_before_unload",!0)&&(b=a.translate("You have unsaved changes are you sure you want to navigate away?"))}),b},b.add("autosave",function(b){function f(a,b){var c={s:1e3,m:6e4};return a=/^(\d+)([ms]?)$/.exec(""+(a||b)),(a[2]?c[a[2]]:1)*parseInt(a,10)}function g(){var a=parseInt(c.getItem(o+"time"),10)||0;return!((new Date).getTime()-a>q.autosave_retention)||(h(!1),!1)}function h(a){c.removeItem(o+"draft"),c.removeItem(o+"time"),a!==!1&&b.fire("RemoveDraft")}function i(){!n()&&b.isDirty()&&(c.setItem(o+"draft",b.getContent({format:"raw",no_events:!0})),c.setItem(o+"time",(new Date).getTime()),b.fire("StoreDraft"))}function j(){g()&&(b.setContent(c.getItem(o+"draft"),{format:"raw"}),b.fire("RestoreDraft"))}function k(){p||(setInterval(function(){b.removed||i()},q.autosave_interval),p=!0)}function l(){var a=this;a.disabled(!g()),b.on("StoreDraft RestoreDraft RemoveDraft",function(){a.disabled(!g())}),k()}function m(){b.undoManager.beforeChange(),j(),h(),b.undoManager.add()}function n(a){var c=b.settings.forced_root_block;return a=d.trim("undefined"==typeof a?b.getBody().innerHTML:a),""===a||new RegExp("^<"+c+"[^>]*>((\xa0| |[ \t]|<br[^>]*>)+?|)</"+c+">|<br>$","i").test(a)}var o,p,q=b.settings;o=q.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",o=o.replace(/\{path\}/g,document.location.pathname),o=o.replace(/\{query\}/g,document.location.search),o=o.replace(/\{id\}/g,b.id),q.autosave_interval=f(q.autosave_interval,"30s"),q.autosave_retention=f(q.autosave_retention,"20m"),b.addButton("restoredraft",{title:"Restore last draft",onclick:m,onPostRender:l}),b.addMenuItem("restoredraft",{text:"Restore last draft",onclick:m,onPostRender:l,context:"file"}),b.settings.autosave_restore_when_empty!==!1&&(b.on("init",function(){g()&&n()&&j()}),b.on("saveContent",function(){h()})),e.onbeforeunload=a._beforeUnloadHandler,this.hasDraft=g,this.storeDraft=i,this.restoreDraft=j,this.removeDraft=h,this.isEmpty=n}),function(){}}),d("0")()}();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("2",tinymce.util.Tools.resolve),g("1",["2"],function(a){return a("tinymce.PluginManager")}),g("0",["1"],function(a){return a.add("nonbreaking",function(a){var b=a.getParam("nonbreaking_force_tab");if(a.addCommand("mceNonBreaking",function(){a.insertContent(a.plugins.visualchars&&a.plugins.visualchars.state?'<span class="mce-nbsp"> </span>':" "),a.dom.setAttrib(a.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),a.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),a.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),b){var c=+b>1?+b:3;a.on("keydown",function(b){if(9==b.keyCode){if(b.shiftKey)return;b.preventDefault();for(var d=0;d<c;d++)a.execCommand("mceNonBreaking")}})}}),function(){}}),d("0")()}();
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("2",tinymce.util.Tools.resolve),g("1",["2"],function(a){return a("tinymce.PluginManager")}),g("0",["1"],function(a){return a.add("nonbreaking",function(a){var b=a.getParam("nonbreaking_force_tab"),c=function(a,b){for(var c="",d=0;d<b;d++)c+=a;return c},d=function(b){var d=a.plugins.visualchars&&a.plugins.visualchars.state?'<span class="mce-nbsp"> </span>':" ";a.insertContent(c(d,b)),a.dom.setAttrib(a.dom.select("span.mce-nbsp"),"data-mce-bogus","1")};if(a.addCommand("mceNonBreaking",function(){d(1)}),a.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),a.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),b){var e=+b>1?+b:3;a.on("keydown",function(a){if(9==a.keyCode){if(a.shiftKey)return;a.preventDefault(),d(e)}})}}),function(){}}),d("0")()}();
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("4",tinymce.util.Tools.resolve),g("1",["4"],function(a){return a("tinymce.PluginManager")}),g("2",["4"],function(a){return a("tinymce.Env")}),g("3",["4"],function(a){return a("tinymce.util.Tools")}),g("0",["1","2","3"],function(a,b,c){return a.add("preview",function(a){var d=a.settings,e=!b.ie;a.addCommand("mcePreview",function(){a.windowManager.open({title:"Preview",width:parseInt(a.getParam("plugin_preview_width","650"),10),height:parseInt(a.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(e?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var b,f="";f+='<base href="'+a.documentBaseURI.getURI()+'">',c.each(a.contentCSS,function(b){f+='<link type="text/css" rel="stylesheet" href="'+a.documentBaseURI.toAbsolute(b)+'">'});var g=d.body_id||"tinymce";g.indexOf("=")!=-1&&(g=a.getParam("body_id","","hash"),g=g[a.id]||g);var h=d.body_class||"";h.indexOf("=")!=-1&&(h=a.getParam("body_class","","hash"),h=h[a.id]||"");var i='<script>document.addEventListener && document.addEventListener("click", function(e) {for (var elm = e.target; elm; elm = elm.parentNode) {if (elm.nodeName === "A") {e.preventDefault();}}}, false);</script> ',j=a.settings.directionality?' dir="'+a.settings.directionality+'"':"";if(b="<!DOCTYPE html><html><head>"+f+'</head><body id="'+g+'" class="mce-content-body '+h+'"'+j+">"+a.getContent()+i+"</body></html>",e)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(b);else{var k=this.getEl("body").firstChild.contentWindow.document;k.open(),k.write(b),k.close()}}})}),a.addButton("preview",{title:"Preview",cmd:"mcePreview"}),a.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})}),function(){}}),d("0")()}();
|
||||
!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;i<g;++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;f<c;++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("4",tinymce.util.Tools.resolve),g("1",["4"],function(a){return a("tinymce.PluginManager")}),g("2",["4"],function(a){return a("tinymce.Env")}),g("3",["4"],function(a){return a("tinymce.util.Tools")}),g("0",["1","2","3"],function(a,b,c){return a.add("preview",function(a){var d=a.settings,e=!b.ie;a.addCommand("mcePreview",function(){a.windowManager.open({title:"Preview",width:parseInt(a.getParam("plugin_preview_width","650"),10),height:parseInt(a.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(e?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var b,f="",g=a.dom.encode;f+='<base href="'+g(a.documentBaseURI.getURI())+'">',c.each(a.contentCSS,function(b){f+='<link type="text/css" rel="stylesheet" href="'+g(a.documentBaseURI.toAbsolute(b))+'">'});var h=d.body_id||"tinymce";h.indexOf("=")!=-1&&(h=a.getParam("body_id","","hash"),h=h[a.id]||h);var i=d.body_class||"";i.indexOf("=")!=-1&&(i=a.getParam("body_class","","hash"),i=i[a.id]||"");var j='<script>document.addEventListener && document.addEventListener("click", function(e) {for (var elm = e.target; elm; elm = elm.parentNode) {if (elm.nodeName === "A") {e.preventDefault();}}}, false);</script> ',k=a.settings.directionality?' dir="'+a.settings.directionality+'"':"";if(b="<!DOCTYPE html><html><head>"+f+'</head><body id="'+g(h)+'" class="mce-content-body '+g(i)+'"'+g(k)+">"+a.getContent()+j+"</body></html>",e)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(b);else{var l=this.getEl("body").firstChild.contentWindow.document;l.open(),l.write(b),l.close()}}})}),a.addButton("preview",{title:"Preview",cmd:"mcePreview"}),a.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})}),function(){}}),d("0")()}();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1}.mce-content-body p,.mce-content-body div,.mce-content-body h1,.mce-content-body h2,.mce-content-body h3,.mce-content-body h4,.mce-content-body h5,.mce-content-body h6{line-height:1.2em}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7ACAFF}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-content-body a[data-mce-selected],.mce-content-body code[data-mce-selected]{background:#bfe6ff}.mce-content-body hr{cursor:default}
|
||||
.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7ACAFF}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-content-body a[data-mce-selected],.mce-content-body code[data-mce-selected],.mce-content-body b[data-mce-selected],.mce-content-body i[data-mce-selected],.mce-content-body em[data-mce-selected],.mce-content-body strong[data-mce-selected],.mce-content-body sup[data-mce-selected],.mce-content-body sub[data-mce-selected]{background:#bfe6ff}.mce-content-body hr{cursor:default}.mce-content-body{line-height:1.3}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user