mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-02-05 16:49:47 +03:00
Compare commits
500 Commits
drawio_ren
...
v25.07.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5940a91809 | ||
|
|
9a4651badb | ||
|
|
a50a256939 | ||
|
|
4830248a1e | ||
|
|
1256b30ad4 | ||
|
|
777cca76da | ||
|
|
a2d13124af | ||
|
|
bd966ef99e | ||
|
|
a6b5733ec2 | ||
|
|
e899066e96 | ||
|
|
f4f2435856 | ||
|
|
fca4a0563e | ||
|
|
0bc9ddd780 | ||
|
|
c66f3b2a37 | ||
|
|
7bc0d54af1 | ||
|
|
92d15d9cf2 | ||
|
|
b06147fef7 | ||
|
|
776ec7b9e7 | ||
|
|
8aa6bdc8ab | ||
|
|
4ab17157b1 | ||
|
|
6d7ffab115 | ||
|
|
c8cfec96dc | ||
|
|
d145efb6f6 | ||
|
|
c54101c603 | ||
|
|
865e5aecc9 | ||
|
|
ae4d1d804a | ||
|
|
5fc19b0edf | ||
|
|
0a73b70b64 | ||
|
|
2668aae09b | ||
|
|
3b9c0b34ae | ||
|
|
53f32849a9 | ||
|
|
7ca8bdc231 | ||
|
|
6621d55f3d | ||
|
|
d55db06c01 | ||
|
|
6b4b500a33 | ||
|
|
5ffec2c52d | ||
|
|
ec07793cda | ||
|
|
61adc735c8 | ||
|
|
7bbf591a7f | ||
|
|
61f8d18af5 | ||
|
|
f786d25f2e | ||
|
|
e62f4426ea | ||
|
|
32ba3a591f | ||
|
|
73025719a4 | ||
|
|
d55684531f | ||
|
|
d15eb129b0 | ||
|
|
3626a2265b | ||
|
|
d13abc7e1d | ||
|
|
2442829ef2 | ||
|
|
795b28162a | ||
|
|
31706ea06b | ||
|
|
4b9e6042d5 | ||
|
|
d279b0830b | ||
|
|
181ab91b1d | ||
|
|
841350a937 | ||
|
|
12183bac07 | ||
|
|
306f41b6f0 | ||
|
|
c1d76d2571 | ||
|
|
f83074d50e | ||
|
|
2be892be70 | ||
|
|
c934b9319f | ||
|
|
35a51197ce | ||
|
|
47fd578edb | ||
|
|
add091305c | ||
|
|
3d017594a8 | ||
|
|
0dcb2ec78c | ||
|
|
9186e77d27 | ||
|
|
6045aff33a | ||
|
|
dca9765d5d | ||
|
|
a37d0c57dc | ||
|
|
054475135a | ||
|
|
02a35b6db4 | ||
|
|
b80992ca59 | ||
|
|
c606970e38 | ||
|
|
dfeca246a0 | ||
|
|
3476d83ecc | ||
|
|
3617ab1540 | ||
|
|
e65b4b63a2 | ||
|
|
7cac3f4780 | ||
|
|
92cd11d105 | ||
|
|
13115ace84 | ||
|
|
73f9834e6f | ||
|
|
3afe855156 | ||
|
|
bfde896f0b | ||
|
|
1cdc0a7a3d | ||
|
|
d19b86640b | ||
|
|
2936ba609b | ||
|
|
64da80cbf4 | ||
|
|
5fa728f28a | ||
|
|
c61ce8dee4 | ||
|
|
573a2dd22a | ||
|
|
b55cc803d3 | ||
|
|
304ade418e | ||
|
|
997931c42f | ||
|
|
268e353431 | ||
|
|
b491b5fbca | ||
|
|
387c786768 | ||
|
|
2641586a6f | ||
|
|
6d2cd20e80 | ||
|
|
b0c574356a | ||
|
|
07e45a20e5 | ||
|
|
14056c69e6 | ||
|
|
fb9c840c46 | ||
|
|
5fba4a5399 | ||
|
|
c0b377050e | ||
|
|
f3efb6441d | ||
|
|
0cf313a21e | ||
|
|
26aadffb20 | ||
|
|
a5f48e3202 | ||
|
|
b0dda6e6a7 | ||
|
|
d4025d95e7 | ||
|
|
d6021f4d22 | ||
|
|
b9a3290731 | ||
|
|
48f235ea5a | ||
|
|
047771b9f4 | ||
|
|
b5375114d3 | ||
|
|
fc13e56cea | ||
|
|
77fc37ac25 | ||
|
|
3424351e84 | ||
|
|
606f9d92d0 | ||
|
|
a5e25abb9c | ||
|
|
b310e87e4c | ||
|
|
425baf9d6e | ||
|
|
825c369ad9 | ||
|
|
10bab70438 | ||
|
|
350e0b281b | ||
|
|
08805ea3c8 | ||
|
|
9441e32c69 | ||
|
|
530fc37067 | ||
|
|
369e499dce | ||
|
|
655815de6d | ||
|
|
457adc1fee | ||
|
|
e86a90967e | ||
|
|
5d08f7cf14 | ||
|
|
8744eb2d62 | ||
|
|
d8383cfa80 | ||
|
|
4626278447 | ||
|
|
c61af9c22b | ||
|
|
72521d0906 | ||
|
|
7e44b195c5 | ||
|
|
5b45eac5e1 | ||
|
|
c1d30341e7 | ||
|
|
80d2b4913b | ||
|
|
3f473528b1 | ||
|
|
d0dcd4f61b | ||
|
|
bde66a1396 | ||
|
|
4de5a2d9bf | ||
|
|
27bf4299cf | ||
|
|
164f01bb25 | ||
|
|
f563a005f5 | ||
|
|
a14d8e30cc | ||
|
|
a9194ffb63 | ||
|
|
2f9c1b7127 | ||
|
|
bbea76668b | ||
|
|
becc630acf | ||
|
|
4ac8ecad6b | ||
|
|
903e88c700 | ||
|
|
ed96aa820e | ||
|
|
63ec079b7b | ||
|
|
d485fcb3db | ||
|
|
0f895668a4 | ||
|
|
6c577ac3bf | ||
|
|
31cc2423d2 | ||
|
|
c9ed32e518 | ||
|
|
6b4c3a0969 | ||
|
|
2dad92d1bd | ||
|
|
c1fb7ab7dc | ||
|
|
98315f3899 | ||
|
|
8c82aaabd6 | ||
|
|
ce9b536b78 | ||
|
|
d9c50e5bc1 | ||
|
|
bf075f7dd8 | ||
|
|
a4fd673285 | ||
|
|
e794c977bc | ||
|
|
0b088ef1d3 | ||
|
|
bf6a6af683 | ||
|
|
914790fd99 | ||
|
|
edb0c6a9e8 | ||
|
|
84049de696 | ||
|
|
da0531e63b | ||
|
|
421dc75f4e | ||
|
|
8ae91df038 | ||
|
|
64b41dd626 | ||
|
|
ebd6e4d3a2 | ||
|
|
80374aea5c | ||
|
|
2ac9efae7d | ||
|
|
a11d565ba4 | ||
|
|
1fdf854ea7 | ||
|
|
e9c9792cb9 | ||
|
|
5ae524c25a | ||
|
|
0d7287fc8b | ||
|
|
e77c96f6b7 | ||
|
|
9b8a10dd3a | ||
|
|
49200ca5ce | ||
|
|
34aa4dbf10 | ||
|
|
5ee79d16c9 | ||
|
|
a1ea4006e0 | ||
|
|
9078188939 | ||
|
|
ed0aad1a7a | ||
|
|
5c59cfb020 | ||
|
|
3ca15ad68a | ||
|
|
60014989f5 | ||
|
|
57b10f195e | ||
|
|
b1e95eb39f | ||
|
|
b3da77b8f9 | ||
|
|
1a345b74bb | ||
|
|
8ffc3a4abf | ||
|
|
7233c1c7b2 | ||
|
|
1309a01131 | ||
|
|
0333185b6d | ||
|
|
83f89f64e8 | ||
|
|
11a1a6fb16 | ||
|
|
882c609296 | ||
|
|
176a0dcd59 | ||
|
|
94b0f70bfa | ||
|
|
08b2a77d41 | ||
|
|
3e8e9a23cf | ||
|
|
58b83b64c8 | ||
|
|
dfe4cde6ee | ||
|
|
d11144d9e2 | ||
|
|
f96b0ea5f3 | ||
|
|
815f8d79ed | ||
|
|
b62dab32e0 | ||
|
|
262f863981 | ||
|
|
a4c94390a1 | ||
|
|
53f3cca85d | ||
|
|
ed08bbcecc | ||
|
|
de97ebf9b7 | ||
|
|
f492a660a8 | ||
|
|
09436836a5 | ||
|
|
bb455d7788 | ||
|
|
009212ab80 | ||
|
|
ba9cb591c8 | ||
|
|
d00ac2f34e | ||
|
|
bd4dc6d463 | ||
|
|
d91180a909 | ||
|
|
bc2913a5cb | ||
|
|
4802394562 | ||
|
|
1755556468 | ||
|
|
01cdbdb7ae | ||
|
|
fc8bbf3eab | ||
|
|
3cdab19319 | ||
|
|
5661d20e87 | ||
|
|
91f80123e8 | ||
|
|
7a0636d0f8 | ||
|
|
0fe5bdfbac | ||
|
|
f88687e977 | ||
|
|
68d437d05b | ||
|
|
1e56aaea04 | ||
|
|
dab170a6fe | ||
|
|
a8de717d9b | ||
|
|
78fe95b6fc | ||
|
|
e0c24e41aa | ||
|
|
fa8553839b | ||
|
|
b8fcefc794 | ||
|
|
88bcb68fcb | ||
|
|
7c000553ae | ||
|
|
391fa35c80 | ||
|
|
c6773a8c9f | ||
|
|
9b226e7d39 | ||
|
|
9865446267 | ||
|
|
926abbe776 | ||
|
|
4fabef3a57 | ||
|
|
5ef4cd80c3 | ||
|
|
e01f23583f | ||
|
|
7792cb3915 | ||
|
|
be26253a18 | ||
|
|
1bdd1f8189 | ||
|
|
fa62c79b17 | ||
|
|
d7d8fa1e5b | ||
|
|
18562f1e10 | ||
|
|
86090a694f | ||
|
|
1ee8287c73 | ||
|
|
8eb98cd591 | ||
|
|
0f9ba21b05 | ||
|
|
834f8e7046 | ||
|
|
32e3399334 | ||
|
|
2d8698a218 | ||
|
|
454fb883a2 | ||
|
|
6f4a6ab8ea | ||
|
|
9c4b6f36f1 | ||
|
|
78886b1e67 | ||
|
|
d9debaf032 | ||
|
|
d4360d6347 | ||
|
|
175b1785c0 | ||
|
|
c8740c0171 | ||
|
|
91ee895a74 | ||
|
|
a045e46571 | ||
|
|
44eaa65c3b | ||
|
|
0a22af7b14 | ||
|
|
b54702ab08 | ||
|
|
c4fdcfc5d1 | ||
|
|
cb8117e8df | ||
|
|
5a218d5056 | ||
|
|
8dbc5cf9c6 | ||
|
|
71e81615a3 | ||
|
|
611d37da04 | ||
|
|
0e799a3857 | ||
|
|
b91d6e2bfa | ||
|
|
ea16ad7e94 | ||
|
|
ba6eb54552 | ||
|
|
f705e7683b | ||
|
|
dc996adb20 | ||
|
|
a64c638ccc | ||
|
|
359c067279 | ||
|
|
66a746e297 | ||
|
|
a4d43ee24b | ||
|
|
f7793a70a9 | ||
|
|
ceba3d31fb | ||
|
|
eecc08edde | ||
|
|
eb19aadc75 | ||
|
|
06c81e69b9 | ||
|
|
3dc3d4a639 | ||
|
|
94c59c1e3d | ||
|
|
4d2205853a | ||
|
|
751772b87a | ||
|
|
76e30869e1 | ||
|
|
3edc9fe9eb | ||
|
|
616c62703e | ||
|
|
ecd56917e7 | ||
|
|
e22c9cae91 | ||
|
|
29ddb6e1b9 | ||
|
|
2ff90e2ff0 | ||
|
|
04ecc128a2 | ||
|
|
87d1d3423b | ||
|
|
4818192a2a | ||
|
|
965dd97f54 | ||
|
|
195b74926c | ||
|
|
2120db12b2 | ||
|
|
ed563fef28 | ||
|
|
0d31a8e3f1 | ||
|
|
b8354b974b | ||
|
|
034c1e289d | ||
|
|
f31605a3de | ||
|
|
e7cc75c74d | ||
|
|
4b79d5e4e8 | ||
|
|
34854915b3 | ||
|
|
af6f34b529 | ||
|
|
fb82a2b896 | ||
|
|
5b464938b6 | ||
|
|
81f954890d | ||
|
|
0e2bbcec62 | ||
|
|
fdd339f525 | ||
|
|
8cf7d6a83d | ||
|
|
58a5008718 | ||
|
|
c44a8df55d | ||
|
|
ff1494c519 | ||
|
|
b8ce8fd852 | ||
|
|
75e7454a5f | ||
|
|
2558ea8931 | ||
|
|
ac0f47a4b2 | ||
|
|
4f16129869 | ||
|
|
64a8037fdd | ||
|
|
7502ba1bc8 | ||
|
|
33a04697ef | ||
|
|
b70a5c0cdb | ||
|
|
9443ae9f40 | ||
|
|
220c2a4102 | ||
|
|
e9914eb301 | ||
|
|
934512d09c | ||
|
|
9102c90986 | ||
|
|
c3e74219c4 | ||
|
|
13c9d7bc2d | ||
|
|
119b539586 | ||
|
|
29a5c180f0 | ||
|
|
7906602291 | ||
|
|
6dafe773ff | ||
|
|
25bc28a1be | ||
|
|
4c561c7fa0 | ||
|
|
95b3e78573 | ||
|
|
63a345bc93 | ||
|
|
e093a172cb | ||
|
|
4b01f8934b | ||
|
|
bc116b45b5 | ||
|
|
a059960b9e | ||
|
|
7770966fed | ||
|
|
d7adcf6c69 | ||
|
|
04a364dcc3 | ||
|
|
db83ac7eaa | ||
|
|
3ca9dddf61 | ||
|
|
bf74f53ca7 | ||
|
|
9d67efb4a4 | ||
|
|
3a39b9f440 | ||
|
|
27f7aab375 | ||
|
|
337da0c467 | ||
|
|
f56b3560c4 | ||
|
|
02dfe11ce6 | ||
|
|
83d06beb70 | ||
|
|
a8cfc059c8 | ||
|
|
1614b2bab0 | ||
|
|
4bdec0d214 | ||
|
|
6a7d7e7c2b | ||
|
|
30d4674657 | ||
|
|
9f961f95f8 | ||
|
|
bab99a26ec | ||
|
|
9a7fecd269 | ||
|
|
a8dc0d449b | ||
|
|
a0381f76bf | ||
|
|
6102f66daa | ||
|
|
c6134d162d | ||
|
|
2046f9b9de | ||
|
|
ac3ba594a4 | ||
|
|
22df25a480 | ||
|
|
8b30c7f02e | ||
|
|
757cdddc7c | ||
|
|
df95e99680 | ||
|
|
5a6d544db7 | ||
|
|
16117d329c | ||
|
|
e90da18ada | ||
|
|
a08d80e1cc | ||
|
|
6258175922 | ||
|
|
15736777a0 | ||
|
|
75915e8a94 | ||
|
|
9bde0ae4ea | ||
|
|
0c802d1f86 | ||
|
|
b7a96c6466 | ||
|
|
4b645a82c7 | ||
|
|
d599b77b6f | ||
|
|
26e93dc8c1 | ||
|
|
a4c9a8491b | ||
|
|
70ee636d87 | ||
|
|
b35f6dbb03 | ||
|
|
67d9e24d8f | ||
|
|
3903fda6ca | ||
|
|
441e46ebaa | ||
|
|
1f4260f359 | ||
|
|
dc0bf8ad4e | ||
|
|
102e326e6a | ||
|
|
2b25bf6f3b | ||
|
|
f93280696d | ||
|
|
1787391b07 | ||
|
|
a74a8ee483 | ||
|
|
7fa5405cb7 | ||
|
|
6725ddcc41 | ||
|
|
bce941db3f | ||
|
|
6d926048ec | ||
|
|
5335c973b4 | ||
|
|
15c3e5c96e | ||
|
|
a5d5904969 | ||
|
|
598758b991 | ||
|
|
9926e23bc8 | ||
|
|
5d3264bc63 | ||
|
|
d71f819f95 | ||
|
|
ee13509760 | ||
|
|
82d7bb1f32 | ||
|
|
cdfda508d8 | ||
|
|
da941e584f | ||
|
|
65874d7b96 | ||
|
|
ac9b8f405c | ||
|
|
8d1419a12e | ||
|
|
04f7a7d301 | ||
|
|
c10d2a1493 | ||
|
|
97bbf79ffd | ||
|
|
f7b01ae53d | ||
|
|
d704e1dbba | ||
|
|
ef2ff5e093 | ||
|
|
7caed3b0db | ||
|
|
45641d0754 | ||
|
|
4b1d08ba99 | ||
|
|
160fa99ba4 | ||
|
|
d2a5ab49ed | ||
|
|
c6404d8917 | ||
|
|
7113807f12 | ||
|
|
be711215e8 | ||
|
|
7e3b404240 | ||
|
|
e86901ca20 | ||
|
|
bdfa61c8b2 | ||
|
|
2cc36787f5 | ||
|
|
448ac61b48 | ||
|
|
753f6394f7 | ||
|
|
b1faf65934 | ||
|
|
09f478bd74 | ||
|
|
a0497feddd | ||
|
|
789693bde9 | ||
|
|
1fe933e4ea | ||
|
|
724b4b5a70 | ||
|
|
1778a56146 | ||
|
|
744865fcb2 | ||
|
|
7f8c8b448d | ||
|
|
a67c53826d | ||
|
|
14b131e850 | ||
|
|
9b55a52b85 | ||
|
|
db1d10e80f | ||
|
|
1be576966f | ||
|
|
b97e792c5f | ||
|
|
8dec674cc3 | ||
|
|
f784c03746 | ||
|
|
148e172fe8 | ||
|
|
56ae86646f | ||
|
|
1d2b6fdfa2 | ||
|
|
4fc75beed4 | ||
|
|
3b3bc0c4bf | ||
|
|
910faab88e | ||
|
|
f184d763ad | ||
|
|
a91d42634d | ||
|
|
f517ef3616 | ||
|
|
e99507ddcf | ||
|
|
d2cacf1945 | ||
|
|
448ac1405b | ||
|
|
6ad21ce885 |
16
.github/translators.txt
vendored
16
.github/translators.txt
vendored
@@ -438,7 +438,7 @@ javadataherian :: Persian
|
||||
Ludo-code :: French
|
||||
hollsten :: Swedish
|
||||
Ngoc Lan Phung (lanpncz) :: Vietnamese
|
||||
Worive :: Catalan
|
||||
Worive :: Catalan; French
|
||||
Илья Скаба (skabailya) :: Russian
|
||||
Irjan Olsen (Irch) :: Norwegian Bokmal
|
||||
Aleksandar Jovanovic (jovanoviczaleksandar) :: Serbian (Cyrillic)
|
||||
@@ -489,3 +489,17 @@ Hari (muhhari) :: Indonesian
|
||||
仙君御 (xjy) :: Chinese Simplified
|
||||
TapioM :: Finnish
|
||||
lingb58 :: Chinese Traditional
|
||||
Angel Pandey (angel-pandey) :: Nepali
|
||||
Supriya Shrestha (supriyashrestha) :: Nepali
|
||||
gprabhat :: Nepali
|
||||
CellCat :: Chinese Simplified
|
||||
Al Desrahim (aldesrahim) :: Indonesian
|
||||
ahmad abbaspour (deshneh.dar.diss) :: Persian
|
||||
Erjon K. (ekr) :: Albanian
|
||||
LiZerui (iamzrli) :: Chinese Traditional
|
||||
Ticker (ticker.com) :: Hebrew
|
||||
CrazyComputer :: Chinese Simplified
|
||||
Firr (FirrV) :: Russian
|
||||
João Faro (FaroJoaoFaro) :: Portuguese
|
||||
Danilo dos Santos Barbosa (bozochegou) :: Portuguese, Brazilian
|
||||
Chris (furesoft) :: German
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,10 +8,10 @@ Homestead.yaml
|
||||
.idea
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/public/dist
|
||||
/public/dist/*.map
|
||||
/public/plugins
|
||||
/public/css
|
||||
/public/js
|
||||
/public/css/*.map
|
||||
/public/js/*.map
|
||||
/public/bower
|
||||
/public/build/
|
||||
/public/favicon.ico
|
||||
|
||||
@@ -57,16 +57,13 @@ class LdapSessionGuard extends ExternalBaseSessionGuard
|
||||
/**
|
||||
* Attempt to authenticate a user using the given credentials.
|
||||
*
|
||||
* @param array $credentials
|
||||
* @param bool $remember
|
||||
*
|
||||
* @throws LdapException*@throws \BookStack\Exceptions\JsonDebugException
|
||||
* @throws LdapException
|
||||
* @throws LoginAttemptException
|
||||
* @throws JsonDebugException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function attempt(array $credentials = [], $remember = false)
|
||||
public function attempt(array $credentials = [], $remember = false): bool
|
||||
{
|
||||
$username = $credentials['username'];
|
||||
$userDetails = $this->ldapService->getUserDetails($username);
|
||||
|
||||
@@ -11,6 +11,7 @@ use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||
use BookStack\Permissions\PermissionApplicator;
|
||||
use BookStack\Users\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
class ActivityQueries
|
||||
@@ -67,6 +68,7 @@ class ActivityQueries
|
||||
|
||||
$activity = $query->orderBy('created_at', 'desc')
|
||||
->with(['loggable' => function (Relation $query) {
|
||||
/** @var MorphTo<Entity, Activity> $query */
|
||||
$query->withTrashed();
|
||||
}, 'user.avatar'])
|
||||
->skip($count * ($page - 1))
|
||||
|
||||
@@ -12,6 +12,8 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $value
|
||||
* @property int $entity_id
|
||||
* @property string $entity_type
|
||||
* @property int $order
|
||||
*/
|
||||
class Tag extends Model
|
||||
|
||||
@@ -20,7 +20,8 @@ class PageUpdateNotificationHandler extends BaseNotificationHandler
|
||||
throw new \InvalidArgumentException("Detail for page update notifications must be a page");
|
||||
}
|
||||
|
||||
// Get last update from activity
|
||||
// Get the last update from activity
|
||||
/** @var ?Activity $lastUpdate */
|
||||
$lastUpdate = $detail->activity()
|
||||
->where('type', '=', ActivityType::PAGE_UPDATE)
|
||||
->where('id', '!=', $activity->id)
|
||||
|
||||
@@ -3,17 +3,15 @@
|
||||
namespace BookStack\Activity\Tools;
|
||||
|
||||
use BookStack\Activity\Models\Tag;
|
||||
use BookStack\Entities\Models\BookChild;
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use BookStack\Entities\Models\Page;
|
||||
|
||||
class TagClassGenerator
|
||||
{
|
||||
protected array $tags;
|
||||
|
||||
/**
|
||||
* @param Tag[] $tags
|
||||
*/
|
||||
public function __construct(array $tags)
|
||||
{
|
||||
$this->tags = $tags;
|
||||
public function __construct(
|
||||
protected Entity $entity
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,14 +20,23 @@ class TagClassGenerator
|
||||
public function generate(): array
|
||||
{
|
||||
$classes = [];
|
||||
$tags = $this->entity->tags->all();
|
||||
|
||||
foreach ($this->tags as $tag) {
|
||||
$name = $this->normalizeTagClassString($tag->name);
|
||||
$value = $this->normalizeTagClassString($tag->value);
|
||||
$classes[] = 'tag-name-' . $name;
|
||||
if ($value) {
|
||||
$classes[] = 'tag-value-' . $value;
|
||||
$classes[] = 'tag-pair-' . $name . '-' . $value;
|
||||
foreach ($tags as $tag) {
|
||||
array_push($classes, ...$this->generateClassesForTag($tag));
|
||||
}
|
||||
|
||||
if ($this->entity instanceof BookChild && userCan('view', $this->entity->book)) {
|
||||
$bookTags = $this->entity->book->tags;
|
||||
foreach ($bookTags as $bookTag) {
|
||||
array_push($classes, ...$this->generateClassesForTag($bookTag, 'book-'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->entity instanceof Page && $this->entity->chapter && userCan('view', $this->entity->chapter)) {
|
||||
$chapterTags = $this->entity->chapter->tags;
|
||||
foreach ($chapterTags as $chapterTag) {
|
||||
array_push($classes, ...$this->generateClassesForTag($chapterTag, 'chapter-'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +48,22 @@ class TagClassGenerator
|
||||
return implode(' ', $this->generate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function generateClassesForTag(Tag $tag, string $prefix = ''): array
|
||||
{
|
||||
$classes = [];
|
||||
$name = $this->normalizeTagClassString($tag->name);
|
||||
$value = $this->normalizeTagClassString($tag->value);
|
||||
$classes[] = "{$prefix}tag-name-{$name}";
|
||||
if ($value) {
|
||||
$classes[] = "{$prefix}tag-value-{$value}";
|
||||
$classes[] = "{$prefix}tag-pair-{$name}-{$value}";
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
protected function normalizeTagClassString(string $value): string
|
||||
{
|
||||
$value = str_replace(' ', '', strtolower($value));
|
||||
|
||||
@@ -50,7 +50,7 @@ class WebhookFormatter
|
||||
}
|
||||
|
||||
if ($this->detail instanceof Model) {
|
||||
$data['related_item'] = $this->formatModel();
|
||||
$data['related_item'] = $this->formatModel($this->detail);
|
||||
}
|
||||
|
||||
return $data;
|
||||
@@ -83,10 +83,8 @@ class WebhookFormatter
|
||||
);
|
||||
}
|
||||
|
||||
protected function formatModel(): array
|
||||
protected function formatModel(Model $model): array
|
||||
{
|
||||
/** @var Model $model */
|
||||
$model = $this->detail;
|
||||
$model->unsetRelations();
|
||||
|
||||
foreach ($this->modelFormatters as $formatter) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// Configured mail encryption method.
|
||||
// STARTTLS should still be attempted, but tls/ssl forces TLS usage.
|
||||
$mailEncryption = env('MAIL_ENCRYPTION', null);
|
||||
$mailPort = intval(env('MAIL_PORT', 587));
|
||||
|
||||
return [
|
||||
|
||||
@@ -33,13 +34,13 @@ return [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => null,
|
||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
||||
'port' => env('MAIL_PORT', 587),
|
||||
'port' => $mailPort,
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'verify_peer' => env('MAIL_VERIFY_SSL', true),
|
||||
'timeout' => null,
|
||||
'local_domain' => null,
|
||||
'tls_required' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl'),
|
||||
'require_tls' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl' || $mailPort === 465),
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
|
||||
@@ -18,6 +18,7 @@ use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Http\Controller;
|
||||
use BookStack\References\ReferenceFetcher;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use BookStack\Util\SimpleListOptions;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
@@ -263,7 +264,9 @@ class BookController extends Controller
|
||||
$this->checkPermission('bookshelf-create-all');
|
||||
$this->checkPermission('book-create-all');
|
||||
|
||||
$shelf = $transformer->transformBookToShelf($book);
|
||||
$shelf = (new DatabaseTransaction(function () use ($book, $transformer) {
|
||||
return $transformer->transformBookToShelf($book);
|
||||
}))->run();
|
||||
|
||||
return redirect($shelf->getUrl());
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace BookStack\Entities\Controllers;
|
||||
|
||||
use BookStack\Entities\Models\Book;
|
||||
use BookStack\Entities\Models\Chapter;
|
||||
use BookStack\Entities\Queries\ChapterQueries;
|
||||
use BookStack\Entities\Queries\EntityQueries;
|
||||
@@ -9,12 +10,11 @@ use BookStack\Entities\Repos\ChapterRepo;
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Http\ApiController;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ChapterApiController extends ApiController
|
||||
{
|
||||
protected $rules = [
|
||||
protected array $rules = [
|
||||
'create' => [
|
||||
'book_id' => ['required', 'integer'],
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
@@ -144,7 +144,10 @@ class ChapterApiController extends ApiController
|
||||
$chapter->load(['tags']);
|
||||
$chapter->makeVisible('description_html');
|
||||
$chapter->setAttribute('description_html', $chapter->descriptionHtml());
|
||||
$chapter->setAttribute('book_slug', $chapter->book()->first()->slug);
|
||||
|
||||
/** @var Book $book */
|
||||
$book = $chapter->book()->first();
|
||||
$chapter->setAttribute('book_slug', $book->slug);
|
||||
|
||||
return $chapter;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use BookStack\Exceptions\NotifyException;
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Http\Controller;
|
||||
use BookStack\References\ReferenceFetcher;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Throwable;
|
||||
@@ -269,7 +270,9 @@ class ChapterController extends Controller
|
||||
$this->checkOwnablePermission('chapter-delete', $chapter);
|
||||
$this->checkPermission('book-create-all');
|
||||
|
||||
$book = $transformer->transformChapterToBook($chapter);
|
||||
$book = (new DatabaseTransaction(function () use ($chapter, $transformer) {
|
||||
return $transformer->transformChapterToBook($chapter);
|
||||
}))->run();
|
||||
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use Illuminate\Http\Request;
|
||||
|
||||
class PageApiController extends ApiController
|
||||
{
|
||||
protected $rules = [
|
||||
protected array $rules = [
|
||||
'create' => [
|
||||
'book_id' => ['required_without:chapter_id', 'integer'],
|
||||
'chapter_id' => ['required_without:book_id', 'integer'],
|
||||
|
||||
@@ -26,6 +26,7 @@ use BookStack\Users\Models\HasOwner;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
@@ -283,10 +284,14 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
||||
public function getParent(): ?self
|
||||
{
|
||||
if ($this instanceof Page) {
|
||||
return $this->chapter_id ? $this->chapter()->withTrashed()->first() : $this->book()->withTrashed()->first();
|
||||
/** @var BelongsTo<Chapter|Book, Page> $builder */
|
||||
$builder = $this->chapter_id ? $this->chapter() : $this->book();
|
||||
return $builder->withTrashed()->first();
|
||||
}
|
||||
if ($this instanceof Chapter) {
|
||||
return $this->book()->withTrashed()->first();
|
||||
/** @var BelongsTo<Book, Page> $builder */
|
||||
$builder = $this->book();
|
||||
return $builder->withTrashed()->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -295,7 +300,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
||||
/**
|
||||
* Rebuild the permissions for this entity.
|
||||
*/
|
||||
public function rebuildPermissions()
|
||||
public function rebuildPermissions(): void
|
||||
{
|
||||
app()->make(JointPermissionBuilder::class)->rebuildForEntity(clone $this);
|
||||
}
|
||||
@@ -303,7 +308,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
||||
/**
|
||||
* Index the current entity for search.
|
||||
*/
|
||||
public function indexForSearch()
|
||||
public function indexForSearch(): void
|
||||
{
|
||||
app()->make(SearchIndex::class)->indexEntity(clone $this);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ class BaseRepo
|
||||
$entity->touch();
|
||||
}
|
||||
|
||||
$entity->rebuildPermissions();
|
||||
$entity->indexForSearch();
|
||||
$this->referenceStore->updateForEntity($entity);
|
||||
|
||||
@@ -139,7 +138,7 @@ class BaseRepo
|
||||
|
||||
/**
|
||||
* Sort the parent of the given entity, if any auto sort actions are set for it.
|
||||
* Typical ran during create/update/insert events.
|
||||
* Typically ran during create/update/insert events.
|
||||
*/
|
||||
public function sortParent(Entity $entity): void
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ use BookStack\Exceptions\ImageUploadException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Sorting\SortRule;
|
||||
use BookStack\Uploads\ImageRepo;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
@@ -28,19 +29,22 @@ class BookRepo
|
||||
*/
|
||||
public function create(array $input): Book
|
||||
{
|
||||
$book = new Book();
|
||||
$this->baseRepo->create($book, $input);
|
||||
$this->baseRepo->updateCoverImage($book, $input['image'] ?? null);
|
||||
$this->baseRepo->updateDefaultTemplate($book, intval($input['default_template_id'] ?? null));
|
||||
Activity::add(ActivityType::BOOK_CREATE, $book);
|
||||
return (new DatabaseTransaction(function () use ($input) {
|
||||
$book = new Book();
|
||||
|
||||
$defaultBookSortSetting = intval(setting('sorting-book-default', '0'));
|
||||
if ($defaultBookSortSetting && SortRule::query()->find($defaultBookSortSetting)) {
|
||||
$book->sort_rule_id = $defaultBookSortSetting;
|
||||
$book->save();
|
||||
}
|
||||
$this->baseRepo->create($book, $input);
|
||||
$this->baseRepo->updateCoverImage($book, $input['image'] ?? null);
|
||||
$this->baseRepo->updateDefaultTemplate($book, intval($input['default_template_id'] ?? null));
|
||||
Activity::add(ActivityType::BOOK_CREATE, $book);
|
||||
|
||||
return $book;
|
||||
$defaultBookSortSetting = intval(setting('sorting-book-default', '0'));
|
||||
if ($defaultBookSortSetting && SortRule::query()->find($defaultBookSortSetting)) {
|
||||
$book->sort_rule_id = $defaultBookSortSetting;
|
||||
$book->save();
|
||||
}
|
||||
|
||||
return $book;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ use BookStack\Entities\Models\Bookshelf;
|
||||
use BookStack\Entities\Queries\BookQueries;
|
||||
use BookStack\Entities\Tools\TrashCan;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
|
||||
class BookshelfRepo
|
||||
@@ -23,13 +24,14 @@ class BookshelfRepo
|
||||
*/
|
||||
public function create(array $input, array $bookIds): Bookshelf
|
||||
{
|
||||
$shelf = new Bookshelf();
|
||||
$this->baseRepo->create($shelf, $input);
|
||||
$this->baseRepo->updateCoverImage($shelf, $input['image'] ?? null);
|
||||
$this->updateBooks($shelf, $bookIds);
|
||||
Activity::add(ActivityType::BOOKSHELF_CREATE, $shelf);
|
||||
|
||||
return $shelf;
|
||||
return (new DatabaseTransaction(function () use ($input, $bookIds) {
|
||||
$shelf = new Bookshelf();
|
||||
$this->baseRepo->create($shelf, $input);
|
||||
$this->baseRepo->updateCoverImage($shelf, $input['image'] ?? null);
|
||||
$this->updateBooks($shelf, $bookIds);
|
||||
Activity::add(ActivityType::BOOKSHELF_CREATE, $shelf);
|
||||
return $shelf;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ use BookStack\Entities\Tools\TrashCan;
|
||||
use BookStack\Exceptions\MoveOperationException;
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
|
||||
class ChapterRepo
|
||||
@@ -27,16 +28,18 @@ class ChapterRepo
|
||||
*/
|
||||
public function create(array $input, Book $parentBook): Chapter
|
||||
{
|
||||
$chapter = new Chapter();
|
||||
$chapter->book_id = $parentBook->id;
|
||||
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
||||
$this->baseRepo->create($chapter, $input);
|
||||
$this->baseRepo->updateDefaultTemplate($chapter, intval($input['default_template_id'] ?? null));
|
||||
Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
|
||||
return (new DatabaseTransaction(function () use ($input, $parentBook) {
|
||||
$chapter = new Chapter();
|
||||
$chapter->book_id = $parentBook->id;
|
||||
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
||||
$this->baseRepo->create($chapter, $input);
|
||||
$this->baseRepo->updateDefaultTemplate($chapter, intval($input['default_template_id'] ?? null));
|
||||
Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
|
||||
|
||||
$this->baseRepo->sortParent($chapter);
|
||||
$this->baseRepo->sortParent($chapter);
|
||||
|
||||
return $chapter;
|
||||
return $chapter;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,12 +91,14 @@ class ChapterRepo
|
||||
throw new PermissionsException('User does not have permission to create a chapter within the chosen book');
|
||||
}
|
||||
|
||||
$chapter->changeBook($parent->id);
|
||||
$chapter->rebuildPermissions();
|
||||
Activity::add(ActivityType::CHAPTER_MOVE, $chapter);
|
||||
return (new DatabaseTransaction(function () use ($chapter, $parent) {
|
||||
$chapter->changeBook($parent->id);
|
||||
$chapter->rebuildPermissions();
|
||||
Activity::add(ActivityType::CHAPTER_MOVE, $chapter);
|
||||
|
||||
$this->baseRepo->sortParent($chapter);
|
||||
$this->baseRepo->sortParent($chapter);
|
||||
|
||||
return $parent;
|
||||
return $parent;
|
||||
}))->run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\References\ReferenceStore;
|
||||
use BookStack\References\ReferenceUpdater;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
|
||||
class PageRepo
|
||||
@@ -61,8 +62,10 @@ class PageRepo
|
||||
]);
|
||||
}
|
||||
|
||||
$page->save();
|
||||
$page->refresh()->rebuildPermissions();
|
||||
(new DatabaseTransaction(function () use ($page) {
|
||||
$page->save();
|
||||
$page->refresh()->rebuildPermissions();
|
||||
}))->run();
|
||||
|
||||
return $page;
|
||||
}
|
||||
@@ -72,26 +75,29 @@ class PageRepo
|
||||
*/
|
||||
public function publishDraft(Page $draft, array $input): Page
|
||||
{
|
||||
$draft->draft = false;
|
||||
$draft->revision_count = 1;
|
||||
$draft->priority = $this->getNewPriority($draft);
|
||||
$this->updateTemplateStatusAndContentFromInput($draft, $input);
|
||||
$this->baseRepo->update($draft, $input);
|
||||
return (new DatabaseTransaction(function () use ($draft, $input) {
|
||||
$draft->draft = false;
|
||||
$draft->revision_count = 1;
|
||||
$draft->priority = $this->getNewPriority($draft);
|
||||
$this->updateTemplateStatusAndContentFromInput($draft, $input);
|
||||
$this->baseRepo->update($draft, $input);
|
||||
$draft->rebuildPermissions();
|
||||
|
||||
$summary = trim($input['summary'] ?? '') ?: trans('entities.pages_initial_revision');
|
||||
$this->revisionRepo->storeNewForPage($draft, $summary);
|
||||
$draft->refresh();
|
||||
$summary = trim($input['summary'] ?? '') ?: trans('entities.pages_initial_revision');
|
||||
$this->revisionRepo->storeNewForPage($draft, $summary);
|
||||
$draft->refresh();
|
||||
|
||||
Activity::add(ActivityType::PAGE_CREATE, $draft);
|
||||
$this->baseRepo->sortParent($draft);
|
||||
Activity::add(ActivityType::PAGE_CREATE, $draft);
|
||||
$this->baseRepo->sortParent($draft);
|
||||
|
||||
return $draft;
|
||||
return $draft;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly update the content for the given page from the provided input.
|
||||
* Used for direct content access in a way that performs required changes
|
||||
* (Search index & reference regen) without performing an official update.
|
||||
* (Search index and reference regen) without performing an official update.
|
||||
*/
|
||||
public function setContentFromInput(Page $page, array $input): void
|
||||
{
|
||||
@@ -116,7 +122,7 @@ class PageRepo
|
||||
$page->revision_count++;
|
||||
$page->save();
|
||||
|
||||
// Remove all update drafts for this user & page.
|
||||
// Remove all update drafts for this user and page.
|
||||
$this->revisionRepo->deleteDraftsForCurrentUser($page);
|
||||
|
||||
// Save a revision after updating
|
||||
@@ -269,16 +275,18 @@ class PageRepo
|
||||
throw new PermissionsException('User does not have permission to create a page within the new parent');
|
||||
}
|
||||
|
||||
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
|
||||
$newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
|
||||
$page->changeBook($newBookId);
|
||||
$page->rebuildPermissions();
|
||||
return (new DatabaseTransaction(function () use ($page, $parent) {
|
||||
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
|
||||
$newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
|
||||
$page->changeBook($newBookId);
|
||||
$page->rebuildPermissions();
|
||||
|
||||
Activity::add(ActivityType::PAGE_MOVE, $page);
|
||||
Activity::add(ActivityType::PAGE_MOVE, $page);
|
||||
|
||||
$this->baseRepo->sortParent($page);
|
||||
$this->baseRepo->sortParent($page);
|
||||
|
||||
return $parent;
|
||||
return $parent;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,17 +13,12 @@ use BookStack\Facades\Activity;
|
||||
|
||||
class HierarchyTransformer
|
||||
{
|
||||
protected BookRepo $bookRepo;
|
||||
protected BookshelfRepo $shelfRepo;
|
||||
protected Cloner $cloner;
|
||||
protected TrashCan $trashCan;
|
||||
|
||||
public function __construct(BookRepo $bookRepo, BookshelfRepo $shelfRepo, Cloner $cloner, TrashCan $trashCan)
|
||||
{
|
||||
$this->bookRepo = $bookRepo;
|
||||
$this->shelfRepo = $shelfRepo;
|
||||
$this->cloner = $cloner;
|
||||
$this->trashCan = $trashCan;
|
||||
public function __construct(
|
||||
protected BookRepo $bookRepo,
|
||||
protected BookshelfRepo $shelfRepo,
|
||||
protected Cloner $cloner,
|
||||
protected TrashCan $trashCan
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ use BookStack\Exceptions\NotifyException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Uploads\AttachmentService;
|
||||
use BookStack\Uploads\ImageService;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
@@ -357,25 +358,26 @@ class TrashCan
|
||||
|
||||
/**
|
||||
* Destroy the given entity.
|
||||
* Returns the number of total entities destroyed in the operation.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function destroyEntity(Entity $entity): int
|
||||
{
|
||||
if ($entity instanceof Page) {
|
||||
return $this->destroyPage($entity);
|
||||
}
|
||||
if ($entity instanceof Chapter) {
|
||||
return $this->destroyChapter($entity);
|
||||
}
|
||||
if ($entity instanceof Book) {
|
||||
return $this->destroyBook($entity);
|
||||
}
|
||||
if ($entity instanceof Bookshelf) {
|
||||
return $this->destroyShelf($entity);
|
||||
}
|
||||
$result = (new DatabaseTransaction(function () use ($entity) {
|
||||
if ($entity instanceof Page) {
|
||||
return $this->destroyPage($entity);
|
||||
} else if ($entity instanceof Chapter) {
|
||||
return $this->destroyChapter($entity);
|
||||
} else if ($entity instanceof Book) {
|
||||
return $this->destroyBook($entity);
|
||||
} else if ($entity instanceof Bookshelf) {
|
||||
return $this->destroyShelf($entity);
|
||||
}
|
||||
return null;
|
||||
}))->run();
|
||||
|
||||
return 0;
|
||||
return $result ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Exceptions;
|
||||
|
||||
class DrawioPngReaderException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace BookStack\Exports\Controllers;
|
||||
|
||||
use BookStack\Entities\Queries\BookQueries;
|
||||
use BookStack\Exports\ExportFormatter;
|
||||
use BookStack\Exports\ZipExports\ZipExportBuilder;
|
||||
use BookStack\Http\ApiController;
|
||||
use Throwable;
|
||||
|
||||
@@ -63,4 +64,15 @@ class BookExportApiController extends ApiController
|
||||
|
||||
return $this->download()->directly($markdown, $book->slug . '.md');
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a book as a contained ZIP export file.
|
||||
*/
|
||||
public function exportZip(int $id, ZipExportBuilder $builder)
|
||||
{
|
||||
$book = $this->queries->findVisibleByIdOrFail($id);
|
||||
$zip = $builder->buildForBook($book);
|
||||
|
||||
return $this->download()->streamedFileDirectly($zip, $book->slug . '.zip', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace BookStack\Exports\Controllers;
|
||||
|
||||
use BookStack\Entities\Queries\ChapterQueries;
|
||||
use BookStack\Exports\ExportFormatter;
|
||||
use BookStack\Exports\ZipExports\ZipExportBuilder;
|
||||
use BookStack\Http\ApiController;
|
||||
use Throwable;
|
||||
|
||||
@@ -63,4 +64,15 @@ class ChapterExportApiController extends ApiController
|
||||
|
||||
return $this->download()->directly($markdown, $chapter->slug . '.md');
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a chapter as a contained ZIP file.
|
||||
*/
|
||||
public function exportZip(int $id, ZipExportBuilder $builder)
|
||||
{
|
||||
$chapter = $this->queries->findVisibleByIdOrFail($id);
|
||||
$zip = $builder->buildForChapter($chapter);
|
||||
|
||||
return $this->download()->streamedFileDirectly($zip, $chapter->slug . '.zip', true);
|
||||
}
|
||||
}
|
||||
|
||||
144
app/Exports/Controllers/ImportApiController.php
Normal file
144
app/Exports/Controllers/ImportApiController.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BookStack\Exports\Controllers;
|
||||
|
||||
use BookStack\Exceptions\ZipImportException;
|
||||
use BookStack\Exceptions\ZipValidationException;
|
||||
use BookStack\Exports\ImportRepo;
|
||||
use BookStack\Http\ApiController;
|
||||
use BookStack\Uploads\AttachmentService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class ImportApiController extends ApiController
|
||||
{
|
||||
public function __construct(
|
||||
protected ImportRepo $imports,
|
||||
) {
|
||||
$this->middleware('can:content-import');
|
||||
}
|
||||
|
||||
/**
|
||||
* List existing ZIP imports visible to the user.
|
||||
* Requires permission to import content.
|
||||
*/
|
||||
public function list(): JsonResponse
|
||||
{
|
||||
$query = $this->imports->queryVisible();
|
||||
|
||||
return $this->apiListingResponse($query, [
|
||||
'id', 'name', 'size', 'type', 'created_by', 'created_at', 'updated_at'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new import from a ZIP file.
|
||||
* This does not actually run the import since that is performed via the "run" endpoint.
|
||||
* This uploads, validates and stores the ZIP file so it's ready to be imported.
|
||||
*
|
||||
* This "file" parameter must be a BookStack-compatible ZIP file, and this must be
|
||||
* sent via a 'multipart/form-data' type request.
|
||||
*
|
||||
* Requires permission to import content.
|
||||
*/
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
$this->validate($request, $this->rules()['create']);
|
||||
|
||||
$file = $request->file('file');
|
||||
|
||||
try {
|
||||
$import = $this->imports->storeFromUpload($file);
|
||||
} catch (ZipValidationException $exception) {
|
||||
$message = "ZIP upload failed with the following validation errors: \n" . $this->formatErrors($exception->errors);
|
||||
return $this->jsonError($message, 422);
|
||||
}
|
||||
|
||||
return response()->json($import);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read details of a pending ZIP import.
|
||||
* The "details" property contains high-level metadata regarding the ZIP import content,
|
||||
* and the structure of this will change depending on import "type".
|
||||
* Requires permission to import content.
|
||||
*/
|
||||
public function read(int $id): JsonResponse
|
||||
{
|
||||
$import = $this->imports->findVisible($id);
|
||||
|
||||
$import->setAttribute('details', $import->decodeMetadata());
|
||||
|
||||
return response()->json($import);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the import process for an uploaded ZIP import.
|
||||
* The "parent_id" and "parent_type" parameters are required when the import type is "chapter" or "page".
|
||||
* On success, this endpoint returns the imported item.
|
||||
* Requires permission to import content.
|
||||
*/
|
||||
public function run(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$import = $this->imports->findVisible($id);
|
||||
$parent = null;
|
||||
$rules = $this->rules()['run'];
|
||||
|
||||
if ($import->type === 'page' || $import->type === 'chapter') {
|
||||
$rules['parent_type'][] = 'required';
|
||||
$rules['parent_id'][] = 'required';
|
||||
$data = $this->validate($request, $rules);
|
||||
$parent = "{$data['parent_type']}:{$data['parent_id']}";
|
||||
}
|
||||
|
||||
try {
|
||||
$entity = $this->imports->runImport($import, $parent);
|
||||
} catch (ZipImportException $exception) {
|
||||
$message = "ZIP import failed with the following errors: \n" . $this->formatErrors($exception->errors);
|
||||
return $this->jsonError($message);
|
||||
}
|
||||
|
||||
return response()->json($entity->withoutRelations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a pending ZIP import from the system.
|
||||
* Requires permission to import content.
|
||||
*/
|
||||
public function delete(int $id): Response
|
||||
{
|
||||
$import = $this->imports->findVisible($id);
|
||||
$this->imports->deleteImport($import);
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'create' => [
|
||||
'file' => ['required', ...AttachmentService::getFileValidationRules()],
|
||||
],
|
||||
'run' => [
|
||||
'parent_type' => ['string', 'in:book,chapter'],
|
||||
'parent_id' => ['int'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function formatErrors(array $errors): string
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($errors as $key => $error) {
|
||||
if (is_string($key)) {
|
||||
$parts[] = "[{$key}] {$error}";
|
||||
} else {
|
||||
$parts[] = $error;
|
||||
}
|
||||
}
|
||||
return implode("\n", $parts);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ class ImportController extends Controller
|
||||
try {
|
||||
$entity = $this->imports->runImport($import, $parent);
|
||||
} catch (ZipImportException $exception) {
|
||||
session()->flush();
|
||||
session()->forget(['success', 'warning']);
|
||||
$this->showErrorNotification(trans('errors.import_zip_failed_notification'));
|
||||
return redirect($import->getUrl())->with('import_errors', $exception->errors);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace BookStack\Exports\Controllers;
|
||||
|
||||
use BookStack\Entities\Queries\PageQueries;
|
||||
use BookStack\Exports\ExportFormatter;
|
||||
use BookStack\Exports\ZipExports\ZipExportBuilder;
|
||||
use BookStack\Http\ApiController;
|
||||
use Throwable;
|
||||
|
||||
@@ -63,4 +64,15 @@ class PageExportApiController extends ApiController
|
||||
|
||||
return $this->download()->directly($markdown, $page->slug . '.md');
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a page as a contained ZIP file.
|
||||
*/
|
||||
public function exportZip(int $id, ZipExportBuilder $builder)
|
||||
{
|
||||
$page = $this->queries->findVisibleByIdOrFail($id);
|
||||
$zip = $builder->buildForPage($page);
|
||||
|
||||
return $this->download()->streamedFileDirectly($zip, $page->slug . '.zip', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ class Import extends Model implements Loggable
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $hidden = ['metadata'];
|
||||
|
||||
public function getSizeString(): string
|
||||
{
|
||||
$mb = round($this->size / 1000000, 2);
|
||||
|
||||
@@ -17,6 +17,7 @@ use BookStack\Exports\ZipExports\ZipExportValidator;
|
||||
use BookStack\Exports\ZipExports\ZipImportRunner;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Uploads\FileStorage;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
@@ -34,6 +35,11 @@ class ImportRepo
|
||||
* @return Collection<Import>
|
||||
*/
|
||||
public function getVisibleImports(): Collection
|
||||
{
|
||||
return $this->queryVisible()->get();
|
||||
}
|
||||
|
||||
public function queryVisible(): Builder
|
||||
{
|
||||
$query = Import::query();
|
||||
|
||||
@@ -41,7 +47,7 @@ class ImportRepo
|
||||
$query->where('created_by', user()->id);
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function findVisible(int $id): Import
|
||||
|
||||
@@ -17,17 +17,17 @@ use BookStack\Uploads\Image;
|
||||
|
||||
class ZipExportReferences
|
||||
{
|
||||
/** @var ZipExportPage[] */
|
||||
/** @var array<int, ZipExportPage> */
|
||||
protected array $pages = [];
|
||||
/** @var ZipExportChapter[] */
|
||||
/** @var array<int, ZipExportChapter> */
|
||||
protected array $chapters = [];
|
||||
/** @var ZipExportBook[] */
|
||||
/** @var array<int, ZipExportBook> */
|
||||
protected array $books = [];
|
||||
|
||||
/** @var ZipExportAttachment[] */
|
||||
/** @var array<int, ZipExportAttachment> */
|
||||
protected array $attachments = [];
|
||||
|
||||
/** @var ZipExportImage[] */
|
||||
/** @var array<int, ZipExportImage> */
|
||||
protected array $images = [];
|
||||
|
||||
public function __construct(
|
||||
@@ -134,11 +134,12 @@ class ZipExportReferences
|
||||
|
||||
// Find and include images if in visibility
|
||||
$page = $model->getPage();
|
||||
if ($page && userCan('view', $page)) {
|
||||
$pageExportModel = $this->pages[$page->id] ?? ($exportModel instanceof ZipExportPage ? $exportModel : null);
|
||||
if (isset($this->images[$model->id]) || ($page && $pageExportModel && userCan('view', $page))) {
|
||||
if (!isset($this->images[$model->id])) {
|
||||
$exportImage = ZipExportImage::fromModel($model, $files);
|
||||
$this->images[$model->id] = $exportImage;
|
||||
$exportModel->images[] = $exportImage;
|
||||
$pageExportModel->images[] = $exportImage;
|
||||
}
|
||||
return "[[bsexport:image:{$model->id}]]";
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\Http\JsonResponse;
|
||||
|
||||
abstract class ApiController extends Controller
|
||||
{
|
||||
protected $rules = [];
|
||||
protected array $rules = [];
|
||||
|
||||
/**
|
||||
* Provide a paginated listing JSON response in a standard format
|
||||
|
||||
@@ -16,7 +16,7 @@ class ContentPermissionApiController extends ApiController
|
||||
) {
|
||||
}
|
||||
|
||||
protected $rules = [
|
||||
protected array $rules = [
|
||||
'update' => [
|
||||
'owner_id' => ['int'],
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class JointPermissionBuilder
|
||||
/**
|
||||
* Re-generate all entity permission from scratch.
|
||||
*/
|
||||
public function rebuildForAll()
|
||||
public function rebuildForAll(): void
|
||||
{
|
||||
JointPermission::query()->truncate();
|
||||
|
||||
@@ -51,7 +51,7 @@ class JointPermissionBuilder
|
||||
/**
|
||||
* Rebuild the entity jointPermissions for a particular entity.
|
||||
*/
|
||||
public function rebuildForEntity(Entity $entity)
|
||||
public function rebuildForEntity(Entity $entity): void
|
||||
{
|
||||
$entities = [$entity];
|
||||
if ($entity instanceof Book) {
|
||||
@@ -119,7 +119,7 @@ class JointPermissionBuilder
|
||||
/**
|
||||
* Build joint permissions for the given book and role combinations.
|
||||
*/
|
||||
protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false)
|
||||
protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false): void
|
||||
{
|
||||
$entities = clone $books;
|
||||
|
||||
@@ -143,7 +143,7 @@ class JointPermissionBuilder
|
||||
/**
|
||||
* Rebuild the entity jointPermissions for a collection of entities.
|
||||
*/
|
||||
protected function buildJointPermissionsForEntities(array $entities)
|
||||
protected function buildJointPermissionsForEntities(array $entities): void
|
||||
{
|
||||
$roles = Role::query()->get()->values()->all();
|
||||
$this->deleteManyJointPermissionsForEntities($entities);
|
||||
@@ -155,21 +155,19 @@ class JointPermissionBuilder
|
||||
*
|
||||
* @param Entity[] $entities
|
||||
*/
|
||||
protected function deleteManyJointPermissionsForEntities(array $entities)
|
||||
protected function deleteManyJointPermissionsForEntities(array $entities): void
|
||||
{
|
||||
$simpleEntities = $this->entitiesToSimpleEntities($entities);
|
||||
$idsByType = $this->entitiesToTypeIdMap($simpleEntities);
|
||||
|
||||
DB::transaction(function () use ($idsByType) {
|
||||
foreach ($idsByType as $type => $ids) {
|
||||
foreach (array_chunk($ids, 1000) as $idChunk) {
|
||||
DB::table('joint_permissions')
|
||||
->where('entity_type', '=', $type)
|
||||
->whereIn('entity_id', $idChunk)
|
||||
->delete();
|
||||
}
|
||||
foreach ($idsByType as $type => $ids) {
|
||||
foreach (array_chunk($ids, 1000) as $idChunk) {
|
||||
DB::table('joint_permissions')
|
||||
->where('entity_type', '=', $type)
|
||||
->whereIn('entity_id', $idChunk)
|
||||
->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +193,7 @@ class JointPermissionBuilder
|
||||
* @param Entity[] $originalEntities
|
||||
* @param Role[] $roles
|
||||
*/
|
||||
protected function createManyJointPermissions(array $originalEntities, array $roles)
|
||||
protected function createManyJointPermissions(array $originalEntities, array $roles): void
|
||||
{
|
||||
$entities = $this->entitiesToSimpleEntities($originalEntities);
|
||||
$jointPermissions = [];
|
||||
@@ -225,11 +223,9 @@ class JointPermissionBuilder
|
||||
}
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($jointPermissions) {
|
||||
foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) {
|
||||
DB::table('joint_permissions')->insert($jointPermissionChunk);
|
||||
}
|
||||
});
|
||||
foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) {
|
||||
DB::table('joint_permissions')->insert($jointPermissionChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ use BookStack\Entities\Tools\PermissionsUpdater;
|
||||
use BookStack\Http\Controller;
|
||||
use BookStack\Permissions\Models\EntityPermission;
|
||||
use BookStack\Users\Models\Role;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PermissionsController extends Controller
|
||||
@@ -40,7 +41,9 @@ class PermissionsController extends Controller
|
||||
$page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $page);
|
||||
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($page, $request);
|
||||
(new DatabaseTransaction(function () use ($page, $request) {
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($page, $request);
|
||||
}))->run();
|
||||
|
||||
$this->showSuccessNotification(trans('entities.pages_permissions_success'));
|
||||
|
||||
@@ -70,7 +73,9 @@ class PermissionsController extends Controller
|
||||
$chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $chapter);
|
||||
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($chapter, $request);
|
||||
(new DatabaseTransaction(function () use ($chapter, $request) {
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($chapter, $request);
|
||||
}))->run();
|
||||
|
||||
$this->showSuccessNotification(trans('entities.chapters_permissions_success'));
|
||||
|
||||
@@ -100,7 +105,9 @@ class PermissionsController extends Controller
|
||||
$book = $this->queries->books->findVisibleBySlugOrFail($slug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $book);
|
||||
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($book, $request);
|
||||
(new DatabaseTransaction(function () use ($book, $request) {
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($book, $request);
|
||||
}))->run();
|
||||
|
||||
$this->showSuccessNotification(trans('entities.books_permissions_updated'));
|
||||
|
||||
@@ -130,7 +137,9 @@ class PermissionsController extends Controller
|
||||
$shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $shelf);
|
||||
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($shelf, $request);
|
||||
(new DatabaseTransaction(function () use ($shelf, $request) {
|
||||
$this->permissionsUpdater->updateFromPermissionsForm($shelf, $request);
|
||||
}))->run();
|
||||
|
||||
$this->showSuccessNotification(trans('entities.shelves_permissions_updated'));
|
||||
|
||||
@@ -145,7 +154,10 @@ class PermissionsController extends Controller
|
||||
$shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $shelf);
|
||||
|
||||
$updateCount = $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf);
|
||||
$updateCount = (new DatabaseTransaction(function () use ($shelf) {
|
||||
return $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf);
|
||||
}))->run();
|
||||
|
||||
$this->showSuccessNotification(trans('entities.shelves_copy_permission_success', ['count' => $updateCount]));
|
||||
|
||||
return redirect($shelf->getUrl());
|
||||
|
||||
@@ -7,6 +7,7 @@ use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Permissions\Models\RolePermission;
|
||||
use BookStack\Users\Models\Role;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
@@ -48,38 +49,42 @@ class PermissionsRepo
|
||||
*/
|
||||
public function saveNewRole(array $roleData): Role
|
||||
{
|
||||
$role = new Role($roleData);
|
||||
$role->mfa_enforced = boolval($roleData['mfa_enforced'] ?? false);
|
||||
$role->save();
|
||||
return (new DatabaseTransaction(function () use ($roleData) {
|
||||
$role = new Role($roleData);
|
||||
$role->mfa_enforced = boolval($roleData['mfa_enforced'] ?? false);
|
||||
$role->save();
|
||||
|
||||
$permissions = $roleData['permissions'] ?? [];
|
||||
$this->assignRolePermissions($role, $permissions);
|
||||
$this->permissionBuilder->rebuildForRole($role);
|
||||
$permissions = $roleData['permissions'] ?? [];
|
||||
$this->assignRolePermissions($role, $permissions);
|
||||
$this->permissionBuilder->rebuildForRole($role);
|
||||
|
||||
Activity::add(ActivityType::ROLE_CREATE, $role);
|
||||
Activity::add(ActivityType::ROLE_CREATE, $role);
|
||||
|
||||
return $role;
|
||||
return $role;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing role.
|
||||
* Ensures Admin system role always have core permissions.
|
||||
* Ensures the Admin system role always has core permissions.
|
||||
*/
|
||||
public function updateRole($roleId, array $roleData): Role
|
||||
{
|
||||
$role = $this->getRoleById($roleId);
|
||||
|
||||
if (isset($roleData['permissions'])) {
|
||||
$this->assignRolePermissions($role, $roleData['permissions']);
|
||||
}
|
||||
return (new DatabaseTransaction(function () use ($role, $roleData) {
|
||||
if (isset($roleData['permissions'])) {
|
||||
$this->assignRolePermissions($role, $roleData['permissions']);
|
||||
}
|
||||
|
||||
$role->fill($roleData);
|
||||
$role->save();
|
||||
$this->permissionBuilder->rebuildForRole($role);
|
||||
$role->fill($roleData);
|
||||
$role->save();
|
||||
$this->permissionBuilder->rebuildForRole($role);
|
||||
|
||||
Activity::add(ActivityType::ROLE_UPDATE, $role);
|
||||
Activity::add(ActivityType::ROLE_UPDATE, $role);
|
||||
|
||||
return $role;
|
||||
return $role;
|
||||
}))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +119,7 @@ class PermissionsRepo
|
||||
/**
|
||||
* Delete a role from the system.
|
||||
* Check it's not an admin role or set as default before deleting.
|
||||
* If a migration Role ID is specified the users assign to the current role
|
||||
* If a migration Role ID is specified, the users assigned to the current role
|
||||
* will be added to the role of the specified id.
|
||||
*
|
||||
* @throws PermissionsException
|
||||
@@ -131,17 +136,19 @@ class PermissionsRepo
|
||||
throw new PermissionsException(trans('errors.role_registration_default_cannot_delete'));
|
||||
}
|
||||
|
||||
if ($migrateRoleId !== 0) {
|
||||
$newRole = Role::query()->find($migrateRoleId);
|
||||
if ($newRole) {
|
||||
$users = $role->users()->pluck('id')->toArray();
|
||||
$newRole->users()->sync($users);
|
||||
(new DatabaseTransaction(function () use ($migrateRoleId, $role) {
|
||||
if ($migrateRoleId !== 0) {
|
||||
$newRole = Role::query()->find($migrateRoleId);
|
||||
if ($newRole) {
|
||||
$users = $role->users()->pluck('id')->toArray();
|
||||
$newRole->users()->sync($users);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$role->entityPermissions()->delete();
|
||||
$role->jointPermissions()->delete();
|
||||
Activity::add(ActivityType::ROLE_DELETE, $role);
|
||||
$role->delete();
|
||||
$role->entityPermissions()->delete();
|
||||
$role->jointPermissions()->delete();
|
||||
Activity::add(ActivityType::ROLE_DELETE, $role);
|
||||
$role->delete();
|
||||
}))->run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use Illuminate\Http\Request;
|
||||
|
||||
class SearchApiController extends ApiController
|
||||
{
|
||||
protected $rules = [
|
||||
protected array $rules = [
|
||||
'all' => [
|
||||
'query' => ['required'],
|
||||
'page' => ['integer', 'min:1'],
|
||||
|
||||
@@ -7,6 +7,7 @@ use BookStack\Entities\Queries\BookQueries;
|
||||
use BookStack\Entities\Tools\BookContents;
|
||||
use BookStack\Facades\Activity;
|
||||
use BookStack\Http\Controller;
|
||||
use BookStack\Util\DatabaseTransaction;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BookSortController extends Controller
|
||||
@@ -55,16 +56,18 @@ class BookSortController extends Controller
|
||||
|
||||
// Sort via map
|
||||
if ($request->filled('sort-tree')) {
|
||||
$sortMap = BookSortMap::fromJson($request->get('sort-tree'));
|
||||
$booksInvolved = $sorter->sortUsingMap($sortMap);
|
||||
(new DatabaseTransaction(function () use ($book, $request, $sorter, &$loggedActivityForBook) {
|
||||
$sortMap = BookSortMap::fromJson($request->get('sort-tree'));
|
||||
$booksInvolved = $sorter->sortUsingMap($sortMap);
|
||||
|
||||
// Rebuild permissions and add activity for involved books.
|
||||
foreach ($booksInvolved as $bookInvolved) {
|
||||
Activity::add(ActivityType::BOOK_SORT, $bookInvolved);
|
||||
if ($bookInvolved->id === $book->id) {
|
||||
$loggedActivityForBook = true;
|
||||
// Add activity for involved books.
|
||||
foreach ($booksInvolved as $bookInvolved) {
|
||||
Activity::add(ActivityType::BOOK_SORT, $bookInvolved);
|
||||
if ($bookInvolved->id === $book->id) {
|
||||
$loggedActivityForBook = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}))->run();
|
||||
}
|
||||
|
||||
if ($request->filled('auto-sort')) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace BookStack\Sorting;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Entities\Models\Book;
|
||||
use BookStack\Entities\Models\BookChild;
|
||||
use BookStack\Entities\Models\Chapter;
|
||||
|
||||
@@ -50,6 +50,7 @@ class LocaleManager
|
||||
'ku' => 'ku_TR',
|
||||
'lt' => 'lt_LT',
|
||||
'lv' => 'lv_LV',
|
||||
'ne' => 'ne_NP',
|
||||
'nb' => 'nb_NO',
|
||||
'nl' => 'nl_NL',
|
||||
'nn' => 'nn_NO',
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Uploads;
|
||||
|
||||
use BookStack\Exceptions\DrawioPngReaderException;
|
||||
|
||||
/**
|
||||
* Reads the PNG file format: https://www.w3.org/TR/2003/REC-PNG-20031110/
|
||||
* So that it can extract embedded drawing data for alternative use.
|
||||
*/
|
||||
class DrawioPngReader
|
||||
{
|
||||
/**
|
||||
* @param resource $fileStream
|
||||
*/
|
||||
public function __construct(
|
||||
protected $fileStream
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DrawioPngReaderException
|
||||
*/
|
||||
public function extractDrawing(): string
|
||||
{
|
||||
$signature = fread($this->fileStream, 8);
|
||||
$pngSignature = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
|
||||
if ($signature !== $pngSignature) {
|
||||
throw new DrawioPngReaderException('File does not appear to be a valid PNG file');
|
||||
}
|
||||
|
||||
$offset = 8;
|
||||
$searching = true;
|
||||
|
||||
while ($searching) {
|
||||
fseek($this->fileStream, $offset);
|
||||
|
||||
$lengthBytes = $this->readData(4);
|
||||
$chunkTypeBytes = $this->readData(4);
|
||||
$length = unpack('Nvalue', $lengthBytes)['value'];
|
||||
|
||||
if ($chunkTypeBytes === 'tEXt') {
|
||||
fseek($this->fileStream, $offset + 8);
|
||||
$data = $this->readData($length);
|
||||
$crc = $this->readData(4);
|
||||
$drawingData = $this->readTextForDrawing($data);
|
||||
if ($drawingData !== null) {
|
||||
$crcResult = $this->calculateCrc($chunkTypeBytes . $data);
|
||||
if ($crc !== $crcResult) {
|
||||
throw new DrawioPngReaderException('Drawing data withing PNG file appears to be corrupted');
|
||||
}
|
||||
return $drawingData;
|
||||
}
|
||||
} else if ($chunkTypeBytes === 'IEND') {
|
||||
$searching = false;
|
||||
}
|
||||
|
||||
$offset += 12 + $length; // 12 = length + type + crc bytes
|
||||
}
|
||||
|
||||
throw new DrawioPngReaderException('Unable to find drawing data within PNG file');
|
||||
}
|
||||
|
||||
protected function readTextForDrawing(string $data): ?string
|
||||
{
|
||||
// Check the keyword is mxfile to ensure we're getting the right data
|
||||
if (!str_starts_with($data, "mxfile\u{0}")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract & cleanup the drawing text
|
||||
$drawingText = substr($data, 7);
|
||||
return urldecode($drawingText);
|
||||
}
|
||||
|
||||
protected function readData(int $length): string
|
||||
{
|
||||
$bytes = fread($this->fileStream, $length);
|
||||
if ($bytes === false || strlen($bytes) < $length) {
|
||||
throw new DrawioPngReaderException('Unable to find drawing data within PNG file');
|
||||
}
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
protected function getCrcTable(): array
|
||||
{
|
||||
$table = [];
|
||||
|
||||
for ($n = 0; $n < 256; $n++) {
|
||||
$c = $n;
|
||||
for ($k = 0; $k < 8; $k++) {
|
||||
if ($c & 1) {
|
||||
$c = 0xedb88320 ^ ($c >> 1);
|
||||
} else {
|
||||
$c = $c >> 1;
|
||||
}
|
||||
}
|
||||
$table[$n] = $c;
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a CRC for the given bytes following:
|
||||
* https://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix
|
||||
*/
|
||||
protected function calculateCrc(string $bytes): string
|
||||
{
|
||||
$table = $this->getCrcTable();
|
||||
|
||||
$length = strlen($bytes);
|
||||
$c = 0xffffffff;
|
||||
|
||||
for ($n = 0; $n < $length; $n++) {
|
||||
$tableIndex = ($c ^ ord($bytes[$n])) & 0xff;
|
||||
$c = $table[$tableIndex] ^ ($c >> 8);
|
||||
}
|
||||
|
||||
return pack('N', $c ^ 0xffffffff);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class RoleApiController extends ApiController
|
||||
'display_name', 'description', 'mfa_enforced', 'external_auth_id', 'created_at', 'updated_at',
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
protected array $rules = [
|
||||
'create' => [
|
||||
'display_name' => ['required', 'string', 'min:3', 'max:180'],
|
||||
'description' => ['string', 'max:180'],
|
||||
|
||||
@@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
/**
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
* @property ?User $createdBy
|
||||
* @property ?User $updatedBy
|
||||
*/
|
||||
trait HasCreatorAndUpdater
|
||||
{
|
||||
|
||||
42
app/Util/DatabaseTransaction.php
Normal file
42
app/Util/DatabaseTransaction.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Util;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Run the given code within a database transactions.
|
||||
* Wraps Laravel's own transaction method, but sets a specific runtime isolation method.
|
||||
* This sets a session level since this won't cause issues if already within a transaction,
|
||||
* and this should apply to the next transactions anyway.
|
||||
*
|
||||
* "READ COMMITTED" ensures that changes from other transactions can be read within
|
||||
* a transaction, even if started afterward (and for example, it was blocked by the initial
|
||||
* transaction). This is quite important for things like permission generation, where we would
|
||||
* want to consider the changes made by other committed transactions by the time we come to
|
||||
* regenerate permission access.
|
||||
*
|
||||
* @throws Throwable
|
||||
* @template TReturn of mixed
|
||||
*/
|
||||
class DatabaseTransaction
|
||||
{
|
||||
/**
|
||||
* @param (Closure(static): TReturn) $callback
|
||||
*/
|
||||
public function __construct(
|
||||
protected Closure $callback
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TReturn
|
||||
*/
|
||||
public function run(): mixed
|
||||
{
|
||||
DB::statement('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
|
||||
return DB::transaction($this->callback);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ namespace BookStack\Util;
|
||||
|
||||
use DOMAttr;
|
||||
use DOMElement;
|
||||
use DOMNamedNodeMap;
|
||||
use DOMNode;
|
||||
|
||||
/**
|
||||
@@ -25,6 +24,7 @@ class HtmlDescriptionFilter
|
||||
'ul' => [],
|
||||
'li' => [],
|
||||
'strong' => [],
|
||||
'span' => [],
|
||||
'em' => [],
|
||||
'br' => [],
|
||||
];
|
||||
@@ -59,7 +59,6 @@ class HtmlDescriptionFilter
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var DOMNamedNodeMap $attrs */
|
||||
$attrs = $element->attributes;
|
||||
for ($i = $attrs->length - 1; $i >= 0; $i--) {
|
||||
/** @var DOMAttr $attr */
|
||||
@@ -70,7 +69,8 @@ class HtmlDescriptionFilter
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($element->childNodes as $child) {
|
||||
$childNodes = [...$element->childNodes];
|
||||
foreach ($childNodes as $child) {
|
||||
if ($child instanceof DOMElement) {
|
||||
static::filterElement($child);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
"socialiteproviders/microsoft-azure": "^5.1",
|
||||
"socialiteproviders/okta": "^4.2",
|
||||
"socialiteproviders/twitch": "^5.3",
|
||||
"ssddanbrown/htmldiff": "^1.0.2",
|
||||
"ssddanbrown/symfony-mailer": "7.2.x-dev"
|
||||
"ssddanbrown/htmldiff": "^2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.21",
|
||||
|
||||
747
composer.lock
generated
747
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ class ImportFactory extends Factory
|
||||
'path' => 'uploads/files/imports/' . Str::random(10) . '.zip',
|
||||
'name' => $this->faker->words(3, true),
|
||||
'type' => 'book',
|
||||
'size' => rand(1, 1001),
|
||||
'metadata' => '{"name": "My book"}',
|
||||
'created_at' => User::factory(),
|
||||
];
|
||||
|
||||
4
dev/api/requests/imports-run.json
Normal file
4
dev/api/requests/imports-run.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"parent_type": "book",
|
||||
"parent_id": 28
|
||||
}
|
||||
10
dev/api/responses/imports-create.json
Normal file
10
dev/api/responses/imports-create.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"type": "chapter",
|
||||
"name": "Pension Providers",
|
||||
"created_by": 1,
|
||||
"size": 2757,
|
||||
"path": "uploads\/files\/imports\/ghnxmS3u9QxLWu82.zip",
|
||||
"updated_at": "2025-07-18T14:50:27.000000Z",
|
||||
"created_at": "2025-07-18T14:50:27.000000Z",
|
||||
"id": 31
|
||||
}
|
||||
23
dev/api/responses/imports-list.json
Normal file
23
dev/api/responses/imports-list.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 25,
|
||||
"name": "IT Department",
|
||||
"size": 618462,
|
||||
"type": "book",
|
||||
"created_by": 1,
|
||||
"created_at": "2024-12-20T18:40:38.000000Z",
|
||||
"updated_at": "2024-12-20T18:40:38.000000Z"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"name": "Clients",
|
||||
"size": 15364,
|
||||
"type": "chapter",
|
||||
"created_by": 1,
|
||||
"created_at": "2025-03-20T12:41:44.000000Z",
|
||||
"updated_at": "2025-03-20T12:41:44.000000Z"
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
51
dev/api/responses/imports-read.json
Normal file
51
dev/api/responses/imports-read.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"id": 25,
|
||||
"name": "IT Department",
|
||||
"path": "uploads\/files\/imports\/7YOpZ6sGIEbYdRFL.zip",
|
||||
"size": 618462,
|
||||
"type": "book",
|
||||
"created_by": 1,
|
||||
"created_at": "2024-12-20T18:40:38.000000Z",
|
||||
"updated_at": "2024-12-20T18:40:38.000000Z",
|
||||
"details": {
|
||||
"id": 4,
|
||||
"name": "IT Department",
|
||||
"chapters": [
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Server Systems",
|
||||
"priority": 1,
|
||||
"pages": [
|
||||
{
|
||||
"id": 22,
|
||||
"name": "prod-aws-stonehawk",
|
||||
"priority": 0,
|
||||
"attachments": [],
|
||||
"images": [],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"pages": [
|
||||
{
|
||||
"id": 23,
|
||||
"name": "Member Onboarding Guide",
|
||||
"priority": 0,
|
||||
"attachments": [],
|
||||
"images": [],
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "IT Holiday Party Event",
|
||||
"priority": 2,
|
||||
"attachments": [],
|
||||
"images": [],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
}
|
||||
14
dev/api/responses/imports-run.json
Normal file
14
dev/api/responses/imports-run.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": 1067,
|
||||
"book_id": 28,
|
||||
"slug": "pension-providers",
|
||||
"name": "Pension Providers",
|
||||
"description": "Details on the various pension providers that are available",
|
||||
"priority": 7,
|
||||
"created_at": "2025-07-18T14:53:35.000000Z",
|
||||
"updated_at": "2025-07-18T14:53:36.000000Z",
|
||||
"created_by": 1,
|
||||
"updated_by": 1,
|
||||
"owned_by": 1,
|
||||
"default_template_id": null
|
||||
}
|
||||
@@ -13,7 +13,7 @@ const entryPoints = {
|
||||
app: path.join(__dirname, '../../resources/js/app.ts'),
|
||||
code: path.join(__dirname, '../../resources/js/code/index.mjs'),
|
||||
'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'),
|
||||
markdown: path.join(__dirname, '../../resources/js/markdown/index.mjs'),
|
||||
markdown: path.join(__dirname, '../../resources/js/markdown/index.mts'),
|
||||
wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'),
|
||||
};
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
22e02ee72d21ff719c1073abbec8302f8e2096ba6d072e133051064ed24b45b1
|
||||
8718aa88d39b7ede22c7e7734da8533165776f51210905afc360a996ca3a7356
|
||||
@@ -345,7 +345,7 @@ Link: tj/co
|
||||
codemirror
|
||||
License: MIT
|
||||
License File: node_modules/codemirror/LICENSE
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <*******@*****.***> and others
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others
|
||||
Source: https://github.com/codemirror/basic-setup.git
|
||||
Link: https://github.com/codemirror/basic-setup.git
|
||||
-----------
|
||||
@@ -711,13 +711,13 @@ eslint-scope
|
||||
License: BSD-2-Clause
|
||||
License File: node_modules/eslint-scope/LICENSE
|
||||
Copyright: Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors.
|
||||
Source: eslint/js
|
||||
Source: https://github.com/eslint/js.git
|
||||
Link: https://github.com/eslint/js/blob/main/packages/eslint-scope/README.md
|
||||
-----------
|
||||
eslint-visitor-keys
|
||||
License: Apache-2.0
|
||||
License File: node_modules/eslint-visitor-keys/LICENSE
|
||||
Source: eslint/js
|
||||
Source: https://github.com/eslint/js.git
|
||||
Link: https://github.com/eslint/js/blob/main/packages/eslint-visitor-keys/README.md
|
||||
-----------
|
||||
eslint
|
||||
@@ -731,7 +731,7 @@ License: BSD-2-Clause
|
||||
License File: node_modules/espree/LICENSE
|
||||
Copyright: Copyright (c) Open JS Foundation
|
||||
All rights reserved.
|
||||
Source: eslint/js
|
||||
Source: https://github.com/eslint/js.git
|
||||
Link: https://github.com/eslint/js/blob/main/packages/espree/README.md
|
||||
-----------
|
||||
esprima
|
||||
@@ -1252,6 +1252,13 @@ Copyright: Copyright (c) 2019 Inspect JS
|
||||
Source: git+https://github.com/inspect-js/is-map.git
|
||||
Link: https://github.com/inspect-js/is-map#readme
|
||||
-----------
|
||||
is-negative-zero
|
||||
License: MIT
|
||||
License File: node_modules/is-negative-zero/LICENSE
|
||||
Copyright: Copyright (c) 2014 Jordan Harband
|
||||
Source: git://github.com/inspect-js/is-negative-zero.git
|
||||
Link: https://github.com/inspect-js/is-negative-zero
|
||||
-----------
|
||||
is-number-object
|
||||
License: MIT
|
||||
License File: node_modules/is-number-object/LICENSE
|
||||
@@ -2493,6 +2500,13 @@ Copyright: Copyright (c) 2016-2022 Isaac Z. Schlueter <*@***.**>, James Talmage
|
||||
Source: tapjs/stack-utils
|
||||
Link: tapjs/stack-utils
|
||||
-----------
|
||||
stop-iteration-iterator
|
||||
License: MIT
|
||||
License File: node_modules/stop-iteration-iterator/LICENSE
|
||||
Copyright: Copyright (c) 2023 Jordan Harband
|
||||
Source: git+https://github.com/ljharb/stop-iteration-iterator.git
|
||||
Link: https://github.com/ljharb/stop-iteration-iterator#readme
|
||||
-----------
|
||||
string-length
|
||||
License: MIT
|
||||
License File: node_modules/string-length/license
|
||||
@@ -2992,6 +3006,13 @@ Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
Source: https://github.com/babel/babel.git
|
||||
Link: https://github.com/babel/babel.git
|
||||
-----------
|
||||
@babel/helper-globals
|
||||
License: MIT
|
||||
License File: node_modules/@babel/helper-globals/LICENSE
|
||||
Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
Source: https://github.com/babel/babel.git
|
||||
Link: https://github.com/babel/babel.git
|
||||
-----------
|
||||
@babel/helper-module-imports
|
||||
License: MIT
|
||||
License File: node_modules/@babel/helper-module-imports/LICENSE
|
||||
@@ -3038,7 +3059,7 @@ Link: https://github.com/babel/babel.git
|
||||
License: MIT
|
||||
License File: node_modules/@babel/helpers/LICENSE
|
||||
Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
Copyright (c) 2014-present, Facebook, Inc. (ONLY ./src/helpers/regeneratorRuntime.js)
|
||||
Copyright (c) 2014-present, Facebook, Inc. (ONLY ./src/helpers/regenerator* files)
|
||||
Source: https://github.com/babel/babel.git
|
||||
Link: https://babel.dev/docs/en/next/babel-helpers
|
||||
-----------
|
||||
@@ -3233,7 +3254,7 @@ Link: https://github.com/codemirror/lang-javascript.git
|
||||
@codemirror/lang-json
|
||||
License: MIT
|
||||
License File: node_modules/@codemirror/lang-json/LICENSE
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <*******@*****.***> and others
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others
|
||||
Source: https://github.com/codemirror/lang-json.git
|
||||
Link: https://github.com/codemirror/lang-json.git
|
||||
-----------
|
||||
@@ -3247,7 +3268,7 @@ Link: https://github.com/codemirror/lang-markdown.git
|
||||
@codemirror/lang-php
|
||||
License: MIT
|
||||
License File: node_modules/@codemirror/lang-php/LICENSE
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <*******@*****.***> and others
|
||||
Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others
|
||||
Source: https://github.com/codemirror/lang-php.git
|
||||
Link: https://github.com/codemirror/lang-php.git
|
||||
-----------
|
||||
@@ -3337,7 +3358,7 @@ Link: https://github.com/eslint-community/regexpp#readme
|
||||
License: Apache-2.0
|
||||
License File: node_modules/@eslint/config-array/LICENSE
|
||||
Source: git+https://github.com/eslint/rewrite.git
|
||||
Link: https://github.com/eslint/rewrite#readme
|
||||
Link: https://github.com/eslint/rewrite/tree/main/packages/config-array#readme
|
||||
-----------
|
||||
@eslint/config-helpers
|
||||
License: Apache-2.0
|
||||
@@ -3349,7 +3370,7 @@ Link: https://github.com/eslint/rewrite/tree/main/packages/config-helpers#readme
|
||||
License: Apache-2.0
|
||||
License File: node_modules/@eslint/core/LICENSE
|
||||
Source: git+https://github.com/eslint/rewrite.git
|
||||
Link: https://github.com/eslint/rewrite#readme
|
||||
Link: https://github.com/eslint/rewrite/tree/main/packages/core#readme
|
||||
-----------
|
||||
@eslint/eslintrc
|
||||
License: MIT
|
||||
@@ -3373,7 +3394,7 @@ Link: https://github.com/eslint/rewrite#readme
|
||||
License: Apache-2.0
|
||||
License File: node_modules/@eslint/plugin-kit/LICENSE
|
||||
Source: git+https://github.com/eslint/rewrite.git
|
||||
Link: https://github.com/eslint/rewrite#readme
|
||||
Link: https://github.com/eslint/rewrite/tree/main/packages/plugin-kit#readme
|
||||
-----------
|
||||
@humanfs/core
|
||||
License: Apache-2.0
|
||||
@@ -3514,9 +3535,9 @@ Link: https://github.com/jestjs/jest.git
|
||||
@jridgewell/gen-mapping
|
||||
License: MIT
|
||||
License File: node_modules/@jridgewell/gen-mapping/LICENSE
|
||||
Copyright: Copyright 2022 Justin Ridgewell <**********@******.***>
|
||||
Source: https://github.com/jridgewell/gen-mapping
|
||||
Link: https://github.com/jridgewell/gen-mapping
|
||||
Copyright: Copyright 2024 Justin Ridgewell <******@*********.****>
|
||||
Source: git+https://github.com/jridgewell/sourcemaps.git
|
||||
Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/gen-mapping
|
||||
-----------
|
||||
@jridgewell/resolve-uri
|
||||
License: MIT
|
||||
@@ -3525,26 +3546,19 @@ Copyright: Copyright 2019 Justin Ridgewell <**********@******.***>
|
||||
Source: https://github.com/jridgewell/resolve-uri
|
||||
Link: https://github.com/jridgewell/resolve-uri
|
||||
-----------
|
||||
@jridgewell/set-array
|
||||
License: MIT
|
||||
License File: node_modules/@jridgewell/set-array/LICENSE
|
||||
Copyright: Copyright 2022 Justin Ridgewell <**********@******.***>
|
||||
Source: https://github.com/jridgewell/set-array
|
||||
Link: https://github.com/jridgewell/set-array
|
||||
-----------
|
||||
@jridgewell/sourcemap-codec
|
||||
License: MIT
|
||||
License File: node_modules/@jridgewell/sourcemap-codec/LICENSE
|
||||
Copyright: Copyright (c) 2015 Rich Harris
|
||||
Source: git+https://github.com/jridgewell/sourcemap-codec.git
|
||||
Link: git+https://github.com/jridgewell/sourcemap-codec.git
|
||||
Copyright: Copyright 2024 Justin Ridgewell <******@*********.****>
|
||||
Source: git+https://github.com/jridgewell/sourcemaps.git
|
||||
Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/sourcemap-codec
|
||||
-----------
|
||||
@jridgewell/trace-mapping
|
||||
License: MIT
|
||||
License File: node_modules/@jridgewell/trace-mapping/LICENSE
|
||||
Copyright: Copyright 2022 Justin Ridgewell <******@*********.****>
|
||||
Source: git+https://github.com/jridgewell/trace-mapping.git
|
||||
Link: git+https://github.com/jridgewell/trace-mapping.git
|
||||
Copyright: Copyright 2024 Justin Ridgewell <******@*********.****>
|
||||
Source: git+https://github.com/jridgewell/sourcemaps.git
|
||||
Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/trace-mapping
|
||||
-----------
|
||||
@lezer/common
|
||||
License: MIT
|
||||
@@ -3813,6 +3827,27 @@ License: MIT
|
||||
Source: https://www.github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||
Link: https://www.github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||
-----------
|
||||
@types/linkify-it
|
||||
License: MIT
|
||||
License File: node_modules/@types/linkify-it/LICENSE
|
||||
Copyright: Copyright (c) Microsoft Corporation.
|
||||
Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||
Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/linkify-it
|
||||
-----------
|
||||
@types/markdown-it
|
||||
License: MIT
|
||||
License File: node_modules/@types/markdown-it/LICENSE
|
||||
Copyright: Copyright (c) Microsoft Corporation.
|
||||
Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||
Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/markdown-it
|
||||
-----------
|
||||
@types/mdurl
|
||||
License: MIT
|
||||
License File: node_modules/@types/mdurl/LICENSE
|
||||
Copyright: Copyright (c) Microsoft Corporation.
|
||||
Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||
Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mdurl
|
||||
-----------
|
||||
@types/node
|
||||
License: MIT
|
||||
License File: node_modules/@types/node/LICENSE
|
||||
|
||||
@@ -467,7 +467,7 @@ License: MIT
|
||||
License File: vendor/psy/psysh/LICENSE
|
||||
Copyright: Copyright (c) 2012-2023 Justin Hileman
|
||||
Source: https://github.com/bobthecow/psysh.git
|
||||
Link: http://psysh.org
|
||||
Link: https://psysh.org
|
||||
-----------
|
||||
ralouphie/getallheaders
|
||||
License: MIT
|
||||
@@ -543,13 +543,6 @@ Copyright: Copyright (c) 2024 Nathan Herald, Rohland de Charmoy, Dan Brown
|
||||
Source: https://codeberg.org/danb/HtmlDiff
|
||||
Link: https://codeberg.org/danb/HtmlDiff
|
||||
-----------
|
||||
ssddanbrown/symfony-mailer
|
||||
License: MIT
|
||||
License File: vendor/ssddanbrown/symfony-mailer/LICENSE
|
||||
Copyright: Copyright (c) 2019-present Fabien Potencier
|
||||
Source: https://github.com/ssddanbrown/symfony-mailer.git
|
||||
Link: https://symfony.com
|
||||
-----------
|
||||
symfony/clock
|
||||
License: MIT
|
||||
License File: vendor/symfony/clock/LICENSE
|
||||
@@ -620,6 +613,13 @@ Copyright: Copyright (c) 2004-present Fabien Potencier
|
||||
Source: https://github.com/symfony/http-kernel.git
|
||||
Link: https://symfony.com
|
||||
-----------
|
||||
symfony/mailer
|
||||
License: MIT
|
||||
License File: vendor/symfony/mailer/LICENSE
|
||||
Copyright: Copyright (c) 2019-present Fabien Potencier
|
||||
Source: https://github.com/symfony/mailer.git
|
||||
Link: https://symfony.com
|
||||
-----------
|
||||
symfony/mime
|
||||
License: MIT
|
||||
License File: vendor/symfony/mime/LICENSE
|
||||
|
||||
@@ -30,8 +30,8 @@ return [
|
||||
'create' => 'إنشاء',
|
||||
'update' => 'تحديث',
|
||||
'edit' => 'تعديل',
|
||||
'archive' => 'Archive',
|
||||
'unarchive' => 'Un-Archive',
|
||||
'archive' => 'أرشف',
|
||||
'unarchive' => 'إلغاء الأرشفة',
|
||||
'sort' => 'سرد',
|
||||
'move' => 'نقل',
|
||||
'copy' => 'نسخ',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'نص مرتفع',
|
||||
'subscript' => 'نص منخفض',
|
||||
'text_color' => 'لون النص',
|
||||
'highlight_color' => 'لون التمييز',
|
||||
'custom_color' => 'لون مخصص',
|
||||
'remove_color' => 'إزالة اللون',
|
||||
'background_color' => 'لون الخلفية',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'سيؤدي هذا إلى حذف مِلَفّ الاستيراد المضغوط ZIP، ولا يمكن التراجع عنه.',
|
||||
'import_errors' => 'أخطاء الاستيراد',
|
||||
'import_errors_desc' => 'حدثت الأخطاء التالية خلال محاولة الاستيراد:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'الأذونات',
|
||||
@@ -248,7 +252,7 @@ return [
|
||||
'pages_edit_switch_to_markdown_stable' => '(محتوى مستقر)',
|
||||
'pages_edit_switch_to_wysiwyg' => 'التبديل إلى محرر ما تراه هو ما تحصل عليه -WYSIWYG-',
|
||||
'pages_edit_switch_to_new_wysiwyg' => 'التبديل إلى محرر ما تراه هو ما تحصل عليه الجديد -new WYSIWYG-',
|
||||
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)',
|
||||
'pages_edit_switch_to_new_wysiwyg_desc' => '(في الاختبار التجريبي)',
|
||||
'pages_edit_set_changelog' => 'تثبيت سجل التعديل',
|
||||
'pages_edit_enter_changelog_desc' => 'ضع وصف مختصر للتعديلات التي تمت',
|
||||
'pages_edit_enter_changelog' => 'أدخل سجل التعديل',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'إدخال رسمة',
|
||||
'pages_md_show_preview' => 'عرض المعاينة',
|
||||
'pages_md_sync_scroll' => 'مزامنة معاينة التمرير',
|
||||
'pages_md_plain_editor' => 'محرر النصوص العادي',
|
||||
'pages_drawing_unsaved' => 'تم العثور على رسم غير محفوظ',
|
||||
'pages_drawing_unsaved_confirm' => 'تم العثور على بيانات رسم غير محفوظة من محاولة حفظ رسم سابقة فاشلة. هل ترغب في استعادة هذا الرسم غير المحفوظ ومواصلة تحريره؟',
|
||||
'pages_not_in_chapter' => 'صفحة ليست في فصل',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Горен индекс',
|
||||
'subscript' => 'Долен индекс',
|
||||
'text_color' => 'Цвят на текста',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Цвят по избор',
|
||||
'remove_color' => 'Премахване на цвят',
|
||||
'background_color' => 'Фонов цвят',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Права',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Вмъкни рисунка',
|
||||
'pages_md_show_preview' => 'Show preview',
|
||||
'pages_md_sync_scroll' => 'Sync preview scroll',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Unsaved Drawing Found',
|
||||
'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?',
|
||||
'pages_not_in_chapter' => 'Страницата не принадлежи в никоя глава',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superscript',
|
||||
'subscript' => 'Subscript',
|
||||
'text_color' => 'Text color',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Custom color',
|
||||
'remove_color' => 'Remove color',
|
||||
'background_color' => 'Background color',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Permissions',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insert Drawing',
|
||||
'pages_md_show_preview' => 'Show preview',
|
||||
'pages_md_sync_scroll' => 'Sync preview scroll',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Unsaved Drawing Found',
|
||||
'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?',
|
||||
'pages_not_in_chapter' => 'Page is not in a chapter',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superscript',
|
||||
'subscript' => 'Subscript',
|
||||
'text_color' => 'Text color',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Custom color',
|
||||
'remove_color' => 'Remove color',
|
||||
'background_color' => 'Background color',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Dozvole',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insert Drawing',
|
||||
'pages_md_show_preview' => 'Show preview',
|
||||
'pages_md_sync_scroll' => 'Sync preview scroll',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Unsaved Drawing Found',
|
||||
'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?',
|
||||
'pages_not_in_chapter' => 'Page is not in a chapter',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -13,7 +13,7 @@ return [
|
||||
'cancel' => 'Cancel·la',
|
||||
'save' => 'Desa',
|
||||
'close' => 'Tanca',
|
||||
'apply' => 'Apply',
|
||||
'apply' => 'Aplicar',
|
||||
'undo' => 'Desfés',
|
||||
'redo' => 'Refés',
|
||||
'left' => 'Esquerra',
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superíndex',
|
||||
'subscript' => 'Subíndex',
|
||||
'text_color' => 'Color del text',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Color personalitzat',
|
||||
'remove_color' => 'Elimina el color',
|
||||
'background_color' => 'Color de fons',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Permisos',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insereix un dibuix',
|
||||
'pages_md_show_preview' => 'Mostra la visualització prèvia',
|
||||
'pages_md_sync_scroll' => 'Sincronitza el desplaçament de la visualització prèvia',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'S’ha trobat un dibuix sense desar',
|
||||
'pages_drawing_unsaved_confirm' => 'S’han trobat dades d’un dibuix d’un intent anterior de desar un dibuix. Voleu restaurar aquest dibuix no desat per a reprendre’n l’edició?',
|
||||
'pages_not_in_chapter' => 'La pàgina no és un capítol',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -47,7 +47,8 @@ return [
|
||||
'strikethrough' => 'Proškrtnuté',
|
||||
'superscript' => 'horní index',
|
||||
'subscript' => 'Dolní index',
|
||||
'text_color' => 'Barva textu:',
|
||||
'text_color' => 'Barva textu',
|
||||
'highlight_color' => 'Barva zvýraznění',
|
||||
'custom_color' => 'Vlastní barva',
|
||||
'remove_color' => 'Odstranit barvu',
|
||||
'background_color' => 'Barva pozadí',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Potvrzením odstraníte nahraný ZIP soubor. Tento krok nelze vrátit zpět.',
|
||||
'import_errors' => 'Chyby importu',
|
||||
'import_errors_desc' => 'Při pokusu o import došlo k následujícím chybám:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Oprávnění',
|
||||
@@ -134,7 +138,7 @@ return [
|
||||
'shelves_copy_permissions_to_books' => 'Kopírovat oprávnění na knihy',
|
||||
'shelves_copy_permissions' => 'Kopírovat oprávnění',
|
||||
'shelves_copy_permissions_explain' => 'Tímto se použije aktuální nastavení oprávnění police na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této police.',
|
||||
'shelves_copy_permission_success' => 'Oprávnění police byla zkopírována na :count knih',
|
||||
'shelves_copy_permission_success' => '{1}Oprávnění police byla zkopírována na :count knihu|[2,4]Oprávnění police byla zkopírována na :count knihy|[5,*]Oprávnění police byla zkopírována na :count knih',
|
||||
|
||||
// Books
|
||||
'book' => 'Kniha',
|
||||
@@ -194,7 +198,7 @@ return [
|
||||
// Chapters
|
||||
'chapter' => 'Kapitola',
|
||||
'chapters' => 'Kapitoly',
|
||||
'x_chapters' => '{0}:count Kapitol|{1}:count Kapitola|[2,4]:count Kapitoly|[5,*]:count Kapitol',
|
||||
'x_chapters' => '{0}:count kapitol|{1}:count kapitola|[2,4]:count kapitoly|[5,*]:count kapitol',
|
||||
'chapters_popular' => 'Populární kapitoly',
|
||||
'chapters_new' => 'Nová kapitola',
|
||||
'chapters_create' => 'Vytvořit novou kapitolu',
|
||||
@@ -219,7 +223,7 @@ return [
|
||||
// Pages
|
||||
'page' => 'Stránka',
|
||||
'pages' => 'Stránky',
|
||||
'x_pages' => '{0}:count Stran|{1}:count Strana|[2,4]:count Strany|[5,*]:count Stran',
|
||||
'x_pages' => '{0}:count stran|{1}:count strana|[2,4]:count strany|[5,*]:count stran',
|
||||
'pages_popular' => 'Populární stránky',
|
||||
'pages_new' => 'Nová stránka',
|
||||
'pages_attachments' => 'Přílohy',
|
||||
@@ -244,8 +248,8 @@ return [
|
||||
'pages_edit_delete_draft_confirm' => 'Jste si jisti, že chcete odstranit změny vašich konceptů? Všechny vaše změny, od posledního úplného uložení, budou ztraceny a editor bude aktualizován s nejnovějším stavem nekonceptu stránky.',
|
||||
'pages_edit_discard_draft' => 'Zahodit koncept',
|
||||
'pages_edit_switch_to_markdown' => 'Přepnout na Markdown Editor',
|
||||
'pages_edit_switch_to_markdown_clean' => '(Vytvořený obsah)',
|
||||
'pages_edit_switch_to_markdown_stable' => '(Stabilní obsah)',
|
||||
'pages_edit_switch_to_markdown_clean' => '(Prostý zápis)',
|
||||
'pages_edit_switch_to_markdown_stable' => '(Formátovaný zápis)',
|
||||
'pages_edit_switch_to_wysiwyg' => 'Přepnout na WYSIWYG Editor',
|
||||
'pages_edit_switch_to_new_wysiwyg' => 'Přepnout na nový WYSIWYG',
|
||||
'pages_edit_switch_to_new_wysiwyg_desc' => '(V beta testování)',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Vložit kresbu',
|
||||
'pages_md_show_preview' => 'Zobrazit náhled',
|
||||
'pages_md_sync_scroll' => 'Synchronizovat náhled',
|
||||
'pages_md_plain_editor' => 'Upravovat prostý text',
|
||||
'pages_drawing_unsaved' => 'Nalezen neuložený výkres',
|
||||
'pages_drawing_unsaved_confirm' => 'Byly nalezeny neuložené kresby z předchozí neúspěšné pokusu o uložení kresby. Chcete je obnovit a pokračovat v úpravě této neuložené kresby?',
|
||||
'pages_not_in_chapter' => 'Stránka není v kapitole',
|
||||
@@ -312,7 +317,7 @@ return [
|
||||
'pages_draft_edited_notification' => 'Tato stránka se od té doby změnila. Je doporučeno aktuální koncept zahodit.',
|
||||
'pages_draft_page_changed_since_creation' => 'Tato stránka byla aktualizována od vytvoření tohoto konceptu. Doporučuje se zrušit tento koncept nebo se postarat o to, abyste si nepřepsali žádné již zadané změny.',
|
||||
'pages_draft_edit_active' => [
|
||||
'start_a' => 'Uživatelé začali upravovat tuto stránku (celkem :count)',
|
||||
'start_a' => '{1}:count uživatel začal upravovat tuto stránku|[2,4]:count uživatelé začali upravovat tuto stránku|[5,*]:count uživatelů začalo upravovat tuto stránku',
|
||||
'start_b' => ':userName začal/a upravovat tuto stránku',
|
||||
'time_a' => 'od doby, kdy byla tato stránky naposledy aktualizována',
|
||||
'time_b' => 'v posledních minutách (:minCount min.)',
|
||||
@@ -342,7 +347,7 @@ return [
|
||||
'tags_assigned_chapters' => 'Přiřazeno ke kapitolám',
|
||||
'tags_assigned_books' => 'Přiřazeno ke knihám',
|
||||
'tags_assigned_shelves' => 'Přiřazeno k policím',
|
||||
'tags_x_unique_values' => ':count jedinečných hodnot',
|
||||
'tags_x_unique_values' => '{1}:count jedinečná hodnota|[2,4]:count jedinečné hodnoty|[5,*]:count jedinečných hodnot',
|
||||
'tags_all_values' => 'Všechny hodnoty',
|
||||
'tags_view_tags' => 'Zobrazit štítky',
|
||||
'tags_view_existing_tags' => 'Zobrazit existující štítky',
|
||||
@@ -394,8 +399,8 @@ return [
|
||||
'comment_add' => 'Přidat komentář',
|
||||
'comment_none' => 'Žádné komentáře k zobrazení',
|
||||
'comment_placeholder' => 'Zde zadejte komentář',
|
||||
'comment_thread_count' => ':count vlákno komentáře|:count vláken komentářů',
|
||||
'comment_archived_count' => ':count archivováno',
|
||||
'comment_thread_count' => '{0}:count vláken komentářů|{1}:count vlákno komentářů|[2,4]:count vlákna komentářů|[5,*]:count vláken komentářů',
|
||||
'comment_archived_count' => '[0,1]:count archivováno|[2,4]:count archivována|[5,*]:count archivováno',
|
||||
'comment_archived_threads' => 'Archivovaná vlákna',
|
||||
'comment_save' => 'Uložit komentář',
|
||||
'comment_new' => 'Nový komentář',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Uwchysgrif',
|
||||
'subscript' => 'Isysgrif',
|
||||
'text_color' => 'Lliw testun',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Lliw addasu',
|
||||
'remove_color' => 'Dileu lliw',
|
||||
'background_color' => 'Lliw cefnder',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Bydd hwn yn dileu\'r mewnforyn ffeil ZIP sy wedi\'i lwytho i fyny, a fydd e ddim gallu cael ei ddadwneud.',
|
||||
'import_errors' => 'Gwallau Mewnforyn',
|
||||
'import_errors_desc' => 'Digwyddodd y gwallau canlynol yn ystod cynnig y mewnforyn:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Caniatâd',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Mewnosod Llun',
|
||||
'pages_md_show_preview' => 'Dangos rhagolwg',
|
||||
'pages_md_sync_scroll' => 'Cydamseru sgrôl ragolwg',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Canfuwyd Llun heb ei Gadw',
|
||||
'pages_drawing_unsaved_confirm' => 'Canfuwyd data llun heb ei gadw o ymgais aflwyddiannus blaenorol i gadw llun. Hoffech chi adfer a pharhau i olygu\'r llun heb ei gadw?',
|
||||
'pages_not_in_chapter' => 'Nid yw\'r dudalen mewn pennod',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Hævet',
|
||||
'subscript' => 'Sænket',
|
||||
'text_color' => 'Tekstfarve',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Tilpasset farve',
|
||||
'remove_color' => 'Fjern farve',
|
||||
'background_color' => 'Baggrundsfarve',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Dette vil slette den uploadede ZIP-fil og kan ikke fortrydes.',
|
||||
'import_errors' => 'Importfejl',
|
||||
'import_errors_desc' => 'Følgende fejl opstod under importforsøget:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Rettigheder',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Indsæt tegning',
|
||||
'pages_md_show_preview' => 'Vis forhåndsvisning',
|
||||
'pages_md_sync_scroll' => 'Rulning af forhåndsvisning af synkronisering',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Ikke gemt tegning fundet',
|
||||
'pages_drawing_unsaved_confirm' => 'Der blev fundet ikke-gemte tegningsdata fra et tidligere mislykket forsøg på at gemme en tegning. Vil du gendanne og fortsætte med at redigere denne ikke-gemte tegning?',
|
||||
'pages_not_in_chapter' => 'Side er ikke i et kapitel',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Hochgestellt',
|
||||
'subscript' => 'Tiefgestellt',
|
||||
'text_color' => 'Schriftfarbe',
|
||||
'highlight_color' => 'Markierungsfarbe',
|
||||
'custom_color' => 'Benutzerdefinierte Farbe',
|
||||
'remove_color' => 'Farbe entfernen',
|
||||
'background_color' => 'Hintergrundfarbe',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Dies löscht die hochgeladene ZIP-Datei und kann nicht rückgängig gemacht werden.',
|
||||
'import_errors' => 'Importfehler',
|
||||
'import_errors_desc' => 'Die folgenden Fehler sind während des Importversuchs aufgetreten:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Berechtigungen',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Zeichnung einfügen',
|
||||
'pages_md_show_preview' => 'Vorschau anzeigen',
|
||||
'pages_md_sync_scroll' => 'Vorschau synchronisieren',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Ungespeicherte Zeichnung gefunden',
|
||||
'pages_drawing_unsaved_confirm' => 'Es wurden ungespeicherte Zeichnungsdaten von einem früheren, fehlgeschlagenen Versuch, die Zeichnung zu speichern, gefunden. Möchten Sie diese ungespeicherte Zeichnung wiederherstellen und weiter bearbeiten?',
|
||||
'pages_not_in_chapter' => 'Seite ist in keinem Kapitel',
|
||||
|
||||
@@ -349,6 +349,7 @@ Hinweis: Benutzer können ihre E-Mail-Adresse nach erfolgreicher Registrierung
|
||||
'lt' => 'Litauisch',
|
||||
'lv' => 'Lettisch',
|
||||
'nb' => 'Norwegisch (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Niederländisch',
|
||||
'pl' => 'Polnisch',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Hochgestellt',
|
||||
'subscript' => 'Tiefgestellt',
|
||||
'text_color' => 'Textfarbe',
|
||||
'highlight_color' => 'Markierungsfarbe',
|
||||
'custom_color' => 'Benutzerdefinierte Farbe',
|
||||
'remove_color' => 'Farbe entfernen',
|
||||
'background_color' => 'Hintergrundfarbe',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Dies löscht die hochgeladene ZIP-Datei und kann nicht rückgängig gemacht werden.',
|
||||
'import_errors' => 'Importfehler',
|
||||
'import_errors_desc' => 'Die folgenden Fehler sind während des Importversuchs aufgetreten:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Berechtigungen',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Zeichnung einfügen',
|
||||
'pages_md_show_preview' => 'Vorschau anzeigen',
|
||||
'pages_md_sync_scroll' => 'Vorschau synchronisieren',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Ungespeicherte Zeichnung gefunden',
|
||||
'pages_drawing_unsaved_confirm' => 'Es wurden ungespeicherte Zeichnungsdaten von einem früheren, fehlgeschlagenen Versuch, die Zeichnung zu speichern, gefunden. Möchtest du diese ungespeicherte Zeichnung wiederherstellen und weiter bearbeiten?',
|
||||
'pages_not_in_chapter' => 'Seite ist in keinem Kapitel',
|
||||
|
||||
@@ -349,6 +349,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung
|
||||
'lt' => 'Litauisch',
|
||||
'lv' => 'Lettisch',
|
||||
'nb' => 'Norwegisch (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Niederländisch',
|
||||
'pl' => 'Polnisch',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Εκθέτης',
|
||||
'subscript' => 'Δείκτης',
|
||||
'text_color' => 'Χρώμα κειμένου',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Προσαρμογή χρώματος',
|
||||
'remove_color' => 'Αφαίρεση χρώματος',
|
||||
'background_color' => 'Χρώμα φόντου',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Δικαιώματα',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Εισαγωγή Σχεδίου',
|
||||
'pages_md_show_preview' => 'Εμφάνιση προεπισκόπησης',
|
||||
'pages_md_sync_scroll' => 'Συγχρονισμός προεπισκόπησης',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Unsaved Drawing Found',
|
||||
'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?',
|
||||
'pages_not_in_chapter' => 'Η σελίδα δεν είναι σε κεφάλαιο',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superscript',
|
||||
'subscript' => 'Subscript',
|
||||
'text_color' => 'Text color',
|
||||
'highlight_color' => 'Highlight color',
|
||||
'custom_color' => 'Custom color',
|
||||
'remove_color' => 'Remove color',
|
||||
'background_color' => 'Background color',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
|
||||
'import_errors' => 'Import Errors',
|
||||
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Permissions',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insert Drawing',
|
||||
'pages_md_show_preview' => 'Show preview',
|
||||
'pages_md_sync_scroll' => 'Sync preview scroll',
|
||||
'pages_md_plain_editor' => 'Plaintext editor',
|
||||
'pages_drawing_unsaved' => 'Unsaved Drawing Found',
|
||||
'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?',
|
||||
'pages_not_in_chapter' => 'Page is not in a chapter',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lietuvių Kalba',
|
||||
'lv' => 'Latviešu Valoda',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Nederlands',
|
||||
'pl' => 'Polski',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superíndice',
|
||||
'subscript' => 'Subíndice',
|
||||
'text_color' => 'Color de texto',
|
||||
'highlight_color' => 'Color de resaltado',
|
||||
'custom_color' => 'Color personalizado',
|
||||
'remove_color' => 'Eliminar color',
|
||||
'background_color' => 'Color de fondo',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Esto eliminará el archivo ZIP de importación subido y no se puede deshacer.',
|
||||
'import_errors' => 'Errores de Importación',
|
||||
'import_errors_desc' => 'Se han producido los siguientes errores durante el intento de importación:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Permisos',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insertar Dibujo',
|
||||
'pages_md_show_preview' => 'Mostrar vista previa',
|
||||
'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa',
|
||||
'pages_md_plain_editor' => 'Editor de texto plano',
|
||||
'pages_drawing_unsaved' => 'Encontrado dibujo sin guardar',
|
||||
'pages_drawing_unsaved_confirm' => 'Se encontraron datos no guardados del dibujo de un intento de guardado fallido. ¿Desea restaurar y continuar editando el dibujo no guardado?',
|
||||
'pages_not_in_chapter' => 'La página no está en un capítulo',
|
||||
|
||||
@@ -348,6 +348,7 @@ return [
|
||||
'lt' => 'Lituano',
|
||||
'lv' => 'Letón',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Holanda',
|
||||
'pl' => 'Polaco',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Superíndice',
|
||||
'subscript' => 'Subíndice',
|
||||
'text_color' => 'Color del texto',
|
||||
'highlight_color' => 'Color de resaltado',
|
||||
'custom_color' => 'Color personalizado',
|
||||
'remove_color' => 'Eliminar color',
|
||||
'background_color' => 'Color de fondo',
|
||||
|
||||
@@ -63,6 +63,10 @@ return [
|
||||
'import_delete_desc' => 'Esto eliminará el archivo ZIP de importación subido y no se puede deshacer.',
|
||||
'import_errors' => 'Errores de Importación',
|
||||
'import_errors_desc' => 'Se produjeron los siguientes errores durante el intento de importación:',
|
||||
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
|
||||
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
|
||||
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
|
||||
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
|
||||
|
||||
// Permissions and restrictions
|
||||
'permissions' => 'Permisos',
|
||||
@@ -268,6 +272,7 @@ return [
|
||||
'pages_md_insert_drawing' => 'Insertar Dibujo',
|
||||
'pages_md_show_preview' => 'Mostrar vista previa',
|
||||
'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa',
|
||||
'pages_md_plain_editor' => 'Editor de texto plano',
|
||||
'pages_drawing_unsaved' => 'Encontrado dibujo sin guardar',
|
||||
'pages_drawing_unsaved_confirm' => 'Se encontraron datos del dibujo no guardados durante un intento de guardado fallido anterior. ¿Desea restaurar y continuar editando el dibujo no guardado?',
|
||||
'pages_not_in_chapter' => 'La página no esá en el capítulo',
|
||||
|
||||
@@ -349,6 +349,7 @@ return [
|
||||
'lt' => 'Lituano',
|
||||
'lv' => 'Letón',
|
||||
'nb' => 'Norsk (Bokmål)',
|
||||
'ne' => 'नेपाली',
|
||||
'nn' => 'Nynorsk',
|
||||
'nl' => 'Holanda',
|
||||
'pl' => 'Polaco',
|
||||
|
||||
@@ -48,6 +48,7 @@ return [
|
||||
'superscript' => 'Ülaindeks',
|
||||
'subscript' => 'Alaindeks',
|
||||
'text_color' => 'Teksti värv',
|
||||
'highlight_color' => 'Esiletõstuvärv',
|
||||
'custom_color' => 'Kohandatud värv',
|
||||
'remove_color' => 'Eemalda värv',
|
||||
'background_color' => 'Taustavärv',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user