# Mail configuration
# Refer to https://www.bookstackapp.com/docs/admin/email-webhooks/#email-configuration
MAIL_DRIVER=smtp
-MAIL_FROM=mail@bookstackapp.com
+MAIL_FROM=bookstack@example.com
MAIL_FROM_NAME=BookStack
MAIL_HOST=localhost
# Current host and source for the "DRAWIO" setting will be auto-appended to the sources configured.
ALLOWED_IFRAME_SOURCES="https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com"
+# A list of the sources/hostnames that can be reached by application SSR calls.
+# This is used wherever users can provide URLs/hosts in-platform, like for webhooks.
+# Host-specific functionality (usually controlled via other options) like auth
+# or user avatars for example, won't use this list.
+# Space seperated if multiple. Can use '*' as a wildcard.
+# Values will be compared prefix-matched, case-insensitive, against called SSR urls.
+# Defaults to allow all hosts.
+ALLOWED_SSR_HOSTS="*"
+
# The default and maximum item-counts for listing API requests.
API_DEFAULT_ITEM_COUNT=100
API_MAX_ITEM_COUNT=500
name: Bug Report
-description: Create a report to help us improve or fix things
+description: Create a report to help us fix bugs & issues in existing supported functionality
labels: [":bug: Bug"]
body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out a bug report!
+ Please note that this form is for reporting bugs in existing supported functionality.
+
+ If you are reporting something that's not an issue in functionality we've previously supported and/or is simply something different to your expectations, then it may be more appropriate to raise via a feature or support request instead.
- type: textarea
id: description
attributes:
id: reproduction
attributes:
label: Steps to Reproduce
- description: Detail the steps that would replicate this issue
+ description: Detail the steps that would replicate this issue.
placeholder: |
1. Go to '...'
2. Click on '....'
id: context
attributes:
label: Screenshots or Additional Context
- description: Provide any additional context and screenshots here to help us solve this issue
+ description: Provide any additional context and screenshots here to help us solve this issue.
validations:
required: false
- type: input
id: bsversion
attributes:
label: Exact BookStack Version
- description: This can be found in the settings view of BookStack. Please provide an exact version.
- placeholder: (eg. v21.08.5)
- validations:
- required: true
- - type: input
- id: phpversion
- attributes:
- label: PHP Version
- description: Keep in mind your command-line PHP version may differ to that of your webserver. Provide that relevant to the issue.
- placeholder: (eg. 7.4)
- validations:
- required: false
- - type: textarea
- id: hosting
- attributes:
- label: Hosting Environment
- description: Describe your hosting environment as much as possible including any proxies used (If applicable).
- placeholder: (eg. Ubuntu 20.04 VPS, installed using official installation script)
+ description: This can be found in the settings view of BookStack. Please provide an exact version(s) you've tested on.
+ placeholder: (eg. v23.06.7)
validations:
required: true
attributes:
label: Have you searched for an existing open/closed issue?
description: |
- To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue) for any existing issues that cover the fundemental benefit/goal of your request.
+ To help us keep these issues under control, please ensure you have first [searched our issue list](https://github.com/BookStackApp/BookStack/issues?q=is%3Aissue) for any existing issues that cover the fundamental benefit/goal of your request.
options:
- - label: I have searched for existing issues and none cover my fundemental request
+ - label: I have searched for existing issues and none cover my fundamental request
required: true
- type: dropdown
id: existing_usage
label: How long have you been using BookStack?
options:
- Not using yet, just scoping
- - 0 to 6 months
- - 6 months to 1 year
+ - Under 3 months
+ - 3 months to 1 year
- 1 to 5 years
- Over 5 years
validations:
attributes:
label: Exact BookStack Version
description: This can be found in the settings view of BookStack. Please provide an exact version.
- placeholder: (eg. v21.08.5)
+ placeholder: (eg. v23.06.7)
validations:
required: true
- type: textarea
placeholder: Be sure to remove any confidential details in your logs
validations:
required: false
- - type: input
- id: phpversion
- attributes:
- label: PHP Version
- description: Keep in mind your command-line PHP version may differ to that of your webserver. Provide that most relevant to the issue.
- placeholder: (eg. 7.4)
- validations:
- required: false
- type: textarea
id: hosting
attributes:
label: Hosting Environment
description: Describe your hosting environment as much as possible including any proxies used (If applicable).
- placeholder: (eg. Ubuntu 20.04 VPS, installed using official installation script)
+ placeholder: (eg. PHP8.1 on Ubuntu 22.04 VPS, installed using official installation script)
validations:
required: true
@Jokuna :: Korean
@smartshogu :: German; German Informal
@samadha56 :: Persian
+@mrmuminov :: Uzbek
cipi1965 :: Italian
Mykola Ronik (Mantikor) :: Ukrainian
furkanoyk :: Turkish
LiZerui (CNLiZerui) :: Chinese Traditional
Fabrice Boyer (FabriceBoyer) :: French
mikael (bitcanon) :: Swedish
-Matthias Mai (schnapsidee) :: German; German Informal
+Matthias Mai (schnapsidee) :: German Informal; German
Ufuk Ayyıldız (ufukayyildiz) :: Turkish
Jan Mitrof (jan.kachlik) :: Czech
edwardsmirnov :: Russian
Tomislav Kraljević (tomislav.kraljevic) :: Croatian
Taygun Yıldırım (yildirimtaygun) :: Turkish
robing29 :: German
+Bruno Eduardo de Jesus Barroso (brunoejb) :: Portuguese, Brazilian
+Igor V Belousov (biv) :: Russian
+David Bauer (davbauer) :: German
+Guttorm Hveem (guttormhveem) :: Norwegian Bokmal; Norwegian Nynorsk
+Minh Giang Truong (minhgiang1204) :: Vietnamese
+Ioannis Ioannides (i.ioannides) :: Greek
+Vadim (vadrozh) :: Russian
+Flip333 :: German Informal; German
+Paulo Henrique (paulohsantos114) :: Portuguese, Brazilian
+Dženan (Dzenan) :: Swedish
+Péter Péli (peter.peli) :: Hungarian
+TWME :: Chinese Traditional
+Sascha (Man-in-Black) :: German
+Mohammadreza Madadi (madadi.efl) :: Persian
+Konstantin Kovacheli (kkovacheli) :: Ukrainian
*/
protected function limiter(): RateLimiter
{
- return app(RateLimiter::class);
+ return app()->make(RateLimiter::class);
}
/**
namespace BookStack\Access;
+use BookStack\Access\Notifications\ConfirmEmailNotification;
use BookStack\Exceptions\ConfirmationEmailException;
-use BookStack\Notifications\ConfirmEmail;
use BookStack\Users\Models\User;
class EmailConfirmationService extends UserTokenService
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
- $user->notify(new ConfirmEmail($token));
+ $user->notify(new ConfirmEmailNotification($token));
}
/**
--- /dev/null
+<?php
+
+namespace BookStack\Access\Notifications;
+
+use BookStack\App\MailNotification;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class ConfirmEmailNotification extends MailNotification
+{
+ public function __construct(
+ public string $token
+ ) {
+ }
+
+ public function toMail(User $notifiable): MailMessage
+ {
+ $appName = ['appName' => setting('app-name')];
+
+ return $this->newMailMessage()
+ ->subject(trans('auth.email_confirm_subject', $appName))
+ ->greeting(trans('auth.email_confirm_greeting', $appName))
+ ->line(trans('auth.email_confirm_text'))
+ ->action(trans('auth.email_confirm_action'), url('/register/confirm/' . $this->token));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Access\Notifications;
+
+use BookStack\App\MailNotification;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class ResetPasswordNotification extends MailNotification
+{
+ public function __construct(
+ public string $token
+ ) {
+ }
+
+ public function toMail(User $notifiable): MailMessage
+ {
+ return $this->newMailMessage()
+ ->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')]))
+ ->line(trans('auth.email_reset_text'))
+ ->action(trans('auth.reset_password'), url('password/reset/' . $this->token))
+ ->line(trans('auth.email_reset_not_requested'));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Access\Notifications;
+
+use BookStack\App\MailNotification;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class UserInviteNotification extends MailNotification
+{
+ public function __construct(
+ public string $token
+ ) {
+ }
+
+ public function toMail(User $notifiable): MailMessage
+ {
+ $appName = ['appName' => setting('app-name')];
+ $locale = $notifiable->getLocale();
+
+ return $this->newMailMessage($locale)
+ ->subject($locale->trans('auth.user_invite_email_subject', $appName))
+ ->greeting($locale->trans('auth.user_invite_email_greeting', $appName))
+ ->line($locale->trans('auth.user_invite_email_text'))
+ ->action($locale->trans('auth.user_invite_email_action'), url('/register/invite/' . $this->token));
+ }
+}
{
use BearerAuthorizationTrait;
- /**
- * @var string
- */
- protected $authorizationEndpoint;
-
- /**
- * @var string
- */
- protected $tokenEndpoint;
+ protected string $authorizationEndpoint;
+ protected string $tokenEndpoint;
/**
* Scopes to use for the OIDC authorization call.
}
/**
- * Add an additional scope to this provider upon the default.
+ * Add another scope to this provider upon the default.
*/
public function addScope(string $scope): void
{
}
}
- if (strpos($this->issuer, 'https://') !== 0) {
+ if (!str_starts_with($this->issuer, 'https://')) {
throw new InvalidArgumentException('Issuer value must start with https://');
}
}
use BookStack\Exceptions\StoppedAuthenticationException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Theme;
+use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
-use Psr\Http\Client\ClientInterface as HttpClient;
/**
* Class OpenIdConnectService
public function __construct(
protected RegistrationService $registrationService,
protected LoginService $loginService,
- protected HttpClient $httpClient,
+ protected HttpRequestService $http,
protected GroupSyncService $groupService
) {
}
// Run discovery
if ($config['discover'] ?? false) {
try {
- $settings->discoverFromIssuer($this->httpClient, Cache::store(null), 15);
+ $settings->discoverFromIssuer($this->http->buildClient(5), Cache::store(null), 15);
} catch (OidcIssuerDiscoveryException $exception) {
throw new OidcException('OIDC Discovery Error: ' . $exception->getMessage());
}
protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider
{
$provider = new OidcOAuthProvider($settings->arrayForProvider(), [
- 'httpClient' => $this->httpClient,
+ 'httpClient' => $this->http->buildClient(5),
'optionProvider' => new HttpBasicAuthOptionProvider(),
]);
*/
protected function getUserDisplayName(OidcIdToken $token, string $defaultValue): string
{
- $displayNameAttr = $this->config()['display_name_claims'];
+ $displayNameAttrString = $this->config()['display_name_claims'] ?? '';
+ $displayNameAttrs = explode('|', $displayNameAttrString);
$displayName = [];
- foreach ($displayNameAttr as $dnAttr) {
+ foreach ($displayNameAttrs as $dnAttr) {
$dnComponent = $token->getClaim($dnAttr) ?? '';
if ($dnComponent !== '') {
$displayName[] = $dnComponent;
namespace BookStack\Access;
-use BookStack\Notifications\UserInvite;
+use BookStack\Access\Notifications\UserInviteNotification;
use BookStack\Users\Models\User;
class UserInviteService extends UserTokenService
{
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
- $user->notify(new UserInvite($token));
+ $user->notify(new UserInviteNotification($token));
}
}
use BookStack\App\Model;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\TopFavourites;
+use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class FavouriteController extends Controller
{
+ public function __construct(
+ protected MixedEntityRequestHelper $entityHelper,
+ ) {
+ }
+
/**
* Show a listing of all favourite items for the current user.
*/
*/
public function add(Request $request)
{
- $favouritable = $this->getValidatedModelFromRequest($request);
- $favouritable->favourites()->firstOrCreate([
+ $modelInfo = $this->validate($request, $this->entityHelper->validationRules());
+ $entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
+ $entity->favourites()->firstOrCreate([
'user_id' => user()->id,
]);
$this->showSuccessNotification(trans('activities.favourite_add_notification', [
- 'name' => $favouritable->name,
+ 'name' => $entity->name,
]));
return redirect()->back();
*/
public function remove(Request $request)
{
- $favouritable = $this->getValidatedModelFromRequest($request);
- $favouritable->favourites()->where([
+ $modelInfo = $this->validate($request, $this->entityHelper->validationRules());
+ $entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
+ $entity->favourites()->where([
'user_id' => user()->id,
])->delete();
$this->showSuccessNotification(trans('activities.favourite_remove_notification', [
- 'name' => $favouritable->name,
+ 'name' => $entity->name,
]));
return redirect()->back();
}
-
- /**
- * @throws \Illuminate\Validation\ValidationException
- * @throws \Exception
- */
- protected function getValidatedModelFromRequest(Request $request): Entity
- {
- $modelInfo = $this->validate($request, [
- 'type' => ['required', 'string'],
- 'id' => ['required', 'integer'],
- ]);
-
- if (!class_exists($modelInfo['type'])) {
- throw new \Exception('Model not found');
- }
-
- /** @var Model $model */
- $model = new $modelInfo['type']();
- if (!$model instanceof Favouritable) {
- throw new \Exception('Model not favouritable');
- }
-
- $modelInstance = $model->newQuery()
- ->where('id', '=', $modelInfo['id'])
- ->first(['id', 'name', 'owned_by']);
-
- $inaccessibleEntity = ($modelInstance instanceof Entity && !userCan('view', $modelInstance));
- if (is_null($modelInstance) || $inaccessibleEntity) {
- throw new \Exception('Model instance not found');
- }
-
- return $modelInstance;
- }
}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Controllers;
+
+use BookStack\Activity\Tools\UserEntityWatchOptions;
+use BookStack\Entities\Tools\MixedEntityRequestHelper;
+use BookStack\Http\Controller;
+use Illuminate\Http\Request;
+
+class WatchController extends Controller
+{
+ public function update(Request $request, MixedEntityRequestHelper $entityHelper)
+ {
+ $this->checkPermission('receive-notifications');
+ $this->preventGuestAccess();
+
+ $requestData = $this->validate($request, array_merge([
+ 'level' => ['required', 'string'],
+ ], $entityHelper->validationRules()));
+
+ $watchable = $entityHelper->getVisibleEntityFromRequestData($requestData);
+ $watchOptions = new UserEntityWatchOptions(user(), $watchable);
+ $watchOptions->updateLevelByName($requestData['level']);
+
+ $this->showSuccessNotification(trans('activities.watch_update_level_notification'));
+
+ return redirect()->back();
+ }
+}
use BookStack\Activity\Models\Webhook;
use BookStack\Activity\Tools\WebhookFormatter;
use BookStack\Facades\Theme;
+use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
+use BookStack\Util\SsrUrlValidator;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
-use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class DispatchWebhookJob implements ShouldQueue
*
* @return void
*/
- public function handle()
+ public function handle(HttpRequestService $http)
{
$lastError = null;
try {
- $response = Http::asJson()
- ->withOptions(['allow_redirects' => ['strict' => true]])
- ->timeout($this->webhook->timeout)
- ->post($this->webhook->endpoint, $this->webhookData);
- } catch (\Exception $exception) {
- $lastError = $exception->getMessage();
- Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with error \"{$lastError}\"");
- }
+ (new SsrUrlValidator())->ensureAllowed($this->webhook->endpoint);
+
+ $client = $http->buildClient($this->webhook->timeout, [
+ 'connect_timeout' => 10,
+ 'allow_redirects' => ['strict' => true],
+ ]);
- if (isset($response) && $response->failed()) {
- $lastError = "Response status from endpoint was {$response->status()}";
- Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with status {$response->status()}");
+ $response = $client->sendRequest($http->jsonRequest('POST', $this->webhook->endpoint, $this->webhookData));
+ $statusCode = $response->getStatusCode();
+
+ if ($statusCode >= 400) {
+ $lastError = "Response status from endpoint was {$statusCode}";
+ Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with status {$statusCode}");
+ }
+ } catch (\Exception $error) {
+ $lastError = $error->getMessage();
+ Log::error("Webhook call to endpoint {$this->webhook->endpoint} failed with error \"{$lastError}\"");
}
$this->webhook->last_called_at = now();
use BookStack\App\Model;
use BookStack\Users\Models\HasCreatorAndUpdater;
use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property int $id
* @property string $text
* @property string $html
- * @property int|null $parent_id
+ * @property int|null $parent_id - Relates to local_id, not id
* @property int $local_id
* @property string $entity_type
* @property int $entity_id
+ * @property int $created_by
+ * @property int $updated_by
*/
class Comment extends Model implements Loggable
{
return $this->morphTo('entity');
}
+ /**
+ * Get the parent comment this is in reply to (if existing).
+ */
+ public function parent(): BelongsTo
+ {
+ return $this->belongsTo(Comment::class, 'parent_id', 'local_id', 'parent')
+ ->where('entity_type', '=', $this->entity_type)
+ ->where('entity_id', '=', $this->entity_id);
+ }
+
/**
* Check if a comment has been updated since creation.
*/
/**
* Get created date as a relative diff.
- *
- * @return mixed
*/
- public function getCreatedAttribute()
+ public function getCreatedAttribute(): string
{
return $this->created_at->diffForHumans();
}
/**
* Get updated date as a relative diff.
- *
- * @return mixed
*/
- public function getUpdatedAttribute()
+ public function getUpdatedAttribute(): string
{
return $this->updated_at->diffForHumans();
}
public static function incrementFor(Viewable $viewable): int
{
$user = user();
- if (is_null($user) || $user->isDefault()) {
+ if ($user->isGuest()) {
return 0;
}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Models;
+
+use BookStack\Activity\WatchLevels;
+use BookStack\Permissions\Models\JointPermission;
+use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
+
+/**
+ * @property int $id
+ * @property int $user_id
+ * @property int $watchable_id
+ * @property string $watchable_type
+ * @property int $level
+ * @property Carbon $created_at
+ * @property Carbon $updated_at
+ */
+class Watch extends Model
+{
+ protected $guarded = [];
+
+ public function watchable(): MorphTo
+ {
+ return $this->morphTo();
+ }
+
+ public function jointPermissions(): HasMany
+ {
+ return $this->hasMany(JointPermission::class, 'entity_id', 'watchable_id')
+ ->whereColumn('watches.watchable_type', '=', 'joint_permissions.entity_type');
+ }
+
+ public function getLevelName(): string
+ {
+ return WatchLevels::levelValueToName($this->level);
+ }
+
+ public function ignoring(): bool
+ {
+ return $this->level === WatchLevels::IGNORE;
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Handlers;
+
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\Messages\BaseActivityNotification;
+use BookStack\Entities\Models\Entity;
+use BookStack\Permissions\PermissionApplicator;
+use BookStack\Users\Models\User;
+
+abstract class BaseNotificationHandler implements NotificationHandler
+{
+ /**
+ * @param class-string<BaseActivityNotification> $notification
+ * @param int[] $userIds
+ */
+ protected function sendNotificationToUserIds(string $notification, array $userIds, User $initiator, string|Loggable $detail, Entity $relatedModel): void
+ {
+ $users = User::query()->whereIn('id', array_unique($userIds))->get();
+
+ foreach ($users as $user) {
+ // Prevent sending to the user that initiated the activity
+ if ($user->id === $initiator->id) {
+ continue;
+ }
+
+ // Prevent sending of the user does not have notification permissions
+ if (!$user->can('receive-notifications')) {
+ continue;
+ }
+
+ // Prevent sending if the user does not have access to the related content
+ $permissions = new PermissionApplicator($user);
+ if (!$permissions->checkOwnableUserAccess($relatedModel, 'view')) {
+ continue;
+ }
+
+ // Send the notification
+ $user->notify(new $notification($detail, $initiator));
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Handlers;
+
+use BookStack\Activity\Models\Activity;
+use BookStack\Activity\Models\Comment;
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\Messages\CommentCreationNotification;
+use BookStack\Activity\Tools\EntityWatchers;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\Page;
+use BookStack\Settings\UserNotificationPreferences;
+use BookStack\Users\Models\User;
+
+class CommentCreationNotificationHandler extends BaseNotificationHandler
+{
+ public function handle(Activity $activity, Loggable|string $detail, User $user): void
+ {
+ if (!($detail instanceof Comment)) {
+ throw new \InvalidArgumentException("Detail for comment creation notifications must be a comment");
+ }
+
+ // Main watchers
+ /** @var Page $page */
+ $page = $detail->entity;
+ $watchers = new EntityWatchers($page, WatchLevels::COMMENTS);
+ $watcherIds = $watchers->getWatcherUserIds();
+
+ // Page owner if user preferences allow
+ if (!$watchers->isUserIgnoring($page->owned_by) && $page->ownedBy) {
+ $userNotificationPrefs = new UserNotificationPreferences($page->ownedBy);
+ if ($userNotificationPrefs->notifyOnOwnPageComments()) {
+ $watcherIds[] = $page->owned_by;
+ }
+ }
+
+ // Parent comment creator if preferences allow
+ $parentComment = $detail->parent()->first();
+ if ($parentComment && !$watchers->isUserIgnoring($parentComment->created_by) && $parentComment->createdBy) {
+ $parentCommenterNotificationsPrefs = new UserNotificationPreferences($parentComment->createdBy);
+ if ($parentCommenterNotificationsPrefs->notifyOnCommentReplies()) {
+ $watcherIds[] = $parentComment->created_by;
+ }
+ }
+
+ $this->sendNotificationToUserIds(CommentCreationNotification::class, $watcherIds, $user, $detail, $page);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Handlers;
+
+use BookStack\Activity\Models\Activity;
+use BookStack\Activity\Models\Loggable;
+use BookStack\Users\Models\User;
+
+interface NotificationHandler
+{
+ /**
+ * Run this handler.
+ * Provides the activity, related activity detail/model
+ * along with the user that triggered the activity.
+ */
+ public function handle(Activity $activity, string|Loggable $detail, User $user): void;
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Handlers;
+
+use BookStack\Activity\Models\Activity;
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\Messages\PageCreationNotification;
+use BookStack\Activity\Tools\EntityWatchers;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+
+class PageCreationNotificationHandler extends BaseNotificationHandler
+{
+ public function handle(Activity $activity, Loggable|string $detail, User $user): void
+ {
+ if (!($detail instanceof Page)) {
+ throw new \InvalidArgumentException("Detail for page create notifications must be a page");
+ }
+
+ $watchers = new EntityWatchers($detail, WatchLevels::NEW);
+ $this->sendNotificationToUserIds(PageCreationNotification::class, $watchers->getWatcherUserIds(), $user, $detail, $detail);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Handlers;
+
+use BookStack\Activity\ActivityType;
+use BookStack\Activity\Models\Activity;
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\Messages\PageUpdateNotification;
+use BookStack\Activity\Tools\EntityWatchers;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\Page;
+use BookStack\Settings\UserNotificationPreferences;
+use BookStack\Users\Models\User;
+
+class PageUpdateNotificationHandler extends BaseNotificationHandler
+{
+ public function handle(Activity $activity, Loggable|string $detail, User $user): void
+ {
+ if (!($detail instanceof Page)) {
+ throw new \InvalidArgumentException("Detail for page update notifications must be a page");
+ }
+
+ // Get last update from activity
+ $lastUpdate = $detail->activity()
+ ->where('type', '=', ActivityType::PAGE_UPDATE)
+ ->where('id', '!=', $activity->id)
+ ->latest('created_at')
+ ->first();
+
+ // Return if the same user has already updated the page in the last 15 mins
+ if ($lastUpdate && $lastUpdate->user_id === $user->id) {
+ if ($lastUpdate->created_at->gt(now()->subMinutes(15))) {
+ return;
+ }
+ }
+
+ // Get active watchers
+ $watchers = new EntityWatchers($detail, WatchLevels::UPDATES);
+ $watcherIds = $watchers->getWatcherUserIds();
+
+ // Add page owner if preferences allow
+ if (!$watchers->isUserIgnoring($detail->owned_by) && $detail->ownedBy) {
+ $userNotificationPrefs = new UserNotificationPreferences($detail->ownedBy);
+ if ($userNotificationPrefs->notifyOnOwnPageChanges()) {
+ $watcherIds[] = $detail->owned_by;
+ }
+ }
+
+ $this->sendNotificationToUserIds(PageUpdateNotification::class, $watcherIds, $user, $detail, $detail);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\MessageParts;
+
+use Illuminate\Contracts\Support\Htmlable;
+use Stringable;
+
+/**
+ * A line of text with linked text included, intended for use
+ * in MailMessages. The line should have a ':link' placeholder for
+ * where the link should be inserted within the line.
+ */
+class LinkedMailMessageLine implements Htmlable, Stringable
+{
+ public function __construct(
+ protected string $url,
+ protected string $line,
+ protected string $linkText,
+ ) {
+ }
+
+ public function toHtml(): string
+ {
+ $link = '<a href="' . e($this->url) . '">' . e($this->linkText) . '</a>';
+ return str_replace(':link', $link, e($this->line));
+ }
+
+ public function __toString(): string
+ {
+ $link = "{$this->linkText} ({$this->url})";
+ return str_replace(':link', $link, $this->line);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\MessageParts;
+
+use Illuminate\Contracts\Support\Htmlable;
+use Stringable;
+
+/**
+ * A bullet point list of content, where the keys of the given list array
+ * are bolded header elements, and the values follow.
+ */
+class ListMessageLine implements Htmlable, Stringable
+{
+ public function __construct(
+ protected array $list
+ ) {
+ }
+
+ public function toHtml(): string
+ {
+ $list = [];
+ foreach ($this->list as $header => $content) {
+ $list[] = '<strong>' . e($header) . '</strong> ' . e($content);
+ }
+ return implode("<br>\n", $list);
+ }
+
+ public function __toString(): string
+ {
+ $list = [];
+ foreach ($this->list as $header => $content) {
+ $list[] = $header . ' ' . $content;
+ }
+ return implode("\n", $list);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Messages;
+
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\MessageParts\LinkedMailMessageLine;
+use BookStack\App\MailNotification;
+use BookStack\Translation\LocaleDefinition;
+use BookStack\Users\Models\User;
+use Illuminate\Bus\Queueable;
+
+abstract class BaseActivityNotification extends MailNotification
+{
+ use Queueable;
+
+ public function __construct(
+ protected Loggable|string $detail,
+ protected User $user,
+ ) {
+ }
+
+ /**
+ * Get the array representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function toArray($notifiable)
+ {
+ return [
+ 'activity_detail' => $this->detail,
+ 'activity_creator' => $this->user,
+ ];
+ }
+
+ /**
+ * Build the common reason footer line used in mail messages.
+ */
+ protected function buildReasonFooterLine(LocaleDefinition $locale): LinkedMailMessageLine
+ {
+ return new LinkedMailMessageLine(
+ url('/preferences/notifications'),
+ $locale->trans('notifications.footer_reason'),
+ $locale->trans('notifications.footer_reason_link'),
+ );
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Messages;
+
+use BookStack\Activity\Models\Comment;
+use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class CommentCreationNotification extends BaseActivityNotification
+{
+ public function toMail(User $notifiable): MailMessage
+ {
+ /** @var Comment $comment */
+ $comment = $this->detail;
+ /** @var Page $page */
+ $page = $comment->entity;
+
+ $locale = $notifiable->getLocale();
+
+ return $this->newMailMessage($locale)
+ ->subject($locale->trans('notifications.new_comment_subject', ['pageName' => $page->getShortName()]))
+ ->line($locale->trans('notifications.new_comment_intro', ['appName' => setting('app-name')]))
+ ->line(new ListMessageLine([
+ $locale->trans('notifications.detail_page_name') => $page->name,
+ $locale->trans('notifications.detail_commenter') => $this->user->name,
+ $locale->trans('notifications.detail_comment') => strip_tags($comment->html),
+ ]))
+ ->action($locale->trans('notifications.action_view_comment'), $page->getUrl('#comment' . $comment->local_id))
+ ->line($this->buildReasonFooterLine($locale));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Messages;
+
+use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class PageCreationNotification extends BaseActivityNotification
+{
+ public function toMail(User $notifiable): MailMessage
+ {
+ /** @var Page $page */
+ $page = $this->detail;
+
+ $locale = $notifiable->getLocale();
+
+ return $this->newMailMessage($locale)
+ ->subject($locale->trans('notifications.new_page_subject', ['pageName' => $page->getShortName()]))
+ ->line($locale->trans('notifications.new_page_intro', ['appName' => setting('app-name')], $locale))
+ ->line(new ListMessageLine([
+ $locale->trans('notifications.detail_page_name') => $page->name,
+ $locale->trans('notifications.detail_created_by') => $this->user->name,
+ ]))
+ ->action($locale->trans('notifications.action_view_page'), $page->getUrl())
+ ->line($this->buildReasonFooterLine($locale));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications\Messages;
+
+use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class PageUpdateNotification extends BaseActivityNotification
+{
+ public function toMail(User $notifiable): MailMessage
+ {
+ /** @var Page $page */
+ $page = $this->detail;
+
+ $locale = $notifiable->getLocale();
+
+ return $this->newMailMessage($locale)
+ ->subject($locale->trans('notifications.updated_page_subject', ['pageName' => $page->getShortName()]))
+ ->line($locale->trans('notifications.updated_page_intro', ['appName' => setting('app-name')]))
+ ->line(new ListMessageLine([
+ $locale->trans('notifications.detail_page_name') => $page->name,
+ $locale->trans('notifications.detail_updated_by') => $this->user->name,
+ ]))
+ ->line($locale->trans('notifications.updated_page_debounce'))
+ ->action($locale->trans('notifications.action_view_page'), $page->getUrl())
+ ->line($this->buildReasonFooterLine($locale));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Notifications;
+
+use BookStack\Activity\ActivityType;
+use BookStack\Activity\Models\Activity;
+use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Notifications\Handlers\CommentCreationNotificationHandler;
+use BookStack\Activity\Notifications\Handlers\NotificationHandler;
+use BookStack\Activity\Notifications\Handlers\PageCreationNotificationHandler;
+use BookStack\Activity\Notifications\Handlers\PageUpdateNotificationHandler;
+use BookStack\Users\Models\User;
+
+class NotificationManager
+{
+ /**
+ * @var class-string<NotificationHandler>[]
+ */
+ protected array $handlers = [];
+
+ public function handle(Activity $activity, string|Loggable $detail, User $user): void
+ {
+ $activityType = $activity->type;
+ $handlersToRun = $this->handlers[$activityType] ?? [];
+ foreach ($handlersToRun as $handlerClass) {
+ /** @var NotificationHandler $handler */
+ $handler = new $handlerClass();
+ $handler->handle($activity, $detail, $user);
+ }
+ }
+
+ /**
+ * @param class-string<NotificationHandler> $handlerClass
+ */
+ public function registerHandler(string $activityType, string $handlerClass): void
+ {
+ if (!isset($this->handlers[$activityType])) {
+ $this->handlers[$activityType] = [];
+ }
+
+ if (!in_array($handlerClass, $this->handlers[$activityType])) {
+ $this->handlers[$activityType][] = $handlerClass;
+ }
+ }
+
+ public function loadDefaultHandlers(): void
+ {
+ $this->registerHandler(ActivityType::PAGE_CREATE, PageCreationNotificationHandler::class);
+ $this->registerHandler(ActivityType::PAGE_UPDATE, PageUpdateNotificationHandler::class);
+ $this->registerHandler(ActivityType::COMMENT_CREATE, CommentCreationNotificationHandler::class);
+ }
+}
use BookStack\Activity\Models\Activity;
use BookStack\Activity\Models\Loggable;
use BookStack\Activity\Models\Webhook;
-use BookStack\App\Model;
+use BookStack\Activity\Notifications\NotificationManager;
use BookStack\Entities\Models\Entity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
class ActivityLogger
{
+ public function __construct(
+ protected NotificationManager $notifications
+ ) {
+ $this->notifications->loadDefaultHandlers();
+ }
+
/**
* Add a generic activity event to the database.
*/
- public function add(string $type, $detail = '')
+ public function add(string $type, string|Loggable $detail = ''): void
{
$detailToStore = ($detail instanceof Loggable) ? $detail->logDescriptor() : $detail;
$this->setNotification($type);
$this->dispatchWebhooks($type, $detail);
+ $this->notifications->handle($activity, $detail, user());
Theme::dispatch(ThemeEvents::ACTIVITY_LOGGED, $type, $detail);
}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\Models\Watch;
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use Illuminate\Database\Eloquent\Builder;
+
+class EntityWatchers
+{
+ /**
+ * @var int[]
+ */
+ protected array $watchers = [];
+
+ /**
+ * @var int[]
+ */
+ protected array $ignorers = [];
+
+ public function __construct(
+ protected Entity $entity,
+ protected int $watchLevel,
+ ) {
+ $this->build();
+ }
+
+ public function getWatcherUserIds(): array
+ {
+ return $this->watchers;
+ }
+
+ public function isUserIgnoring(int $userId): bool
+ {
+ return in_array($userId, $this->ignorers);
+ }
+
+ protected function build(): void
+ {
+ $watches = $this->getRelevantWatches();
+
+ // Sort before de-duping, so that the order looped below follows book -> chapter -> page ordering
+ usort($watches, function (Watch $watchA, Watch $watchB) {
+ $entityTypeDiff = $watchA->watchable_type <=> $watchB->watchable_type;
+ return $entityTypeDiff === 0 ? ($watchA->user_id <=> $watchB->user_id) : $entityTypeDiff;
+ });
+
+ // De-dupe by user id to get their most relevant level
+ $levelByUserId = [];
+ foreach ($watches as $watch) {
+ $levelByUserId[$watch->user_id] = $watch->level;
+ }
+
+ // Populate the class arrays
+ $this->watchers = array_keys(array_filter($levelByUserId, fn(int $level) => $level >= $this->watchLevel));
+ $this->ignorers = array_keys(array_filter($levelByUserId, fn(int $level) => $level === 0));
+ }
+
+ /**
+ * @return Watch[]
+ */
+ protected function getRelevantWatches(): array
+ {
+ /** @var Entity[] $entitiesInvolved */
+ $entitiesInvolved = array_filter([
+ $this->entity,
+ $this->entity instanceof BookChild ? $this->entity->book : null,
+ $this->entity instanceof Page ? $this->entity->chapter : null,
+ ]);
+
+ $query = Watch::query()->where(function (Builder $query) use ($entitiesInvolved) {
+ foreach ($entitiesInvolved as $entity) {
+ $query->orWhere(function (Builder $query) use ($entity) {
+ $query->where('watchable_type', '=', $entity->getMorphClass())
+ ->where('watchable_id', '=', $entity->id);
+ });
+ }
+ });
+
+ return $query->get([
+ 'level', 'watchable_id', 'watchable_type', 'user_id'
+ ])->all();
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\Models\Watch;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Users\Models\User;
+use Illuminate\Database\Eloquent\Builder;
+
+class UserEntityWatchOptions
+{
+ protected ?array $watchMap = null;
+
+ public function __construct(
+ protected User $user,
+ protected Entity $entity,
+ ) {
+ }
+
+ public function canWatch(): bool
+ {
+ return $this->user->can('receive-notifications') && !$this->user->isGuest();
+ }
+
+ public function getWatchLevel(): string
+ {
+ return WatchLevels::levelValueToName($this->getWatchLevelValue());
+ }
+
+ public function isWatching(): bool
+ {
+ return $this->getWatchLevelValue() !== WatchLevels::DEFAULT;
+ }
+
+ public function getWatchedParent(): ?WatchedParentDetails
+ {
+ $watchMap = $this->getWatchMap();
+ unset($watchMap[$this->entity->getMorphClass()]);
+
+ if (isset($watchMap['chapter'])) {
+ return new WatchedParentDetails('chapter', $watchMap['chapter']);
+ }
+
+ if (isset($watchMap['book'])) {
+ return new WatchedParentDetails('book', $watchMap['book']);
+ }
+
+ return null;
+ }
+
+ public function updateLevelByName(string $level): void
+ {
+ $levelValue = WatchLevels::levelNameToValue($level);
+ $this->updateLevelByValue($levelValue);
+ }
+
+ public function updateLevelByValue(int $level): void
+ {
+ if ($level < 0) {
+ $this->remove();
+ return;
+ }
+
+ $this->updateLevel($level);
+ }
+
+ public function getWatchMap(): array
+ {
+ if (!is_null($this->watchMap)) {
+ return $this->watchMap;
+ }
+
+ $entities = [$this->entity];
+ if ($this->entity instanceof BookChild) {
+ $entities[] = $this->entity->book;
+ }
+ if ($this->entity instanceof Page && $this->entity->chapter) {
+ $entities[] = $this->entity->chapter;
+ }
+
+ $query = Watch::query()
+ ->where('user_id', '=', $this->user->id)
+ ->where(function (Builder $subQuery) use ($entities) {
+ foreach ($entities as $entity) {
+ $subQuery->orWhere(function (Builder $whereQuery) use ($entity) {
+ $whereQuery->where('watchable_type', '=', $entity->getMorphClass())
+ ->where('watchable_id', '=', $entity->id);
+ });
+ }
+ });
+
+ $this->watchMap = $query->get(['watchable_type', 'level'])
+ ->pluck('level', 'watchable_type')
+ ->toArray();
+
+ return $this->watchMap;
+ }
+
+ protected function getWatchLevelValue()
+ {
+ return $this->getWatchMap()[$this->entity->getMorphClass()] ?? WatchLevels::DEFAULT;
+ }
+
+ protected function updateLevel(int $levelValue): void
+ {
+ Watch::query()->updateOrCreate([
+ 'watchable_id' => $this->entity->id,
+ 'watchable_type' => $this->entity->getMorphClass(),
+ 'user_id' => $this->user->id,
+ ], [
+ 'level' => $levelValue,
+ ]);
+ $this->watchMap = null;
+ }
+
+ protected function remove(): void
+ {
+ $this->entityQuery()->delete();
+ $this->watchMap = null;
+ }
+
+ protected function entityQuery(): Builder
+ {
+ return Watch::query()->where('watchable_id', '=', $this->entity->id)
+ ->where('watchable_type', '=', $this->entity->getMorphClass())
+ ->where('user_id', '=', $this->user->id);
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity\Tools;
+
+use BookStack\Activity\WatchLevels;
+
+class WatchedParentDetails
+{
+ public function __construct(
+ public string $type,
+ public int $level,
+ ) {
+ }
+
+ public function ignoring(): bool
+ {
+ return $this->level === WatchLevels::IGNORE;
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Activity;
+
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+
+class WatchLevels
+{
+ /**
+ * Default level, No specific option set
+ * Typically not a stored status
+ */
+ const DEFAULT = -1;
+
+ /**
+ * Ignore all notifications.
+ */
+ const IGNORE = 0;
+
+ /**
+ * Watch for new content.
+ */
+ const NEW = 1;
+
+ /**
+ * Watch for updates and new content
+ */
+ const UPDATES = 2;
+
+ /**
+ * Watch for comments, updates and new content.
+ */
+ const COMMENTS = 3;
+
+ /**
+ * Get all the possible values as an option_name => value array.
+ * @returns array<string, int>
+ */
+ public static function all(): array
+ {
+ $options = [];
+ foreach ((new \ReflectionClass(static::class))->getConstants() as $name => $value) {
+ $options[strtolower($name)] = $value;
+ }
+
+ return $options;
+ }
+
+ /**
+ * Get the watch options suited for the given entity.
+ * @returns array<string, int>
+ */
+ public static function allSuitedFor(Entity $entity): array
+ {
+ $options = static::all();
+
+ if ($entity instanceof Page) {
+ unset($options['new']);
+ } elseif ($entity instanceof Bookshelf) {
+ return [];
+ }
+
+ return $options;
+ }
+
+ /**
+ * Convert the given name to a level value.
+ * Defaults to default value if the level does not exist.
+ */
+ public static function levelNameToValue(string $level): int
+ {
+ return static::all()[$level] ?? static::DEFAULT;
+ }
+
+ /**
+ * Convert the given int level value to a level name.
+ * Defaults to 'default' level name if not existing.
+ */
+ public static function levelValueToName(int $level): string
+ {
+ foreach (static::all() as $name => $value) {
+ if ($level === $value) {
+ return $name;
+ }
+ }
+
+ return 'default';
+ }
+}
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Password;
{
$appVersion = trim(file_get_contents(base_path('version')));
$cacheKey = 'api-docs::' . $appVersion;
- if (Cache::has($cacheKey) && config('app.env') === 'production') {
- $docs = Cache::get($cacheKey);
- } else {
- $docs = (new ApiDocsGenerator())->generate();
- Cache::put($cacheKey, $docs, 60 * 24);
+ $isProduction = config('app.env') === 'production';
+ $cacheVal = $isProduction ? Cache::get($cacheKey) : null;
+
+ if (!is_null($cacheVal)) {
+ return $cacheVal;
}
+ $docs = (new ApiDocsGenerator())->generate();
+ Cache::put($cacheKey, $docs, 60 * 24);
+
return $docs;
}
* The list to be formatted.
* @var Entity[]
*/
- protected $list = [];
+ protected array $list = [];
/**
* The fields to show in the formatted data.
* will be used for the resultant value. A null return value will omit the property.
* @var array<string|int, string|callable>
*/
- protected $fields = [
- 'id', 'name', 'slug', 'book_id', 'chapter_id',
- 'draft', 'template', 'created_at', 'updated_at',
+ protected array $fields = [
+ 'id', 'name', 'slug', 'book_id', 'chapter_id', 'draft',
+ 'template', 'priority', 'created_at', 'updated_at',
];
public function __construct(array $list)
}
if ($homepageOption === 'bookshelves') {
- $shelves = app(BookshelfRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder());
+ $shelves = app()->make(BookshelfRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder());
$data = array_merge($commonData, ['shelves' => $shelves]);
return view('home.shelves', $data);
}
if ($homepageOption === 'books') {
- $books = app(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder());
+ $books = app()->make(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder());
$data = array_merge($commonData, ['books' => $books]);
return view('home.books', $data);
<?php
-namespace BookStack\Notifications;
+namespace BookStack\App;
+use BookStack\Translation\LocaleDefinition;
+use BookStack\Users\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
-class MailNotification extends Notification implements ShouldQueue
+abstract class MailNotification extends Notification implements ShouldQueue
{
use Queueable;
+ /**
+ * Get the mail representation of the notification.
+ */
+ abstract public function toMail(User $notifiable): MailMessage;
+
/**
* Get the notification's channels.
*
/**
* Create a new mail message.
- *
- * @return MailMessage
*/
- protected function newMailMessage()
+ protected function newMailMessage(?LocaleDefinition $locale = null): MailMessage
{
+ $data = ['locale' => $locale ?? user()->getLocale()];
+
return (new MailMessage())->view([
'html' => 'vendor.notifications.email',
'text' => 'vendor.notifications.email-plain',
- ]);
+ ], $data);
}
}
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use BookStack\Exceptions\BookStackExceptionHandlerPage;
+use BookStack\Http\HttpRequestService;
+use BookStack\Permissions\PermissionApplicator;
use BookStack\Settings\SettingService;
use BookStack\Util\CspService;
-use GuzzleHttp\Client;
use Illuminate\Contracts\Foundation\ExceptionRenderer;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
-use Psr\Http\Client\ClientInterface as HttpClientInterface;
class AppServiceProvider extends ServiceProvider
{
SettingService::class => SettingService::class,
SocialAuthService::class => SocialAuthService::class,
CspService::class => CspService::class,
+ HttpRequestService::class => HttpRequestService::class,
];
/**
// Set root URL
$appUrl = config('app.url');
if ($appUrl) {
- $isHttps = (strpos($appUrl, 'https://') === 0);
+ $isHttps = str_starts_with($appUrl, 'https://');
URL::forceRootUrl($appUrl);
URL::forceScheme($isHttps ? 'https' : 'http');
}
*/
public function register()
{
- $this->app->bind(HttpClientInterface::class, function ($app) {
- return new Client([
- 'timeout' => 3,
- ]);
+ $this->app->singleton(PermissionApplicator::class, function ($app) {
+ return new PermissionApplicator(null);
});
}
}
use BookStack\Access\LoginService;
use BookStack\Access\RegistrationService;
use BookStack\Api\ApiTokenGuard;
+use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rules\Password;
Auth::provider('external-users', function ($app, array $config) {
return new ExternalBaseUserProvider($config['model']);
});
+
+ // Bind and provide the default system user as a singleton to the app instance when needed.
+ // This effectively "caches" fetching the user at an app-instance level.
+ $this->app->singleton('users.default', function () {
+ return User::query()->where('system_name', '=', 'public')->first();
+ });
}
}
namespace BookStack\App\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
+use SocialiteProviders\Azure\AzureExtendSocialite;
+use SocialiteProviders\Discord\DiscordExtendSocialite;
+use SocialiteProviders\GitLab\GitLabExtendSocialite;
use SocialiteProviders\Manager\SocialiteWasCalled;
+use SocialiteProviders\Okta\OktaExtendSocialite;
+use SocialiteProviders\Twitch\TwitchExtendSocialite;
class EventServiceProvider extends ServiceProvider
{
*/
protected $listen = [
SocialiteWasCalled::class => [
- 'SocialiteProviders\Slack\SlackExtendSocialite@handle',
- 'SocialiteProviders\Azure\AzureExtendSocialite@handle',
- 'SocialiteProviders\Okta\OktaExtendSocialite@handle',
- 'SocialiteProviders\GitLab\GitLabExtendSocialite@handle',
- 'SocialiteProviders\Twitch\TwitchExtendSocialite@handle',
- 'SocialiteProviders\Discord\DiscordExtendSocialite@handle',
+ AzureExtendSocialite::class . '@handle',
+ OktaExtendSocialite::class . '@handle',
+ GitLabExtendSocialite::class . '@handle',
+ TwitchExtendSocialite::class . '@handle',
+ DiscordExtendSocialite::class . '@handle',
],
];
// Custom blade view directives
Blade::directive('icon', function ($expression) {
- return "<?php echo icon($expression); ?>";
+ return "<?php echo (new \BookStack\Util\SvgIcon($expression))->toHtml(); ?>";
});
}
}
*/
function user(): User
{
- return auth()->user() ?: User::getDefault();
-}
-
-/**
- * Check if current user is a signed in user.
- */
-function signedInUser(): bool
-{
- return auth()->user() && !auth()->user()->isDefault();
-}
-
-/**
- * Check if the current user has general access.
- */
-function hasAppAccess(): bool
-{
- return !auth()->guest() || setting('app-public');
+ return auth()->user() ?: User::getGuest();
}
/**
function userCan(string $permission, Model $ownable = null): bool
{
if ($ownable === null) {
- return user() && user()->can($permission);
+ return user()->can($permission);
}
// Check permission on ownable item
- $permissions = app(PermissionApplicator::class);
+ $permissions = app()->make(PermissionApplicator::class);
return $permissions->checkOwnableUserAccess($ownable, $permission);
}
*/
function userCanOnAny(string $action, string $entityClass = ''): bool
{
- $permissions = app(PermissionApplicator::class);
+ $permissions = app()->make(PermissionApplicator::class);
return $permissions->checkUserHasEntityPermissionOnAny($action, $entityClass);
}
*/
function setting(string $key = null, $default = null)
{
- $settingService = resolve(SettingService::class);
+ $settingService = app()->make(SettingService::class);
if (is_null($key)) {
return $settingService;
return base_path('themes/' . $theme . ($path ? DIRECTORY_SEPARATOR . $path : $path));
}
-/**
- * Get fetch an SVG icon as a string.
- * Checks for icons defined within a custom theme before defaulting back
- * to the 'resources/assets/icons' folder.
- *
- * Returns an empty string if icon file not found.
- */
-function icon(string $name, array $attrs = []): string
-{
- $attrs = array_merge([
- 'class' => 'svg-icon',
- 'data-icon' => $name,
- 'role' => 'presentation',
- ], $attrs);
- $attrString = ' ';
- foreach ($attrs as $attrName => $attr) {
- $attrString .= $attrName . '="' . $attr . '" ';
- }
-
- $iconPath = resource_path('icons/' . $name . '.svg');
- $themeIconPath = theme_path('icons/' . $name . '.svg');
-
- if ($themeIconPath && file_exists($themeIconPath)) {
- $iconPath = $themeIconPath;
- } elseif (!file_exists($iconPath)) {
- return '';
- }
-
- $fileContents = file_get_contents($iconPath);
-
- return str_replace('<svg', '<svg' . $attrString, $fileContents);
-}
-
/**
* Generate a URL with multiple parameters for sorting purposes.
* Works out the logic to set the correct sorting direction
// Current host and source for the "DRAWIO" setting will be auto-appended to the sources configured.
'iframe_sources' => env('ALLOWED_IFRAME_SOURCES', 'https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com'),
+ // A list of the sources/hostnames that can be reached by application SSR calls.
+ // This is used wherever users can provide URLs/hosts in-platform, like for webhooks.
+ // Host-specific functionality (usually controlled via other options) like auth
+ // or user avatars for example, won't use this list.
+ // Space seperated if multiple. Can use '*' as a wildcard.
+ // Values will be compared prefix-matched, case-insensitive, against called SSR urls.
+ // Defaults to allow all hosts.
+ 'ssr_hosts' => env('ALLOWED_SSR_HOSTS', '*'),
+
// Alter the precision of IP addresses stored by BookStack.
// Integer value between 0 (IP hidden) to 4 (Full IP usage)
'ip_address_precision' => env('IP_ADDRESS_PRECISION', 4),
'timezone' => env('APP_TIMEZONE', 'UTC'),
// Default locale to use
+ // A default variant is also stored since Laravel can overwrite
+ // app.locale when dynamically setting the locale in-app.
'locale' => env('APP_LANG', 'en'),
-
- // Locales available
- 'locales' => ['en', 'ar', 'bg', 'bs', 'ca', 'cs', 'cy', 'da', 'de', 'de_informal', 'el', 'es', 'es_AR', 'et', 'eu', 'fa', 'fr', 'he', 'hr', 'hu', 'id', 'it', 'ja', 'ka', 'ko', 'lt', 'lv', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl', 'ro', 'ru', 'tr', 'uk', 'uz', 'vi', 'zh_CN', 'zh_TW'],
+ 'default_locale' => env('APP_LANG', 'en'),
// Application Fallback Locale
'fallback_locale' => 'en',
// Faker Locale
'faker_locale' => 'en_GB',
- // Enable right-to-left text control.
- 'rtl' => false,
-
// Auto-detect the locale for public users
// For public users their locale can be guessed by headers sent by their
// browser. This is usually set by users in their browser settings.
// Global "From" address & name
'from' => [
- 'address' => env('MAIL_FROM', 'mail@bookstackapp.com'),
+ 'address' => env('MAIL_FROM', 'bookstack@example.com'),
'name' => env('MAIL_FROM_NAME', 'BookStack'),
],
'dump_user_details' => env('OIDC_DUMP_USER_DETAILS', false),
// Claim, within an OpenId token, to find the user's display name
- 'display_name_claims' => explode('|', env('OIDC_DISPLAY_NAME_CLAIMS', 'name')),
+ 'display_name_claims' => env('OIDC_DISPLAY_NAME_CLAIMS', 'name'),
// Claim, within an OpenID token, to use to connect a BookStack user to the OIDC user.
'external_id_claim' => env('OIDC_EXTERNAL_ID_CLAIM', 'sub'),
if (!$dryRun) {
$this->warn("This operation is destructive and is not guaranteed to be fully accurate.\nEnsure you have a backup of your images.\n");
- $proceed = $this->confirm("Are you sure you want to proceed?");
+ $proceed = !$this->input->isInteractive() || $this->confirm("Are you sure you want to proceed?");
if (!$proceed) {
return 0;
}
if ($dryRun) {
$this->comment('Dry run, no images have been deleted');
- $this->comment($deleteCount . ' images found that would have been deleted');
+ $this->comment($deleteCount . ' image(s) found that would have been deleted');
$this->showDeletedImages($deleted);
$this->comment('Run with -f or --force to perform deletions');
}
$this->showDeletedImages($deleted);
- $this->comment($deleteCount . ' images deleted');
+ $this->comment("{$deleteCount} image(s) deleted");
+
return 0;
}
}
if (count($paths) > 0) {
- $this->line('Images to delete:');
+ $this->line('Image(s) to delete:');
}
foreach ($paths as $path) {
--- /dev/null
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Users\Models\User;
+use Exception;
+use Illuminate\Console\Command;
+
+/**
+ * @mixin Command
+ */
+trait HandlesSingleUser
+{
+ /**
+ * Fetch a user provided to this command.
+ * Expects the command to accept 'id' and 'email' options.
+ * @throws Exception
+ */
+ private function fetchProvidedUser(): User
+ {
+ $id = $this->option('id');
+ $email = $this->option('email');
+ if (!$id && !$email) {
+ throw new Exception("Either a --id=<number> or --email=<email> option must be provided.\nRun this command with `--help` to show more options.");
+ }
+
+ $field = $id ? 'id' : 'email';
+ $value = $id ?: $email;
+
+ $user = User::query()
+ ->where($field, '=', $value)
+ ->first();
+
+ if (!$user) {
+ throw new Exception("A user where {$field}={$value} could not be found.");
+ }
+
+ return $user;
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Users\Models\User;
+use Exception;
+use Illuminate\Console\Command;
+use BookStack\Uploads\UserAvatars;
+
+class RefreshAvatarCommand extends Command
+{
+ use HandlesSingleUser;
+
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'bookstack:refresh-avatar
+ {--id= : Numeric ID of the user to refresh avatar for}
+ {--email= : Email address of the user to refresh avatar for}
+ {--users-without-avatars : Refresh avatars for users that currently have no avatar}
+ {--a|all : Refresh avatars for all users}
+ {--f|force : Actually run the update, Defaults to a dry-run}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Refresh avatar for the given user(s)';
+
+ public function handle(UserAvatars $userAvatar): int
+ {
+ if (!$userAvatar->avatarFetchEnabled()) {
+ $this->error("Avatar fetching is disabled on this instance.");
+ return self::FAILURE;
+ }
+
+ if ($this->option('users-without-avatars')) {
+ return $this->processUsers(User::query()->whereDoesntHave('avatar')->get()->all(), $userAvatar);
+ }
+
+ if ($this->option('all')) {
+ return $this->processUsers(User::query()->get()->all(), $userAvatar);
+ }
+
+ try {
+ $user = $this->fetchProvidedUser();
+ return $this->processUsers([$user], $userAvatar);
+ } catch (Exception $exception) {
+ $this->error($exception->getMessage());
+ return self::FAILURE;
+ }
+ }
+
+ /**
+ * @param User[] $users
+ */
+ private function processUsers(array $users, UserAvatars $userAvatar): int
+ {
+ $dryRun = !$this->option('force');
+ $this->info(count($users) . " user(s) found to update avatars for.");
+
+ if (count($users) === 0) {
+ return self::SUCCESS;
+ }
+
+ if (!$dryRun) {
+ $fetchHost = parse_url($userAvatar->getAvatarUrl(), PHP_URL_HOST);
+ $this->warn("This will destroy any existing avatar images these users have, and attempt to fetch new avatar images from {$fetchHost}.");
+ $proceed = !$this->input->isInteractive() || $this->confirm('Are you sure you want to proceed?');
+ if (!$proceed) {
+ return self::SUCCESS;
+ }
+ }
+
+ $this->info("");
+
+ $exitCode = self::SUCCESS;
+ foreach ($users as $user) {
+ $linePrefix = "[ID: {$user->id}] $user->email -";
+
+ if ($dryRun) {
+ $this->warn("{$linePrefix} Not updated");
+ continue;
+ }
+
+ if ($this->fetchAvatar($userAvatar, $user)) {
+ $this->info("{$linePrefix} Updated");
+ } else {
+ $this->error("{$linePrefix} Not updated");
+ $exitCode = self::FAILURE;
+ }
+ }
+
+ if ($dryRun) {
+ $this->comment("");
+ $this->comment("Dry run, no avatars were updated.");
+ $this->comment('Run with -f or --force to perform the update.');
+ }
+
+ return $exitCode;
+ }
+
+ private function fetchAvatar(UserAvatars $userAvatar, User $user): bool
+ {
+ $oldId = $user->avatar->id ?? 0;
+
+ $userAvatar->fetchAndAssignToUser($user);
+
+ $user->refresh();
+ $newId = $user->avatar->id ?? $oldId;
+ return $oldId !== $newId;
+ }
+}
namespace BookStack\Console\Commands;
-use BookStack\Users\Models\User;
+use Exception;
use Illuminate\Console\Command;
class ResetMfaCommand extends Command
{
+ use HandlesSingleUser;
+
/**
* The name and signature of the console command.
*
*/
public function handle(): int
{
- $id = $this->option('id');
- $email = $this->option('email');
- if (!$id && !$email) {
- $this->error('Either a --id=<number> or --email=<email> option must be provided.');
-
- return 1;
- }
-
- $field = $id ? 'id' : 'email';
- $value = $id ?: $email;
-
- /** @var User $user */
- $user = User::query()
- ->where($field, '=', $value)
- ->first();
-
- if (!$user) {
- $this->error("A user where {$field}={$value} could not be found.");
-
+ try {
+ $user = $this->fetchProvidedUser();
+ } catch (Exception $exception) {
+ $this->error($exception->getMessage());
return 1;
}
class BreadcrumbsViewComposer
{
- protected $entityContextManager;
-
- /**
- * BreadcrumbsViewComposer constructor.
- *
- * @param ShelfContext $entityContextManager
- */
- public function __construct(ShelfContext $entityContextManager)
- {
- $this->entityContextManager = $entityContextManager;
+ public function __construct(
+ protected ShelfContext $shelfContext
+ ) {
}
/**
* Modify data when the view is composed.
- *
- * @param View $view
*/
- public function compose(View $view)
+ public function compose(View $view): void
{
$crumbs = $view->getData()['crumbs'];
$firstCrumb = $crumbs[0] ?? null;
+
if ($firstCrumb instanceof Book) {
- $shelf = $this->entityContextManager->getContextualShelfForBook($firstCrumb);
+ $shelf = $this->shelfContext->getContextualShelfForBook($firstCrumb);
if ($shelf) {
array_unshift($crumbs, $shelf);
$view->with('crumbs', $crumbs);
use BookStack\Activity\ActivityQueries;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\View;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
'current' => $book,
'bookChildren' => $bookChildren,
'bookParentShelves' => $bookParentShelves,
+ 'watchOptions' => new UserEntityWatchOptions(user(), $book),
'activity' => $activities->entityActivity($book, 20, 1),
'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($book),
]);
'name' => ['required', 'string', 'max:255'],
'description' => ['string', 'max:1000'],
'tags' => ['array'],
+ 'priority' => ['integer'],
],
'update' => [
'book_id' => ['integer'],
'name' => ['string', 'min:1', 'max:255'],
'description' => ['string', 'max:1000'],
'tags' => ['array'],
+ 'priority' => ['integer'],
],
];
namespace BookStack\Entities\Controllers;
use BookStack\Activity\Models\View;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Entities\Tools\BookContents;
'chapter' => $chapter,
'current' => $chapter,
'sidebarTree' => $sidebarTree,
+ 'watchOptions' => new UserEntityWatchOptions(user(), $chapter),
'pages' => $pages,
'next' => $nextPreviousLocator->getNext(),
'previous' => $nextPreviousLocator->getPrevious(),
'html' => ['required_without:markdown', 'string'],
'markdown' => ['required_without:html', 'string'],
'tags' => ['array'],
+ 'priority' => ['integer'],
],
'update' => [
'book_id' => ['integer'],
'html' => ['string'],
'markdown' => ['string'],
'tags' => ['array'],
+ 'priority' => ['integer'],
],
];
use BookStack\Activity\Models\View;
use BookStack\Activity\Tools\CommentTree;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\BookContents;
'sidebarTree' => $sidebarTree,
'commentTree' => $commentTree,
'pageNav' => $pageNav,
+ 'watchOptions' => new UserEntityWatchOptions(user(), $page),
'next' => $nextPreviousLocator->getNext(),
'previous' => $nextPreviousLocator->getPrevious(),
'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($page),
* Fetch all core entity types as an associated array
* with their basic names as the keys.
*
- * @return array<Entity>
+ * @return array<string, Entity>
*/
public function all(): array
{
use BookStack\Activity\Models\Tag;
use BookStack\Activity\Models\View;
use BookStack\Activity\Models\Viewable;
+use BookStack\Activity\Models\Watch;
use BookStack\App\Model;
use BookStack\App\Sluggable;
use BookStack\Entities\Tools\SlugGenerator;
->exists();
}
+ /**
+ * Get the related watches for this entity.
+ */
+ public function watches(): MorphMany
+ {
+ return $this->morphMany(Watch::class, 'watchable');
+ }
+
/**
* {@inheritdoc}
*/
public function run(int $count, int $page): Collection
{
$user = user();
- if ($user === null || $user->isDefault()) {
+ if ($user === null || $user->isGuest()) {
return collect();
}
public function run(int $count, int $skip = 0)
{
$user = user();
- if (is_null($user) || $user->isDefault()) {
+ if ($user->isGuest()) {
return collect();
}
class ChapterRepo
{
- protected $baseRepo;
-
- /**
- * ChapterRepo constructor.
- */
- public function __construct(BaseRepo $baseRepo)
- {
- $this->baseRepo = $baseRepo;
+ public function __construct(
+ protected BaseRepo $baseRepo
+ ) {
}
/**
class PageRepo
{
- protected BaseRepo $baseRepo;
- protected RevisionRepo $revisionRepo;
- protected ReferenceStore $referenceStore;
- protected ReferenceUpdater $referenceUpdater;
-
- /**
- * PageRepo constructor.
- */
public function __construct(
- BaseRepo $baseRepo,
- RevisionRepo $revisionRepo,
- ReferenceStore $referenceStore,
- ReferenceUpdater $referenceUpdater
+ protected BaseRepo $baseRepo,
+ protected RevisionRepo $revisionRepo,
+ protected ReferenceStore $referenceStore,
+ protected ReferenceUpdater $referenceUpdater
) {
- $this->baseRepo = $baseRepo;
- $this->revisionRepo = $revisionRepo;
- $this->referenceStore = $referenceStore;
- $this->referenceUpdater = $referenceUpdater;
}
/**
*/
public function publishDraft(Page $draft, array $input): Page
{
- $this->updateTemplateStatusAndContentFromInput($draft, $input);
- $this->baseRepo->update($draft, $input);
-
$draft->draft = false;
$draft->revision_count = 1;
$draft->priority = $this->getNewPriority($draft);
- $draft->save();
+ $this->updateTemplateStatusAndContentFromInput($draft, $input);
+ $this->baseRepo->update($draft, $input);
$this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision'));
$this->referenceStore->updateForPage($draft);
class ExportFormatter
{
- protected ImageService $imageService;
- protected PdfGenerator $pdfGenerator;
- protected CspService $cspService;
-
- /**
- * ExportService constructor.
- */
- public function __construct(ImageService $imageService, PdfGenerator $pdfGenerator, CspService $cspService)
- {
- $this->imageService = $imageService;
- $this->pdfGenerator = $pdfGenerator;
- $this->cspService = $cspService;
+ public function __construct(
+ protected ImageService $imageService,
+ protected PdfGenerator $pdfGenerator,
+ protected CspService $cspService
+ ) {
}
/**
*
* @throws Throwable
*/
- public function pageToContainedHtml(Page $page)
+ public function pageToContainedHtml(Page $page): string
{
$page->html = (new PageContent($page))->render();
$pageHtml = view('exports.page', [
'page' => $page,
'format' => 'html',
'cspContent' => $this->cspService->getCspMetaTagValue(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->containHtml($pageHtml);
*
* @throws Throwable
*/
- public function chapterToContainedHtml(Chapter $chapter)
+ public function chapterToContainedHtml(Chapter $chapter): string
{
$pages = $chapter->getVisiblePages();
$pages->each(function ($page) {
'pages' => $pages,
'format' => 'html',
'cspContent' => $this->cspService->getCspMetaTagValue(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->containHtml($html);
*
* @throws Throwable
*/
- public function bookToContainedHtml(Book $book)
+ public function bookToContainedHtml(Book $book): string
{
$bookTree = (new BookContents($book))->getTree(false, true);
$html = view('exports.book', [
'bookChildren' => $bookTree,
'format' => 'html',
'cspContent' => $this->cspService->getCspMetaTagValue(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->containHtml($html);
*
* @throws Throwable
*/
- public function pageToPdf(Page $page)
+ public function pageToPdf(Page $page): string
{
$page->html = (new PageContent($page))->render();
$html = view('exports.page', [
'page' => $page,
'format' => 'pdf',
'engine' => $this->pdfGenerator->getActiveEngine(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->htmlToPdf($html);
*
* @throws Throwable
*/
- public function chapterToPdf(Chapter $chapter)
+ public function chapterToPdf(Chapter $chapter): string
{
$pages = $chapter->getVisiblePages();
$pages->each(function ($page) {
'pages' => $pages,
'format' => 'pdf',
'engine' => $this->pdfGenerator->getActiveEngine(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->htmlToPdf($html);
*
* @throws Throwable
*/
- public function bookToPdf(Book $book)
+ public function bookToPdf(Book $book): string
{
$bookTree = (new BookContents($book))->getTree(false, true);
$html = view('exports.book', [
'bookChildren' => $bookTree,
'format' => 'pdf',
'engine' => $this->pdfGenerator->getActiveEngine(),
+ 'locale' => user()->getLocale(),
])->render();
return $this->htmlToPdf($html);
/** @var DOMElement $iframe */
foreach ($iframes as $iframe) {
$link = $iframe->getAttribute('src');
- if (strpos($link, '//') === 0) {
+ if (str_starts_with($link, '//')) {
$link = 'https:' . $link;
}
foreach ($linksOutput[0] as $index => $linkMatch) {
$oldLinkString = $linkMatch;
$srcString = $linksOutput[2][$index];
- if (strpos(trim($srcString), 'http') !== 0) {
+ if (!str_starts_with(trim($srcString), 'http')) {
$newSrcString = url($srcString);
$newLinkString = str_replace($srcString, $newSrcString, $oldLinkString);
$htmlContent = str_replace($oldLinkString, $newLinkString, $htmlContent);
--- /dev/null
+<?php
+
+namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
+
+class MixedEntityRequestHelper
+{
+ public function __construct(
+ protected EntityProvider $entities,
+ ) {
+ }
+
+ /**
+ * Query out an entity, visible to the current user, for the given
+ * entity request details (this provided in a request validated by
+ * this classes' validationRules method).
+ * @param array{type: string, id: string} $requestData
+ */
+ public function getVisibleEntityFromRequestData(array $requestData): Entity
+ {
+ $entityType = $this->entities->get($requestData['type']);
+
+ return $entityType->newQuery()->scopes(['visible'])->findOrFail($requestData['id']);
+ }
+
+ /**
+ * Get the validation rules for an abstract entity request.
+ * @return array{type: string[], id: string[]}
+ */
+ public function validationRules(): array
+ {
+ return [
+ 'type' => ['required', 'string'],
+ 'id' => ['required', 'integer'],
+ ];
+ }
+}
$page->allRevisions()->delete();
// Delete Attached Files
- $attachmentService = app(AttachmentService::class);
+ $attachmentService = app()->make(AttachmentService::class);
foreach ($page->attachments as $attachment) {
$attachmentService->deleteFile($attachment);
}
$entity->searchTerms()->delete();
$entity->deletions()->delete();
$entity->favourites()->delete();
+ $entity->watches()->delete();
$entity->referencesTo()->delete();
$entity->referencesFrom()->delete();
--- /dev/null
+<?php
+
+namespace BookStack\Exceptions;
+
+class ThemeException extends \Exception
+{
+}
}
}
+ /**
+ * Prevent access for guest users beyond this point.
+ */
+ protected function preventGuestAccess(): void
+ {
+ if (user()->isGuest()) {
+ $this->showPermissionError();
+ }
+ }
+
/**
* Check the current user's permissions against an ownable item otherwise throw an exception.
*/
--- /dev/null
+<?php
+
+namespace BookStack\Http;
+
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+
+class HttpClientHistory
+{
+ public function __construct(
+ protected &$container
+ ) {
+ }
+
+ public function requestCount(): int
+ {
+ return count($this->container);
+ }
+
+ public function requestAt(int $index): ?GuzzleRequest
+ {
+ return $this->container[$index]['request'] ?? null;
+ }
+
+ public function latestRequest(): ?GuzzleRequest
+ {
+ return $this->requestAt($this->requestCount() - 1);
+ }
+
+ public function all(): array
+ {
+ return $this->container;
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Http;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Handler\MockHandler;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Middleware;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Client\ClientInterface;
+
+class HttpRequestService
+{
+ protected ?HandlerStack $handler = null;
+
+ /**
+ * Build a new http client for sending requests on.
+ */
+ public function buildClient(int $timeout, array $options = []): ClientInterface
+ {
+ $defaultOptions = [
+ 'timeout' => $timeout,
+ 'handler' => $this->handler,
+ ];
+
+ return new Client(array_merge($options, $defaultOptions));
+ }
+
+ /**
+ * Create a new JSON http request for use with a client.
+ */
+ public function jsonRequest(string $method, string $uri, array $data): GuzzleRequest
+ {
+ $headers = ['Content-Type' => 'application/json'];
+ return new GuzzleRequest($method, $uri, $headers, json_encode($data));
+ }
+
+ /**
+ * Mock any http clients built from this service, and response with the given responses.
+ * Returns history which can then be queried.
+ * @link https://docs.guzzlephp.org/en/stable/testing.html#history-middleware
+ */
+ public function mockClient(array $responses = [], bool $pad = true): HttpClientHistory
+ {
+ // By default, we pad out the responses with 10 successful values so that requests will be
+ // properly recorded for inspection. Otherwise, we can't later check if we're received
+ // too many requests.
+ if ($pad) {
+ $response = new Response(200, [], 'success');
+ $responses = array_merge($responses, array_fill(0, 10, $response));
+ }
+
+ $container = [];
+ $history = Middleware::history($container);
+ $mock = new MockHandler($responses);
+ $this->handler = HandlerStack::create($mock);
+ $this->handler->push($history, 'history');
+
+ return new HttpClientHistory($container);
+ }
+
+ /**
+ * Clear mocking that has been set up for clients.
+ */
+ public function clearMocking(): void
+ {
+ $this->handler = null;
+ }
+}
{
// Return if the user is already found to be signed in via session-based auth.
// This is to make it easy to browser the API via browser after just logging into the system.
- if (signedInUser() || session()->isStarted()) {
+ if (!user()->isGuest() || session()->isStarted()) {
if (!$this->sessionUserHasApiAccess()) {
throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
}
{
$hasApiPermission = user()->can('access-api');
- return $hasApiPermission && hasAppAccess();
+ return $hasApiPermission && user()->hasAppAccess();
}
}
*/
public function handle(Request $request, Closure $next)
{
- if (!hasAppAccess()) {
+ if (!user()->hasAppAccess()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
namespace BookStack\Http\Middleware;
-use BookStack\Translation\LanguageManager;
-use Carbon\Carbon;
+use BookStack\Translation\LocaleManager;
use Closure;
class Localization
{
- protected LanguageManager $languageManager;
-
- public function __construct(LanguageManager $languageManager)
- {
- $this->languageManager = $languageManager;
+ public function __construct(
+ protected LocaleManager $localeManager
+ ) {
}
/**
*/
public function handle($request, Closure $next)
{
- // Get and record the default language in the config
- $defaultLang = config('app.locale');
- config()->set('app.default_locale', $defaultLang);
-
- // Get the user's language and record that in the config for use in views
- $userLang = $this->languageManager->getUserLanguage($request, $defaultLang);
- config()->set('app.lang', str_replace('_', '-', $this->languageManager->getIsoName($userLang)));
-
- // Set text direction
- if ($this->languageManager->isRTL($userLang)) {
- config()->set('app.rtl', true);
- }
+ // Share details of the user's locale for use in views
+ $userLocale = $this->localeManager->getForUser(user());
+ view()->share('locale', $userLocale);
- app()->setLocale($userLang);
- Carbon::setLocale($userLang);
- $this->languageManager->setPhpDateTimeLocale($userLang);
+ // Set locale for system components
+ app()->setLocale($userLocale->appLocale());
return $next($request);
}
/** @var Response $response */
$response = $next($request);
- if (signedInUser()) {
+ if (!user()->isGuest()) {
$response->headers->set('Cache-Control', 'max-age=0, no-store, private');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', 'Sun, 12 Jul 2015 19:01:00 GMT');
+++ /dev/null
-<?php
-
-namespace BookStack\Notifications;
-
-class ConfirmEmail extends MailNotification
-{
- public $token;
-
- /**
- * Create a new notification instance.
- *
- * @param string $token
- */
- public function __construct($token)
- {
- $this->token = $token;
- }
-
- /**
- * Get the mail representation of the notification.
- *
- * @param mixed $notifiable
- *
- * @return \Illuminate\Notifications\Messages\MailMessage
- */
- public function toMail($notifiable)
- {
- $appName = ['appName' => setting('app-name')];
-
- return $this->newMailMessage()
- ->subject(trans('auth.email_confirm_subject', $appName))
- ->greeting(trans('auth.email_confirm_greeting', $appName))
- ->line(trans('auth.email_confirm_text'))
- ->action(trans('auth.email_confirm_action'), url('/register/confirm/' . $this->token));
- }
-}
+++ /dev/null
-<?php
-
-namespace BookStack\Notifications;
-
-class ResetPassword extends MailNotification
-{
- /**
- * The password reset token.
- *
- * @var string
- */
- public $token;
-
- /**
- * Create a notification instance.
- *
- * @param string $token
- */
- public function __construct($token)
- {
- $this->token = $token;
- }
-
- /**
- * Build the mail representation of the notification.
- *
- * @return \Illuminate\Notifications\Messages\MailMessage
- */
- public function toMail()
- {
- return $this->newMailMessage()
- ->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')]))
- ->line(trans('auth.email_reset_text'))
- ->action(trans('auth.reset_password'), url('password/reset/' . $this->token))
- ->line(trans('auth.email_reset_not_requested'));
- }
-}
+++ /dev/null
-<?php
-
-namespace BookStack\Notifications;
-
-use BookStack\Users\Models\User;
-use Illuminate\Notifications\Messages\MailMessage;
-
-class UserInvite extends MailNotification
-{
- public $token;
-
- /**
- * Create a new notification instance.
- */
- public function __construct(string $token)
- {
- $this->token = $token;
- }
-
- /**
- * Get the mail representation of the notification.
- */
- public function toMail(User $notifiable): MailMessage
- {
- $appName = ['appName' => setting('app-name')];
- $language = setting()->getUser($notifiable, 'language');
-
- return $this->newMailMessage()
- ->subject(trans('auth.user_invite_email_subject', $appName, $language))
- ->greeting(trans('auth.user_invite_email_greeting', $appName, $language))
- ->line(trans('auth.user_invite_email_text', [], $language))
- ->action(trans('auth.user_invite_email_action', [], $language), url('/register/invite/' . $this->token));
- }
-}
namespace BookStack\Permissions;
use BookStack\App\Model;
+use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Permissions\Models\EntityPermission;
use BookStack\Users\Models\HasCreatorAndUpdater;
use BookStack\Users\Models\HasOwner;
-use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
+use Illuminate\Database\Query\JoinClause;
use InvalidArgumentException;
class PermissionApplicator
{
+ public function __construct(
+ protected ?User $user = null
+ ) {
+ }
+
/**
* Checks if an entity has a restriction set upon it.
*
});
}
+ /**
+ * Filter out items that have related entity relations where
+ * the entity is marked as deleted.
+ */
+ public function filterDeletedFromEntityRelationQuery(Builder $query, string $tableName, string $entityIdColumn, string $entityTypeColumn): Builder
+ {
+ $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
+ $entityProvider = new EntityProvider();
+
+ $joinQuery = function ($query) use ($entityProvider) {
+ $first = true;
+ /** @var Builder $query */
+ foreach ($entityProvider->all() as $entity) {
+ $entityQuery = function ($query) use ($entity) {
+ /** @var Builder $query */
+ $query->select(['id', 'deleted_at'])
+ ->selectRaw("'{$entity->getMorphClass()}' as type")
+ ->from($entity->getTable())
+ ->whereNotNull('deleted_at');
+ };
+
+ if ($first) {
+ $entityQuery($query);
+ $first = false;
+ } else {
+ $query->union($entityQuery);
+ }
+ }
+ };
+
+ return $query->leftJoinSub($joinQuery, 'deletions', function (JoinClause $join) use ($tableDetails) {
+ $join->on($tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'], '=', 'deletions.id')
+ ->on($tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'], '=', 'deletions.type');
+ })->whereNull('deletions.deleted_at');
+ }
+
/**
* Add conditions to a query for a model that's a relation of a page, so only the model results
* on visible pages are returned by the query.
*/
protected function currentUser(): User
{
- return user();
+ return $this->user ?? user();
}
/**
class PermissionsRepo
{
- protected JointPermissionBuilder $permissionBuilder;
protected array $systemRoles = ['admin', 'public'];
- public function __construct(JointPermissionBuilder $permissionBuilder)
- {
- $this->permissionBuilder = $permissionBuilder;
+ public function __construct(
+ protected JointPermissionBuilder $permissionBuilder
+ ) {
}
/**
$inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
$parsedStandardTerms = static::parseStandardTermString($inputs['search'] ?? '');
- $instance->searches = $parsedStandardTerms['terms'];
- $instance->exacts = $parsedStandardTerms['exacts'];
+ $instance->searches = array_filter($parsedStandardTerms['terms']);
+ $instance->exacts = array_filter($parsedStandardTerms['exacts']);
array_push($instance->exacts, ...array_filter($inputs['exact'] ?? []));
];
$patterns = [
- 'exacts' => '/"(.*?)"/',
+ 'exacts' => '/"(.*?)(?<!\\\)"/',
'tags' => '/\[(.*?)\]/',
'filters' => '/\{(.*?)\}/',
];
}
}
+ // Unescape exacts
+ foreach ($terms['exacts'] as $index => $exact) {
+ $terms['exacts'][$index] = str_replace('\"', '"', $exact);
+ }
+
// Parse standard terms
$parsedStandardTerms = static::parseStandardTermString($searchString);
array_push($terms['searches'], ...$parsedStandardTerms['terms']);
}
$terms['filters'] = $splitFilters;
+ // Filter down terms where required
+ $terms['exacts'] = array_filter($terms['exacts']);
+ $terms['searches'] = array_filter($terms['searches']);
+
return $terms;
}
/**
* Parse a standard search term string into individual search terms and
- * extract any exact terms searches to be made.
+ * convert any required terms to exact matches. This is done since some
+ * characters will never be in the standard index, since we use them as
+ * delimiters, and therefore we convert a term to be exact if it
+ * contains one of those delimiter characters.
*
* @return array{terms: array<string>, exacts: array<string>}
*/
continue;
}
- $parsedList = (strpbrk($searchTerm, $indexDelimiters) === false) ? 'terms' : 'exacts';
- $parsed[$parsedList][] = $searchTerm;
+ $becomeExact = (strpbrk($searchTerm, $indexDelimiters) !== false);
+ $parsed[$becomeExact ? 'exacts' : 'terms'][] = $searchTerm;
}
return $parsed;
*/
public function toString(): string
{
- $string = implode(' ', $this->searches ?? []);
+ $parts = $this->searches;
foreach ($this->exacts as $term) {
- $string .= ' "' . $term . '"';
+ $escaped = str_replace('"', '\"', $term);
+ $parts[] = '"' . $escaped . '"';
}
foreach ($this->tags as $term) {
- $string .= " [{$term}]";
+ $parts[] = "[{$term}]";
}
foreach ($this->filters as $filterName => $filterVal) {
- $string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
+ $parts[] = '{' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
}
- return $string;
+ return implode(' ', $parts);
}
}
use BookStack\Activity\ActivityType;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Http\Controller;
-use BookStack\Notifications\TestEmail;
use BookStack\References\ReferenceStore;
use BookStack\Uploads\ImageService;
use Illuminate\Http\Request;
$this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email');
try {
- user()->notifyNow(new TestEmail());
+ user()->notifyNow(new TestEmailNotification());
$this->showSuccessNotification(trans('settings.maint_send_test_email_success', ['address' => user()->email]));
} catch (\Exception $exception) {
$errorMessage = trans('errors.maintenance_test_email_failure') . "\n" . $exception->getMessage();
return view('settings.' . $category, [
'category' => $category,
'version' => $version,
- 'guestUser' => User::getDefault(),
+ 'guestUser' => User::getGuest(),
]);
}
$default = config('setting-defaults.user.' . $key, false);
}
- if ($user->isDefault()) {
+ if ($user->isGuest()) {
return $this->getFromSession($key, $default);
}
*/
public function putUser(User $user, string $key, string $value): bool
{
- if ($user->isDefault()) {
+ if ($user->isGuest()) {
session()->put($key, $value);
return true;
return DB::table('migrations')->count() > 0;
}),
'cache' => $this->trueWithoutError(function () {
- $rand = Str::random();
- Cache::add('status_test', $rand);
+ $rand = Str::random(12);
+ $key = "status_test_{$rand}";
+ Cache::add($key, $rand);
- return Cache::pull('status_test') === $rand;
+ return Cache::pull($key) === $rand;
}),
'session' => $this->trueWithoutError(function () {
$rand = Str::random();
<?php
-namespace BookStack\Notifications;
+namespace BookStack\Settings;
-class TestEmail extends MailNotification
+use BookStack\App\MailNotification;
+use BookStack\Users\Models\User;
+use Illuminate\Notifications\Messages\MailMessage;
+
+class TestEmailNotification extends MailNotification
{
- /**
- * Get the mail representation of the notification.
- *
- * @param mixed $notifiable
- *
- * @return \Illuminate\Notifications\Messages\MailMessage
- */
- public function toMail($notifiable)
+ public function toMail(User $notifiable): MailMessage
{
return $this->newMailMessage()
->subject(trans('settings.maint_send_test_email_mail_subject'))
--- /dev/null
+<?php
+
+namespace BookStack\Settings;
+
+use BookStack\Users\Models\User;
+
+class UserNotificationPreferences
+{
+ public function __construct(
+ protected User $user
+ ) {
+ }
+
+ public function notifyOnOwnPageChanges(): bool
+ {
+ return $this->getNotificationSetting('own-page-changes');
+ }
+
+ public function notifyOnOwnPageComments(): bool
+ {
+ return $this->getNotificationSetting('own-page-comments');
+ }
+
+ public function notifyOnCommentReplies(): bool
+ {
+ return $this->getNotificationSetting('comment-replies');
+ }
+
+ public function updateFromSettingsArray(array $settings)
+ {
+ $allowList = ['own-page-changes', 'own-page-comments', 'comment-replies'];
+ foreach ($settings as $setting => $status) {
+ if (!in_array($setting, $allowList)) {
+ continue;
+ }
+
+ $value = $status === 'true' ? 'true' : 'false';
+ setting()->putUser($this->user, 'notifications#' . $setting, $value);
+ }
+ }
+
+ protected function getNotificationSetting(string $key): bool
+ {
+ return setting()->getUser($this->user, 'notifications#' . $key);
+ }
+}
namespace BookStack\Theming;
use BookStack\Access\SocialAuthService;
+use BookStack\Exceptions\ThemeException;
use Illuminate\Console\Application;
use Illuminate\Console\Application as Artisan;
use Symfony\Component\Console\Command\Command;
class ThemeService
{
- protected $listeners = [];
+ /**
+ * @var array<string, callable[]>
+ */
+ protected array $listeners = [];
/**
* Listen to a given custom theme event,
* setting up the action to be ran when the event occurs.
*/
- public function listen(string $event, callable $action)
+ public function listen(string $event, callable $action): void
{
if (!isset($this->listeners[$event])) {
$this->listeners[$event] = [];
*
* If a callback returns a non-null value, this method will
* stop and return that value itself.
- *
- * @return mixed
*/
- public function dispatch(string $event, ...$args)
+ public function dispatch(string $event, ...$args): mixed
{
foreach ($this->listeners[$event] ?? [] as $action) {
$result = call_user_func_array($action, $args);
/**
* Register a new custom artisan command to be available.
*/
- public function registerCommand(Command $command)
+ public function registerCommand(Command $command): void
{
Artisan::starting(function (Application $application) use ($command) {
$application->addCommands([$command]);
/**
* Read any actions from the set theme path if the 'functions.php' file exists.
*/
- public function readThemeActions()
+ public function readThemeActions(): void
{
$themeActionsFile = theme_path('functions.php');
if ($themeActionsFile && file_exists($themeActionsFile)) {
- require $themeActionsFile;
+ try {
+ require $themeActionsFile;
+ } catch (\Error $exception) {
+ throw new ThemeException("Failed loading theme functions file at \"{$themeActionsFile}\" with error: {$exception->getMessage()}");
+ }
}
}
/**
* @see SocialAuthService::addSocialDriver
*/
- public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null)
+ public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null): void
{
$socialAuthService = app()->make(SocialAuthService::class);
$socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
+++ /dev/null
-<?php
-
-namespace BookStack\Translation;
-
-use Illuminate\Http\Request;
-
-class LanguageManager
-{
- /**
- * Array of right-to-left language options.
- */
- protected array $rtlLanguages = ['ar', 'fa', 'he'];
-
- /**
- * Map of BookStack language names to best-estimate ISO and windows locale names.
- * Locales can often be found by running `locale -a` on a linux system.
- * Windows locales can be found at:
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/language-strings?view=msvc-170.
- *
- * @var array<string, array{iso: string, windows: string}>
- */
- protected array $localeMap = [
- 'ar' => ['iso' => 'ar', 'windows' => 'Arabic'],
- 'bg' => ['iso' => 'bg_BG', 'windows' => 'Bulgarian'],
- 'bs' => ['iso' => 'bs_BA', 'windows' => 'Bosnian (Latin)'],
- 'ca' => ['iso' => 'ca', 'windows' => 'Catalan'],
- 'cs' => ['iso' => 'cs_CZ', 'windows' => 'Czech'],
- 'da' => ['iso' => 'da_DK', 'windows' => 'Danish'],
- 'de' => ['iso' => 'de_DE', 'windows' => 'German'],
- 'de_informal' => ['iso' => 'de_DE', 'windows' => 'German'],
- 'en' => ['iso' => 'en_GB', 'windows' => 'English'],
- 'el' => ['iso' => 'el_GR', 'windows' => 'Greek'],
- 'es' => ['iso' => 'es_ES', 'windows' => 'Spanish'],
- 'es_AR' => ['iso' => 'es_AR', 'windows' => 'Spanish'],
- 'et' => ['iso' => 'et_EE', 'windows' => 'Estonian'],
- 'eu' => ['iso' => 'eu_ES', 'windows' => 'Basque'],
- 'fa' => ['iso' => 'fa_IR', 'windows' => 'Persian'],
- 'fr' => ['iso' => 'fr_FR', 'windows' => 'French'],
- 'he' => ['iso' => 'he_IL', 'windows' => 'Hebrew'],
- 'hr' => ['iso' => 'hr_HR', 'windows' => 'Croatian'],
- 'hu' => ['iso' => 'hu_HU', 'windows' => 'Hungarian'],
- 'id' => ['iso' => 'id_ID', 'windows' => 'Indonesian'],
- 'it' => ['iso' => 'it_IT', 'windows' => 'Italian'],
- 'ja' => ['iso' => 'ja', 'windows' => 'Japanese'],
- 'ko' => ['iso' => 'ko_KR', 'windows' => 'Korean'],
- 'lt' => ['iso' => 'lt_LT', 'windows' => 'Lithuanian'],
- 'lv' => ['iso' => 'lv_LV', 'windows' => 'Latvian'],
- 'nl' => ['iso' => 'nl_NL', 'windows' => 'Dutch'],
- 'nb' => ['iso' => 'nb_NO', 'windows' => 'Norwegian (Bokmal)'],
- 'pl' => ['iso' => 'pl_PL', 'windows' => 'Polish'],
- 'pt' => ['iso' => 'pt_PT', 'windows' => 'Portuguese'],
- 'pt_BR' => ['iso' => 'pt_BR', 'windows' => 'Portuguese'],
- 'ro' => ['iso' => 'ro_RO', 'windows' => 'Romanian'],
- 'ru' => ['iso' => 'ru', 'windows' => 'Russian'],
- 'sk' => ['iso' => 'sk_SK', 'windows' => 'Slovak'],
- 'sl' => ['iso' => 'sl_SI', 'windows' => 'Slovenian'],
- 'sv' => ['iso' => 'sv_SE', 'windows' => 'Swedish'],
- 'uk' => ['iso' => 'uk_UA', 'windows' => 'Ukrainian'],
- 'vi' => ['iso' => 'vi_VN', 'windows' => 'Vietnamese'],
- 'zh_CN' => ['iso' => 'zh_CN', 'windows' => 'Chinese (Simplified)'],
- 'zh_TW' => ['iso' => 'zh_TW', 'windows' => 'Chinese (Traditional)'],
- 'tr' => ['iso' => 'tr_TR', 'windows' => 'Turkish'],
- ];
-
- /**
- * Get the language specifically for the currently logged-in user if available.
- */
- public function getUserLanguage(Request $request, string $default): string
- {
- try {
- $user = user();
- } catch (\Exception $exception) {
- return $default;
- }
-
- if ($user->isDefault() && config('app.auto_detect_locale')) {
- return $this->autoDetectLocale($request, $default);
- }
-
- return setting()->getUser($user, 'language', $default);
- }
-
- /**
- * Check if the given BookStack language value is a right-to-left language.
- */
- public function isRTL(string $language): bool
- {
- return in_array($language, $this->rtlLanguages);
- }
-
- /**
- * Autodetect the visitors locale by matching locales in their headers
- * against the locales supported by BookStack.
- */
- protected function autoDetectLocale(Request $request, string $default): string
- {
- $availableLocales = config('app.locales');
- foreach ($request->getLanguages() as $lang) {
- if (in_array($lang, $availableLocales)) {
- return $lang;
- }
- }
-
- return $default;
- }
-
- /**
- * Get the ISO version of a BookStack language name.
- */
- public function getIsoName(string $language): string
- {
- return $this->localeMap[$language]['iso'] ?? $language;
- }
-
- /**
- * Set the system date locale for localized date formatting.
- * Will try both the standard locale name and the UTF8 variant.
- */
- public function setPhpDateTimeLocale(string $language): void
- {
- $isoLang = $this->localeMap[$language]['iso'] ?? '';
- $isoLangPrefix = explode('_', $isoLang)[0];
-
- $locales = array_values(array_filter([
- $isoLang ? $isoLang . '.utf8' : false,
- $isoLang ?: false,
- $isoLang ? str_replace('_', '-', $isoLang) : false,
- $isoLang ? $isoLangPrefix . '.UTF-8' : false,
- $this->localeMap[$language]['windows'] ?? false,
- $language,
- ]));
-
- if (!empty($locales)) {
- setlocale(LC_TIME, $locales[0], ...array_slice($locales, 1));
- }
- }
-}
--- /dev/null
+<?php
+
+namespace BookStack\Translation;
+
+class LocaleDefinition
+{
+ public function __construct(
+ protected string $appName,
+ protected string $isoName,
+ protected bool $isRtl
+ ) {
+ }
+
+ /**
+ * Provide the BookStack-specific locale name.
+ */
+ public function appLocale(): string
+ {
+ return $this->appName;
+ }
+
+ /**
+ * Provide the ISO-aligned locale name.
+ */
+ public function isoLocale(): string
+ {
+ return $this->isoName;
+ }
+
+ /**
+ * Returns a string suitable for the HTML "lang" attribute.
+ */
+ public function htmlLang(): string
+ {
+ return str_replace('_', '-', $this->isoName);
+ }
+
+ /**
+ * Returns a string suitable for the HTML "dir" attribute.
+ */
+ public function htmlDirection(): string
+ {
+ return $this->isRtl ? 'rtl' : 'ltr';
+ }
+
+ /**
+ * Translate using this locate.
+ */
+ public function trans(string $key, array $replace = []): string
+ {
+ return trans($key, $replace, $this->appLocale());
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Translation;
+
+use BookStack\Users\Models\User;
+use Illuminate\Http\Request;
+
+class LocaleManager
+{
+ /**
+ * Array of right-to-left locale options.
+ */
+ protected array $rtlLocales = ['ar', 'fa', 'he'];
+
+ /**
+ * Map of BookStack locale names to best-estimate ISO locale names.
+ * Locales can often be found by running `locale -a` on a linux system.
+ *
+ * @var array<string, string>
+ */
+ protected array $localeMap = [
+ 'ar' => 'ar',
+ 'bg' => 'bg_BG',
+ 'bs' => 'bs_BA',
+ 'ca' => 'ca',
+ 'cs' => 'cs_CZ',
+ 'cy' => 'cy_GB',
+ 'da' => 'da_DK',
+ 'de' => 'de_DE',
+ 'de_informal' => 'de_DE',
+ 'el' => 'el_GR',
+ 'en' => 'en_GB',
+ 'es' => 'es_ES',
+ 'es_AR' => 'es_AR',
+ 'et' => 'et_EE',
+ 'eu' => 'eu_ES',
+ 'fa' => 'fa_IR',
+ 'fr' => 'fr_FR',
+ 'he' => 'he_IL',
+ 'hr' => 'hr_HR',
+ 'hu' => 'hu_HU',
+ 'id' => 'id_ID',
+ 'it' => 'it_IT',
+ 'ja' => 'ja',
+ 'ka' => 'ka_GE',
+ 'ko' => 'ko_KR',
+ 'lt' => 'lt_LT',
+ 'lv' => 'lv_LV',
+ 'nb' => 'nb_NO',
+ 'nl' => 'nl_NL',
+ 'pl' => 'pl_PL',
+ 'pt' => 'pt_PT',
+ 'pt_BR' => 'pt_BR',
+ 'ro' => 'ro_RO',
+ 'ru' => 'ru',
+ 'sk' => 'sk_SK',
+ 'sl' => 'sl_SI',
+ 'sv' => 'sv_SE',
+ 'tr' => 'tr_TR',
+ 'uk' => 'uk_UA',
+ 'uz' => 'uz_UZ',
+ 'vi' => 'vi_VN',
+ 'zh_CN' => 'zh_CN',
+ 'zh_TW' => 'zh_TW',
+ ];
+
+ /**
+ * Get the BookStack locale string for the given user.
+ */
+ protected function getLocaleForUser(User $user): string
+ {
+ $default = config('app.default_locale');
+
+ if ($user->isGuest() && config('app.auto_detect_locale')) {
+ return $this->autoDetectLocale(request(), $default);
+ }
+
+ return setting()->getUser($user, 'language', $default);
+ }
+
+ /**
+ * Get a locale definition for the current user.
+ */
+ public function getForUser(User $user): LocaleDefinition
+ {
+ $localeString = $this->getLocaleForUser($user);
+
+ return new LocaleDefinition(
+ $localeString,
+ $this->localeMap[$localeString] ?? $localeString,
+ in_array($localeString, $this->rtlLocales),
+ );
+ }
+
+ /**
+ * Autodetect the visitors locale by matching locales in their headers
+ * against the locales supported by BookStack.
+ */
+ protected function autoDetectLocale(Request $request, string $default): string
+ {
+ $availableLocales = $this->getAllAppLocales();
+
+ foreach ($request->getLanguages() as $lang) {
+ if (in_array($lang, $availableLocales)) {
+ return $lang;
+ }
+ }
+
+ return $default;
+ }
+
+ /**
+ * Get all the available app-specific level locale strings.
+ */
+ public function getAllAppLocales(): array
+ {
+ return array_keys($this->localeMap);
+ }
+}
+++ /dev/null
-<?php
-
-namespace BookStack\Uploads;
-
-use BookStack\Exceptions\HttpFetchException;
-
-class HttpFetcher
-{
- /**
- * Fetch content from an external URI.
- *
- * @param string $uri
- *
- * @throws HttpFetchException
- *
- * @return bool|string
- */
- public function fetch(string $uri)
- {
- $ch = curl_init();
- curl_setopt_array($ch, [
- CURLOPT_URL => $uri,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_CONNECTTIMEOUT => 5,
- ]);
-
- $data = curl_exec($ch);
- $err = curl_error($ch);
- curl_close($ch);
-
- if ($err) {
- $errno = curl_errno($ch);
- throw new HttpFetchException($err, $errno);
- }
-
- return $data;
- }
-}
namespace BookStack\Uploads;
use BookStack\Exceptions\HttpFetchException;
+use BookStack\Http\HttpRequestService;
use BookStack\Users\Models\User;
use Exception;
+use GuzzleHttp\Psr7\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
+use Psr\Http\Client\ClientExceptionInterface;
class UserAvatars
{
- protected $imageService;
- protected $http;
-
- public function __construct(ImageService $imageService, HttpFetcher $http)
- {
- $this->imageService = $imageService;
- $this->http = $http;
+ public function __construct(
+ protected ImageService $imageService,
+ protected HttpRequestService $http
+ ) {
}
/**
/**
* Destroy all user avatars uploaded to the given user.
*/
- public function destroyAllForUser(User $user)
+ public function destroyAllForUser(User $user): void
{
$profileImages = Image::query()->where('type', '=', 'user')
->where('uploaded_to', '=', $user->id)
/**
* Save an avatar image from an external service.
*
- * @throws Exception
+ * @throws HttpFetchException
*/
protected function saveAvatarImage(User $user, int $size = 500): Image
{
protected function getAvatarImageData(string $url): string
{
try {
- $imageData = $this->http->fetch($url);
- } catch (HttpFetchException $exception) {
+ $client = $this->http->buildClient(5);
+ $response = $client->sendRequest(new Request('GET', $url));
+ if ($response->getStatusCode() !== 200) {
+ throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]));
+ }
+
+ return (string) $response->getBody();
+ } catch (ClientExceptionInterface $exception) {
throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]), $exception->getCode(), $exception);
}
-
- return $imageData;
}
/**
* Check if fetching external avatars is enabled.
*/
- protected function avatarFetchEnabled(): bool
+ public function avatarFetchEnabled(): bool
{
$fetchUrl = $this->getAvatarUrl();
- return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
+ return str_starts_with($fetchUrl, 'http');
}
/**
* Get the URL to fetch avatars from.
*/
- protected function getAvatarUrl(): string
+ public function getAvatarUrl(): string
{
$configOption = config('services.avatar_url');
if ($configOption === false) {
class RoleController extends Controller
{
- protected PermissionsRepo $permissionsRepo;
-
- public function __construct(PermissionsRepo $permissionsRepo)
- {
- $this->permissionsRepo = $permissionsRepo;
+ public function __construct(
+ protected PermissionsRepo $permissionsRepo
+ ) {
}
/**
*/
public function edit(int $id, SocialAuthService $socialAuthService)
{
+ $this->preventGuestAccess();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$user = $this->userRepo->getById($id);
public function update(Request $request, int $id)
{
$this->preventAccessInDemoMode();
+ $this->preventGuestAccess();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$validated = $this->validate($request, [
*/
public function delete(int $id)
{
+ $this->preventGuestAccess();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$user = $this->userRepo->getById($id);
public function destroy(Request $request, int $id)
{
$this->preventAccessInDemoMode();
+ $this->preventGuestAccess();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$user = $this->userRepo->getById($id);
namespace BookStack\Users\Controllers;
use BookStack\Http\Controller;
+use BookStack\Permissions\PermissionApplicator;
+use BookStack\Settings\UserNotificationPreferences;
use BookStack\Settings\UserShortcutMap;
use BookStack\Users\UserRepo;
use Illuminate\Http\Request;
class UserPreferencesController extends Controller
{
- protected UserRepo $userRepo;
+ public function __construct(
+ protected UserRepo $userRepo
+ ) {
+ }
- public function __construct(UserRepo $userRepo)
+ /**
+ * Show the overview for user preferences.
+ */
+ public function index()
{
- $this->userRepo = $userRepo;
+ return view('users.preferences.index');
}
/**
$shortcuts = UserShortcutMap::fromUserPreferences();
$enabled = setting()->getForCurrentUser('ui-shortcuts-enabled', false);
+ $this->setPageTitle(trans('preferences.shortcuts_interface'));
+
return view('users.preferences.shortcuts', [
'shortcuts' => $shortcuts,
'enabled' => $enabled,
return redirect('/preferences/shortcuts');
}
+ /**
+ * Show the notification preferences for the current user.
+ */
+ public function showNotifications(PermissionApplicator $permissions)
+ {
+ $this->checkPermission('receive-notifications');
+ $this->preventGuestAccess();
+
+ $preferences = (new UserNotificationPreferences(user()));
+
+ $query = user()->watches()->getQuery();
+ $query = $permissions->restrictEntityRelationQuery($query, 'watches', 'watchable_id', 'watchable_type');
+ $query = $permissions->filterDeletedFromEntityRelationQuery($query, 'watches', 'watchable_id', 'watchable_type');
+ $watches = $query->with('watchable')->paginate(20);
+
+ $this->setPageTitle(trans('preferences.notifications'));
+ return view('users.preferences.notifications', [
+ 'preferences' => $preferences,
+ 'watches' => $watches,
+ ]);
+ }
+
+ /**
+ * Update the notification preferences for the current user.
+ */
+ public function updateNotifications(Request $request)
+ {
+ $this->checkPermission('receive-notifications');
+ $this->preventGuestAccess();
+ $data = $this->validate($request, [
+ 'preferences' => ['required', 'array'],
+ 'preferences.*' => ['required', 'string'],
+ ]);
+
+ $preferences = (new UserNotificationPreferences(user()));
+ $preferences->updateFromSettingsArray($data['preferences']);
+ $this->showSuccessNotification(trans('preferences.notifications_update_success'));
+
+ return redirect('/preferences/notifications');
+ }
+
/**
* Update the preferred view format for a list view of the given type.
*/
*/
public function toggleDarkMode()
{
- $enabled = setting()->getForCurrentUser('dark-mode-enabled', false);
+ $enabled = setting()->getForCurrentUser('dark-mode-enabled');
setting()->putForCurrentUser('dark-mode-enabled', $enabled ? 'false' : 'true');
return redirect()->back();
{
$validated = $this->validate($request, [
'language' => ['required', 'string', 'max:20'],
- 'active' => ['required', 'bool'],
+ 'active' => ['required', 'bool'],
]);
$currentFavoritesStr = setting()->getForCurrentUser('code-language-favourites', '');
*/
public function forSelect(Request $request)
{
- $hasPermission = signedInUser() && (
+ $hasPermission = !user()->isGuest() && (
userCan('users-manage')
|| userCan('restrictions-manage-own')
|| userCan('restrictions-manage-all')
namespace BookStack\Users\Models;
use BookStack\Access\Mfa\MfaValue;
+use BookStack\Access\Notifications\ResetPasswordNotification;
use BookStack\Access\SocialAccount;
use BookStack\Activity\Models\Favourite;
use BookStack\Activity\Models\Loggable;
+use BookStack\Activity\Models\Watch;
use BookStack\Api\ApiToken;
use BookStack\App\Model;
use BookStack\App\Sluggable;
use BookStack\Entities\Tools\SlugGenerator;
-use BookStack\Notifications\ResetPassword;
+use BookStack\Translation\LocaleDefinition;
+use BookStack\Translation\LocaleManager;
use BookStack\Uploads\Image;
use Carbon\Carbon;
use Exception;
*/
protected string $avatarUrl = '';
- /**
- * This holds the default user when loaded.
- *
- * @var null|User
- */
- protected static ?User $defaultUser = null;
-
/**
* Returns the default public user.
+ * Fetches from the container as a singleton to effectively cache at an app level.
*/
- public static function getDefault(): self
+ public static function getGuest(): self
{
- if (!is_null(static::$defaultUser)) {
- return static::$defaultUser;
- }
-
- static::$defaultUser = static::query()->where('system_name', '=', 'public')->first();
-
- return static::$defaultUser;
+ return app()->make('users.default');
}
/**
* Check if the user is the default public user.
*/
- public function isDefault(): bool
+ public function isGuest(): bool
{
return $this->system_name === 'public';
}
+ /**
+ * Check if the user has general access to the application.
+ */
+ public function hasAppAccess(): bool
+ {
+ return !$this->isGuest() || setting('app-public');
+ }
+
/**
* The roles that belong to the user.
*
return $this->hasMany(MfaValue::class);
}
+ /**
+ * Get the tracked entity watches for this user.
+ */
+ public function watches(): HasMany
+ {
+ return $this->hasMany(Watch::class);
+ }
+
/**
* Get the last activity time for this user.
*/
return $splitName[0];
}
- return '';
+ return mb_substr($this->name, 0, max($chars - 2, 0)) . '…';
+ }
+
+ /**
+ * Get the locale for this user.
+ */
+ public function getLocale(): LocaleDefinition
+ {
+ return app()->make(LocaleManager::class)->getForUser($this);
}
/**
*/
public function sendPasswordResetNotification($token)
{
- $this->notify(new ResetPassword($token));
+ $this->notify(new ResetPasswordNotification($token));
}
/**
*/
public function refreshSlug(): string
{
- $this->slug = app(SlugGenerator::class)->generate($this);
+ $this->slug = app()->make(SlugGenerator::class)->generate($this);
return $this->slug;
}
class UserRepo
{
- protected UserAvatars $userAvatar;
- protected UserInviteService $inviteService;
-
- /**
- * UserRepo constructor.
- */
- public function __construct(UserAvatars $userAvatar, UserInviteService $inviteService)
- {
- $this->userAvatar = $userAvatar;
- $this->inviteService = $inviteService;
+ public function __construct(
+ protected UserAvatars $userAvatar,
+ protected UserInviteService $inviteService
+ ) {
}
+
/**
* Get a user by their email address.
*/
$user->apiTokens()->delete();
$user->favourites()->delete();
$user->mfaValues()->delete();
+ $user->watches()->delete();
$user->delete();
// Delete user profile images
--- /dev/null
+<?php
+
+namespace BookStack\Util;
+
+use BookStack\Exceptions\HttpFetchException;
+
+class SsrUrlValidator
+{
+ protected string $config;
+
+ public function __construct(string $config = null)
+ {
+ $this->config = $config ?? config('app.ssr_hosts') ?? '';
+ }
+
+ /**
+ * @throws HttpFetchException
+ */
+ public function ensureAllowed(string $url): void
+ {
+ if (!$this->allowed($url)) {
+ throw new HttpFetchException(trans('errors.http_ssr_url_no_match'));
+ }
+ }
+
+ /**
+ * Check if the given URL is allowed by the configured SSR host values.
+ */
+ public function allowed(string $url): bool
+ {
+ $allowed = $this->getHostPatterns();
+
+ foreach ($allowed as $pattern) {
+ if ($this->urlMatchesPattern($url, $pattern)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function urlMatchesPattern($url, $pattern): bool
+ {
+ $pattern = rtrim(trim($pattern), '/');
+ $url = trim($url);
+
+ if (empty($pattern) || empty($url)) {
+ return false;
+ }
+
+ $quoted = preg_quote($pattern, '/');
+ $regexPattern = str_replace('\*', '.*', $quoted);
+
+ return preg_match('/^' . $regexPattern . '($|\/.*$|#.*$)/i', $url);
+ }
+
+ /**
+ * @return string[]
+ */
+ protected function getHostPatterns(): array
+ {
+ return explode(' ', strtolower($this->config));
+ }
+}
--- /dev/null
+<?php
+
+namespace BookStack\Util;
+
+class SvgIcon
+{
+ public function __construct(
+ protected string $name,
+ protected array $attrs = []
+ ) {
+ }
+
+ public function toHtml(): string
+ {
+ $attrs = array_merge([
+ 'class' => 'svg-icon',
+ 'data-icon' => $this->name,
+ 'role' => 'presentation',
+ ], $this->attrs);
+
+ $attrString = ' ';
+ foreach ($attrs as $attrName => $attr) {
+ $attrString .= $attrName . '="' . $attr . '" ';
+ }
+
+ $iconPath = resource_path('icons/' . $this->name . '.svg');
+ $themeIconPath = theme_path('icons/' . $this->name . '.svg');
+
+ if ($themeIconPath && file_exists($themeIconPath)) {
+ $iconPath = $themeIconPath;
+ } elseif (!file_exists($iconPath)) {
+ return '';
+ }
+
+ $fileContents = file_get_contents($iconPath);
+
+ return str_replace('<svg', '<svg' . $attrString, $fileContents);
+ }
+}
"guzzlehttp/guzzle": "^7.4",
"intervention/image": "^2.7",
"laravel/framework": "^9.0",
- "laravel/socialite": "^5.2",
+ "laravel/socialite": "^5.8",
"laravel/tinker": "^2.6",
"league/commonmark": "^2.3",
"league/flysystem-aws-s3-v3": "^3.0",
"socialiteproviders/gitlab": "^4.1",
"socialiteproviders/microsoft-azure": "^5.1",
"socialiteproviders/okta": "^4.2",
- "socialiteproviders/slack": "^4.1",
"socialiteproviders/twitch": "^5.3",
"ssddanbrown/htmldiff": "^1.0.2"
},
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d010cf625b58a0dc43addda7881ea42f",
+ "content-hash": "db3cca7a93d7d097c9f517b6fe00ee7b",
"packages": [
{
"name": "aws/aws-crt-php",
- "version": "v1.2.1",
+ "version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/awslabs/aws-crt-php.git",
- "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5"
+ "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/1926277fc71d253dfa820271ac5987bdb193ccf5",
- "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5",
+ "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/2f1dc7b7eda080498be96a4a6d683a41583030e9",
+ "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/awslabs/aws-crt-php/issues",
- "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.1"
+ "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.2"
},
- "time": "2023-03-24T20:22:19+00:00"
+ "time": "2023-07-20T16:49:55+00:00"
},
{
"name": "aws/aws-sdk-php",
- "version": "3.269.0",
+ "version": "3.281.3",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78"
+ "reference": "a3cfb20dbfb11117b7174b2594fc597745252e0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78",
- "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a3cfb20dbfb11117b7174b2594fc597745252e0c",
+ "reference": "a3cfb20dbfb11117b7174b2594fc597745252e0c",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"ext-simplexml": "*",
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
- "guzzlehttp/promises": "^1.4.0",
+ "guzzlehttp/promises": "^1.4.0 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"mtdowling/jmespath.php": "^2.6",
- "php": ">=5.5"
+ "php": ">=7.2.5",
+ "psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
"andrewsville/php-token-reflection": "^1.4",
"ext-sockets": "*",
"nette/neon": "^2.3",
"paragonie/random_compat": ">= 2",
- "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
+ "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5",
"psr/cache": "^1.0",
- "psr/http-message": "^1.0",
"psr/simple-cache": "^1.0",
"sebastian/comparator": "^1.2.3 || ^4.0",
"yoast/phpunit-polyfills": "^1.0"
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.269.0"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.281.3"
},
- "time": "2023-04-26T18:21:04+00:00"
+ "time": "2023-09-08T18:06:26+00:00"
},
{
"name": "bacon/bacon-qr-code",
},
{
"name": "dasprid/enum",
- "version": "1.0.4",
+ "version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
- "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f"
+ "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
- "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
+ "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016",
+ "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/DASPRiD/Enum/issues",
- "source": "https://github.com/DASPRiD/Enum/tree/1.0.4"
+ "source": "https://github.com/DASPRiD/Enum/tree/1.0.5"
},
- "time": "2023-03-01T18:44:03+00:00"
+ "time": "2023-08-25T16:18:39+00:00"
},
{
"name": "dflydev/dot-access-data",
},
{
"name": "doctrine/dbal",
- "version": "3.6.4",
+ "version": "3.6.6",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
- "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f"
+ "reference": "63646ffd71d1676d2f747f871be31b7e921c7864"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/dbal/zipball/19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f",
- "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f",
+ "url": "https://api.github.com/repos/doctrine/dbal/zipball/63646ffd71d1676d2f747f871be31b7e921c7864",
+ "reference": "63646ffd71d1676d2f747f871be31b7e921c7864",
"shasum": ""
},
"require": {
"require-dev": {
"doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1",
- "jetbrains/phpstorm-stubs": "2022.3",
- "phpstan/phpstan": "1.10.14",
+ "jetbrains/phpstorm-stubs": "2023.1",
+ "phpstan/phpstan": "1.10.29",
"phpstan/phpstan-strict-rules": "^1.5",
- "phpunit/phpunit": "9.6.7",
+ "phpunit/phpunit": "9.6.9",
"psalm/plugin-phpunit": "0.18.4",
+ "slevomat/coding-standard": "8.13.1",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^5.4|^6.0",
"symfony/console": "^4.4|^5.4|^6.0",
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
- "source": "https://github.com/doctrine/dbal/tree/3.6.4"
+ "source": "https://github.com/doctrine/dbal/tree/3.6.6"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-06-15T07:40:12+00:00"
+ "time": "2023-08-17T05:38:17+00:00"
},
{
"name": "doctrine/deprecations",
},
{
"name": "dragonmantank/cron-expression",
- "version": "v3.3.2",
+ "version": "v3.3.3",
"source": {
"type": "git",
"url": "https://github.com/dragonmantank/cron-expression.git",
- "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8"
+ "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8",
- "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8",
+ "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
+ "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/dragonmantank/cron-expression/issues",
- "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2"
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3"
},
"funding": [
{
"type": "github"
}
],
- "time": "2022-09-10T18:51:20+00:00"
+ "time": "2023-08-10T19:36:49+00:00"
},
{
"name": "egulias/email-validator",
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.7.0",
+ "version": "7.8.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
+ "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
- "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9",
+ "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9",
"shasum": ""
},
"require": {
"ext-json": "*",
- "guzzlehttp/promises": "^1.5.3 || ^2.0",
- "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
+ "guzzlehttp/promises": "^1.5.3 || ^2.0.1",
+ "guzzlehttp/psr7": "^1.9.1 || ^2.5.1",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.7.0"
+ "source": "https://github.com/guzzle/guzzle/tree/7.8.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-05-21T14:04:53+00:00"
+ "time": "2023-08-27T10:20:53+00:00"
},
{
"name": "guzzlehttp/promises",
- "version": "1.5.3",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
- "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e"
+ "reference": "111166291a0f8130081195ac4556a5587d7f1b5d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e",
- "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d",
+ "reference": "111166291a0f8130081195ac4556a5587d7f1b5d",
"shasum": ""
},
"require": {
- "php": ">=5.5"
+ "php": "^7.2.5 || ^8.0"
},
"require-dev": {
- "symfony/phpunit-bridge": "^4.4 || ^5.1"
+ "bamarni/composer-bin-plugin": "^1.8.1",
+ "phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
"autoload": {
- "files": [
- "src/functions_include.php"
- ],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
- "source": "https://github.com/guzzle/promises/tree/1.5.3"
+ "source": "https://github.com/guzzle/promises/tree/2.0.1"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-05-21T12:31:43+00:00"
+ "time": "2023-08-03T15:11:55+00:00"
},
{
"name": "guzzlehttp/psr7",
- "version": "2.5.0",
+ "version": "2.6.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "b635f279edd83fc275f822a1188157ffea568ff6"
+ "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
- "reference": "b635f279edd83fc275f822a1188157ffea568ff6",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
+ "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.5.0"
+ "source": "https://github.com/guzzle/psr7/tree/2.6.1"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-04-17T16:11:26+00:00"
+ "time": "2023-08-27T10:13:57+00:00"
},
{
"name": "guzzlehttp/uri-template",
- "version": "v1.0.1",
+ "version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/uri-template.git",
- "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2"
+ "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2",
- "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2",
+ "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d",
+ "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d",
"shasum": ""
},
"require": {
"symfony/polyfill-php80": "^1.17"
},
"require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.19 || ^9.5.8",
"uri-template/tests": "1.0.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
"autoload": {
"psr-4": {
"GuzzleHttp\\UriTemplate\\": "src"
],
"support": {
"issues": "https://github.com/guzzle/uri-template/issues",
- "source": "https://github.com/guzzle/uri-template/tree/v1.0.1"
+ "source": "https://github.com/guzzle/uri-template/tree/v1.0.2"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2021-10-07T12:57:01+00:00"
+ "time": "2023-08-27T10:19:19+00:00"
},
{
"name": "intervention/image",
},
{
"name": "knplabs/knp-snappy",
- "version": "v1.4.2",
+ "version": "v1.4.3",
"source": {
"type": "git",
"url": "https://github.com/KnpLabs/snappy.git",
- "reference": "b66f79334421c26d9c244427963fa2d92980b5d3"
+ "reference": "d3b742d61a68bf93866032c2c0a7f1486128b67e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/b66f79334421c26d9c244427963fa2d92980b5d3",
- "reference": "b66f79334421c26d9c244427963fa2d92980b5d3",
+ "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/d3b742d61a68bf93866032c2c0a7f1486128b67e",
+ "reference": "d3b742d61a68bf93866032c2c0a7f1486128b67e",
"shasum": ""
},
"require": {
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16||^3.0",
"pedrotroller/php-cs-custom-fixer": "^2.19",
- "phpstan/phpstan": "^0.12.7",
- "phpstan/phpstan-phpunit": "^0.12.6",
+ "phpstan/phpstan": "^1.0.0",
+ "phpstan/phpstan-phpunit": "^1.0.0",
"phpunit/phpunit": "~7.4||~8.5"
},
"suggest": {
],
"support": {
"issues": "https://github.com/KnpLabs/snappy/issues",
- "source": "https://github.com/KnpLabs/snappy/tree/v1.4.2"
+ "source": "https://github.com/KnpLabs/snappy/tree/v1.4.3"
},
- "time": "2023-03-17T14:47:54+00:00"
+ "time": "2023-09-06T15:24:48+00:00"
},
{
"name": "laravel/framework",
- "version": "v9.52.10",
+ "version": "v9.52.15",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "858add225ce88a76c43aec0e7866288321ee0ee9"
+ "reference": "e3350e87a52346af9cc655a3012d2175d2d05ad7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/858add225ce88a76c43aec0e7866288321ee0ee9",
- "reference": "858add225ce88a76c43aec0e7866288321ee0ee9",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/e3350e87a52346af9cc655a3012d2175d2d05ad7",
+ "reference": "e3350e87a52346af9cc655a3012d2175d2d05ad7",
"shasum": ""
},
"require": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2023-06-27T13:25:54+00:00"
+ "time": "2023-08-08T14:28:40+00:00"
},
{
"name": "laravel/serializable-closure",
- "version": "v1.3.0",
+ "version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37"
+ "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37",
- "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/e5a3057a5591e1cfe8183034b0203921abe2c902",
+ "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902",
"shasum": ""
},
"require": {
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
- "time": "2023-01-30T18:31:20+00:00"
+ "time": "2023-07-14T13:56:28+00:00"
},
{
"name": "laravel/socialite",
- "version": "v5.6.3",
+ "version": "v5.9.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
- "reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e"
+ "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/socialite/zipball/00ea7f8630673ea49304fc8a9fca5a64eb838c7e",
- "reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/14acfa3262875f180fba51efe3c7aaa089a9ef24",
+ "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24",
"shasum": ""
},
"require": {
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
- "time": "2023-06-06T13:42:43+00:00"
+ "time": "2023-09-05T15:20:21+00:00"
},
{
"name": "laravel/tinker",
- "version": "v2.8.1",
+ "version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/tinker.git",
- "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10"
+ "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/tinker/zipball/04a2d3bd0d650c0764f70bf49d1ee39393e4eb10",
- "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10",
+ "url": "https://api.github.com/repos/laravel/tinker/zipball/b936d415b252b499e8c3b1f795cd4fc20f57e1f3",
+ "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3",
"shasum": ""
},
"require": {
},
"require-dev": {
"mockery/mockery": "~1.3.3|^1.4.2",
+ "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^8.5.8|^9.3.3"
},
"suggest": {
],
"support": {
"issues": "https://github.com/laravel/tinker/issues",
- "source": "https://github.com/laravel/tinker/tree/v2.8.1"
+ "source": "https://github.com/laravel/tinker/tree/v2.8.2"
},
- "time": "2023-02-15T16:40:09+00:00"
+ "time": "2023-08-15T14:27:00+00:00"
},
{
"name": "league/commonmark",
- "version": "2.4.0",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048"
+ "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048",
- "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5",
+ "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5",
"shasum": ""
},
"require": {
"type": "tidelift"
}
],
- "time": "2023-03-24T15:16:10+00:00"
+ "time": "2023-08-30T16:55:00+00:00"
},
{
"name": "league/config",
},
{
"name": "league/flysystem",
- "version": "3.15.1",
+ "version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
- "reference": "a141d430414fcb8bf797a18716b09f759a385bed"
+ "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed",
- "reference": "a141d430414fcb8bf797a18716b09f759a385bed",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729",
+ "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729",
"shasum": ""
},
"require": {
"php": "^8.0.2"
},
"conflict": {
+ "async-aws/core": "<1.19.0",
+ "async-aws/s3": "<1.14.0",
"aws/aws-sdk-php": "3.209.31 || 3.210.0",
"guzzlehttp/guzzle": "<7.0",
"guzzlehttp/ringphp": "<1.1.1",
"microsoft/azure-storage-blob": "^1.1",
"phpseclib/phpseclib": "^3.0.14",
"phpstan/phpstan": "^0.12.26",
- "phpunit/phpunit": "^9.5.11",
+ "phpunit/phpunit": "^9.5.11|^10.0",
"sabre/dav": "^4.3.1"
},
"type": "library",
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
- "source": "https://github.com/thephpleague/flysystem/tree/3.15.1"
+ "source": "https://github.com/thephpleague/flysystem/tree/3.16.0"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-05-04T09:04:26+00:00"
+ "time": "2023-09-07T19:22:17+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
- "version": "3.15.0",
+ "version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
- "reference": "d8de61ee10b6a607e7996cff388c5a3a663e8c8a"
+ "reference": "ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/d8de61ee10b6a607e7996cff388c5a3a663e8c8a",
- "reference": "d8de61ee10b6a607e7996cff388c5a3a663e8c8a",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c",
+ "reference": "ded9ba346bb01cb9cc4cc7f2743c2c0e14d18e1c",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
- "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.15.0"
+ "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.16.0"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-05-02T20:02:14+00:00"
+ "time": "2023-08-30T10:14:57+00:00"
},
{
"name": "league/flysystem-local",
- "version": "3.15.0",
+ "version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-local.git",
- "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3"
+ "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3",
- "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781",
+ "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-local/issues",
- "source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0"
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-05-02T20:02:14+00:00"
+ "time": "2023-08-30T10:23:59+00:00"
},
{
"name": "league/html-to-markdown",
- "version": "5.1.0",
+ "version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
- "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1"
+ "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e0fc8cf07bdabbcd3765341ecb50c34c271d64e1",
- "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1",
+ "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd",
+ "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd",
"shasum": ""
},
"require": {
},
"require-dev": {
"mikehaertl/php-shellcommand": "^1.1.0",
- "phpstan/phpstan": "^0.12.99",
+ "phpstan/phpstan": "^1.8.8",
"phpunit/phpunit": "^8.5 || ^9.2",
"scrutinizer/ocular": "^1.6",
- "unleashedtech/php-coding-standard": "^2.7",
- "vimeo/psalm": "^4.22"
+ "unleashedtech/php-coding-standard": "^2.7 || ^3.0",
+ "vimeo/psalm": "^4.22 || ^5.0"
},
"bin": [
"bin/html-to-markdown"
],
"support": {
"issues": "https://github.com/thephpleague/html-to-markdown/issues",
- "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.0"
+ "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-03-02T17:24:08+00:00"
+ "time": "2023-07-12T21:21:09+00:00"
},
{
"name": "league/mime-type-detection",
- "version": "1.11.0",
+ "version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/mime-type-detection.git",
- "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd"
+ "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
- "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96",
+ "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
- "php": "^7.2 || ^8.0"
+ "php": "^7.4 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"phpstan/phpstan": "^0.12.68",
- "phpunit/phpunit": "^8.5.8 || ^9.3"
+ "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
},
"type": "library",
"autoload": {
"description": "Mime-type detection for Flysystem",
"support": {
"issues": "https://github.com/thephpleague/mime-type-detection/issues",
- "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0"
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-04-17T13:12:02+00:00"
+ "time": "2023-08-05T12:09:49+00:00"
},
{
"name": "league/oauth1-client",
},
{
"name": "masterminds/html5",
- "version": "2.8.0",
+ "version": "2.8.1",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
- "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3"
+ "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3",
- "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf",
+ "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
- "source": "https://github.com/Masterminds/html5-php/tree/2.8.0"
+ "source": "https://github.com/Masterminds/html5-php/tree/2.8.1"
},
- "time": "2023-04-26T07:27:39+00:00"
+ "time": "2023-05-10T11:58:31+00:00"
},
{
"name": "monolog/monolog",
},
{
"name": "mtdowling/jmespath.php",
- "version": "2.6.1",
+ "version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/jmespath/jmespath.php.git",
- "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb"
+ "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
- "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
+ "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b",
+ "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b",
"shasum": ""
},
"require": {
- "php": "^5.4 || ^7.0 || ^8.0",
+ "php": "^7.2.5 || ^8.0",
"symfony/polyfill-mbstring": "^1.17"
},
"require-dev": {
- "composer/xdebug-handler": "^1.4 || ^2.0",
- "phpunit/phpunit": "^4.8.36 || ^7.5.15"
+ "composer/xdebug-handler": "^3.0.3",
+ "phpunit/phpunit": "^8.5.33"
},
"bin": [
"bin/jp.php"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
"MIT"
],
"authors": [
+ {
+ "name": "Graham Campbell",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
{
"name": "Michael Dowling",
],
"support": {
"issues": "https://github.com/jmespath/jmespath.php/issues",
- "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1"
+ "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0"
},
- "time": "2021-06-14T00:11:39+00:00"
+ "time": "2023-08-25T10:54:48+00:00"
},
{
"name": "nesbot/carbon",
- "version": "2.68.1",
+ "version": "2.70.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da"
+ "reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4f991ed2a403c85efbc4f23eb4030063fdbe01da",
- "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d3298b38ea8612e5f77d38d1a99438e42f70341d",
+ "reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.1.8 || ^8.0",
+ "psr/clock": "^1.0",
"symfony/polyfill-mbstring": "^1.0",
"symfony/polyfill-php80": "^1.16",
"symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
},
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
"require-dev": {
"doctrine/dbal": "^2.0 || ^3.1.4",
"doctrine/orm": "^2.7",
"type": "tidelift"
}
],
- "time": "2023-06-20T18:29:04+00:00"
+ "time": "2023-09-07T16:43:50+00:00"
},
{
"name": "nette/schema",
- "version": "v1.2.3",
+ "version": "v1.2.4",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
- "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f"
+ "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
- "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
+ "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
+ "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
"shasum": ""
},
"require": {
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
- "php": ">=7.1 <8.3"
+ "php": "7.1 - 8.3"
},
"require-dev": {
"nette/tester": "^2.3 || ^2.4",
],
"support": {
"issues": "https://github.com/nette/schema/issues",
- "source": "https://github.com/nette/schema/tree/v1.2.3"
+ "source": "https://github.com/nette/schema/tree/v1.2.4"
},
- "time": "2022-10-13T01:24:26+00:00"
+ "time": "2023-08-05T18:56:25+00:00"
},
{
"name": "nette/utils",
- "version": "v4.0.0",
+ "version": "v4.0.1",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
- "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e"
+ "reference": "9124157137da01b1f5a5a22d6486cb975f26db7e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e",
- "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e",
+ "url": "https://api.github.com/repos/nette/utils/zipball/9124157137da01b1f5a5a22d6486cb975f26db7e",
+ "reference": "9124157137da01b1f5a5a22d6486cb975f26db7e",
"shasum": ""
},
"require": {
- "php": ">=8.0 <8.3"
+ "php": ">=8.0 <8.4"
},
"conflict": {
"nette/finder": "<3",
},
"require-dev": {
"jetbrains/phpstorm-attributes": "dev-master",
- "nette/tester": "^2.4",
+ "nette/tester": "^2.5",
"phpstan/phpstan": "^1.0",
"tracy/tracy": "^2.9"
},
],
"support": {
"issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.0.0"
+ "source": "https://github.com/nette/utils/tree/v4.0.1"
},
- "time": "2023-02-02T10:41:53+00:00"
+ "time": "2023-07-30T15:42:21+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v4.16.0",
+ "version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "19526a33fb561ef417e822e85f08a00db4059c17"
+ "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
- "reference": "19526a33fb561ef417e822e85f08a00db4059c17",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
+ "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
- "time": "2023-06-25T14:52:30+00:00"
+ "time": "2023-08-13T19:53:39+00:00"
},
{
"name": "nunomaduro/termwind",
},
{
"name": "phpseclib/phpseclib",
- "version": "3.0.20",
+ "version": "3.0.21",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67"
+ "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67",
- "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
+ "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/3.0.20"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-06-13T06:30:34+00:00"
+ "time": "2023-07-09T15:24:48+00:00"
},
{
"name": "pragmarx/google2fa",
},
{
"name": "predis/predis",
- "version": "v2.2.0",
+ "version": "v2.2.1",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
- "reference": "33b70b971a32b0d28b4f748b0547593dce316e0d"
+ "reference": "5f2b410a74afaff296a87a494e4c5488cf9fab57"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/predis/predis/zipball/33b70b971a32b0d28b4f748b0547593dce316e0d",
- "reference": "33b70b971a32b0d28b4f748b0547593dce316e0d",
+ "url": "https://api.github.com/repos/predis/predis/zipball/5f2b410a74afaff296a87a494e4c5488cf9fab57",
+ "reference": "5f2b410a74afaff296a87a494e4c5488cf9fab57",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/predis/predis/issues",
- "source": "https://github.com/predis/predis/tree/v2.2.0"
+ "source": "https://github.com/predis/predis/tree/v2.2.1"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-06-14T10:37:31+00:00"
+ "time": "2023-08-15T23:01:46+00:00"
},
{
"name": "psr/cache",
},
"time": "2021-02-03T23:26:27+00:00"
},
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
{
"name": "psr/container",
"version": "2.0.2",
},
{
"name": "psy/psysh",
- "version": "v0.11.18",
+ "version": "v0.11.20",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
- "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec"
+ "reference": "0fa27040553d1d280a67a4393194df5228afea5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4f00ee9e236fa6a48f4560d1300b9c961a70a7ec",
- "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/0fa27040553d1d280a67a4393194df5228afea5b",
+ "reference": "0fa27040553d1d280a67a4393194df5228afea5b",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
- "source": "https://github.com/bobthecow/psysh/tree/v0.11.18"
+ "source": "https://github.com/bobthecow/psysh/tree/v0.11.20"
},
- "time": "2023-05-23T02:31:11+00:00"
+ "time": "2023-07-31T14:32:22+00:00"
},
{
"name": "ralouphie/getallheaders",
},
{
"name": "socialiteproviders/discord",
- "version": "4.1.2",
+ "version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Discord.git",
- "reference": "11f6a8ded5b1948723886f2e5413b91139fcce6b"
+ "reference": "c71c379acfdca5ba4aa65a3db5ae5222852a919c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/11f6a8ded5b1948723886f2e5413b91139fcce6b",
- "reference": "11f6a8ded5b1948723886f2e5413b91139fcce6b",
+ "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/c71c379acfdca5ba4aa65a3db5ae5222852a919c",
+ "reference": "c71c379acfdca5ba4aa65a3db5ae5222852a919c",
"shasum": ""
},
"require": {
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers"
},
- "time": "2023-02-01T08:54:49+00:00"
+ "time": "2023-07-24T23:28:47+00:00"
},
{
"name": "socialiteproviders/gitlab",
},
{
"name": "socialiteproviders/manager",
- "version": "v4.3.0",
+ "version": "v4.4.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
- "reference": "47402cbc5b7ef445317e799bf12fd5a12062206c"
+ "reference": "df5e45b53d918ec3d689f014d98a6c838b98ed96"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/47402cbc5b7ef445317e799bf12fd5a12062206c",
- "reference": "47402cbc5b7ef445317e799bf12fd5a12062206c",
+ "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/df5e45b53d918ec3d689f014d98a6c838b98ed96",
+ "reference": "df5e45b53d918ec3d689f014d98a6c838b98ed96",
"shasum": ""
},
"require": {
"illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
"laravel/socialite": "~5.0",
- "php": "^7.4 || ^8.0"
+ "php": "^8.0"
},
"require-dev": {
"mockery/mockery": "^1.2",
"issues": "https://github.com/socialiteproviders/manager/issues",
"source": "https://github.com/socialiteproviders/manager"
},
- "time": "2023-01-26T23:11:27+00:00"
+ "time": "2023-08-27T23:46:34+00:00"
},
{
"name": "socialiteproviders/microsoft-azure",
},
{
"name": "socialiteproviders/okta",
- "version": "4.2.1",
+ "version": "4.3.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Okta.git",
- "reference": "7c0b7522423943131f680e74123b71ccd3989541"
+ "reference": "e5fb62035bfa0ccdbc8facf4cf205428fc502edb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/7c0b7522423943131f680e74123b71ccd3989541",
- "reference": "7c0b7522423943131f680e74123b71ccd3989541",
+ "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/e5fb62035bfa0ccdbc8facf4cf205428fc502edb",
+ "reference": "e5fb62035bfa0ccdbc8facf4cf205428fc502edb",
"shasum": ""
},
"require": {
"ext-json": "*",
- "php": "^7.2 || ^8.0",
+ "php": "^7.4 || ^8.0",
"socialiteproviders/manager": "~4.0"
},
"type": "library",
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers"
},
- "time": "2022-03-14T23:25:14+00:00"
- },
- {
- "name": "socialiteproviders/slack",
- "version": "4.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/SocialiteProviders/Slack.git",
- "reference": "2b781c95daf06ec87a8f3deba2ab613d6bea5e8d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/2b781c95daf06ec87a8f3deba2ab613d6bea5e8d",
- "reference": "2b781c95daf06ec87a8f3deba2ab613d6bea5e8d",
- "shasum": ""
- },
- "require": {
- "ext-json": "*",
- "php": "^7.2 || ^8.0",
- "socialiteproviders/manager": "~4.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "SocialiteProviders\\Slack\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Brian Faust",
- }
- ],
- "description": "Slack OAuth2 Provider for Laravel Socialite",
- "support": {
- "source": "https://github.com/SocialiteProviders/Slack/tree/4.1.1"
- },
- "time": "2021-03-26T04:10:10+00:00"
+ "time": "2022-09-06T03:39:26+00:00"
},
{
"name": "socialiteproviders/twitch",
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+ "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
- "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
+ "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "511a08c03c1960e08a883f4cffcacd219b758354"
+ "reference": "875e90aeea2777b6f135677f618529449334a612"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
- "reference": "511a08c03c1960e08a883f4cffcacd219b758354",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
+ "reference": "875e90aeea2777b6f135677f618529449334a612",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "639084e360537a19f9ee352433b84ce831f3d2da"
+ "reference": "ecaafce9f77234a6a449d29e49267ba10499116d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
- "reference": "639084e360537a19f9ee352433b84ce831f3d2da",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d",
+ "reference": "ecaafce9f77234a6a449d29e49267ba10499116d",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:30:37+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
- "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
+ "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+ "reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
- "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
+ "reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-07-28T09:04:16+00:00"
},
{
"name": "symfony/polyfill-php72",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
- "reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
+ "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
+ "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
- "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
+ "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php81",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
- "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
+ "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
- "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
+ "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b",
+ "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-uuid",
- "version": "v1.27.0",
+ "version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-uuid.git",
- "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166"
+ "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166",
- "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166",
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e",
+ "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.27-dev"
+ "dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"uuid"
],
"support": {
- "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0"
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2022-11-03T14:55:06+00:00"
+ "time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/process",
},
{
"name": "filp/whoops",
- "version": "2.15.2",
+ "version": "2.15.3",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
- "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73"
+ "reference": "c83e88a30524f9360b11f585f71e6b17313b7187"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
- "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187",
+ "reference": "c83e88a30524f9360b11f585f71e6b17313b7187",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
- "source": "https://github.com/filp/whoops/tree/2.15.2"
+ "source": "https://github.com/filp/whoops/tree/2.15.3"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-04-12T12:00:00+00:00"
+ "time": "2023-07-13T12:00:00+00:00"
},
{
"name": "hamcrest/hamcrest-php",
},
{
"name": "mockery/mockery",
- "version": "1.6.2",
+ "version": "1.6.6",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
- "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191"
+ "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/mockery/mockery/zipball/13a7fa2642c76c58fa2806ef7f565344c817a191",
- "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e",
+ "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "^2.0.1",
"lib-pcre": ">=7.0",
- "php": "^7.4 || ^8.0"
+ "php": ">=7.3"
},
"conflict": {
"phpunit/phpunit": "<8.0"
},
"require-dev": {
- "phpunit/phpunit": "^8.5 || ^9.3",
- "psalm/plugin-phpunit": "^0.18",
- "vimeo/psalm": "^5.9"
+ "phpunit/phpunit": "^8.5 || ^9.6.10",
+ "psalm/plugin-phpunit": "^0.18.4",
+ "symplify/easy-coding-standard": "^11.5.0",
+ "vimeo/psalm": "^4.30"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.6.x-dev"
- }
- },
"autoload": {
"files": [
"library/helpers.php",
{
"name": "Pádraic Brady",
- "homepage": "http://blog.astrumfutura.com"
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
},
{
"name": "Dave Marshall",
- "homepage": "http://davedevelopment.co.uk"
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework",
"testing"
],
"support": {
+ "docs": "https://docs.mockery.io/",
"issues": "https://github.com/mockery/mockery/issues",
- "source": "https://github.com/mockery/mockery/tree/1.6.2"
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
},
- "time": "2023-06-07T09:07:52+00:00"
+ "time": "2023-08-09T00:03:52+00:00"
},
{
"name": "myclabs/deep-copy",
},
{
"name": "nunomaduro/larastan",
- "version": "v2.6.3",
+ "version": "v2.6.4",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/larastan.git",
- "reference": "73e5be5f5c732212ce6ca77ffd2753a136f36a23"
+ "reference": "6c5e8820f3db6397546f3ce48520af9d312aed27"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/73e5be5f5c732212ce6ca77ffd2753a136f36a23",
- "reference": "73e5be5f5c732212ce6ca77ffd2753a136f36a23",
+ "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/6c5e8820f3db6397546f3ce48520af9d312aed27",
+ "reference": "6c5e8820f3db6397546f3ce48520af9d312aed27",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/nunomaduro/larastan/issues",
- "source": "https://github.com/nunomaduro/larastan/tree/v2.6.3"
+ "source": "https://github.com/nunomaduro/larastan/tree/v2.6.4"
},
"funding": [
{
"type": "patreon"
}
],
- "time": "2023-06-13T21:39:27+00:00"
+ "time": "2023-07-29T12:13:13+00:00"
},
{
"name": "phar-io/manifest",
},
{
"name": "phpstan/phpstan",
- "version": "1.10.23",
+ "version": "1.10.33",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "65ab678d1248a8bc6fde456f0d7ff3562a61a4cd"
+ "reference": "03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/65ab678d1248a8bc6fde456f0d7ff3562a61a4cd",
- "reference": "65ab678d1248a8bc6fde456f0d7ff3562a61a4cd",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1",
+ "reference": "03b1cf9f814ba0863c4e9affea49a4d1ed9a2ed1",
"shasum": ""
},
"require": {
"type": "tidelift"
}
],
- "time": "2023-07-04T13:32:44+00:00"
+ "time": "2023-09-04T12:20:53+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "9.2.26",
+ "version": "9.2.27",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
+ "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
- "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1",
+ "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-03-06T12:58:08+00:00"
+ "time": "2023-07-26T13:44:30+00:00"
},
{
"name": "phpunit/php-file-iterator",
},
{
"name": "phpunit/phpunit",
- "version": "9.6.9",
+ "version": "9.6.11",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "a9aceaf20a682aeacf28d582654a1670d8826778"
+ "reference": "810500e92855eba8a7a5319ae913be2da6f957b0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a9aceaf20a682aeacf28d582654a1670d8826778",
- "reference": "a9aceaf20a682aeacf28d582654a1670d8826778",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/810500e92855eba8a7a5319ae913be2da6f957b0",
+ "reference": "810500e92855eba8a7a5319ae913be2da6f957b0",
"shasum": ""
},
"require": {
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.9"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.11"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-06-11T06:13:56+00:00"
+ "time": "2023-08-19T07:10:56+00:00"
},
{
"name": "sebastian/cli-parser",
},
{
"name": "sebastian/global-state",
- "version": "5.0.5",
+ "version": "5.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
+ "reference": "bde739e7565280bda77be70044ac1047bc007e34"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
- "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
+ "reference": "bde739e7565280bda77be70044ac1047bc007e34",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
},
"funding": [
{
"type": "github"
}
],
- "time": "2022-02-14T08:28:10+00:00"
+ "time": "2023-08-02T09:26:13+00:00"
},
{
"name": "sebastian/lines-of-code",
--- /dev/null
+<?php
+
+use Carbon\Carbon;
+use Illuminate\Database\Migrations\Migration;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ // Create new receive-notifications permission and assign to admin role
+ $permissionId = DB::table('role_permissions')->insertGetId([
+ 'name' => 'receive-notifications',
+ 'display_name' => 'Receive & Manage Notifications',
+ 'created_at' => Carbon::now()->toDateTimeString(),
+ 'updated_at' => Carbon::now()->toDateTimeString(),
+ ]);
+
+ $adminRoleId = DB::table('roles')->where('system_name', '=', 'admin')->first()->id;
+ DB::table('permission_role')->insert([
+ 'role_id' => $adminRoleId,
+ 'permission_id' => $permissionId,
+ ]);
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ $permission = DB::table('role_permissions')
+ ->where('name', '=', 'receive-notifications')
+ ->first();
+
+ if ($permission) {
+ DB::table('permission_role')->where([
+ 'permission_id' => $permission->id,
+ ])->delete();
+ }
+
+ DB::table('role_permissions')
+ ->where('name', '=', 'receive-notifications')
+ ->delete();
+ }
+};
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create('watches', function (Blueprint $table) {
+ $table->increments('id');
+ $table->integer('user_id')->index();
+ $table->integer('watchable_id');
+ $table->string('watchable_type', 100);
+ $table->tinyInteger('level', false, true)->index();
+ $table->timestamps();
+
+ $table->index(['watchable_id', 'watchable_type'], 'watchable_index');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('watches');
+ }
+};
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('cache', function (Blueprint $table) {
+ $table->mediumText('value')->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('cache', function (Blueprint $table) {
+ $table->text('value')->change();
+ });
+ }
+};
// Create an editor user
$editorUser = User::factory()->create();
$editorRole = Role::getRole('editor');
+ $additionalEditorPerms = ['receive-notifications', 'comment-create-all'];
+ $editorRole->permissions()->syncWithoutDetaching(RolePermission::whereIn('name', $additionalEditorPerms)->pluck('id'));
$editorUser->attachRole($editorRole);
// Create a viewer user
"book_id": 1,
"name": "My fantastic new chapter",
"description": "This is a great new chapter that I've created via the API",
+ "priority": 15,
"tags": [
{"name": "Category", "value": "Top Content"},
{"name": "Rating", "value": "Highest"}
]
-}
\ No newline at end of file
+}
"book_id": 1,
"name": "My fantastic updated chapter",
"description": "This is an updated chapter that I've altered via the API",
+ "priority": 16,
"tags": [
{"name": "Category", "value": "Kinda Good Content"},
{"name": "Rating", "value": "Medium"}
]
-}
\ No newline at end of file
+}
"book_id": 1,
"name": "My API Page",
"html": "<p>my new API page</p>",
+ "priority": 15,
"tags": [
{"name": "Category", "value": "Not Bad Content"},
{"name": "Rating", "value": "Average"}
]
-}
\ No newline at end of file
+}
"chapter_id": 1,
"name": "My updated API Page",
"html": "<p>my new API page - Updated</p>",
+ "priority": 16,
"tags": [
{"name": "Category", "value": "API Examples"},
{"name": "Rating", "value": "Alright"}
]
-}
\ No newline at end of file
+}
"slug": "my-fantastic-new-chapter",
"name": "My fantastic new chapter",
"description": "This is a great new chapter that I've created via the API",
- "priority": 6,
+ "priority": 15,
"created_by": 1,
"updated_by": 1,
"owned_by": 1,
"slug": "my-fantastic-updated-chapter",
"name": "My fantastic updated chapter",
"description": "This is an updated chapter that I've altered via the API",
- "priority": 7,
+ "priority": 16,
"created_at": "2020-05-22T23:03:35.000000Z",
"updated_at": "2020-05-22T23:07:20.000000Z",
"created_by": 1,
"slug": "my-api-page",
"html": "<p id=\"bkmrk-my-new-api-page\">my new API page</p>",
"raw_html": "<p id=\"bkmrk-my-new-api-page\">my new API page</p>",
- "priority": 14,
+ "priority": 15,
"created_at": "2020-11-28T15:01:39.000000Z",
"updated_at": "2020-11-28T15:01:39.000000Z",
"created_by": {
All development on BookStack is currently done on the `development` branch.
When it's time for a release the `development` branch is merged into release with built & minified CSS & JS then tagged at its version. Here are the current development requirements:
-* [Node.js](https://nodejs.org/en/) v16.0+
+* [Node.js](https://nodejs.org/en/) v18.0+
## Building CSS & JavaScript Assets
npm run dev
```
-BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the database name, username and password all defined as `bookstack-test`. You will have to create that database and that set of credentials before testing.
+Further details about the BookStack JavaScript codebase can be found in the [javascript-code.md document](javascript-code.md).
-The testing database will also need migrating and seeding beforehand. This can be done by running `composer refresh-test-database`.
+## Automated App Testing
-Once done you can run `composer test` in the application root directory to run all tests. Tests can be ran in parallel by running them via `composer t`. This will use Laravel's built-in parallel testing functionality, and attempt to create and seed a database instance for each testing thread. If required these parallel testing instances can be reset, before testing again, by running `composer t-reset`.
+BookStack has a large suite of PHP tests to cover application functionality. We try to ensure that all additions and changes to the platform are covered with testing.
-If the codebase needs to be tested with deprecations, this can be done via uncommenting the relevant line within the TestCase@setUp function.
+For details about setting-up, running and writing tests please see the [php-testing.md document](php-testing.md).
## Code Standards
A component has the below shown properties & methods available for use. As mentioned above, most of these should be used within the `setup()` function to make the requirements/dependencies of the component clear.
```javascript
-// The root element that the compontent has been applied to.
+// The root element that the component has been applied to.
this.$el
-// A map of defined element references within the compontent.
+// A map of defined element references within the component.
// See "Element References" above.
this.$refs
-// A map of defined multi-element references within the compontent.
+// A map of defined multi-element references within the component.
// See "Element References" above.
this.$manyRefs
-// Options defined for the compontent.
+// Options defined for the component.
this.$opts
// The registered name of the component, usually kebab-case.
There are a range of available events that are emitted as part of a public & supported API for accessing or extending JavaScript libraries & components used in the system.
-Details on these events can be found in the [JavaScript Public Events file](javascript-public-events.md).
\ No newline at end of file
+Details on these events can be found in the [JavaScript Public Events file](javascript-public-events.md).
#### Event Data
-- `editorViewConfig` - An [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) object that will eventially be passed when creating the CodeMirror EditorView instance.
+- `editorViewConfig` - An [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) object that will eventually be passed when creating the CodeMirror EditorView instance.
##### Example
detail.registerHighlightStyle(highlightStyleBuilder);
});
```
-</details>
\ No newline at end of file
+</details>
--- /dev/null
+# BookStack PHP Testing
+
+BookStack has many test cases defined within the `tests/` directory of the app. These are built upon [PHPUnit](https://phpunit.de/) along with Laravel's own test framework additions, and a bunch of custom helper classes.
+
+## Setup
+
+The application tests are mostly functional, rather than unit tests, meaning they simulate user actions and system components and therefore these require use of the database. To avoid potential conflicts within your development environment, the tests use a separate database. This is defined via a specific `mysql_testing` database connection in our configuration, and expects to use the following database access details:
+
+- Host: `127.0.0.1`
+- Username: `bookstack-test`
+- Password: `bookstack-test`
+- Database: `bookstack-test`
+
+You will need to create a database, with access for these credentials, to allow the system to connect when running tests. Alternatively, if those don't suit, you can define a `TEST_DATABASE_URL` option in your `.env` file, or environment, with connection details like so:
+
+```bash
+TEST_DATABASE_URL="mysql://username:password@host-name:port/database-name"
+```
+
+The testing database will need migrating and seeding with test data beforehand. This can be done by running `composer refresh-test-database`.
+
+## Running Tests
+
+You can run all tests via composer with `composer test` in the application root directory.
+Alternatively, you can run PHPUnit directly with `php vendor/bin/phpunit`.
+
+Some editors, like PHPStorm, have in-built support for running tests on a per file, directory or class basis.
+Otherwise, you can run PHPUnit with specified tests and/or filter to limit the tests ran:
+
+```bash
+# Run all test in the "./tests/HomepageTest.php" file
+php vendor/bin/phpunit ./tests/HomepageTest.php
+
+# Run all test in the "./tests/User" directory
+php vendor/bin/phpunit ./tests/User
+
+# Filter to a particular test method name
+php vendor/bin/phpunit --filter test_default_homepage_visible
+
+# Filter to a particular test class name
+php vendor/bin/phpunit --filter HomepageTest
+```
+
+If the codebase needs to be tested with deprecations, this can be done via uncommenting the relevant line within the `TestCase@setUp` function. This is not expected for most PRs to the project, but instead used for maintenance tasks like dependency & PHP upgrades.
+
+## Writing Tests
+
+To understand how tests are written & used, it's advised you read through existing test cases similar to what you need to write. Tests are written in a rather scrappy manner, compared to the core app codebase, which is fine and expected since there's often hoops to jump through for various functionality. Scrappy tests are better than no tests.
+
+Test classes have to be within the `tests/` folder, and be named ending in `Test`. These should always extend the `Tests\TestCase` class.
+Test methods should be written in snake_case, start with `test_`, and be public methods.
+
+Here are some general rules & patterns we follow in the tests:
+
+- All external remote system resources, like HTTP calls and LDAP connections, are mocked.
+- We prefer to hard-code expected text & URLs to better detect potential changes in the system rather than use dynamic references. This provides higher sensitivity to changes, and has never been much of a maintenance issue.
+- Only test with an admin user if needed, otherwise keep to less privileged users to ensure permission systems are active and exercised within tests.
+- If testing for the lack of something (e.g. `$this->assertDontSee('TextAfterChange')`) then this should be accompanied by some form of positive confirmation (e.g. `$this->assertSee('TextBeforeChange')`).
+
+### Test Helpers
+
+Our default `TestCase` is bloated with helpers to assist in testing scenarios. Some of these shown below, but you should jump through and explore these in your IDE/editor to explore their full capabilities and options:
+
+```php
+// Run the test as a logged-in-user at a certain privilege level
+$this->asAdmin();
+$this->asEditor();
+$this->asViewer();
+
+// Provides a bunch of entity (shelf/book/chapter/page) content and actions
+$this->entities;
+
+// Provides various user & role abilities
+$this->users;
+
+// Provides many helpful actions relate to system & content permissions
+$this->permissions;
+
+// Provides a range of methods for dealing with files & uploads in tests
+$this->files;
+
+// Parse HTML of a response to assert HTML-based conditions
+// Uses https://github.com/ssddanbrown/asserthtml library.
+$this->withHtml($resp);
+// Example:
+$this->withHtml($this->get('/'))->assertElementContains('p[id="top"]', 'Hello!');
+```
\ No newline at end of file
'favourite_add_notification' => 'تم إضافة ":name" إلى المفضلة لديك',
'favourite_remove_notification' => 'تم إزالة ":name" من المفضلة لديك',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'تم التعليق',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'تحديث الأذونات',
];
'remove' => 'إزالة',
'add' => 'إضافة',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'شاشة كاملة',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'إدخال رسمة',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'صفحة ليست في فصل',
'pages_move' => 'نقل الصفحة',
'pages_copy' => 'نسخ الصفحة',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'حدث خطأ عند إرسال بريد إلكتروني تجريبي:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'إدارة إعدادات التطبيق',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'أذونات الأصول',
'roles_system_warning' => 'اعلم أن الوصول إلى أي من الأذونات الثلاثة المذكورة أعلاه يمكن أن يسمح للمستخدم بتغيير امتيازاته الخاصة أو امتيازات الآخرين في النظام. قم بتعيين الأدوار مع هذه الأذونات فقط للمستخدمين الموثوق بهم.',
'role_asset_desc' => 'تتحكم هذه الأذونات في الوصول الافتراضي إلى الأصول داخل النظام. ستتجاوز الأذونات الخاصة بالكتب والفصول والصفحات هذه الأذونات.',
'favourite_add_notification' => '":name" е добавен към любими успешно',
'favourite_remove_notification' => '":name" е премахнат от любими успешно',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'коментирано на',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'обновени права',
];
'remove' => 'Премахване',
'add' => 'Добавяне',
'configure' => 'Конфигуриране',
+ 'manage' => 'Manage',
'fullscreen' => 'Цял екран',
'favourite' => 'Любимо',
'unfavourite' => 'Не е любимо',
'pages_md_insert_drawing' => 'Вмъкни рисунка',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Страницата не принадлежи в никоя глава',
'pages_move' => 'Премести страницата',
'pages_copy' => 'Копиране на страницата',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Беше върната грешка, когато се изпрати тестовият емейл:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Преки пътища',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Запазване на преките пътища',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Обновени предпочитания за преки пътища!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Управление на настройките на приложението',
'role_export_content' => 'Експортирай съдържанието',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Настройки за достъп до активи',
'roles_system_warning' => 'Важно: Добавянето на потребител в някое от горните три роли може да му позволи да промени собствените си права или правата на другите в системата. Възлагайте тези роли само на доверени потребители.',
'role_asset_desc' => 'Тези настройки за достъп контролират достъпа по подразбиране до активите в системата. Настройките за достъп до книги, глави и страници ще отменят тези настройки.',
'favourite_add_notification' => '":name" je dodan u tvoje favorite',
'favourite_remove_notification' => '":name" je uklonjen iz tvojih favorita',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'je komentarisao/la na',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'je ažurirao/la dozvole',
];
'remove' => 'Ukloni',
'add' => 'Dodaj',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Prikaz preko čitavog ekrana',
'favourite' => 'Favorit',
'unfavourite' => 'Ukloni favorit',
'pages_md_insert_drawing' => 'Insert Drawing',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Došlo je do greške prilikom slanja testnog e-maila:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Manage app settings',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Asset Permissions',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'ha comentat a',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'ha actualitzat els permisos',
];
'remove' => 'Elimina',
'add' => 'Afegeix',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Pantalla completa',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Insereix un diagrama',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'La pàgina no pertany a cap capítol',
'pages_move' => 'Mou la pàgina',
'pages_copy' => 'Copia la pàgina',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'S\'ha produït un error en enviar un correu electrònic de prova:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Gestiona la configuració de l\'aplicació',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Permisos de recursos',
'roles_system_warning' => 'Tingueu en compte que l\'accés a qualsevol dels tres permisos de dalt pot permetre que un usuari alteri els seus propis permisos o els privilegis d\'altres usuaris del sistema. Assigneu rols amb aquests permisos només a usuaris de confiança.',
'role_asset_desc' => 'Aquests permisos controlen l\'accés per defecte als recursos del sistema. Els permisos de llibres, capítols i pàgines tindran més importància que aquests permisos.',
'page_restore' => 'obnovil/a stránku',
'page_restore_notification' => 'Stránka byla úspěšně obnovena',
'page_move' => 'přesunul/a stránku',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'Strana byla úspěšně přesunuta',
// Chapters
'chapter_create' => 'vytvořil/a kapitolu',
'chapter_delete' => 'odstranila/a kapitolu',
'chapter_delete_notification' => 'Kapitola byla úspěšně odstraněna',
'chapter_move' => 'přesunul/a kapitolu',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'Kapitola byla úspěšně přesunuta',
// Books
'book_create' => 'vytvořil/a knihu',
'bookshelf_delete_notification' => 'Knihovna byla úspěšně smazána',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => 'obnovil revizi',
+ 'revision_delete' => 'odstranil revizi',
+ 'revision_delete_notification' => 'Revize byla úspěšně odstraněna',
// Favourites
'favourite_add_notification' => '":name" byla přidána do Vašich oblíbených',
'favourite_remove_notification' => '":name" byla odstraněna z Vašich oblíbených',
+ // Watching
+ 'watch_update_level_notification' => 'Předvolby sledování úspěšně aktualizovány',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => 'se přihlásil',
+ 'auth_register' => 'se zaregistroval jako nový uživatel',
+ 'auth_password_reset_request' => 'zažádal o resetování hesla',
+ 'auth_password_reset_update' => 'zresetoval uživatelské heslo',
+ 'mfa_setup_method' => 'nastavil MFA metodu',
'mfa_setup_method_notification' => 'Vícefaktorová metoda byla úspěšně nakonfigurována',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => 'odstranil MFA metodu',
'mfa_remove_method_notification' => 'Vícefaktorová metoda byla úspěšně odstraněna',
// Settings
- 'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'settings_update' => 'aktualizoval nastavení',
+ 'settings_update_notification' => 'Nastavení bylo úspěšně aktualizováno',
+ 'maintenance_action_run' => 'spustil údržbu',
// Webhooks
'webhook_create' => 'vytvořil/a webhook',
'webhook_delete_notification' => 'Webhook byl úspěšně odstraněn',
// Users
- 'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
- 'user_update' => 'updated user',
+ 'user_create' => 'vytvořil uživatele',
+ 'user_create_notification' => 'Uživatel byl úspěšně vytvořen',
+ 'user_update' => 'aktualizoval uživatele',
'user_update_notification' => 'Uživatel byl úspěšně aktualizován',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'odstranil uživatele',
'user_delete_notification' => 'Uživatel byl úspěšně odstraněn',
// API Tokens
- 'api_token_create' => 'created api token',
- 'api_token_create_notification' => 'API token successfully created',
- 'api_token_update' => 'updated api token',
- 'api_token_update_notification' => 'API token successfully updated',
- 'api_token_delete' => 'deleted api token',
- 'api_token_delete_notification' => 'API token successfully deleted',
+ 'api_token_create' => 'vytvořil api token',
+ 'api_token_create_notification' => 'API token úspěšně vytvořen',
+ 'api_token_update' => 'aktualizoval api token',
+ 'api_token_update_notification' => 'API token úspěšně aktualizován',
+ 'api_token_delete' => 'odstranil api token',
+ 'api_token_delete_notification' => 'API token úspěšně odstraněn',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => 'vytvořil roli',
'role_create_notification' => 'Role byla úspěšně vytvořena',
- 'role_update' => 'updated role',
+ 'role_update' => 'aktualizoval roli',
'role_update_notification' => 'Role byla úspěšně aktualizována',
- 'role_delete' => 'deleted role',
+ 'role_delete' => 'odstranil roli',
'role_delete_notification' => 'Role byla odstraněna',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => 'vyprázdnil koš',
+ 'recycle_bin_restore' => 'obnovil z koše',
+ 'recycle_bin_destroy' => 'odstranil z koše',
- // Other
+ // Comments
'commented_on' => 'okomentoval/a',
+ 'comment_create' => 'přidal komentář',
+ 'comment_update' => 'aktualizoval komentář',
+ 'comment_delete' => 'odstranil komentář',
+
+ // Other
'permissions_update' => 'oprávnění upravena',
];
// Buttons
'cancel' => 'Zrušit',
- 'close' => 'Close',
+ 'close' => 'Zavřít',
'confirm' => 'Potvrdit',
'back' => 'Zpět',
'save' => 'Uložit',
'remove' => 'Odebrat',
'add' => 'Přidat',
'configure' => 'Nastavit',
+ 'manage' => 'Spravovat',
'fullscreen' => 'Celá obrazovka',
'favourite' => 'Přidat do oblíbených',
'unfavourite' => 'Odebrat z oblíbených',
'pages_md_insert_drawing' => 'Vložit kresbu',
'pages_md_show_preview' => 'Zobrazit náhled',
'pages_md_sync_scroll' => 'Synchronizovat náhled',
+ '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' => 'Stránka není v kapitole',
'pages_move' => 'Přesunout stránku',
'pages_copy' => 'Kopírovat stránku',
'references' => 'Odkazy',
'references_none' => 'Nebyly nalezeny žádné odkazy na tuto položku.',
'references_to_desc' => 'Níže jsou uvedeny všechny známé stránky systému, které odkazují na tuto položku.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Drawing & Images
'image_upload_error' => 'Nastala chyba během nahrávání souboru',
'image_upload_type_error' => 'Typ nahrávaného obrázku je neplatný.',
- 'image_upload_replace_type' => 'Image file replacements must be of the same type',
+ 'image_upload_replace_type' => 'Náhrady souboru obrázku musí být stejného typu',
'drawing_data_not_found' => 'Data výkresu nelze načíst. Výkresový soubor již nemusí existovat nebo nemusí mít oprávnění k němu přistupovat.',
// Attachments
'attachment_not_found' => 'Příloha nenalezena',
- 'attachment_upload_error' => 'An error occurred uploading the attachment file',
+ 'attachment_upload_error' => 'Nastala chyba během nahrávání přiloženého souboru',
// Pages
'page_draft_autosave_fail' => 'Nepovedlo se uložit koncept. Než stránku uložíte, ujistěte se, že jste připojeni k internetu.',
- 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content',
+ 'page_draft_delete_fail' => 'Nepodařilo se odstranit koncept stránky a načíst její aktuální obsah',
'page_custom_home_deletion' => 'Nelze odstranit tuto stránku, protože je nastavena jako uvítací stránka',
// Entities
// Settings & Maintenance
'maintenance_test_email_failure' => 'Při posílání testovacího e-mailu nastala chyba:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URL adresa neodpovídá povoleným SSR poskytovatelům',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Předvolby',
+
'shortcuts' => 'Zkratky',
'shortcuts_interface' => 'Zobrazit klávesové zkratky',
'shortcuts_toggle_desc' => 'Zde můžete povolit nebo zakázat klávesové zkratky systémového rozhraní používané pro navigaci a akce.',
'shortcuts_save' => 'Uložit zkratky',
'shortcuts_overlay_desc' => 'Poznámka: Když jsou povoleny zkratky, je k dispozici pomocný překryv stisknutím "? která zvýrazní dostupné zkratky pro akce viditelné na obrazovce.',
'shortcuts_update_success' => 'Nastavení pro zkratky bylo aktualizováno!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Správa nastavení aplikace',
'role_export_content' => 'Exportovat obsah',
'role_editor_change' => 'Změnit editor stránek',
+ 'role_notifications' => 'Přijímat a spravovat oznámení',
'role_asset' => 'Obsahová oprávnění',
'roles_system_warning' => 'Berte na vědomí, že přístup k některému ze tří výše uvedených oprávnění může uživateli umožnit změnit svá vlastní oprávnění nebo oprávnění ostatních uživatelů v systému. Přiřazujte role s těmito oprávněními pouze důvěryhodným uživatelům.',
'role_asset_desc' => 'Tato oprávnění řídí přístup k obsahu napříč systémem. Specifická oprávnění na knihách, kapitolách a stránkách převáží tato nastavení.',
'favourite_add_notification' => 'Mae ":name" wedi\'i ychwanegu at eich ffefrynnau',
'favourite_remove_notification' => 'Mae ":name" wedi\'i tynnu o\'ch ffefrynnau',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'gwnaeth sylwadau ar',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'caniatadau wedi\'u diweddaru',
];
'remove' => 'Remove',
'add' => 'Add',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Fullscreen',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Insert Drawing',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Gwall a daflwyd wrth anfon e-bost prawf:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Manage app settings',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Asset Permissions',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'favourite_add_notification' => '":name" er blevet tilføjet til dine favoritter',
'favourite_remove_notification' => '":name" er blevet fjernet fra dine favoritter',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'kommenterede til',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'Tilladelser opdateret',
];
'remove' => 'Fjern',
'add' => 'Tilføj',
'configure' => 'Konfigurer',
+ 'manage' => 'Manage',
'fullscreen' => 'Fuld skærm',
'favourite' => 'Foretrukken',
'unfavourite' => 'Fjern som foretrukken',
'pages_md_insert_drawing' => 'Indsæt tegning',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Side er ikke i et kapitel',
'pages_move' => 'Flyt side',
'pages_copy' => 'Kopier side',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Følgende fejl opstod under afsendelse af testemail:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Administrer app-indstillinger',
'role_export_content' => 'Eksporter indhold',
'role_editor_change' => 'Skift side editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Tilladelser for medier og "assets"',
'roles_system_warning' => 'Vær opmærksom på, at adgang til alle af de ovennævnte tre tilladelser, kan give en bruger mulighed for at ændre deres egne brugerrettigheder eller brugerrettigheder for andre i systemet. Tildel kun roller med disse tilladelser til betroede brugere.',
'role_asset_desc' => 'Disse tilladelser kontrollerer standardadgang til medier og "assets" i systemet. Tilladelser til bøger, kapitler og sider tilsidesætter disse tilladelser.',
'favourite_add_notification' => '":name" wurde zu Ihren Favoriten hinzugefügt',
'favourite_remove_notification' => '":name" wurde aus Ihren Favoriten entfernt',
+ // Watching
+ 'watch_update_level_notification' => 'Beobachtungseinstellungen erfolgreich aktualisiert',
+
// Auth
'auth_login' => 'hat sich eingeloggt',
'auth_register' => 'hat sich als neuer Benutzer registriert',
'recycle_bin_restore' => 'aus dem Papierkorb wiederhergestellt',
'recycle_bin_destroy' => 'aus dem Papierkorb gelöscht',
- // Other
+ // Comments
'commented_on' => 'hat einen Kommentar hinzugefügt',
+ 'comment_create' => 'Kommentar hinzugefügt',
+ 'comment_update' => 'Kommentar aktualisiert',
+ 'comment_delete' => 'Kommentar gelöscht',
+
+ // Other
'permissions_update' => 'hat die Berechtigungen aktualisiert',
];
'remove' => 'Entfernen',
'add' => 'Hinzufügen',
'configure' => 'Konfigurieren',
+ 'manage' => 'Verwalten',
'fullscreen' => 'Vollbild',
'favourite' => 'Favoriten',
'unfavourite' => 'Kein Favorit',
'tab_content_label' => 'Tab: Hauptinhalt anzeigen',
// Email Content
- 'email_action_help' => 'Sollte es beim Anklicken der Schaltfläche ":action_text" Probleme geben, öffnen Sie folgende URL in Ihrem Browser:',
+ 'email_action_help' => 'Sollte es beim Anklicken der Schaltfläche ":actionText" Probleme geben, öffnen Sie folgende URL in Ihrem Browser:',
'email_rights' => 'Alle Rechte vorbehalten',
// Footer Link Options
'shelves_permissions_updated' => 'Regalberechtigungen aktualisiert',
'shelves_permissions_active' => 'Regalberechtigungen aktiv',
'shelves_permissions_cascade_warning' => 'Berechtigungen für Regale werden nicht automatisch auf die enthaltenen Bücher übertragen. Das liegt daran, dass ein Buch in mehreren Regalen vorhanden sein kann. Berechtigungen können jedoch auf untergeordnete Bücher kopiert werden, indem Sie die unten stehende Option verwenden.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Regalerstellungsberechtigungen werden nur zum Kopieren von Berechtigungen für untergeordnete Bücher mit der folgenden Aktion verwendet. Sie kontrollieren nicht die Fähigkeit, Bücher zu erstellen.',
'shelves_copy_permissions_to_books' => 'Kopiere die Berechtigungen zum Buch',
'shelves_copy_permissions' => 'Berechtigungen kopieren',
'shelves_copy_permissions_explain' => 'Dadurch werden die aktuellen Berechtigungseinstellungen dieses Regals auf alle darin enthaltenen Bücher angewendet. Vergewissern Sie sich vor der Aktivierung, dass alle Änderungen an den Berechtigungen für dieses Regal gespeichert wurden.',
'pages_md_insert_drawing' => 'Zeichnung einfügen',
'pages_md_show_preview' => 'Vorschau anzeigen',
'pages_md_sync_scroll' => 'Vorschau synchronisieren',
+ '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',
'pages_move' => 'Seite verschieben',
'pages_copy' => 'Seite kopieren',
'references' => 'Verweise',
'references_none' => 'Es gibt keine nachverfolgten Referenzen zu diesem Element.',
'references_to_desc' => 'Nachfolgend sind alle bekannten Seiten im System aufgeführt, die auf diesen Artikel verweisen.',
+
+ // Watch Options
+ 'watch' => 'Beobachten',
+ 'watch_title_default' => 'Standardeinstellungen',
+ 'watch_desc_default' => 'Rückgängig machen auf Standard-Benachrichtigungseinstellungen.',
+ 'watch_title_ignore' => 'Ignorieren',
+ 'watch_desc_ignore' => 'Ignorieren aller Benachrichtigungen, auch die von den Einstellungen auf Benutzerebene.',
+ 'watch_title_new' => 'Neue Seiten',
+ 'watch_desc_new' => 'Benachrichtigung, wenn eine neue Seite in diesem Element erstellt wird.',
+ 'watch_title_updates' => 'Alle Seitenupdates',
+ 'watch_desc_updates' => 'Bei allen neuen Seiten und Seitenänderungen benachrichtigen.',
+ 'watch_desc_updates_page' => 'Bei allen Seitenänderungen benachrichtigen.',
+ 'watch_title_comments' => 'Alle Seitenupdates & Kommentare',
+ 'watch_desc_comments' => 'Benachrichtigung bei allen neuen Seiten, Seitenänderungen und neuen Kommentaren.',
+ 'watch_desc_comments_page' => 'Benachrichtigung bei Seitenänderungen und neuen Kommentaren.',
+ 'watch_change_default' => 'Standard-Benachrichtigungseinstellungen ändern',
+ 'watch_detail_ignore' => 'Benachrichtigungen ignorieren',
+ 'watch_detail_new' => 'Beobachten von neuen Seiten',
+ 'watch_detail_updates' => 'Beobachtung neuer Seiten und Aktualisierungen',
+ 'watch_detail_comments' => 'Beobachtung neuer Seiten, Aktualisierungen und Kommentare',
+ 'watch_detail_parent_book' => 'Beobachten über übergeordnetes Buch',
+ 'watch_detail_parent_book_ignore' => 'Ignorieren über übergeordnetes Buch',
+ 'watch_detail_parent_chapter' => 'Beobachten über übergeordnetes Kapitel',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorieren über übergeordnetes Kapitel',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Fehler beim Versenden einer Test E-Mail:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'Die URL stimmt nicht mit den konfigurierten erlaubten SSR-Hosts überein',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Neuer Kommentar auf Seite: :pageName',
+ 'new_comment_intro' => 'Ein Benutzer hat eine Seite in :appName kommentiert:',
+ 'new_page_subject' => 'Neue Seite: :pageName',
+ 'new_page_intro' => 'Es wurde eine neue Seite in :appName erstellt:',
+ 'updated_page_subject' => 'Aktualisierte Seite: :pageName',
+ 'updated_page_intro' => 'Eine Seite wurde in :appName aktualisiert:',
+ 'updated_page_debounce' => 'Um eine Flut von Benachrichtigungen zu vermeiden, werden Sie für eine gewisse Zeit keine Benachrichtigungen für weitere Bearbeitungen dieser Seite durch denselben Bearbeiter erhalten.',
+
+ 'detail_page_name' => 'Name der Seite:',
+ 'detail_commenter' => 'Kommentator:',
+ 'detail_comment' => 'Kommentar:',
+ 'detail_created_by' => 'Erstellt von:',
+ 'detail_updated_by' => 'Aktualisiert von:',
+
+ 'action_view_comment' => 'Kommentar anzeigen',
+ 'action_view_page' => 'Seite anzeigen',
+
+ 'footer_reason' => 'Diese Benachrichtigung wurde an Sie gesendet, weil :link diese Art von Aktivität für dieses Element abdeckt.',
+ 'footer_reason_link' => 'ihre Benachrichtigungseinstellungen',
+];
*/
return [
+ 'preferences' => 'Profil-Einstellungen',
+
'shortcuts' => 'Tastenkürzel',
'shortcuts_interface' => 'Oberflächen-Tastaturkürzel',
'shortcuts_toggle_desc' => 'Hier können Sie Tastaturkürzel für die Systemoberfläche für Navigation und Aktionen aktivieren oder deaktivieren.',
'shortcuts_save' => 'Tastenkürzel speichern',
'shortcuts_overlay_desc' => 'Hinweis: Wenn Tastenkürzel aktiviert sind, ist ein Hilfsoverlay durch Drücken von "?" verfügbar, welches die verfügbaren Tastenkürzel für Aktionen hervorhebt, die aktuell auf dem Bildschirm sichtbar sind.',
'shortcuts_update_success' => 'Tastenkürzel Einstellungen wurden aktualisiert!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Verwalten von Tastenkombinationen, die zur Navigation der Benutzeroberfläche verwendet werden können.',
+
+ 'notifications' => 'Benachrichtigungseinstellungen',
+ 'notifications_desc' => 'Legen Sie fest, welche E-Mail-Benachrichtigungen Sie erhalten, wenn bestimmte Aktivitäten im System durchgeführt werden.',
+ 'notifications_opt_own_page_changes' => 'Benachrichtigung bei Änderungen an eigenen Seiten',
+ 'notifications_opt_own_page_comments' => 'Benachrichtigung bei Kommentaren an eigenen Seiten',
+ 'notifications_opt_comment_replies' => 'Bei Antworten auf meine Kommentare benachrichtigen',
+ 'notifications_save' => 'Einstellungen speichern',
+ 'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!',
+ 'notifications_watched' => 'Beobachtete und ignorierte Elemente',
+ 'notifications_watched_desc' => ' Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um Ihre Einstellungen für diese Elemente zu aktualisieren, sehen Sie sich das Element an und suchen dann die Überwachungsoptionen in der Seitenleiste.',
+
+ 'profile_overview_desc' => ' Verwalten Sie die Details Ihres Benutzerprofils einschließlich bevorzugter Sprache und Authentifizierungsoptionen.',
+];
'role_manage_settings' => 'Globaleinstellungen verwalten',
'role_export_content' => 'Inhalt exportieren',
'role_editor_change' => 'Seiten-Editor ändern',
+ 'role_notifications' => 'Empfangen und Verwalten von Benachrichtigungen',
'role_asset' => 'Berechtigungen',
'roles_system_warning' => 'Beachten Sie, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weisen Sie nur Rollen, mit diesen Berechtigungen, vertrauenswürdigen Benutzern zu.',
'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
// Pages
'page_create' => 'erstellt Seite',
'page_create_notification' => 'Seite erfolgreich erstellt',
- 'page_update' => 'aktualisiert Seite',
+ 'page_update' => 'Seite aktualisiert',
'page_update_notification' => 'Seite erfolgreich aktualisiert',
- 'page_delete' => 'löscht Seite',
+ 'page_delete' => 'Seite gelöscht',
'page_delete_notification' => 'Seite erfolgreich gelöscht',
- 'page_restore' => 'stellt Seite wieder her',
+ 'page_restore' => 'Seite wiederhergestellt',
'page_restore_notification' => 'Seite erfolgreich wiederhergestellt',
- 'page_move' => 'verschiebt Seite',
+ 'page_move' => 'Seite verschoben',
'page_move_notification' => 'Seite erfolgreich verschoben',
// Chapters
- 'chapter_create' => 'erstellt Kapitel',
+ 'chapter_create' => 'Kapitel erstellt',
'chapter_create_notification' => 'Kapitel erfolgreich erstellt',
- 'chapter_update' => 'aktualisiert Kapitel',
+ 'chapter_update' => 'Kapitel aktualisiert',
'chapter_update_notification' => 'Kapitel erfolgreich aktualisiert',
- 'chapter_delete' => 'löscht Kapitel',
+ 'chapter_delete' => 'Kapitel gelöscht',
'chapter_delete_notification' => 'Kapitel erfolgreich gelöscht',
- 'chapter_move' => 'verschiebt Kapitel',
+ 'chapter_move' => 'Kapitel verschoben',
'chapter_move_notification' => 'Kapitel erfolgreich verschoben',
// Books
- 'book_create' => 'erstellt Buch',
+ 'book_create' => 'Buch erstellt',
'book_create_notification' => 'Buch erfolgreich erstellt',
'book_create_from_chapter' => 'Kapitel zu Buch umgewandelt',
'book_create_from_chapter_notification' => 'Kapitel erfolgreich in ein Buch umgewandelt',
- 'book_update' => 'aktualisiert Buch',
+ 'book_update' => 'Buch aktualisiert',
'book_update_notification' => 'Buch erfolgreich aktualisiert',
- 'book_delete' => 'löscht Buch',
+ 'book_delete' => 'Buch gelöscht',
'book_delete_notification' => 'Buch erfolgreich gelöscht',
- 'book_sort' => 'sortiert Buch',
+ 'book_sort' => 'Buch sortiert',
'book_sort_notification' => 'Buch erfolgreich umsortiert',
// Bookshelves
'bookshelf_delete_notification' => 'Regal erfolgreich gelöscht',
// Revisions
- 'revision_restore' => 'stellte Revision wieder her:',
- 'revision_delete' => 'löschte Revision',
+ 'revision_restore' => 'Revision wiederhergestellt',
+ 'revision_delete' => 'Revision gelöscht',
'revision_delete_notification' => 'Revision erfolgreich gelöscht',
// Favourites
'favourite_add_notification' => '":name" wurde zu deinen Favoriten hinzugefügt',
'favourite_remove_notification' => '":name" wurde aus deinen Favoriten entfernt',
+ // Watching
+ 'watch_update_level_notification' => 'Beobachtungseinstellungen erfolgreich aktualisiert',
+
// Auth
'auth_login' => 'hat sich eingeloggt',
'auth_register' => 'hat sich als neuer Benutzer registriert',
'auth_password_reset_request' => 'hat eine Rücksetzung des Benutzerpassworts beantragt',
- 'auth_password_reset_update' => 'hat Benutzerpasswort zurückgesetzt',
+ 'auth_password_reset_update' => 'Benutzerpasswort zurückgesetzt',
'mfa_setup_method' => 'hat MFA-Methode konfiguriert',
'mfa_setup_method_notification' => 'Multi-Faktor-Methode erfolgreich konfiguriert',
'mfa_remove_method' => 'hat MFA-Methode entfernt',
'recycle_bin_restore' => 'aus dem Papierkorb wiederhergestellt',
'recycle_bin_destroy' => 'aus dem Papierkorb gelöscht',
- // Other
+ // Comments
'commented_on' => 'kommentiert',
+ 'comment_create' => 'Kommentar hinzugefügt',
+ 'comment_update' => 'Kommentar aktualisiert',
+ 'comment_delete' => 'Kommentar gelöscht',
+
+ // Other
'permissions_update' => 'aktualisierte Berechtigungen',
];
'remove' => 'Entfernen',
'add' => 'Hinzufügen',
'configure' => 'Konfigurieren',
+ 'manage' => 'Verwalten',
'fullscreen' => 'Vollbild',
'favourite' => 'Favoriten',
'unfavourite' => 'Kein Favorit',
'shelves_permissions_updated' => 'Regalberechtigungen aktualisiert',
'shelves_permissions_active' => 'Regalberechtigungen aktiv',
'shelves_permissions_cascade_warning' => 'Berechtigungen für Regale werden nicht automatisch auf die enthaltenen Bücher übertragen. Das liegt daran, dass ein Buch in mehreren Regalen vorhanden sein kann. Berechtigungen können jedoch auf untergeordnete Bücher kopiert werden, indem du die unten stehende Option verwendest.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => '"Regal erstellen"-Berechtigungen werden nur zum Kopieren von Berechtigungen für untergeordnete Bücher mit der folgenden Aktion verwendet. Sie kontrollieren nicht die Fähigkeit, Bücher zu erstellen.',
'shelves_copy_permissions_to_books' => 'Kopiere die Berechtigungen zum Buch',
'shelves_copy_permissions' => 'Berechtigungen kopieren',
'shelves_copy_permissions_explain' => 'Dadurch werden die aktuellen Berechtigungseinstellungen dieses Regals auf alle darin enthaltenen Bücher angewendet. Vergewissere dich vor der Aktivierung, dass alle Änderungen an den Berechtigungen für dieses Regal gespeichert wurden.',
'book' => 'Buch',
'books' => 'Bücher',
'x_books' => ':count Buch|:count Bücher',
- 'books_empty' => 'Keine Bücher vorhanden',
+ 'books_empty' => 'Es wurden noch keine Bücher angelegt',
'books_popular' => 'Beliebte Bücher',
'books_recent' => 'Kürzlich angesehene Bücher',
'books_new' => 'Neue Bücher',
'pages_md_insert_drawing' => 'Zeichnung einfügen',
'pages_md_show_preview' => 'Vorschau anzeigen',
'pages_md_sync_scroll' => 'Vorschau synchronisieren',
+ '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',
'pages_move' => 'Seite verschieben',
'pages_copy' => 'Seite kopieren',
'references' => 'Verweise',
'references_none' => 'Es gibt keine nachverfolgten Referenzen zu diesem Element.',
'references_to_desc' => 'Nachfolgend sind alle bekannten Seiten im System aufgeführt, die auf diesen Artikel verweisen.',
+
+ // Watch Options
+ 'watch' => 'Beobachten',
+ 'watch_title_default' => 'Standardeinstellungen',
+ 'watch_desc_default' => 'Rückgängig machen auf Standard-Benachrichtigungseinstellungen.',
+ 'watch_title_ignore' => 'Ignorieren',
+ 'watch_desc_ignore' => 'Ignorieren aller Benachrichtigungen, auch die von den Einstellungen auf Benutzerebene.',
+ 'watch_title_new' => 'Neue Seiten',
+ 'watch_desc_new' => 'Benachrichtigen, wenn eine neue Seite in diesem Element erstellt wird.',
+ 'watch_title_updates' => 'Alle Seitenupdates',
+ 'watch_desc_updates' => 'Bei allen neuen Seiten und Seitenänderungen benachrichtigen.',
+ 'watch_desc_updates_page' => 'Bei allen Seitenänderungen benachrichtigen.',
+ 'watch_title_comments' => 'Alle Seitenupdates & Kommentare',
+ 'watch_desc_comments' => 'Benachrichtigung bei allen neuen Seiten, Seitenänderungen und neuen Kommentaren.',
+ 'watch_desc_comments_page' => 'Benachrichtigung bei Seitenänderungen und neuen Kommentaren.',
+ 'watch_change_default' => 'Standard-Benachrichtigungseinstellungen ändern',
+ 'watch_detail_ignore' => 'Benachrichtigungen ignorieren',
+ 'watch_detail_new' => 'Beobachten von neuen Seiten',
+ 'watch_detail_updates' => 'Beobachtung neuer Seiten und Aktualisierungen',
+ 'watch_detail_comments' => 'Beobachtung neuer Seiten, Aktualisierungen und Kommentare',
+ 'watch_detail_parent_book' => 'Beobachten über übergeordnetes Buch',
+ 'watch_detail_parent_book_ignore' => 'Ignorieren über übergeordnetes Buch',
+ 'watch_detail_parent_chapter' => 'Beobachten über übergeordnetes Kapitel',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorieren über übergeordnetes Kapitel',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Fehler beim Senden einer Test E-Mail:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'Die URL stimmt nicht mit den konfigurierten erlaubten SSR-Hosts überein',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Neuer Kommentar auf Seite: :pageName',
+ 'new_comment_intro' => 'Ein Benutzer hat eine Seite in :appName kommentiert:',
+ 'new_page_subject' => 'Neue Seite: :pageName',
+ 'new_page_intro' => 'Es wurde eine neue Seite in :appName erstellt:',
+ 'updated_page_subject' => 'Aktualisierte Seite: :pageName',
+ 'updated_page_intro' => 'Eine Seite wurde in :appName aktualisiert:',
+ 'updated_page_debounce' => 'Um eine Flut von Benachrichtigungen zu vermeiden, wirst du für eine gewisse Zeit keine Benachrichtigungen für weitere Bearbeitungen dieser Seite durch denselben Bearbeiter erhalten.',
+
+ 'detail_page_name' => 'Seitenname:',
+ 'detail_commenter' => 'Kommentator:',
+ 'detail_comment' => 'Kommentar:',
+ 'detail_created_by' => 'Erstellt von:',
+ 'detail_updated_by' => 'Aktualisiert von:',
+
+ 'action_view_comment' => 'Kommentar anzeigen',
+ 'action_view_page' => 'Seite anzeigen',
+
+ 'footer_reason' => 'Diese Benachrichtigung wurde an dich gesendet, weil :link diese Art von Aktivität für dieses Element abdeckt.',
+ 'footer_reason_link' => 'deine Benachrichtigungseinstellungen',
+];
*/
return [
- 'password' => 'Passwörter müssen aus mindestens sechs Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.',
+ 'password' => 'Passwörter müssen aus mindestens acht Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.',
'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.",
'token' => 'Der Token zum Zurücksetzen des Passworts für diese E-Mail-Adresse ist ungültig.',
'sent' => 'Wir haben dir einen Link zum Zurücksetzen des Passwortes per E-Mail geschickt!',
*/
return [
+ 'preferences' => 'Profil-Einstellungen',
+
'shortcuts' => 'Kürzel',
'shortcuts_interface' => 'Oberflächen-Tastaturkürzel',
'shortcuts_toggle_desc' => 'Hier kannst du Tastaturkürzel für die Systemoberfläche für Navigation und Aktionen aktivieren oder deaktivieren.',
'shortcuts_save' => 'Tastenkürzel speichern',
'shortcuts_overlay_desc' => 'Hinweis: Wenn Tastenkürzel aktiviert sind, ist ein Hilfefähnchen durch Drücken von "?" verfügbar, welches die verfügbaren Tastenkürzel für Aktionen hervorhebt, die aktuell auf dem Bildschirm sichtbar sind.',
'shortcuts_update_success' => 'Tastenkürzel Einstellungen wurden aktualisiert!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Verwalten von Tastenkombinationen, die zur Navigation der Benutzeroberfläche verwendet werden können.',
+
+ 'notifications' => 'Benachrichtigungseinstellungen',
+ 'notifications_desc' => 'Lege fest, welche E-Mail-Benachrichtigungen du erhältst, wenn bestimmte Aktivitäten im System durchgeführt werden.',
+ 'notifications_opt_own_page_changes' => 'Benachrichtigung bei Änderungen an eigenen Seiten',
+ 'notifications_opt_own_page_comments' => 'Benachrichtigung bei Kommentaren an eigenen Seiten',
+ 'notifications_opt_comment_replies' => 'Bei Antworten auf meine Kommentare benachrichtigen',
+ 'notifications_save' => 'Einstellungen speichern',
+ 'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!',
+ 'notifications_watched' => 'Beobachtete und ignorierte Elemente',
+ 'notifications_watched_desc' => ' Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um deine Einstellungen für diese Elemente zu aktualisieren, sieh dir das Element an und suche dann die Überwachungsoptionen in der Seitenleiste.',
+
+ 'profile_overview_desc' => ' Verwalte die Details deines Benutzerprofils, einschließlich bevorzugter Sprache und Authentifizierungsoptionen.',
+];
'role_manage_settings' => 'Globaleinstellungen verwalten',
'role_export_content' => 'Inhalt exportieren',
'role_editor_change' => 'Seiteneditor ändern',
+ 'role_notifications' => 'Empfangen und Verwalten von Benachrichtigungen',
'role_asset' => 'Berechtigungen',
'roles_system_warning' => 'Beachte, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weise nur Rollen mit diesen Berechtigungen vertrauenswürdigen Benutzern zu.',
'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungen.',
'page_restore' => 'αποκατεστημένη σελίδα',
'page_restore_notification' => 'Η σελίδα αποκαταστάθηκε με επιτυχία',
'page_move' => 'Η σελίδα μετακινήθηκε',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'Η σελίδα μετακινήθηκε με επιτυχία',
// Chapters
'chapter_create' => 'δημιουργήθηκε κεφάλαιο',
'chapter_delete' => 'διαγραμμένο κεφάλαιο',
'chapter_delete_notification' => 'Το κεφάλαιο διαγράφηκε επιτυχώς',
'chapter_move' => 'το κεφάλαιο μετακινήθηκε',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'Το κεφάλαιο μετακινήθηκε με επιτυχία',
// Books
'book_create' => 'το βιβλίο δημιουργήθηκε',
'bookshelf_delete_notification' => 'Το ράφι ενημερώθηκε επιτυχώς',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => 'αποκατεστημένη αναθεώρηση',
+ 'revision_delete' => 'διαγραμμένη αναθεώρηση',
+ 'revision_delete_notification' => 'Η αναθεώρηση διαγράφηκε με επιτυχία',
// Favourites
'favourite_add_notification' => '":name" προστέθηκε στα αγαπημένα σας',
'favourite_remove_notification' => '":name" προστέθηκε στα αγαπημένα σας',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => 'συνδεδεμένος',
+ 'auth_register' => 'εγγεγραμμένος ως νέος χρήστης',
+ 'auth_password_reset_request' => 'ζητήθηκε επαναφορά κωδικού πρόσβασης χρήστη',
+ 'auth_password_reset_update' => 'επαναφορά κωδικού πρόσβασης χρήστη',
+ 'mfa_setup_method' => 'διαμορφωμένη μέθοδος MFA',
'mfa_setup_method_notification' => 'Η μέθοδος πολλαπλών παραγόντων διαμορφώθηκε επιτυχώς',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => 'καταργήθηκε η μέθοδος MFA',
'mfa_remove_method_notification' => 'Η μέθοδος πολλαπλών παραγόντων καταργήθηκε με επιτυχία',
// Settings
- 'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'settings_update' => 'ενημερωμένες ρυθμίσεις',
+ 'settings_update_notification' => 'Οι ρυθμίσεις ενημερώθηκαν με επιτυχία',
+ 'maintenance_action_run' => 'έτρεξε δράση συντήρησης',
// Webhooks
'webhook_create' => 'Το webhook δημιουργήθηκε',
'webhook_delete_notification' => 'Το Webhook διαγράφηκε επιτυχώς',
// Users
- 'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
- 'user_update' => 'updated user',
+ 'user_create' => 'δημιουργημένος χρήστης',
+ 'user_create_notification' => 'Ο χρήστης δημιουργήθηκε με επιτυχία',
+ 'user_update' => 'ενημερωμένος χρήστης',
'user_update_notification' => 'Ο Χρήστης ενημερώθηκε με επιτυχία',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'διαγραμμένος χρήστης',
'user_delete_notification' => 'Ο Χρήστης αφαιρέθηκε επιτυχώς',
// API Tokens
- 'api_token_create' => 'created api token',
- 'api_token_create_notification' => 'API token successfully created',
- 'api_token_update' => 'updated api token',
- 'api_token_update_notification' => 'API token successfully updated',
- 'api_token_delete' => 'deleted api token',
- 'api_token_delete_notification' => 'API token successfully deleted',
+ 'api_token_create' => 'δημιουργήθηκε κωδικός API',
+ 'api_token_create_notification' => 'O κωδικός API δημιουργήθηκε με επιτυχία',
+ 'api_token_update' => 'κωδικός API ενημερώθηκε',
+ 'api_token_update_notification' => 'κωδικός API ενημερώθηκε με επιτυχία',
+ 'api_token_delete' => 'διαγραμμένο api token',
+ 'api_token_delete_notification' => 'Το διακριτικό API διαγράφηκε με επιτυχία',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => 'δημιουργημένος ρόλος',
'role_create_notification' => 'Ο Ρόλος δημιουργήθηκε με επιτυχία',
- 'role_update' => 'updated role',
+ 'role_update' => 'Ενημέρωση ρόλου',
'role_update_notification' => 'Ο Ρόλος ενημερώθηκε με επιτυχία',
'role_delete' => 'deleted role',
'role_delete_notification' => 'Ο Ρόλος διαγράφηκε επιτυχώς',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'σχολίασε',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'ενημερωμένα δικαιώματα',
];
'remove' => 'Αφαίρεση',
'add' => 'Προσθήκη',
'configure' => 'Διαμόρφωση',
+ 'manage' => 'Manage',
'fullscreen' => 'Πλήρης οθόνη',
'favourite' => 'Αγαπημένα',
'unfavourite' => 'Αφαίρεση από Αγαπημένα',
'pages_md_insert_drawing' => 'Εισαγωγή Σχεδίου',
'pages_md_show_preview' => 'Εμφάνιση προεπισκόπησης',
'pages_md_sync_scroll' => 'Συγχρονισμός προεπισκόπησης',
+ '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' => 'Η σελίδα δεν είναι σε κεφάλαιο',
'pages_move' => 'Μετακίνηση Σελίδας',
'pages_copy' => 'Αντιγραφή Σελίδας',
'references' => 'Αναφορές',
'references_none' => 'Δεν υπάρχουν αναφορές παρακολούθησης σε αυτό το στοιχείο.',
'references_to_desc' => 'Παρακάτω εμφανίζονται όλες οι γνωστές σελίδες του συστήματος που συνδέονται με αυτό το στοιχείο.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Σφάλμα κατά την αποστολή δοκιμαστικού email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Συντομεύσεις',
'shortcuts_interface' => 'Συντομεύσεις Πληκτρολογίου Διεπαφής',
'shortcuts_toggle_desc' => 'Εδώ μπορείτε να ενεργοποιήσετε ή να απενεργοποιήσετε τις συντομεύσεις του συστήματος πληκτρολογίου, που χρησιμοποιούνται για την πλοήγηση και τις ενέργειες.',
'shortcuts_save' => 'Αποθήκευση Συντομεύσεων',
'shortcuts_overlay_desc' => 'Σημείωση: Όταν οι συντομεύσεις είναι ενεργοποιημένες μια βοηθητική επικάλυψη είναι διαθέσιμη πατώντας "?" που θα τονίσει τις διαθέσιμες συντομεύσεις για ενέργειες που είναι ορατές στην οθόνη.',
'shortcuts_update_success' => 'Οι προτιμήσεις σας αποθηκεύτηκαν!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Διαχειριστείτε τις ρυθμίσεις του ΑΡΙ',
'role_export_content' => 'Εξαγωγή περιεχομένου',
'role_editor_change' => 'Αλλαγή προγράμματος επεξεργασίας σελίδας',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Δικαιώματα Συστήματος',
'roles_system_warning' => 'Λάβετε υπόψη ότι η πρόσβαση σε οποιοδήποτε από τις τρεις παραπάνω άδειες (δικαιώματα) μπορεί να επιτρέψει σε έναν χρήστη να αλλάξει τα δικά του προνόμια ή τα προνόμια άλλων στο σύστημα. Εκχωρήστε ρόλους με αυτά τα δικαιώματα μόνο σε αξιόπιστους χρήστες.',
'role_asset_desc' => 'Αυτά τα δικαιώματα ελέγχουν την προεπιλεγμένη πρόσβαση στα στοιχεία (άδειες) εντός του συστήματος. Τα δικαιώματα σε Βιβλία, Κεφάλαια και Σελίδες θα παρακάμψουν αυτές τις άδειες.',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'remove' => 'Remove',
'add' => 'Add',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Fullscreen',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Insert Drawing',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Manage app settings',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Asset Permissions',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'sv' => 'Svenska',
'tr' => 'Türkçe',
'uk' => 'Українська',
+ 'uz' => 'O‘zbekcha',
'vi' => 'Tiếng Việt',
'zh_CN' => '简体中文',
'zh_TW' => '繁體中文',
'favourite_add_notification' => '".name" ha sido añadido a sus favoritos',
'favourite_remove_notification' => '".name" ha sido eliminado de sus favoritos',
+ // Watching
+ 'watch_update_level_notification' => 'Preferencias de suscripciones actualizadas correctamente',
+
// Auth
'auth_login' => 'conectado',
'auth_register' => 'registrado como nuevo usuario',
'recycle_bin_restore' => 'restaurado de la papelera de reciclaje',
'recycle_bin_destroy' => 'eliminado de la papelera de reciclaje',
- // Other
+ // Comments
'commented_on' => 'comentada el',
+ 'comment_create' => 'comentario añadido',
+ 'comment_update' => 'comentario actualizado',
+ 'comment_delete' => 'comentario borrado',
+
+ // Other
'permissions_update' => 'permisos actualizados',
];
'remove' => 'Remover',
'add' => 'Añadir',
'configure' => 'Configurar',
+ 'manage' => 'Gestionar',
'fullscreen' => 'Pantalla completa',
'favourite' => 'Añadir a favoritos',
'unfavourite' => 'Eliminar de favoritos',
'shelves_permissions_updated' => 'Permisos del estante actualizados',
'shelves_permissions_active' => 'Permisos del estante activos',
'shelves_permissions_cascade_warning' => 'Los permisos en los estantes no se aplican automáticamente a los libros que contengan. Esto se debe a que un libro puede existir en múltiples estantes. Sin embargo, los permisos pueden ser aplicados a los libros del estante utilizando la opción a continuación.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Los permisos de creación de estantes sólo se utilizan para copiar los permisos a los libros contenidos utilizando la acción a continuación. No controlan la capacidad de crear libros.',
'shelves_copy_permissions_to_books' => 'Copiar permisos a los libros',
'shelves_copy_permissions' => 'Copiar permisos',
'shelves_copy_permissions_explain' => 'Esto aplicará los permisos de este estante para todos sus libros. Antes de activarlo, asegúrese de que todos los cambios de permisos para este estante han sido guardados.',
'pages_md_insert_drawing' => 'Insertar Dibujo',
'pages_md_show_preview' => 'Mostrar vista previa',
'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa',
+ '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',
'pages_move' => 'Mover página',
'pages_copy' => 'Copiar página',
'references' => 'Referencias',
'references_none' => 'No hay referencias a este elemento.',
'references_to_desc' => 'A continuación se muestran todas las páginas en el sistema que enlazan a este elemento.',
+
+ // Watch Options
+ 'watch' => 'Suscribirme',
+ 'watch_title_default' => 'Preferencias por defecto',
+ 'watch_desc_default' => 'Revertir suscripciones a tus preferencias de notificación por defecto.',
+ 'watch_title_ignore' => 'Ignorar',
+ 'watch_desc_ignore' => 'Ignorar todas las notificaciones, incluyendo las de las preferencias a nivel de usuario.',
+ 'watch_title_new' => 'Nuevas páginas',
+ 'watch_desc_new' => 'Notificar cuando se crea una nueva página dentro de este elemento.',
+ 'watch_title_updates' => 'Todas las actualizaciones de páginas',
+ 'watch_desc_updates' => 'Notificar todos los cambios de páginas y páginas nuevas.',
+ 'watch_desc_updates_page' => 'Notificar todos los cambios en la página.',
+ 'watch_title_comments' => 'Todas las actualizaciones de páginas y comentarios',
+ 'watch_desc_comments' => 'Notificar sobre todas las páginas nuevas, cambios de página y nuevos comentarios.',
+ 'watch_desc_comments_page' => 'Notificar los cambios en las páginas y los nuevos comentarios.',
+ 'watch_change_default' => 'Cambiar preferencias de notificación por defecto',
+ 'watch_detail_ignore' => 'Ignorar notificaciones',
+ 'watch_detail_new' => 'Suscripciones de nuevas páginas',
+ 'watch_detail_updates' => 'Suscripciones de nuevas páginas y actualizaciones de páginas',
+ 'watch_detail_comments' => 'Suscripciones de nuevas páginas, actualizaciones de páginas y comentarios',
+ 'watch_detail_parent_book' => 'Subscripciones por libro contenedor',
+ 'watch_detail_parent_book_ignore' => 'Ignorando a través del libro contenedor',
+ 'watch_detail_parent_chapter' => 'Subscripciones por capítulo superior',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorar por capítulo superior',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'La URL no coincide con los hosts SSR permitidos',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nuevo comentario en la página: :pageName',
+ 'new_comment_intro' => 'Un usuario ha comentado en una página de :appName:',
+ 'new_page_subject' => 'Nueva página: :pageName',
+ 'new_page_intro' => 'Una nueva página ha sido creada en :appName:',
+ 'updated_page_subject' => 'Página actualizada: :pageName',
+ 'updated_page_intro' => 'Una página ha sido actualizada en :appName:',
+ 'updated_page_debounce' => 'Para prevenir notificaciones en masa, durante un tiempo no se enviarán notificaciones para futuras ediciones de esta página por el mismo editor.',
+
+ 'detail_page_name' => 'Nombre de página:',
+ 'detail_commenter' => 'Autor del comentario:',
+ 'detail_comment' => 'Comentario:',
+ 'detail_created_by' => 'Creado por:',
+ 'detail_updated_by' => 'Actualizado por:',
+
+ 'action_view_comment' => 'Ver comentario',
+ 'action_view_page' => 'Ver página',
+
+ 'footer_reason' => 'Esta notificación fue enviada porque :link cubre este tipo de actividad para este artículo.',
+ 'footer_reason_link' => 'sus preferencias de notificación',
+];
*/
return [
+ 'preferences' => 'Preferencias',
+
'shortcuts' => 'Accesos directos',
'shortcuts_interface' => 'Accesos directos de la interfaz',
'shortcuts_toggle_desc' => 'Aquí puede activar o desactivar los accesos directos de la interfaz, utilizados para la navegación y las acciones.',
'shortcuts_save' => 'Guardar accesos directos',
'shortcuts_overlay_desc' => 'Nota: Cuando se activan los accesos directos se puede mostrar la ayuda presionando la tecla "?" que resaltará los accesos rápidos disponibles para las acciones actualmente visibles en la pantalla.',
'shortcuts_update_success' => '¡Las preferencias de accesos directos han sido actualizadas!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gestione los atajos de teclado que puede utilizar para navegar por la interfaz de usuario del sistema.',
+
+ 'notifications' => 'Preferencias de notificaciones',
+ 'notifications_desc' => 'Controle las notificaciones por correo electrónico que recibe cuando se realiza cierta actividad dentro del sistema.',
+ 'notifications_opt_own_page_changes' => 'Notificar sobre los cambios en las páginas en las que soy propietario',
+ 'notifications_opt_own_page_comments' => 'Notificar sobre comentarios en las páginas en las que soy propietario',
+ 'notifications_opt_comment_replies' => 'Notificar sobre respuestas a mis comentarios',
+ 'notifications_save' => 'Guardar preferencias',
+ 'notifications_update_success' => '¡Se han actualizado las preferencias de notificaciones!',
+ 'notifications_watched' => 'Elementos vistos e ignorados',
+ 'notifications_watched_desc' => ' A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el artículo y las opciones se mostrarán en la barra lateral.',
+
+ 'profile_overview_desc' => ' Gestione los detalles de su perfil de usuario, incluyendo las opciones de idioma y autenticación preferidas.',
+];
'role_manage_settings' => 'Gestionar ajustes de la aplicación',
'role_export_content' => 'Exportar contenido',
'role_editor_change' => 'Cambiar editor de página',
+ 'role_notifications' => 'Recibir y gestionar notificaciones',
'role_asset' => 'Permisos de contenido',
'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario alterar sus propios privilegios o los privilegios de otros en el sistema. Sólo asignar roles con estos permisos a usuarios de confianza.',
'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los contenidos del sistema. Los permisos de Libros, Capítulos y Páginas sobreescribiran estos permisos.',
'favourite_add_notification' => '".name" se añadió a sus favoritos',
'favourite_remove_notification' => '".name" se eliminó de sus favoritos',
+ // Watching
+ 'watch_update_level_notification' => 'Preferencias de suscripciones actualizadas correctamente',
+
// Auth
'auth_login' => 'sesión iniciada',
'auth_register' => 'registrado como usuario nuevo',
'recycle_bin_restore' => 'restaurado desde la papelera de reciclaje',
'recycle_bin_destroy' => 'eliminado de la papelera de reciclaje',
- // Other
+ // Comments
'commented_on' => 'comentado',
+ 'comment_create' => 'comentario añadido',
+ 'comment_update' => 'comentario actualizado',
+ 'comment_delete' => 'comentario borrado',
+
+ // Other
'permissions_update' => 'permisos actualizados',
];
'remove' => 'Remover',
'add' => 'Agregar',
'configure' => 'Configurar',
+ 'manage' => 'Gestionar',
'fullscreen' => 'Pantalla completa',
'favourite' => 'Favoritos',
'unfavourite' => 'Eliminar de favoritos',
'shelves_permissions_updated' => 'Permisos del Estante Actualizados',
'shelves_permissions_active' => 'Permisos Activos del Estante',
'shelves_permissions_cascade_warning' => 'Los permisos en los estantes no se aplican automáticamente a los libros que contengan. Esto se debe a que un libro puede existir en múltiples estantes. Sin embargo, los permisos pueden ser aplicados a los libros del estante utilizando la opción de abajo.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Los permisos de creación de estantes sólo se utilizan para copiar los permisos a los libros contenidos utilizando la acción a continuación. No controlan la capacidad de crear libros.',
'shelves_copy_permissions_to_books' => 'Copiar Permisos a los Libros',
'shelves_copy_permissions' => 'Copiar Permisos',
'shelves_copy_permissions_explain' => 'Esta acción aplicará los permisos del estante a todos los libros dentro del mismo. Antes de activarlo, asegúrese de que todos los cambios de permisos para este estante fueron guardados.',
'pages_md_insert_drawing' => 'Insertar Dibujo',
'pages_md_show_preview' => 'Mostrar vista previa',
'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa',
+ '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 esá en el capítulo',
'pages_move' => 'Mover página',
'pages_copy' => 'Copiar página',
'references' => 'Referencias',
'references_none' => 'No hay referencias a este elemento.',
'references_to_desc' => 'A continuación se muestran todas las páginas en el sistema que tienen un enlace a este elemento.',
+
+ // Watch Options
+ 'watch' => 'Suscribirme',
+ 'watch_title_default' => 'Preferencias por defecto',
+ 'watch_desc_default' => 'Revertir suscripciones a tus preferencias de notificación por defecto.',
+ 'watch_title_ignore' => 'Ignorar',
+ 'watch_desc_ignore' => 'Ignorar todas las notificaciones, incluyendo las de las preferencias a nivel de usuario.',
+ 'watch_title_new' => 'Nuevas páginas',
+ 'watch_desc_new' => 'Notificar cuando se crea una nueva página dentro de este elemento.',
+ 'watch_title_updates' => 'Todas las actualizaciones de páginas',
+ 'watch_desc_updates' => 'Notificar todos los cambios de páginas y páginas nuevas.',
+ 'watch_desc_updates_page' => 'Notificar todos los cambios en la página.',
+ 'watch_title_comments' => 'Todas las actualizaciones de páginas y comentarios',
+ 'watch_desc_comments' => 'Notificar sobre todas las páginas nuevas, cambios de página y nuevos comentarios.',
+ 'watch_desc_comments_page' => 'Notificar los cambios en las páginas y los nuevos comentarios.',
+ 'watch_change_default' => 'Cambiar preferencias de notificación por defecto',
+ 'watch_detail_ignore' => 'Ignorar notificaciones',
+ 'watch_detail_new' => 'Suscripciones de nuevas páginas',
+ 'watch_detail_updates' => 'Suscripciones de nuevas páginas y actualizaciones de páginas',
+ 'watch_detail_comments' => 'Suscripciones de nuevas páginas, actualizaciones de páginas y comentarios',
+ 'watch_detail_parent_book' => 'Subscripciones por libro contenedor',
+ 'watch_detail_parent_book_ignore' => 'Ignorando a través del libro contenedor',
+ 'watch_detail_parent_chapter' => 'Subscripciones por capítulo superior',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorar por capítulo superior',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'La URL no coincide con los hosts SSR permitidos',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nuevo comentario en la página: :pageName',
+ 'new_comment_intro' => 'Un usuario ha comentado en una página de :appName:',
+ 'new_page_subject' => 'Nueva página: :pageName',
+ 'new_page_intro' => 'Una nueva página ha sido creada en :appName:',
+ 'updated_page_subject' => 'Página actualizada: :pageName',
+ 'updated_page_intro' => 'Una página ha sido actualizada en :appName:',
+ 'updated_page_debounce' => 'Para prevenir notificaciones en masa, durante un tiempo no se enviarán notificaciones para futuras ediciones de esta página por el mismo editor.',
+
+ 'detail_page_name' => 'Nombre de página:',
+ 'detail_commenter' => 'Autor del comentario:',
+ 'detail_comment' => 'Comentario:',
+ 'detail_created_by' => 'Creado por:',
+ 'detail_updated_by' => 'Actualizado por:',
+
+ 'action_view_comment' => 'Ver comentario',
+ 'action_view_page' => 'Ver página',
+
+ 'footer_reason' => 'Esta notificación fue enviada porque :link cubre este tipo de actividad para este artículo.',
+ 'footer_reason_link' => 'sus preferencias de notificación',
+];
*/
return [
+ 'preferences' => 'Preferencias',
+
'shortcuts' => 'Atajos',
'shortcuts_interface' => 'Atajos del Teclado para la Interfaz',
'shortcuts_toggle_desc' => 'Aquí puede activar o desactivar los accesos rápidos de la interfaz, utilizados para la navegación y las acciones.',
'shortcuts_save' => 'Guardar atajos',
'shortcuts_overlay_desc' => 'Nota: Cuando se activan los atajos de teclado se puede visualizar la ayuda presionando la tecla "?", que resaltará los atajos disponibles para las acciones visibles actualmente en la pantalla.',
'shortcuts_update_success' => '¡Se actualizaron las preferencias de atajos de teclado!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gestione los atajos de teclado que puede utilizar para navegar por la interfaz de usuario del sistema.',
+
+ 'notifications' => 'Preferencias de notificaciones',
+ 'notifications_desc' => 'Controle las notificaciones por correo electrónico que recibe cuando se realiza cierta actividad dentro del sistema.',
+ 'notifications_opt_own_page_changes' => 'Notificar sobre los cambios en las páginas en las que soy propietario',
+ 'notifications_opt_own_page_comments' => 'Notificar sobre comentarios en las páginas en las que soy propietario',
+ 'notifications_opt_comment_replies' => 'Notificar sobre respuestas a mis comentarios',
+ 'notifications_save' => 'Guardar preferencias',
+ 'notifications_update_success' => '¡Se han actualizado las preferencias de notificaciones!',
+ 'notifications_watched' => 'Elementos vistos e ignorados',
+ 'notifications_watched_desc' => ' A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el artículo y las opciones se mostrarán en la barra lateral.',
+
+ 'profile_overview_desc' => ' Gestione los detalles de su perfil de usuario, incluyendo las opciones de idioma y autenticación preferidas.',
+];
'role_manage_settings' => 'Gestionar ajustes de activos',
'role_export_content' => 'Exportar contenido',
'role_editor_change' => 'Cambiar editor de página',
+ 'role_notifications' => 'Recibir y gestionar notificaciones',
'role_asset' => 'Permisos de activos',
'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario modificar sus propios privilegios o los privilegios de otros usuarios en el sistema. Asignar roles con estos permisos sólo a usuarios de comfianza.',
'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos definidos en Libros, Capítulos y Páginas ignorarán estos permisos.',
'favourite_add_notification' => '":name" lisati su lemmikute hulka',
'favourite_remove_notification' => '":name" eemaldati su lemmikute hulgast',
+ // Watching
+ 'watch_update_level_notification' => 'Jälgimise eelistused edukalt salvestatud',
+
// Auth
'auth_login' => 'logis sisse',
'auth_register' => 'registreerus uue kasutajana',
'recycle_bin_restore' => 'taastas prügikastist',
'recycle_bin_destroy' => 'eemaldas prügikastist',
- // Other
+ // Comments
'commented_on' => 'kommenteeris lehte',
+ 'comment_create' => 'lisas kommentaari',
+ 'comment_update' => 'muutis kommentaari',
+ 'comment_delete' => 'kustutas kommentaari',
+
+ // Other
'permissions_update' => 'muutis õiguseid',
];
'remove' => 'Eemalda',
'add' => 'Lisa',
'configure' => 'Seadista',
+ 'manage' => 'Halda',
'fullscreen' => 'Täisekraan',
'favourite' => 'Lemmik',
'unfavourite' => 'Eemalda lemmik',
'pages_editing_page' => 'Lehe muutmine',
'pages_edit_draft_save_at' => 'Mustand salvestatud ',
'pages_edit_delete_draft' => 'Kustuta mustand',
- 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.',
+ 'pages_edit_delete_draft_confirm' => 'Kas oled kindel, et soovid mustandi muudatused kustutada? Kõik viimasest salvestamisest saadik tehtud muudatused kaovad ning redaktorisse laetakse viimati salvestatud seis.',
'pages_edit_discard_draft' => 'Loobu mustandist',
'pages_edit_switch_to_markdown' => 'Kasuta Markdown redaktorit',
'pages_edit_switch_to_markdown_clean' => '(Puhas sisu)',
'pages_md_insert_drawing' => 'Lisa joonis',
'pages_md_show_preview' => 'Näita eelvaadet',
'pages_md_sync_scroll' => 'Sünkrooni eelvaate kerimine',
+ 'pages_drawing_unsaved' => 'Leiti salvestamata joonis',
+ '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' => 'Leht ei kuulu peatüki alla',
'pages_move' => 'Liiguta leht',
'pages_copy' => 'Kopeeri leht',
'pages_revisions_restore' => 'Taasta',
'pages_revisions_none' => 'Sellel lehel ei ole redaktsioone',
'pages_copy_link' => 'Kopeeri link',
- 'pages_edit_content_link' => 'Jump to section in editor',
+ 'pages_edit_content_link' => 'Hüppa redaktoris sektsioonini',
'pages_pointer_enter_mode' => 'Enter section select mode',
- 'pages_pointer_label' => 'Page Section Options',
+ 'pages_pointer_label' => 'Lehe sektsiooni valikud',
'pages_pointer_permalink' => 'Page Section Permalink',
'pages_pointer_include_tag' => 'Page Section Include Tag',
'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
'time_b' => 'viimase :minCount minuti jooksul',
'message' => ':start :time. Ärge teineteise muudatusi üle kirjutage!',
],
- 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content',
- 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content',
+ 'pages_draft_discarded' => 'Mustand ära visatud! Redaktorisse laeti lehe värske sisu',
+ 'pages_draft_deleted' => 'Mustand kustutatud! Redaktorisse laeti lehe värske sisu',
'pages_specific' => 'Spetsiifiline leht',
'pages_is_template' => 'Lehe mall',
'comment_new' => 'Uus kommentaar',
'comment_created' => 'kommenteeris :createDiff',
'comment_updated' => 'Muudetud :updateDiff :username poolt',
- 'comment_updated_indicator' => 'Updated',
+ 'comment_updated_indicator' => 'Uuendatud',
'comment_deleted_success' => 'Kommentaar kustutatud',
'comment_created_success' => 'Kommentaar lisatud',
'comment_updated_success' => 'Kommentaar muudetud',
'references' => 'Viited',
'references_none' => 'Sellele objektile ei ole viiteid.',
'references_to_desc' => 'Allpool on kõik teadaolevad lehed, mis sellele objektile viitavad.',
+
+ // Watch Options
+ 'watch' => 'Jälgi',
+ 'watch_title_default' => 'Vaikimisi eelistused',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignoreeri',
+ 'watch_desc_ignore' => 'Ignoreeri kõiki teavitusi, ka kasutaja tasemel määratud eelistusi.',
+ 'watch_title_new' => 'Uued lehed',
+ 'watch_desc_new' => 'Teavita, kui sellesse objekti lisatakse uus leht.',
+ 'watch_title_updates' => 'Kõik lehed',
+ 'watch_desc_updates' => 'Teavita kõigist uutest lehtedest ja lehtede muudatustest.',
+ 'watch_desc_updates_page' => 'Teavita kõigist lehtede muudatustest.',
+ 'watch_title_comments' => 'Kõik lehed ja kommentaarid',
+ 'watch_desc_comments' => 'Teavita kõigist uutest lehtedest, lehtede muudatustest ja uutest kommentaaridest.',
+ 'watch_desc_comments_page' => 'Teavita lehtede muudatustest ja uutest kommentaaridest.',
+ 'watch_change_default' => 'Muuda vaikimisi teavituste eelistusi',
+ 'watch_detail_ignore' => 'Teavitusi ignoreeritakse',
+ 'watch_detail_new' => 'Jälgitakse uusi lehti',
+ 'watch_detail_updates' => 'Jälgitakse uusi lehti ja muudatusi',
+ 'watch_detail_comments' => 'Jälgitakse uusi lehti, muudatusi ja kommentaare',
+ 'watch_detail_parent_book' => 'Jälgitakse raamatu kaudu',
+ 'watch_detail_parent_book_ignore' => 'Ignoreeritakse raamatu kaudu',
+ 'watch_detail_parent_chapter' => 'Jälgitakse peatüki kaudu',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoreeritakse peatüki kaudu',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Test e-kirja saatmisel tekkis viga:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URL ei klapi ühegi lubatud SSR hostiga',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Uus kommentaar lehel: :pageName',
+ 'new_comment_intro' => 'Rakenduses :appName kommenteeriti lehte:',
+ 'new_page_subject' => 'Uus leht: :pageName',
+ 'new_page_intro' => 'Rakenduses :appName lisati uus leht:',
+ 'updated_page_subject' => 'Muudetud leht: :pageName',
+ 'updated_page_intro' => 'Rakenduses :appName muudeti lehte:',
+ 'updated_page_debounce' => 'Et vältida liigseid teavitusi, ei saadeta sulle mõnda aega teavitusi selle lehe muutmiste kohta sama kasutaja poolt.',
+
+ 'detail_page_name' => 'Lehe nimetus:',
+ 'detail_commenter' => 'Kommenteerija:',
+ 'detail_comment' => 'Kommentaar:',
+ 'detail_created_by' => 'Autor:',
+ 'detail_updated_by' => 'Muutja:',
+
+ 'action_view_comment' => 'Vaata kommentaari',
+ 'action_view_page' => 'Vaata lehte',
+
+ 'footer_reason' => 'See teavitus saadeti sulle, sest :link sisaldavad selle objekti kohta sellist tegevust.',
+ 'footer_reason_link' => 'sinu teavituste eelistused',
+];
*/
return [
+ 'preferences' => 'Eelistused',
+
'shortcuts' => 'Kiirklahvid',
'shortcuts_interface' => 'Kasutajaliidese kiirklahvid',
'shortcuts_toggle_desc' => 'Siit saad sisse ja välja lülitada navigeerimiseks ja tegevusteks kasutatavad kiirklahvid.',
'shortcuts_save' => 'Salvesta kiirklahvid',
'shortcuts_overlay_desc' => 'Märkus: Kui kiirklahvid on sisse lülitatud, saab "?" vajutades kuvada abiinfo, mis märgib ära kõigi hetkel ekraanil nähtavate tegevuste kiirklahvid.',
'shortcuts_update_success' => 'Kiirklahvide eelistused on salvestatud!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Halda klaviatuuri kiirklahve süsteemi kasutajaliideses navigeerimiseks.',
+
+ 'notifications' => 'Teavituste eelistused',
+ 'notifications_desc' => 'Halda e-posti teavitusi, mis saadetakse teatud tegevuste puhul.',
+ 'notifications_opt_own_page_changes' => 'Teavita muudatustest minu lehtedel',
+ 'notifications_opt_own_page_comments' => 'Teavita kommentaaridest minu lehtedel',
+ 'notifications_opt_comment_replies' => 'Teavita vastustest minu kommentaaridele',
+ 'notifications_save' => 'Salvesta eelistused',
+ 'notifications_update_success' => 'Teavituste eelistused on salvestatud!',
+ 'notifications_watched' => 'Jälgitud ja ignoreeritud objektid',
+ 'notifications_watched_desc' => ' Allpool on objektid, millele on määratud kohaldatud jälgimise eelistused. Eelistuste muutmiseks ava vastav objekt ning leia jälgimise valikud külgmenüüs.',
+
+ 'profile_overview_desc' => ' Halda oma kasutajaprofiili andmeid, kaasa arvatud keele eelistust ja autentimisvalikuid.',
+];
'role_manage_settings' => 'Rakenduse seadete haldamine',
'role_export_content' => 'Sisu eksport',
'role_editor_change' => 'Lehe redaktori muutmine',
+ 'role_notifications' => 'Võta vastu ja halda teavitusi',
'role_asset' => 'Sisu õigused',
'roles_system_warning' => 'Pane tähele, et ülalolevad kolm õigust võimaldavad kasutajal enda või teiste kasutajate õiguseid muuta. Määra nende õigustega roll ainult usaldusväärsetele kasutajatele.',
'role_asset_desc' => 'Need load kontrollivad vaikimisi ligipääsu süsteemis olevale sisule. Raamatute, peatükkide ja lehtede õigused rakenduvad esmajärjekorras.',
'favourite_add_notification' => '":name" zure gogoetara gehitua izan da',
'favourite_remove_notification' => '":name" zure gogokoetatik ezabatua izan da',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'iruzkinak',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'eguneratu baimenak',
];
'remove' => 'Ezabatu',
'add' => 'Gehitu',
'configure' => 'Konfiguratu',
+ 'manage' => 'Manage',
'fullscreen' => 'Pantaila osoa',
'favourite' => 'Gogokoa',
'unfavourite' => 'Desatsegina',
'pages_md_insert_drawing' => 'Txertatu marrazki berria',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Lastertekla',
'shortcuts_interface' => 'Teklatuko lasterbideak ikusi',
'shortcuts_toggle_desc' => 'Hemen, nabigaziorako eta ekintzetarako erabiltzen diren teklatu-sistemako lasterbideak gaitu edo desgaitu daitezke.',
'shortcuts_save' => 'Gorde lasterbideak',
'shortcuts_overlay_desc' => 'Oharra: Lasterbideak gaituta daudenean, "?" sakagailuaren bidez laguntzaileen gainjartze bat egongo da, eta horrek pantailan gaur egun ikus daitezkeen ekintzetarako dauden lasterbideak nabarmenduko ditu.',
'shortcuts_update_success' => 'Zure lehentasunak gorde dira!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Kudeatu aplikazio ezarpenak',
'role_export_content' => 'Exportatu edukia',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Fitxategi baimenak',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'page_restore' => 'بازیابی صفحه',
'page_restore_notification' => 'صفحه با موفقیت بازیابی شد',
'page_move' => 'انتقال صفحه',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'صفحه با موفقیت جابهجا شد',
// Chapters
'chapter_create' => 'ایجاد فصل',
'chapter_delete' => 'حذف فصل',
'chapter_delete_notification' => 'فصل با موفقیت حذف شد',
'chapter_move' => 'انتقال فصل',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'فصل با موفقیت جابهجا شد',
// Books
'book_create' => 'ایجاد کتاب',
// Revisions
'revision_restore' => 'restored revision',
'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_delete_notification' => 'نسخه مورد نظر با موفقیت حذف شد',
// Favourites
'favourite_add_notification' => '":name" به علاقه مندی های شما اضافه شد',
'favourite_remove_notification' => '":name" از علاقه مندی های شما حذف شد',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
// Settings
'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
+ 'settings_update_notification' => 'تنظیمات با موفقیت به روز شد',
'maintenance_action_run' => 'ran maintenance action',
// Webhooks
// Users
'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
+ 'user_create_notification' => 'کاربر با موفقیت به ایجاد شد',
'user_update' => 'updated user',
'user_update_notification' => 'کاربر با موفقیت به روز شد',
'user_delete' => 'deleted user',
// Recycle Bin
'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_restore' => 'از سطل بازیافت، بازآوری شده است',
+ 'recycle_bin_destroy' => 'از سطل بازیافت حذف شده است',
- // Other
+ // Comments
'commented_on' => 'ثبت دیدگاه',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'به روزرسانی مجوزها',
];
// Buttons
'cancel' => 'لغو',
- 'close' => 'Close',
+ 'close' => 'خروج',
'confirm' => 'تایید',
'back' => 'بازگشت',
'save' => 'ذخیره',
'remove' => 'حذف',
'add' => 'ﺍﻓﺰﻭﺩﻥ',
'configure' => 'پیکربندی کنید',
+ 'manage' => 'Manage',
'fullscreen' => 'تمام صفحه',
'favourite' => 'علاقهمندی',
'unfavourite' => 'حذف از علاقهمندی',
// Misc
'deleted_user' => 'کاربر حذف شده',
'no_activity' => 'بایگانی برای نمایش وجود ندارد',
- 'no_items' => 'هیچ آیتمی موجود نیست',
+ 'no_items' => 'هیچ موردی در دسترس نیست',
'back_to_top' => 'بازگشت به بالا',
'skip_to_main_content' => 'رفتن به محتوای اصلی',
'toggle_details' => 'معکوس کردن اطلاعات',
// Image Manager
'image_select' => 'انتخاب تصویر',
- 'image_list' => 'Image List',
- 'image_details' => 'Image Details',
- 'image_upload' => 'Upload Image',
- 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.',
- 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.',
+ 'image_list' => 'لیست تصاویر',
+ 'image_details' => 'جزئیات تصویر',
+ 'image_upload' => 'بارگذاری تصویر',
+ 'image_intro' => 'در اینجا می توانید تصاویری که قبلاً در سیستم آپلود شده اند را انتخاب و مدیریت کنید.',
+ 'image_intro_upload' => 'با کشیدن یک فایل تصویری به این پنجره یا با استفاده از دکمه "بارگذاری تصویر" در بالا، یک تصویر جدید آپلود کنید.',
'image_all' => 'همه',
'image_all_title' => 'نمایش تمام تصاویر',
'image_book_title' => 'تصاویر بارگذاری شده در این کتاب را مشاهده کنید',
'image_page_title' => 'تصاویر بارگذاری شده در این صفحه را مشاهده کنید',
'image_search_hint' => 'جستجو بر اساس نام تصویر',
'image_uploaded' => 'بارگذاری شده :uploadedDate',
- 'image_uploaded_by' => 'Uploaded by :userName',
- 'image_uploaded_to' => 'Uploaded to :pageLink',
- 'image_updated' => 'Updated :updateDate',
+ 'image_uploaded_by' => 'بارگذاری شده توسط:userName',
+ 'image_uploaded_to' => 'بارگذاری شده در صفحه:pageLink',
+ 'image_updated' => 'بهروزرسانی شده در:updateDate',
'image_load_more' => 'بارگذاری بیشتر',
'image_image_name' => 'نام تصویر',
'image_delete_used' => 'این تصویر در صفحات زیر استفاده شده است.',
'image_delete_confirm_text' => 'آیا مطمئن هستید که میخواهید این عکس را پاک کنید؟',
'image_select_image' => 'انتخاب تصویر',
'image_dropzone' => 'تصاویر را رها کنید یا برای بارگذاری اینجا را کلیک کنید',
- 'image_dropzone_drop' => 'Drop images here to upload',
+ 'image_dropzone_drop' => 'تصویر را برای بارگذاری به اینجا بکشید و رها کنید',
'images_deleted' => 'تصاویر حذف شده',
'image_preview' => 'پیش نمایش تصویر',
'image_upload_success' => 'تصویر با موفقیت بارگذاری شد',
'image_update_success' => 'جزئیات تصویر با موفقیت به روز شد',
'image_delete_success' => 'تصویر با موفقیت حذف شد',
- 'image_replace' => 'Replace Image',
- 'image_replace_success' => 'Image file successfully updated',
+ 'image_replace' => 'جایگزینی تصویر',
+ 'image_replace_success' => 'تصویر با موفقیت به روز شد',
// Code Editor
'code_editor' => 'ویرایش کد',
'search_terms' => 'عبارات جستجو',
'search_content_type' => 'نوع محتوا',
'search_exact_matches' => 'مطابقت کامل',
- 'search_tags' => 'جستجوها را برچسب بزنید',
- 'search_options' => 'گزینه ها',
+ 'search_tags' => 'جستجو در برچسبها',
+ 'search_options' => 'گزینهها',
'search_viewed_by_me' => 'بازدید شده به وسیله من',
'search_not_viewed_by_me' => 'توسط من مشاهده نشده است',
'search_permissions_set' => 'مجوزها تنظیم شده است',
// Shelves
'shelf' => 'قفسه',
- 'shelves' => 'قفسه ها',
+ 'shelves' => 'قفسهها',
'x_shelves' => ':count قفسه|:count قفسهها',
'shelves_empty' => 'هیچ قفسه ای ایجاد نشده است',
'shelves_create' => 'ایجاد قفسه جدید',
- 'shelves_popular' => 'قفسه های محبوب',
- 'shelves_new' => 'قفسه های جدید',
+ 'shelves_popular' => 'قفسههای محبوب',
+ 'shelves_new' => 'قفسههای جدید',
'shelves_new_action' => 'قفسه جدید',
'shelves_popular_empty' => 'محبوب ترین قفسه ها در اینجا ظاهر می شوند.',
'shelves_new_empty' => 'جدیدترین قفسه های ایجاد شده در اینجا ظاهر می شوند.',
'shelves_permissions_updated' => 'مجوزهای کانال بروزرسانی شد',
'shelves_permissions_active' => 'مجوزهای قفسه فعال است',
'shelves_permissions_cascade_warning' => 'مجوزهای موجود در قفسهها به طور خودکار به کتابهای حاوی اطلاق نمیشوند. دلیل آن این است که یک کتاب می تواند در چندین قفسه وجود داشته باشد. با این حال، مجوزها را میتوان با استفاده از گزینه پایین همین صفحه در کتابهای فرزند کپی کرد.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'مجوزهای «ایجاد» قفسه فقط برای کپی کردن مجوزها در کتابهای کودک با استفاده از عملکرد زیر استفاده میشوند. آنها توانایی ایجاد کتاب را کنترل نمیکنند.',
'shelves_copy_permissions_to_books' => 'کپی مجوزها در کتابها',
'shelves_copy_permissions' => 'کپی مجوزها',
'shelves_copy_permissions_explain' => 'با این کار تنظیمات مجوز فعلی این قفسه برای همه کتابهای موجود در آن اعمال میشود. قبل از فعال کردن، مطمئن شوید که هر گونه تغییر در مجوزهای این قفسه، ذخیره شده است.',
// Books
'book' => 'کتاب',
- 'books' => 'کتابها',
+ 'books' => 'کتابها',
'x_books' => ':count کتاب|:count کتاب',
'books_empty' => 'هیچ کتابی ایجاد نشده است',
'books_popular' => 'کتاب های محبوب',
'pages_md_insert_drawing' => 'درج طرح',
'pages_md_show_preview' => 'دیدن پیش نمایش',
'pages_md_sync_scroll' => 'هماهنگ سازی اسکرول پیش نمایش',
+ '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' => 'صفحه در یک فصل نیست',
'pages_move' => 'انتقال صفحه',
'pages_copy' => 'کپی صفحه',
'attachments_explain_instant_save' => 'تغییرات در اینجا فورا ذخیره می شوند.',
'attachments_upload' => 'آپلود فایل',
'attachments_link' => 'پیوند را ضمیمه کنید',
- 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.',
+ 'attachments_upload_drop' => 'میتوانید فایلی را در اینجا بکشید و رها کنید تا آن را به عنوان پیوست آپلود کنید.',
'attachments_set_link' => 'پیوند را تنظیم کنید',
'attachments_delete' => 'آیا مطمئن هستید که می خواهید این پیوست را حذف کنید؟',
- 'attachments_dropzone' => 'Drop files here to upload',
+ 'attachments_dropzone' => 'فایل را برای بارگذاری به اینجا بکشید و رها کنید',
'attachments_no_files' => 'هیچ فایلی آپلود نشده است',
'attachments_explain_link' => 'اگر ترجیح می دهید فایلی را آپلود نکنید، می توانید پیوندی را پیوست کنید. این می تواند پیوندی به صفحه دیگر یا پیوندی به فایلی در فضای ابری باشد.',
'attachments_link_name' => 'نام پیوند',
'comment_updated_success' => 'نظر به روز شد',
'comment_delete_confirm' => 'آیا مطمئن هستید که می خواهید این نظر را حذف کنید؟',
'comment_in_reply_to' => 'در پاسخ به :commentId',
- 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
+ 'comment_editor_explain' => 'در اینجا نظراتی که در این صفحه گذاشته شده است، مشاهده میشود. نظرات را میتوان در هنگام مشاهده صفحه ذخیره شده، اضافه و مدیریت کرد.',
// Revision
'revision_delete_confirm' => 'آیا مطمئن هستید که می خواهید این ویرایش را حذف کنید؟',
'references' => 'مراجع',
'references_none' => 'هیچ رفرنسی برای این قلم یافت نشد.',
'references_to_desc' => 'در زیر تمام صفحات شناخته شده در سیستم که به این مورد پیوند دارند، نشان داده شده است.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'خطا در هنگام ارسال ایمیل آزمایشی:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'میانبرها',
'shortcuts_interface' => 'میانبرهای صفحه کلید',
'shortcuts_toggle_desc' => 'در اینجا می توانید میانبرهای سیستم را که برای پیمایش و ... استفاده می شود، فعال یا غیرفعال کنید.',
'shortcuts_save' => 'ذخیره کلیدهای میانبر',
'shortcuts_overlay_desc' => 'توجه: هنگامی که میانبرها فعال هستند، یک رابط کمکی با فشار دادن "؟" در دسترس است که میانبرهای موجود برای اقداماتی که در حال حاضر روی صفحه قابل مشاهده است را برجسته میکند.',
'shortcuts_update_success' => 'تنظیمات میانبر به روز شده است!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'تنظیمات برنامه را مدیریت کنید',
'role_export_content' => 'صادرات محتوا',
'role_editor_change' => 'تغییر ویرایشگر صفحه',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'مجوزهای دارایی',
'roles_system_warning' => 'توجه داشته باشید که دسترسی به هر یک از سه مجوز فوق میتواند به کاربر اجازه دهد تا امتیازات خود یا امتیازات دیگران را در سیستم تغییر دهد. فقط نقش هایی را با این مجوزها به کاربران مورد اعتماد اختصاص دهید.',
'role_asset_desc' => 'این مجوزها دسترسی پیشفرض به داراییهای درون سیستم را کنترل میکنند. مجوزهای مربوط به کتابها، فصلها و صفحات این مجوزها را لغو میکنند.',
'favourite_add_notification' => '":name" a été ajouté dans vos favoris',
'favourite_remove_notification' => '":name" a été supprimé de vos favoris',
+ // Watching
+ 'watch_update_level_notification' => 'Suivre les préférences mises à jour avec succès',
+
// Auth
'auth_login' => 'connecté',
'auth_register' => 'enregistré en tant que nouvel utilisateur',
'recycle_bin_restore' => 'restauré à partir de la corbeille',
'recycle_bin_destroy' => 'supprimé de la corbeille',
- // Other
+ // Comments
'commented_on' => 'a commenté',
+ 'comment_create' => 'Commentaire ajouté',
+ 'comment_update' => 'Commentaire mis à jour',
+ 'comment_delete' => 'Commentaire supprimé',
+
+ // Other
'permissions_update' => 'a mis à jour les autorisations sur',
];
'remove' => 'Enlever',
'add' => 'Ajouter',
'configure' => 'Configurer',
+ 'manage' => 'Gérer',
'fullscreen' => 'Plein écran',
'favourite' => 'Favoris',
'unfavourite' => 'Supprimer des favoris',
'shelves_permissions_updated' => 'Permissions de l\'étagère mises à jour',
'shelves_permissions_active' => 'Permissions de l\'étagère activées',
'shelves_permissions_cascade_warning' => 'Les permissions sur les étagères ne sont pas automatiquement recopiées aux livres qu\'elles contiennent, car un livre peut exister dans plusieurs étagères. Les permissions peuvent cependant être recopiées vers les livres contenus en utilisant l\'option ci-dessous.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Les permissions de création d\'une étagère sont uniquement utilisées pour copier les permissions vers les livres enfants en utilisant l\'action ci-dessous. Elles ne contrôlent pas la possibilité de créer des livres.',
'shelves_copy_permissions_to_books' => 'Copier les permissions vers les livres',
'shelves_copy_permissions' => 'Copier les permissions',
'shelves_copy_permissions_explain' => 'Ceci va appliquer les permissions actuelles de cette étagère à tous les livres qu\'elle contient. Avant de continuer, assurez-vous que toutes les permissions de cette étagère ont été sauvegardées.',
'pages_md_insert_drawing' => 'Insérer un dessin',
'pages_md_show_preview' => 'Prévisualisation',
'pages_md_sync_scroll' => 'Défilement prévisualisation',
+ 'pages_drawing_unsaved' => 'Dessin non enregistré trouvé',
+ 'pages_drawing_unsaved_confirm' => 'Des données de dessin non enregistrées ont été trouvées à partir d\'une tentative de sauvegarde de dessin échouée. Voulez-vous restaurer et continuer à modifier ce dessin non sauvegardé ?',
'pages_not_in_chapter' => 'La page n\'est pas dans un chapitre',
'pages_move' => 'Déplacer la page',
'pages_copy' => 'Copier la page',
'references' => 'Références',
'references_none' => 'Il n\'y a pas de références suivies à cet élément.',
'references_to_desc' => 'Vous trouverez ci-dessous toutes les pages connues du système qui ont un lien vers cet élément.',
+
+ // Watch Options
+ 'watch' => 'Suivre',
+ 'watch_title_default' => 'Préférences par défaut',
+ 'watch_desc_default' => 'Revenir à vos préférences de notification par défaut.',
+ 'watch_title_ignore' => 'Ignorer',
+ 'watch_desc_ignore' => 'Ignorer toutes les notifications, y compris celles des préférences de niveau utilisateur.',
+ 'watch_title_new' => 'Nouvelles Pages',
+ 'watch_desc_new' => 'Notifier quand une nouvelle page est créée dans cet élément.',
+ 'watch_title_updates' => 'Toutes les mises à jour de page',
+ 'watch_desc_updates' => 'Notifier toutes les nouvelles pages et les changements de page.',
+ 'watch_desc_updates_page' => 'Notifier lors de toutes les modifications de page.',
+ 'watch_title_comments' => 'Toutes les mises à jour et commentaires de page',
+ 'watch_desc_comments' => 'Notifier toutes les nouvelles pages, les changements de page et les nouveaux commentaires.',
+ 'watch_desc_comments_page' => 'Notifier les changements de page et les nouveaux commentaires.',
+ 'watch_change_default' => 'Modifier les préférences de notification par défaut',
+ 'watch_detail_ignore' => 'Ignorer les notifications',
+ 'watch_detail_new' => 'Suivre les nouvelles pages',
+ 'watch_detail_updates' => 'Suivre les nouvelles pages et mises à jour',
+ 'watch_detail_comments' => 'Suivre les nouvelles pages, mises à jour et commentaires',
+ 'watch_detail_parent_book' => 'Suivre via le livre parent',
+ 'watch_detail_parent_book_ignore' => 'Ignorer via le livre parent',
+ 'watch_detail_parent_chapter' => 'Suivre via le chapitre parent',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorer via le chapitre parent',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erreur émise lors de l\'envoi d\'un e-mail de test :',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'L\'URL ne correspond pas aux hôtes SSR autorisés configurés',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nouveau commentaire sur la page : :pageName',
+ 'new_comment_intro' => 'Un utilisateur a commenté une page dans :appName:',
+ 'new_page_subject' => 'Nouvelle page : :pageName',
+ 'new_page_intro' => 'Une nouvelle page a été créée dans :appName:',
+ 'updated_page_subject' => 'Page mise à jour : :pageName',
+ 'updated_page_intro' => 'Une page a été mise à jour dans :appName:',
+ 'updated_page_debounce' => 'Pour éviter de nombreuses notifications, pendant un certain temps, vous ne recevrez pas de notifications pour d\'autres modifications de cette page par le même éditeur.',
+
+ 'detail_page_name' => 'Nom de la page :',
+ 'detail_commenter' => 'Commenta·teur·trice :',
+ 'detail_comment' => 'Commentaire :',
+ 'detail_created_by' => 'Créé par :',
+ 'detail_updated_by' => 'Mis à jour par :',
+
+ 'action_view_comment' => 'Voir le commentaire',
+ 'action_view_page' => 'Afficher la page',
+
+ 'footer_reason' => 'Cette notification vous a été envoyée car :link couvre ce type d\'activité pour cet élément.',
+ 'footer_reason_link' => 'vos préférences de notification',
+];
*/
return [
+ 'preferences' => 'Préférences',
+
'shortcuts' => 'Raccourcis',
'shortcuts_interface' => 'Raccourcis clavier',
'shortcuts_toggle_desc' => 'Ici vous pouvez activer ou désactiver les raccourcis clavier, utilisés pour la navigation et les actions.',
'shortcuts_save' => 'Sauvegarder les raccourcis',
'shortcuts_overlay_desc' => 'Note : Lorsque les raccourcis sont activés, assistant est disponible en appuyant sur "?" qui mettra en surbrillance les raccourcis disponibles pour les actions actuellement visibles à l\'écran.',
'shortcuts_update_success' => 'Les préférences de raccourci ont été mises à jour !',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gérer les raccourcis clavier que vous pouvez utiliser pour naviguer dans l\'interface utilisateur du système.',
+
+ 'notifications' => 'Préférences de notification',
+ 'notifications_desc' => 'Contrôlez les notifications par e-mail que vous recevez lorsque certaines activités sont effectuées dans le système.',
+ 'notifications_opt_own_page_changes' => 'Notifier lors des modifications des pages que je possède',
+ 'notifications_opt_own_page_comments' => 'Notifier lorsque les pages que je possède sont commentées',
+ 'notifications_opt_comment_replies' => 'Notifier les réponses à mes commentaires',
+ 'notifications_save' => 'Enregistrer les préférences',
+ 'notifications_update_success' => 'Les préférences de notification ont été mises à jour !',
+ 'notifications_watched' => 'Éléments surveillés et ignorés',
+ 'notifications_watched_desc' => ' Voici les éléments qui ont des préférences de surveillance personnalisées appliquées. Pour mettre à jour vos préférences pour celles-ci, consultez l\'élément puis trouvez les options de surveillance dans la barre latérale.',
+
+ 'profile_overview_desc' => ' Gérer les détails de votre profil utilisateur y compris la langue préférée et les options d\'authentification.',
+];
'role_manage_settings' => 'Gérer les préférences de l\'application',
'role_export_content' => 'Exporter le contenu',
'role_editor_change' => 'Changer l\'éditeur de page',
+ 'role_notifications' => 'Recevoir et gérer les notifications',
'role_asset' => 'Permissions des ressources',
'roles_system_warning' => 'Sachez que l\'accès à l\'une des trois permissions ci-dessus peut permettre à un utilisateur de modifier ses propres privilèges ou les privilèges des autres utilisateurs du système. N\'attribuez uniquement des rôles avec ces permissions qu\'à des utilisateurs de confiance.',
'role_asset_desc' => 'Ces permissions contrôlent l\'accès par défaut des ressources dans le système. Les permissions dans les livres, les chapitres et les pages ignoreront ces permissions',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'הגיב/ה על',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'הרשאות עודכנו',
];
'remove' => 'הסר',
'add' => 'הוסף',
'configure' => 'הגדרות',
+ 'manage' => 'Manage',
'fullscreen' => 'מסך מלא',
'favourite' => 'מועדף',
'unfavourite' => 'בטל מועדף',
'pages_md_insert_drawing' => 'הכנס סרטוט',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'דף אינו חלק מפרק',
'pages_move' => 'העבר דף',
'pages_copy' => 'העתק דף',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'קיצורי דרך',
'shortcuts_interface' => 'קיצורי מקשים של המערכת',
'shortcuts_toggle_desc' => 'כאן תוכל להפעיל או לבטל קיצורי דרך לממשק מערכת המקלדת, המשמשים לניווט ולפעולות.',
'shortcuts_save' => 'שמור קיצורי דרך',
'shortcuts_overlay_desc' => 'הערה: כאשר קיצורי דרך מופעלים, שכבת-על מסייעת זמינה באמצעות לחיצה על "?" אשר ידגיש את קיצורי הדרך הזמינים לפעולות הנראות כעת על המסך.',
'shortcuts_update_success' => 'העדפותיך נשמרו!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'ניהול הגדרות יישום',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'הרשאות משאבים',
'roles_system_warning' => 'שימו לב לכך שגישה לכל אחת משלושת ההרשאות הנ"ל יכולה לאפשר למשתמש לשנות את הפריווילגיות שלהם או של אחרים במערכת. הגדירו תפקידים להרשאות אלה למשתמשים בהם אתם בוטחים בלבד.',
'role_asset_desc' => 'הרשאות אלו שולטות בגישת ברירת המחדל למשאבים בתוך המערכת. הרשאות של ספרים, פרקים ודפים יגברו על הרשאות אלו.',
'favourite_add_notification' => '".ime" će biti dodano u tvoje favorite',
'favourite_remove_notification' => '".ime" je uspješno maknuta iz tvojih favorita',
+ // Watching
+ 'watch_update_level_notification' => 'Postavke gledanja uspješno ažurirane',
+
// Auth
'auth_login' => 'prijavljen',
'auth_register' => 'registriran kao novi korisnik',
'recycle_bin_restore' => 'reciklirano iz koša za smeće',
'recycle_bin_destroy' => 'uklonjeno iz koša za smeće',
- // Other
+ // Comments
'commented_on' => 'komentirano',
+ 'comment_create' => 'dodani komentar',
+ 'comment_update' => 'ažurirani komentar',
+ 'comment_delete' => 'obrisani komentar',
+
+ // Other
'permissions_update' => 'ažurirana dopuštenja',
];
'remove' => 'Ukloni',
'add' => 'Dodaj',
'configure' => 'Konfiguriraj',
+ 'manage' => 'Upravljaj',
'fullscreen' => 'Cijeli zaslon',
'favourite' => 'Favorit',
'unfavourite' => 'Ukloni iz favorita',
'shelves_permissions_updated' => 'Ažurirana dopuštenja za Policu',
'shelves_permissions_active' => 'Aktivna Dopuštenja za Policu',
'shelves_permissions_cascade_warning' => 'Dozvole na policama se automatski ne prenose na knjige koje se nalaze na njima. To je zato što se jedna knjiga može nalaziti na više polica. Međutim, dozvole se mogu kopirati na podređene knjige koristeći opciju koja se nalazi ispod.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Dozvole za stvaranje police koriste se samo za kopiranje dozvola na podređene knjige pomoću radnje u nastavku. Ne kontroliraju sposobnost stvaranja knjiga.',
'shelves_copy_permissions_to_books' => 'Kopiraj dopuštenja za knjige',
'shelves_copy_permissions' => 'Kopiraj dopuštenja',
'shelves_copy_permissions_explain' => 'Ovo će primijeniti trenutne postavke dozvola ove police na sve knjige koje se nalaze na njoj. Prije aktiviranja, provjerite jeste li spremili sve promjene dozvola na ovoj polici.',
'pages_md_insert_drawing' => 'Umetni crtež',
'pages_md_show_preview' => 'Prikaži pregled',
'pages_md_sync_scroll' => 'Sinkroniziraj pomicanje pregleda',
+ 'pages_drawing_unsaved' => 'Pronađen je Nespremljen Crtež',
+ 'pages_drawing_unsaved_confirm' => 'Pronađeni su nespremljeni podaci crteža iz prethodnog neuspjelog pokušaja spremanja crteža. Želite li obnoviti i nastaviti uređivati ovaj nespremljeni crtež?',
'pages_not_in_chapter' => 'Stranica nije u poglavlju',
'pages_move' => 'Premjesti stranicu',
'pages_copy' => 'Kopiraj stranicu',
'references' => 'Reference',
'references_none' => 'Nema praćenih referenci na ovu stavku.',
'references_to_desc' => 'U nastavku su prikazane sve poznate stranice u sustavu koje se povezuju s ovom stavkom.',
+
+ // Watch Options
+ 'watch' => 'Prati',
+ 'watch_title_default' => 'Zadane Postavke',
+ 'watch_desc_default' => 'Vratite praćenje samo na vaše zadane postavke obavijesti.',
+ 'watch_title_ignore' => 'Zanemari',
+ 'watch_desc_ignore' => 'Ignorirajte sve obavijesti, uključujući one iz postavki na razini korisnika.',
+ 'watch_title_new' => 'Nove Stranice',
+ 'watch_desc_new' => 'Obavijesti kada se stvori nova stranica unutar ove stavke.',
+ 'watch_title_updates' => 'Sve Promjene na Stranicama',
+ 'watch_desc_updates' => 'Obavijesti o svim novim stranicama i promjenama na stranicama.',
+ 'watch_desc_updates_page' => 'Obavijesti o svim promjenama na stranicama.',
+ 'watch_title_comments' => 'Sve Promjene na Stranicama i Komentari',
+ 'watch_desc_comments' => 'Obavijesti o svim novim stranicama, promjenama na stranicama i novim komentarima.',
+ 'watch_desc_comments_page' => 'Obavijesti o promjenama na stranicama i novim komentarima.',
+ 'watch_change_default' => 'Promijenite zadane postavke obavijesti',
+ 'watch_detail_ignore' => 'Ignoriranje obavijesti',
+ 'watch_detail_new' => 'Prati nove stranice',
+ 'watch_detail_updates' => 'Prati nove stranice i ažuriranja',
+ 'watch_detail_comments' => 'Prati nove stranice, ažuriranja i komentare',
+ 'watch_detail_parent_book' => 'Prati putem nadređene knjige',
+ 'watch_detail_parent_book_ignore' => 'Ignoriraj putem nadređene knjige',
+ 'watch_detail_parent_chapter' => 'Prati puten nadređenog poglavlja',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoriraj putem nadređenog poglavlja',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Pogreška prilikom slanja testnog email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URL se ne podudara s konfiguriranim dozvoljenim SSR domaćinima',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Novi komentar na stranici: :pageName',
+ 'new_comment_intro' => 'Korisnik je komentirao stranicu u :appName:',
+ 'new_page_subject' => 'Nova stranica: :pageName',
+ 'new_page_intro' => 'Nova stranica je stvorena u :appName:',
+ 'updated_page_subject' => 'ChatGPT
+
+Ažurirana stranica: :pageName',
+ 'updated_page_intro' => 'Stranica je ažurirana u :appName:',
+ 'updated_page_debounce' => 'Kako biste spriječili velik broj obavijesti, nećete primati obavijesti o daljnjim izmjenama ove stranice od istog urednika neko vrijeme.',
+
+ 'detail_page_name' => 'Naziv Stranice:',
+ 'detail_commenter' => 'Komentator:',
+ 'detail_comment' => 'Komentar:',
+ 'detail_created_by' => 'Kreirao Korisnik:',
+ 'detail_updated_by' => 'Ažurirao Korisnik:',
+
+ 'action_view_comment' => 'Pogledaj Komentar',
+ 'action_view_page' => 'Pogledaj Stranicu',
+
+ 'footer_reason' => 'Ova obavijest vam je poslana jer :link pokriva ovu vrstu aktivnosti za ovu stavku.',
+ 'footer_reason_link' => 'vaše postavke obavijesti',
+];
*/
return [
+ 'preferences' => 'Postavke',
+
'shortcuts' => 'Prečaci',
'shortcuts_interface' => 'Prečaci tipkovnice u Sučelju',
'shortcuts_toggle_desc' => 'Ovdje možete omogućiti ili onemogućiti prečace tastature u korisničkom sučelju sustava koji se koriste za navigaciju i akcije.',
'shortcuts_save' => 'Spremi prečace',
'shortcuts_overlay_desc' => 'Napomena: Kada su prečaci tastature omogućeni, dostupan je pomoćni prikaz preko pritiska na znak "?" koji će istaknuti dostupne prečace za radnje trenutno vidljive na zaslonu.',
'shortcuts_update_success' => 'Postavke prečaca su ažurirane!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Upravljajte prečacima tastature koje možete koristiti za navigaciju korisničkim sučeljem sustava.',
+
+ 'notifications' => 'Postavke Obavijesti',
+ 'notifications_desc' => 'Kontrolirajte e-mail obavijesti koje primate kada se određene aktivnosti izvrše unutar sustava.',
+ 'notifications_opt_own_page_changes' => 'Obavijesti o promjenama na stranicama koje posjedujem',
+ 'notifications_opt_own_page_comments' => 'ChatGPT
+
+Obavijesti o komentarima na stranicama koje posjedujem',
+ 'notifications_opt_comment_replies' => 'Obavijesti o odgovorima na moje komentare',
+ 'notifications_save' => 'Spremi Postavke',
+ 'notifications_update_success' => 'Postavke obavijesti su ažurirane!',
+ 'notifications_watched' => 'Praćene i ignorirane stavke',
+ 'notifications_watched_desc' => ' Ispod su stavke na koje su primijenjene prilagođene postavke praćenja. Da biste ažurirali svoje postavke za ove stavke, pregledajte stavku, a zatim pronađite opcije praćenja u bočnoj traci.',
+
+ 'profile_overview_desc' => ' Upravljajte detaljima svog korisničkog profila, uključujući željeni jezik i opcije za autentifikaciju.',
+];
'role_manage_settings' => 'Upravljanje postavkama aplikacija',
'role_export_content' => 'Izvoz sadržaja',
'role_editor_change' => 'Promijeni uređivač stranica',
+ 'role_notifications' => 'Primanje i upravljanje obavijestima',
'role_asset' => 'Upravljanje vlasništvom',
'roles_system_warning' => 'Uzmite u obzir da pristup bilo kojem od ovih dopuštenja dozvoljavate korisniku upravljanje dopuštenjima ostalih u sustavu. Ova dopuštenja dodijelite pouzdanim korisnicima.',
'role_asset_desc' => 'Ova dopuštenja kontroliraju zadane pristupe. Dopuštenja za knjige, poglavlja i stranice ih poništavaju.',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'megjegyzést fűzött hozzá:',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'updated permissions',
];
'remove' => 'Eltávolítás',
'add' => 'Hozzáadás',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Teljes képernyő',
'favourite' => 'Kedvencekhez ad',
'unfavourite' => 'Kedvencekből eltávolít',
// General editor terms
'general' => 'Általános',
'advanced' => 'Haladó',
- 'none' => 'None',
+ 'none' => 'Egyik sem',
'cancel' => 'Mégsem',
'save' => 'Mentés',
'close' => 'Bezárás',
'width' => 'Szélesség',
'height' => 'Magasság',
'More' => 'Több',
- 'select' => 'Select...',
+ 'select' => 'Kiválasztás...',
// Toolbar
'formats' => 'Formátumok',
'insert_link_title' => 'Hivatkozás Beszúrása/Szerkesztése',
'insert_horizontal_line' => 'Vízszintes vonal beszúrása',
'insert_code_block' => 'Kódrészlet beszúrása',
- 'edit_code_block' => 'Edit code block',
+ 'edit_code_block' => 'Kódrészlet beszúrása',
'insert_drawing' => 'Rajz beszúrása/szerkesztése',
'drawing_manager' => 'Rajzkezelő',
'insert_media' => 'Media beszúrása/szerkesztése',
'pages_md_insert_drawing' => 'Rajz beillesztése',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Az oldal nincs fejezetben',
'pages_move' => 'Oldal áthelyezése',
'pages_copy' => 'Oldal másolása',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Hiba történt egy teszt email küldésekor:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Alkalmazás beállításainak kezelése',
'role_export_content' => 'Tartalom exportálása',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Eszköz jogosultságok',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'Ezek a jogosultságok vezérlik az alapértelmezés szerinti hozzáférést a rendszerben található eszközökhöz. A könyvek, fejezetek és oldalak jogosultságai felülírják ezeket a jogosultságokat.',
'favourite_add_notification' => '":name" telah ditambahkan ke favorit Anda',
'favourite_remove_notification' => '":name" telah dihapus dari favorit Anda',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'berkomentar pada',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'izin diperbarui',
];
'remove' => 'Hapus',
'add' => 'Tambah',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Layar Penuh',
'favourite' => 'Favorit',
'unfavourite' => 'Batal favorit',
'pages_md_insert_drawing' => 'Sisipkan Gambar',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Halaman tidak dalam satu bab',
'pages_move' => 'Pindahkan Halaman',
'pages_copy' => 'Salin Halaman',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Kesalahan dilempar saat mengirim email uji:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Kelola setelan aplikasi',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Izin Aset',
'roles_system_warning' => 'Ketahuilah bahwa akses ke salah satu dari tiga izin di atas dapat memungkinkan pengguna untuk mengubah hak mereka sendiri atau orang lain dalam sistem. Hanya tetapkan peran dengan izin ini untuk pengguna tepercaya.',
'role_asset_desc' => 'Izin ini mengontrol akses default ke aset dalam sistem. Izin pada Buku, Bab, dan Halaman akan menggantikan izin ini.',
'favourite_add_notification' => '":name" è stato aggiunto ai tuoi preferiti',
'favourite_remove_notification' => '":name" è stato rimosso dai tuoi preferiti',
+ // Watching
+ 'watch_update_level_notification' => 'Preferenze di monitoraggio aggiornate con successo',
+
// Auth
'auth_login' => 'connesso',
'auth_register' => 'registrato come nuovo utente',
'recycle_bin_restore' => 'ripristinato dal cestino',
'recycle_bin_destroy' => 'rimosso dal cestino',
- // Other
+ // Comments
'commented_on' => 'ha commentato in',
+ 'comment_create' => 'commento aggiunto',
+ 'comment_update' => 'commento aggiornato',
+ 'comment_delete' => 'commento rimosso',
+
+ // Other
'permissions_update' => 'autorizzazioni aggiornate',
];
'remove' => 'Rimuovi',
'add' => 'Aggiungi',
'configure' => 'Configura',
+ 'manage' => 'Gestisci',
'fullscreen' => 'Schermo intero',
'favourite' => 'Aggiungi ai Preferiti',
'unfavourite' => 'Rimuovi dai preferiti',
'shelves_permissions_updated' => 'Permessi Libreria Aggiornati',
'shelves_permissions_active' => 'Permessi Libreria Attivi',
'shelves_permissions_cascade_warning' => 'I permessi delle librerie non si estendono automaticamente ai libri contenuti. Questo perché un libro può essere presente su più scaffali. I permessi possono comunque essere copiati ai libri al suo interno usando l\'opzione sottostante.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Le autorizzazioni per la creazione di librerie sono utilizzate solo per copiare le autorizzazioni ai libri figli utilizzando l\'azione sottostante. Non controllano la capacità di creare libri.',
'shelves_copy_permissions_to_books' => 'Copia Permessi ai Libri',
'shelves_copy_permissions' => 'Copia Permessi',
'shelves_copy_permissions_explain' => 'Verranno applicati tutti i permessi della libreria ai libri al suo interno. Prima dell\'attivazione, assicurati che ogni permesso di questa libreria sia salvato.',
'pages_md_insert_drawing' => 'Inserisci Disegno',
'pages_md_show_preview' => 'Visualizza anteprima',
'pages_md_sync_scroll' => 'Sincronizza scorrimento anteprima',
+ 'pages_drawing_unsaved' => 'Trovato Disegno Non Salvato',
+ 'pages_drawing_unsaved_confirm' => 'Sono stati trovati i dati di un disegno non salvati da un precedente tentativo di salvataggio di disegno non riuscito. Ripristinare e continuare a modificare questo disegno non salvato?',
'pages_not_in_chapter' => 'La pagina non è in un capitolo',
'pages_move' => 'Muovi Pagina',
'pages_copy' => 'Copia Pagina',
'references' => 'Riferimenti',
'references_none' => 'Non ci sono riferimenti tracciati a questa voce.',
'references_to_desc' => 'Di seguito sono riportate tutte le pagine conosciute nel sistema che collegano a questo elemento.',
+
+ // Watch Options
+ 'watch' => 'Osserva',
+ 'watch_title_default' => 'Preferenze Predefinite',
+ 'watch_desc_default' => 'Ripristina la visualizzazione delle tue preferenze di notifica predefinite.',
+ 'watch_title_ignore' => 'Ignora',
+ 'watch_desc_ignore' => 'Ignora tutte le notifiche, comprese quelle dalle preferenze di livello utente.',
+ 'watch_title_new' => 'Nuove Pagine',
+ 'watch_desc_new' => 'Notifica quando viene creata una nuova pagina all\'interno di questo elemento.',
+ 'watch_title_updates' => 'Tutti Gli Aggiornamenti Della Pagina',
+ 'watch_desc_updates' => 'Notificare su tutte le nuove pagine e modifiche di pagina.',
+ 'watch_desc_updates_page' => 'Notifica su tutte le modifiche alla pagina.',
+ 'watch_title_comments' => 'Tutti Gli Aggiornamenti Della Pagina E Commenti',
+ 'watch_desc_comments' => 'Notificare su tutte le nuove pagine, modifiche di pagina e nuovi commenti.',
+ 'watch_desc_comments_page' => 'Notificare le modifiche alla pagina e i nuovi commenti.',
+ 'watch_change_default' => 'Modifica le preferenze di notifica predefinite',
+ 'watch_detail_ignore' => 'Ignorare le notifiche',
+ 'watch_detail_new' => 'In attesa di nuove pagine',
+ 'watch_detail_updates' => 'Osservare le nuove pagine e gli aggiornamenti',
+ 'watch_detail_comments' => 'Osservare le nuove pagine, aggiornamenti e commenti',
+ 'watch_detail_parent_book' => 'Osservare tramite il libro madre',
+ 'watch_detail_parent_book_ignore' => 'Ignorato tramite il libro madre',
+ 'watch_detail_parent_chapter' => 'Osservare tramite il capitolo madre',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorato tramite il capitolo madre',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Si è verificato un errore durante l\'invio di una e-mail di prova:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'L\'URL non corrisponde agli host SSR configurati',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nuovo commento alla pagina: :pageName',
+ 'new_comment_intro' => 'Un utente ha commentato una pagina in :appName:',
+ 'new_page_subject' => 'Nuova pagina: :pageName',
+ 'new_page_intro' => 'Una nuova pagina è stata creata in :appName:',
+ 'updated_page_subject' => 'Pagina aggiornata: :pageName',
+ 'updated_page_intro' => 'Una pagina è stata aggiornata in :appName:',
+ 'updated_page_debounce' => 'Per evitare una massa di notifiche, per un po \'non verranno inviate notifiche per ulteriori modifiche a questa pagina dallo stesso editor.',
+
+ 'detail_page_name' => 'Nome Della Pagina:',
+ 'detail_commenter' => 'Commentatore:',
+ 'detail_comment' => 'Commento:',
+ 'detail_created_by' => 'Creato Da:',
+ 'detail_updated_by' => 'Aggiornato Da:',
+
+ 'action_view_comment' => 'Visualizza Commento',
+ 'action_view_page' => 'Visualizza Pagina',
+
+ 'footer_reason' => 'Questa notifica è stata inviata perché :link copre questo tipo di attività per questo elemento.',
+ 'footer_reason_link' => 'le tue preferenze di notifica',
+];
*/
return [
+ 'preferences' => 'Preferenze',
+
'shortcuts' => 'Scorciatoie',
'shortcuts_interface' => 'Interfaccia scorciatoie da tastiera',
'shortcuts_toggle_desc' => 'Qui puoi abilitare o disabilitare le scorciatoie dell\'interfaccia di sistema da tastiera, utilizzate per la navigazione e le azioni.',
'shortcuts_save' => 'Salva Scorciatoie',
'shortcuts_overlay_desc' => 'Nota: quando le scorciatoie sono abilitate, premendo "?" è possibile visualizzare le scorciatoie disponibili per le azioni attualmente visibili sullo schermo.',
'shortcuts_update_success' => 'Le preferenze delle scorciatoie sono state aggiornate!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gestisci le scorciatoie da tastiera che puoi usare per navigare nell\'interfaccia utente di sistema.',
+
+ 'notifications' => 'Preferenze Di Notifica',
+ 'notifications_desc' => 'Controlla le notifiche email che ricevi quando viene eseguita una determinata attività all\'interno del sistema.',
+ 'notifications_opt_own_page_changes' => 'Notifica in caso di modifiche alle pagine che possiedo',
+ 'notifications_opt_own_page_comments' => 'Notifica i commenti sulle pagine che possiedo',
+ 'notifications_opt_comment_replies' => 'Notificare le risposte ai miei commenti',
+ 'notifications_save' => 'Salva Preferenze',
+ 'notifications_update_success' => 'Le preferenze di notifica sono state aggiornate!',
+ 'notifications_watched' => 'Oggetti Osservati E Ignorati',
+ 'notifications_watched_desc' => 'Di seguito sono riportati gli articoli a cui sono state applicate le preferenze di monitoraggio personalizzate. Per aggiornare le preferenze, visualizzare l\'articolo e trovare le opzioni di monitoraggio nella barra laterale.',
+
+ 'profile_overview_desc' => ' Gestisci i dettagli del tuo profilo utente, incluse la lingua preferita e le opzioni di autenticazione.',
+];
'role_manage_settings' => 'Gestire impostazioni app',
'role_export_content' => 'Esporta contenuto',
'role_editor_change' => 'Cambia editor di pagina',
+ 'role_notifications' => 'Ricevi & gestisci le notifiche',
'role_asset' => 'Permessi Entità',
'roles_system_warning' => 'Siate consapevoli che l\'accesso a uno dei tre permessi qui sopra, può consentire a un utente di modificare i propri privilegi o i privilegi di altri nel sistema. Assegna ruoli con questi permessi solo ad utenti fidati.',
'role_asset_desc' => 'Questi permessi controllano l\'accesso di default alle entità. I permessi nei Libri, Capitoli e Pagine sovrascriveranno questi.',
'favourite_add_notification' => '":name"がお気に入りに追加されました',
'favourite_remove_notification' => '":name"がお気に入りから削除されました',
+ // Watching
+ 'watch_update_level_notification' => 'ウォッチ設定を更新しました',
+
// Auth
'auth_login' => 'がログイン',
'auth_register' => 'が新規ユーザ登録',
'recycle_bin_restore' => 'がゴミ箱から復元',
'recycle_bin_destroy' => 'がゴミ箱から完全に削除',
- // Other
+ // Comments
'commented_on' => 'がコメント:',
+ 'comment_create' => 'がコメントを追加',
+ 'comment_update' => 'がコメントを更新',
+ 'comment_delete' => 'がコメントを削除',
+
+ // Other
'permissions_update' => 'が権限を更新:',
];
'remove' => '削除',
'add' => '追加',
'configure' => '設定',
+ 'manage' => '管理',
'fullscreen' => '全画面',
'favourite' => 'お気に入り',
'unfavourite' => 'お気に入りから削除',
'shelves_permissions_updated' => '本棚の権限を更新しました',
'shelves_permissions_active' => '本棚の権限は有効です',
'shelves_permissions_cascade_warning' => '本棚の権限は含まれる本には自動的に継承されません。これは、1つのブックが複数の本棚に存在する可能性があるためです。ただし、以下のオプションを使用すると権限を子ブックにコピーできます。',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => '本棚の作成権限は、以下のアクションを使用した子ブックへの権限コピーにのみ使用されます。これはブックの作成を制御するものではありません。',
'shelves_copy_permissions_to_books' => 'ブックに権限をコピー',
'shelves_copy_permissions' => '権限をコピー',
'shelves_copy_permissions_explain' => 'これにより、この本棚の現在の権限設定を本棚に含まれるすべてのブックに適用します。有効にする前に、この本棚の権限への変更が保存されていることを確認してください。',
'pages_md_preview' => 'プレビュー',
'pages_md_insert_image' => '画像を挿入',
'pages_md_insert_link' => 'エンティティへのリンクを挿入',
- 'pages_md_insert_drawing' => '描画を追加',
+ 'pages_md_insert_drawing' => '図を追加',
'pages_md_show_preview' => 'プレビューを表示',
'pages_md_sync_scroll' => 'プレビューとスクロールを同期',
+ 'pages_drawing_unsaved' => '未保存の図が見つかりました',
+ 'pages_drawing_unsaved_confirm' => '以前に保存操作が失敗した、未保存の図が見つかりました。
+未保存の図面を復元して編集を続けますか?',
'pages_not_in_chapter' => 'チャプターが設定されていません',
'pages_move' => 'ページを移動',
'pages_copy' => 'ページをコピー',
'comments' => 'コメント',
'comment_add' => 'コメント追加',
'comment_placeholder' => 'コメントを記入してください',
- 'comment_count' => '{0} コメントはありません|[1,*] コメント:count件',
+ 'comment_count' => '{0} コメントはありません|[1,*] :count 件のコメント',
'comment_save' => 'コメントを保存',
'comment_new' => '新規コメント作成',
'comment_created' => 'コメントを作成しました :createDiff',
'references' => '参照',
'references_none' => 'この項目への追跡された参照はありません。',
'references_to_desc' => 'この項目にリンクしている、システム内のすべての既知のページを以下に示します。',
+
+ // Watch Options
+ 'watch' => 'ウォッチ',
+ 'watch_title_default' => 'デフォルト設定',
+ 'watch_desc_default' => 'デフォルトの通知設定に戻します。',
+ 'watch_title_ignore' => '無効',
+ 'watch_desc_ignore' => 'ユーザーの通知設定に関わらず、すべての通知を無効にします。',
+ 'watch_title_new' => 'ページの作成',
+ 'watch_desc_new' => 'このアイテム内に新しいページが作成されたときに通知します。',
+ 'watch_title_updates' => 'すべてのページ更新',
+ 'watch_desc_updates' => 'ページの作成や更新を通知します。',
+ 'watch_desc_updates_page' => 'ページの更新を通知します。',
+ 'watch_title_comments' => 'すべてのページ更新とコメント',
+ 'watch_desc_comments' => 'ページの作成・更新、およびコメント追加を通知します。',
+ 'watch_desc_comments_page' => 'ページの更新およびコメント追加を通知します。',
+ 'watch_change_default' => 'デフォルトの通知設定を変更する',
+ 'watch_detail_ignore' => '通知無効',
+ 'watch_detail_new' => 'ページ作成をウォッチ',
+ 'watch_detail_updates' => 'ページの作成と更新をウォッチ',
+ 'watch_detail_comments' => 'ページの作成・更新とコメントをウォッチ',
+ 'watch_detail_parent_book' => '親ブックでウォッチ',
+ 'watch_detail_parent_book_ignore' => '親ブックで通知無効',
+ 'watch_detail_parent_chapter' => '親チャプタでウォッチ',
+ 'watch_detail_parent_chapter_ignore' => '親チャプタで通知無効',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'テストメール送信時にエラーが発生しました:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URLはサーバサイドリクエストが許可されたホストではありません。',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'ページへのコメント追加: :pageName',
+ 'new_comment_intro' => ':appName でページにコメントが追加されました',
+ 'new_page_subject' => 'ページの作成: :pageName',
+ 'new_page_intro' => ':appName でページが作成されました',
+ 'updated_page_subject' => 'ページの更新: :pageName',
+ 'updated_page_intro' => ':appName でページが更新されました',
+ 'updated_page_debounce' => '大量の通知を防ぐために、しばらくの間は同じユーザがこのページをさらに編集しても通知は送信されません。',
+
+ 'detail_page_name' => 'ページ名:',
+ 'detail_commenter' => 'コメントユーザ:',
+ 'detail_comment' => 'コメント:',
+ 'detail_created_by' => '作成ユーザ:',
+ 'detail_updated_by' => '更新ユーザ:',
+
+ 'action_view_comment' => 'コメントを表示',
+ 'action_view_page' => 'ページを表示',
+
+ 'footer_reason' => 'この項目のアクティビティは :link による対象となっているため、この通知が送信されました。',
+ 'footer_reason_link' => '通知設定',
+];
*/
return [
+ 'preferences' => '設定',
+
'shortcuts' => 'ショートカット',
'shortcuts_interface' => 'インターフェイスのキーボードショートカット',
'shortcuts_toggle_desc' => 'ここでは、ナビゲーションやアクションに使用されるキーボードシステムインターフェイスのショートカットを有効または無効にすることができます。',
'shortcuts_section_actions' => '共通のアクション',
'shortcuts_save' => 'ショートカットを保存',
'shortcuts_overlay_desc' => '注:ショートカットが有効な場合はヘルパーオーバーレイが利用できます。「?」を押すと現在画面に表示されているアクションで利用可能なショートカットをハイライト表示します。',
- 'shortcuts_update_success' => 'ショートカットの設定が更新されました!',
-];
\ No newline at end of file
+ 'shortcuts_update_success' => 'ショートカットの設定を更新しました。',
+ 'shortcuts_overview_desc' => 'システムのユーザーインターフェイスを操作するためのキーボードショートカットを管理します。',
+
+ 'notifications' => '通知設定',
+ 'notifications_desc' => 'システム内で特定のアクティビティが実行されたときに受信する電子メール通知を制御します。',
+ 'notifications_opt_own_page_changes' => '自分が所有するページの変更を通知する',
+ 'notifications_opt_own_page_comments' => '自分が所有するページへのコメントを通知する',
+ 'notifications_opt_comment_replies' => '自分のコメントへの返信を通知する',
+ 'notifications_save' => '設定を保存',
+ 'notifications_update_success' => '通知設定を更新しました。',
+ 'notifications_watched' => 'ウォッチ/通知無効 項目',
+ 'notifications_watched_desc' => ' 以下はカスタムウォッチの設定が適用されている項目です。 これらの設定を更新するには、項目を表示してサイドバーのウォッチオプションを参照してください。',
+
+ 'profile_overview_desc' => ' 言語や認証オプションを含むユーザープロファイルの詳細を管理します。',
+];
'role_manage_settings' => 'アプリケーション設定の管理',
'role_export_content' => 'コンテンツのエクスポート',
'role_editor_change' => 'ページエディタの変更',
+ 'role_notifications' => '通知の受信と管理',
'role_asset' => 'アセット権限',
'roles_system_warning' => '上記の3つの権限のいずれかを付与することは、ユーザーが自分の特権またはシステム内の他のユーザーの特権を変更できる可能性があることに注意してください。これらの権限は信頼できるユーザーにのみ割り当ててください。',
'role_asset_desc' => '各アセットに対するデフォルトの権限を設定します。ここで設定した権限が優先されます。',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'commented on',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'updated permissions',
];
'remove' => 'Remove',
'add' => 'Add',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Fullscreen',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Insert Drawing',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Manage app settings',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Asset Permissions',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'favourite_add_notification' => '":name" 북마크에 추가함',
'favourite_remove_notification' => '":name" 북마크에서 삭제함',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => '댓글 쓰기',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => '권한 수정함',
];
'remove' => '제거',
'add' => '추가',
'configure' => '설정',
+ 'manage' => 'Manage',
'fullscreen' => '전체화면',
'favourite' => '즐겨찾기',
'unfavourite' => '즐겨찾기 해제',
'pages_md_insert_drawing' => '드로잉 추가',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => '챕터에 있는 문서가 아닙니다.',
'pages_move' => '문서 이동하기',
'pages_copy' => '문서 복제',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => '메일을 발송하는 도중 문제가 생겼습니다:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => '단축키',
'shortcuts_interface' => '키보드 단축키',
'shortcuts_toggle_desc' => '여기에서 탐색과 행동에 사용될 수 있는 키보드 단축키를 활성화하거나 비활성화할 수 있습니다.',
'shortcuts_save' => '단축키 저장',
'shortcuts_overlay_desc' => '참고: 바로가기가 활성화된 경우 "?"를 누르면 현재 화면에 표시되는 작업에 대해 사용 가능한 바로가기를 강조 표시하는 도우미 오버레이를 사용할 수 있습니다.',
'shortcuts_update_success' => '단축키 설정이 수정되었습니다!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => '사이트 설정 관리',
'role_export_content' => '항목 내보내기',
'role_editor_change' => '페이지 편집기 변경',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => '권한 항목',
'roles_system_warning' => '위 세 권한은 자신의 권한이나 다른 유저의 권한을 바꿀 수 있습니다.',
'role_asset_desc' => '책, 챕터, 문서별 권한은 이 설정에 우선합니다.',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'pakomentavo',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'atnaujinti leidimai',
];
'remove' => 'Pašalinti',
'add' => 'Pridėti',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Visas ekranas',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Įterpti piešinį',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Puslapio nėra skyriuje',
'pages_move' => 'Perkelti puslapį',
'pages_copy' => 'Nukopijuoti puslapį',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Siunčiant bandymo email: įvyko klaida',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Tvarkyti programos nustatymus',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Nuosavybės leidimai',
'roles_system_warning' => 'Būkite sąmoningi, kad prieiga prie bet kurio iš trijų leidimų viršuje gali leisti naudotojui pakeisti jų pačių privilegijas arba kitų privilegijas sistemoje. Paskirkite vaidmenis su šiais leidimais tik patikimiems naudotojams.',
'role_asset_desc' => 'Šie leidimai kontroliuoja numatytą prieigą į nuosavybę, esančią sistemoje. Knygų, skyrių ir puslapių leidimai nepaisys šių leidimų.',
'favourite_add_notification' => '":name" ir pievienots jūsu favorītiem',
'favourite_remove_notification' => '":name" ir izņemts no jūsu favorītiem',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'komentēts',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'atjaunoja atļaujas',
];
'remove' => 'Noņemt',
'add' => 'Pievienot',
'configure' => 'Mainīt konfigurāciju',
+ 'manage' => 'Manage',
'fullscreen' => 'Pilnekrāns',
'favourite' => 'Pievienot favorītiem',
'unfavourite' => 'Noņemt no favorītiem',
'pages_md_insert_drawing' => 'Ievietot zīmējumu',
'pages_md_show_preview' => 'Rādīt priekšskatu',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Lapa nav nodaļā',
'pages_move' => 'Pārvietot lapu',
'pages_copy' => 'Kopēt lapu',
'references' => 'Atsauces',
'references_none' => 'Uz šo vienumu nav atrasta neviena atsauce.',
'references_to_desc' => 'Zemāk parādītas visas sistēmā atrastās lapas, kas norāda uz šo vienumu.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Radusies kļūda sūtot testa epastu:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Saīsnes',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Saglabāt saīsnes',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Saīsņu uzstādījumi ir saglabāt!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Pārvaldīt iestatījumus',
'role_export_content' => 'Eksportēt saturu',
'role_editor_change' => 'Mainīt lapu redaktoru',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Resursa piekļuves tiesības',
'roles_system_warning' => 'Jebkuras no trīs augstāk redzamajām atļaujām dod iespēju lietotājam mainīt savas un citu lietotāju sistēmas atļaujas. Pievieno šīs grupu atļaujas tikai tiem lietotājiem, kuriem uzticies.',
'role_asset_desc' => 'Šīs piekļuves tiesības kontrolē noklusēto piekļuvi sistēmas resursiem. Grāmatām, nodaļām un lapām norādītās tiesības būs pārākas par šīm.',
'page_restore' => 'gjenopprettet side',
'page_restore_notification' => 'Siden ble gjenopprettet',
'page_move' => 'flyttet side',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'Siden ble flyttet',
// Chapters
'chapter_create' => 'opprettet kapittel',
'chapter_delete_notification' => 'Kapittelet ble slettet',
'chapter_move' => 'flyttet kapittel
',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'Kapitelet ble flyttet',
// Books
'book_create' => 'opprettet bok',
'bookshelf_delete_notification' => 'Hyllen ble slettet',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => 'gjenopprettet revisjon',
+ 'revision_delete' => 'slettet revisjon',
+ 'revision_delete_notification' => 'Revisjon slettet',
// Favourites
'favourite_add_notification' => '«:name» ble lagt til i dine favoritter',
'favourite_remove_notification' => '«:name» ble fjernet fra dine favoritter',
+ // Watching
+ 'watch_update_level_notification' => 'Overvåkingsinnstillingene ble oppdatert',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => 'logget inn',
+ 'auth_register' => 'registrert som ny bruker',
+ 'auth_password_reset_request' => 'etterspurt tilbakestilling av passord',
+ 'auth_password_reset_update' => 'tilbakestill bruker passord',
+ 'mfa_setup_method' => 'konfigurert MFA-metode',
'mfa_setup_method_notification' => 'Flerfaktor-metoden ble konfigurert',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => 'fjernet MFA-metode',
'mfa_remove_method_notification' => 'Flerfaktor-metoden ble fjernet',
// Settings
- 'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'settings_update' => 'oppdaterte innstillinger',
+ 'settings_update_notification' => 'Innstillingene er oppdatert',
+ 'maintenance_action_run' => 'kjørte vedlikeholdshandling',
// Webhooks
'webhook_create' => 'opprettet webhook',
'webhook_delete_notification' => 'Webhook ble slettet',
// Users
- 'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
- 'user_update' => 'updated user',
+ 'user_create' => 'opprettet bruker',
+ 'user_create_notification' => 'Bruker ble opprettet',
+ 'user_update' => 'oppdatert bruker',
'user_update_notification' => 'Brukeren ble oppdatert',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'slettet bruker',
'user_delete_notification' => 'Brukeren ble fjernet',
// API Tokens
- 'api_token_create' => 'created api token',
- 'api_token_create_notification' => 'API token successfully created',
- 'api_token_update' => 'updated api token',
- 'api_token_update_notification' => 'API token successfully updated',
- 'api_token_delete' => 'deleted api token',
- 'api_token_delete_notification' => 'API token successfully deleted',
+ 'api_token_create' => 'opprettet api token',
+ 'api_token_create_notification' => 'API-token er opprettet',
+ 'api_token_update' => 'oppdatert api token',
+ 'api_token_update_notification' => 'API-token oppdatert',
+ 'api_token_delete' => 'slettet api token',
+ 'api_token_delete_notification' => 'API-token ble slettet',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => 'opprettet rolle',
'role_create_notification' => 'Rollen ble opprettet',
- 'role_update' => 'updated role',
+ 'role_update' => 'oppdatert rolle',
'role_update_notification' => 'Rollen ble oppdatert',
- 'role_delete' => 'deleted role',
+ 'role_delete' => 'slettet rolle',
'role_delete_notification' => 'Rollen ble fjernet',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => 'tømt resirkulering bin',
+ 'recycle_bin_restore' => 'gjenopprettet fra papirkurven',
+ 'recycle_bin_destroy' => 'fjernet fra papirkurven',
- // Other
+ // Comments
'commented_on' => 'kommenterte på',
+ 'comment_create' => 'lagt til kommentar',
+ 'comment_update' => 'oppdatert kommentar',
+ 'comment_delete' => 'slettet kommentar',
+
+ // Other
'permissions_update' => 'oppdaterte tilganger',
];
'remove' => 'Fjern',
'add' => 'Legg til',
'configure' => 'Konfigurer',
+ 'manage' => 'Administrer',
'fullscreen' => 'Fullskjerm',
'favourite' => 'Favorisér',
'unfavourite' => 'Avfavorisér',
'shelves_permissions_updated' => 'Oppdaterte hyllerettigheter',
'shelves_permissions_active' => 'Aktiverte hyllerettigheter',
'shelves_permissions_cascade_warning' => 'Rettigheter på en hylle blir ikke automatisk arvet av bøker på hylla. Dette er fordi en bok kan finnes på flere hyller samtidig. Rettigheter kan likevel kopieres til bøker på hylla ved å bruke alternativene under.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Bokhylle-tillatelser brukes kun for kopiering av tillatelser til under-bøker ved hjelp av handlingen nedenfor. De kontrollerer ikke muligheten til å lage bøker.',
'shelves_copy_permissions_to_books' => 'Kopier tilganger til bøkene på hylla',
'shelves_copy_permissions' => 'Kopier tilganger',
'shelves_copy_permissions_explain' => 'Dette vil kopiere rettighetene på denne hylla til alle bøkene som er plassert på den. Før du starter kopieringen bør du sjekke at rettighetene på hylla er lagret først.',
'pages_editing_page' => 'Redigerer side',
'pages_edit_draft_save_at' => 'Sist lagret ',
'pages_edit_delete_draft' => 'Slett utkast',
- 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.',
+ 'pages_edit_delete_draft_confirm' => 'Er du sikker på at du vil slette utkastendringer i utkastet? Alle dine endringer, siden siste lagring vil gå tapt, og editoren vil bli oppdatert med den siste siden uten utkast til lagring.',
'pages_edit_discard_draft' => 'Tilbakestill endring',
'pages_edit_switch_to_markdown' => 'Bytt til Markdown tekstredigering',
'pages_edit_switch_to_markdown_clean' => '(Renset innhold)',
'pages_md_insert_drawing' => 'Sett inn tegning',
'pages_md_show_preview' => 'Forhåndsvisning',
'pages_md_sync_scroll' => 'Synkroniser forhåndsvisningsrulle',
+ 'pages_drawing_unsaved' => 'Ulagret tegning funnet',
+ 'pages_drawing_unsaved_confirm' => 'Ulagret tegningsdata ble funnet fra en tidligere mislykket lagring. Vil du gjenopprette og fortsette å redigere denne ulagrede tegningen?',
'pages_not_in_chapter' => 'Siden tilhører ingen kapittel',
'pages_move' => 'Flytt side',
'pages_copy' => 'Kopiér side',
'time_b' => 'i løpet av de siste :minCount minuttene',
'message' => ':start :time. Prøv å ikke overskriv hverandres endringer!',
],
- 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content',
- 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content',
+ 'pages_draft_discarded' => 'Utkastet er forkastet! Redigeringsprogrammet er oppdatert med gjeldende sideinnhold',
+ 'pages_draft_deleted' => 'Utkast slettet! Redigeringsprogrammet er oppdatert med gjeldende sideinnhold',
'pages_specific' => 'Bestemt side',
'pages_is_template' => 'Sidemal',
'comment_new' => 'Ny kommentar',
'comment_created' => 'kommenterte :createDiff',
'comment_updated' => 'Oppdatert :updateDiff av :username',
- 'comment_updated_indicator' => 'Updated',
+ 'comment_updated_indicator' => 'Oppdatert',
'comment_deleted_success' => 'Kommentar fjernet',
'comment_created_success' => 'Kommentar skrevet',
'comment_updated_success' => 'Kommentar endret',
'comment_delete_confirm' => 'Er du sikker på at du vil fjerne kommentaren?',
'comment_in_reply_to' => 'Som svar til :commentId',
- 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
+ 'comment_editor_explain' => 'Her er kommentarene som er på denne siden. Kommentarer kan legges til og administreres når du ser på den lagrede siden.',
// Revision
'revision_delete_confirm' => 'Vil du slette revisjonen?',
'references' => 'Referanser',
'references_none' => 'Det er ingen sporede referanser til dette elementet.',
'references_to_desc' => 'Nedenfor vises alle de kjente sidene i systemet som lenker til denne oppføringen.',
+
+ // Watch Options
+ 'watch' => 'Overvåk',
+ 'watch_title_default' => 'Standardinnstillinger',
+ 'watch_desc_default' => 'Bytt til dine standardinnstilleringer for varsling.',
+ 'watch_title_ignore' => 'Ignorer',
+ 'watch_desc_ignore' => 'Ignorer alle varslinger, inkludert de fra preferanser for brukernivå.',
+ 'watch_title_new' => 'Nye sider',
+ 'watch_desc_new' => 'Varsle når en ny side er opprettet innenfor dette elementet.',
+ 'watch_title_updates' => 'Alle sideoppdateringer',
+ 'watch_desc_updates' => 'Varsle på alle nye sider og endringer av siden.',
+ 'watch_desc_updates_page' => 'Varsle ved alle sideendringer.',
+ 'watch_title_comments' => 'Alle sideoppdateringer og kommentarer',
+ 'watch_desc_comments' => 'Varsle om alle nye sider, endringer på side og nye kommentarer.',
+ 'watch_desc_comments_page' => 'Varsle ved sideendringer og nye kommentarer.',
+ 'watch_change_default' => 'Endre standard varslingsinnstillinger',
+ 'watch_detail_ignore' => 'Ignorerer varsler',
+ 'watch_detail_new' => 'Varsling for nye sider',
+ 'watch_detail_updates' => 'Varsling for nye sider og oppdateringer',
+ 'watch_detail_comments' => 'Varsling for nye sider, oppdateringer og kommentarer',
+ 'watch_detail_parent_book' => 'Overvåker via overordnet bok',
+ 'watch_detail_parent_book_ignore' => 'Ignorerer via overordnet bok',
+ 'watch_detail_parent_chapter' => 'Overvåker via overordnet kapittel',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorerer via overordnet kapittel',
];
// Pages
'page_draft_autosave_fail' => 'Kunne ikke lagre utkastet, forsikre deg om at du er tilkoblet tjeneren (Har du nettilgang?)',
- 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content',
+ 'page_draft_delete_fail' => 'Kunne ikke slette sideutkast og hente gjeldende side lagret innhold',
'page_custom_home_deletion' => 'Kan ikke slette en side som er satt som forside.',
// Entities
// Settings & Maintenance
'maintenance_test_email_failure' => 'Feil kastet når du sendte en test-e-post:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URLen samsvarer ikke med de konfigurerte SSR-vertene',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Ny kommentar på siden: :pageName',
+ 'new_comment_intro' => 'En bruker har kommentert en side i :appName:',
+ 'new_page_subject' => 'Ny side: :pageName',
+ 'new_page_intro' => 'En ny side er opprettet i :appName:',
+ 'updated_page_subject' => 'Oppdatert side: :pageName',
+ 'updated_page_intro' => 'En side er oppdatert i :appName:',
+ 'updated_page_debounce' => 'For å forhindre mange varslinger, vil du ikke få nye varslinger for endringer på denne siden fra samme forfatter.',
+
+ 'detail_page_name' => 'Sidenavn:',
+ 'detail_commenter' => 'Kommentar fra:',
+ 'detail_comment' => 'Kommentar:',
+ 'detail_created_by' => 'Opprettet av:',
+ 'detail_updated_by' => 'Oppdatert av:',
+
+ 'action_view_comment' => 'Vis kommentar',
+ 'action_view_page' => 'Se side',
+
+ 'footer_reason' => 'Denne meldingen ble sendt til deg fordi :link dekker denne typen aktivitet for dette elementet.',
+ 'footer_reason_link' => 'dine varslingsinnstillinger',
+];
*/
return [
+ 'preferences' => 'Innstillinger',
+
'shortcuts' => 'Snarveier',
'shortcuts_interface' => 'Grensesnitt hurtigtaster',
'shortcuts_toggle_desc' => 'Her kan du aktivere eller deaktivere snarveier for tastatur system som brukes til navigasjon og handlinger.',
'shortcuts_save' => 'Lagre snarveier',
'shortcuts_overlay_desc' => 'Merk: Når snarveier er aktivert er et hjelperoverlegg tilgjengelig via å trykke "?" som vil fremheve de tilgjengelige snarveiene som for øyeblikket er synlige på skjermen.',
'shortcuts_update_success' => 'Snarvei innstillinger er oppdatert!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Behandle tastatursnarveier du kan bruke for å navigere i systembrukergrensesnittet.',
+
+ 'notifications' => 'Innstillinger for varsling',
+ 'notifications_desc' => 'Kontroller e-postvarslene du mottar når en bestemt aktivitet utføres i systemet.',
+ 'notifications_opt_own_page_changes' => 'Varsle ved endringer til sider jeg eier',
+ 'notifications_opt_own_page_comments' => 'Varsle om kommentarer på sider jeg eier',
+ 'notifications_opt_comment_replies' => 'Varsle ved svar på mine kommentarer',
+ 'notifications_save' => 'Lagre innstillinger',
+ 'notifications_update_success' => 'Varslingsinnstillingene er oppdatert!',
+ 'notifications_watched' => 'Overvåka & ignorerte elementer',
+ 'notifications_watched_desc' => ' Nedenfor er elementene som har egendefinerte varslingsinnstillinger i bruk. For å oppdatere innstillingene for disse, se elementet, finn varslingsalternativene i sidepanelet.',
+
+ 'profile_overview_desc' => ' Behandle brukerprofildetaljene dine, inkludert foretrukne språk og autentiseringsalternativer.',
+];
'role_manage_settings' => 'Behandle applikasjonsinnstillinger',
'role_export_content' => 'Eksporter innhold',
'role_editor_change' => 'Endre sideredigering',
+ 'role_notifications' => 'Motta og administrere varslinger',
'role_asset' => 'Eiendomstillatelser',
'roles_system_warning' => 'Vær oppmerksom på at tilgang til noen av de ovennevnte tre tillatelsene kan tillate en bruker å endre sine egne rettigheter eller rettighetene til andre i systemet. Bare tildel roller med disse tillatelsene til pålitelige brukere.',
'role_asset_desc' => 'Disse tillatelsene kontrollerer standard tilgang til eiendelene i systemet. Tillatelser til bøker, kapitler og sider overstyrer disse tillatelsene.',
'favourite_add_notification' => '":name" is toegevoegd aan je favorieten',
'favourite_remove_notification' => '":name" is verwijderd uit je favorieten',
+ // Watching
+ 'watch_update_level_notification' => 'Volg voorkeuren succesvol aangepast',
+
// Auth
'auth_login' => 'heeft ingelogd',
'auth_register' => 'geregistreerd als nieuwe gebruiker',
'recycle_bin_restore' => 'is van prullenbak hersteld',
'recycle_bin_destroy' => 'is van prullenbak verwijderd',
- // Other
+ // Comments
'commented_on' => 'reageerde op',
+ 'comment_create' => 'heeft opmerking toegevoegd',
+ 'comment_update' => 'heeft opmerking aangepast',
+ 'comment_delete' => 'heeft opmerking verwijderd',
+
+ // Other
'permissions_update' => 'wijzigde machtigingen',
];
'remove' => 'Verwijder',
'add' => 'Voeg toe',
'configure' => 'Configureer',
+ 'manage' => 'Beheer',
'fullscreen' => 'Volledig scherm',
'favourite' => 'Favoriet',
'unfavourite' => 'Verwijderen als favoriet',
'shelves_permissions_updated' => 'Boekenplank Machtigingen Bijgewerkt',
'shelves_permissions_active' => 'Machtigingen op Boekenplank Actief',
'shelves_permissions_cascade_warning' => 'De ingestelde machtigingen op deze boekenplank worden niet automatisch toegepast op de boeken van deze boekenplank. Dit is omdat een boek toegekend kan worden op meerdere boekenplanken. De machtigingen van deze boekenplank kunnen echter wel gekopieerd worden naar de boeken van deze boekenplank via de optie hieronder.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => '\'Maak boekenplank\' machtigingen worden enkel gebruikt om machtigingen te kopiëren naar boeken binnenin een boekenplank door gebruik te maken van onderstaande actie. Deze machtigingen laten niet toe om een nieuw boek aan te maken.',
'shelves_copy_permissions_to_books' => 'Kopieer Machtigingen naar Boeken',
'shelves_copy_permissions' => 'Kopieer Machtigingen',
'shelves_copy_permissions_explain' => 'Met deze actie worden de machtigingen van deze boekenplank gekopieërd naar alle boeken van deze boekenplank. Voor je deze actie uitvoert, moet je ervoor zorgen dat alle wijzigingen in de machtigingen van deze boekenplank zijn opgeslagen.',
'pages_md_insert_drawing' => 'Tekening invoegen',
'pages_md_show_preview' => 'Toon preview',
'pages_md_sync_scroll' => 'Synchroniseer preview scroll',
+ 'pages_drawing_unsaved' => 'Niet-opgeslagen Tekening Gevonden',
+ 'pages_drawing_unsaved_confirm' => 'Er zijn niet-opgeslagen tekeninggegevens gevonden van een eerdere mislukte poging om de tekening op te slaan. Wilt u deze niet-opgeslagen tekening herstellen en verder bewerken?',
'pages_not_in_chapter' => 'Deze pagina staat niet in een hoofdstuk',
'pages_move' => 'Pagina verplaatsten',
'pages_copy' => 'Pagina kopiëren',
'references' => 'Verwijzingen',
'references_none' => 'Er zijn geen verwijzingen naar dit artikel bijgehouden.',
'references_to_desc' => 'Hieronder staan alle gekende pagina\'s in het systeem die naar dit item linken.',
+
+ // Watch Options
+ 'watch' => 'Volg',
+ 'watch_title_default' => 'Standaard Voorkeuren',
+ 'watch_desc_default' => 'Terugkeren naar alleen je standaardvoorkeuren voor meldingen.',
+ 'watch_title_ignore' => 'Negeer',
+ 'watch_desc_ignore' => 'Negeer alle meldingen, inclusief die van voorkeuren op gebruikersniveau.',
+ 'watch_title_new' => 'Nieuwe pagina\'s',
+ 'watch_desc_new' => 'Geef een melding wanneer er een nieuwe pagina wordt gemaakt binnen dit item.',
+ 'watch_title_updates' => 'Alle pagina updates',
+ 'watch_desc_updates' => 'Geef een melding van alle nieuwe pagina\'s en pagina wijzigingen.',
+ 'watch_desc_updates_page' => 'Geef een melding van pagina wijzigingen.',
+ 'watch_title_comments' => 'Alle Pagina Updates & Opmerkingen',
+ 'watch_desc_comments' => 'Geef een melding van alle nieuwe pagina\'s, pagina wijzigingen en nieuwe opmerkingen.',
+ 'watch_desc_comments_page' => 'Geef een melding van pagina wijzigingen en nieuwe opmerkingen.',
+ 'watch_change_default' => 'Standaardvoorkeuren voor meldingen wijzigen',
+ 'watch_detail_ignore' => 'Meldingen negeren',
+ 'watch_detail_new' => 'Op de uitkijk voor nieuwe pagina\'s',
+ 'watch_detail_updates' => 'Op de uitkijk voor nieuwe pagina\'s en aanpassingen',
+ 'watch_detail_comments' => 'Op de uitkijk voor nieuwe pagina\'s, aanpassingen en opmerkingen',
+ 'watch_detail_parent_book' => 'Op de uitkijk via hogerliggend boek',
+ 'watch_detail_parent_book_ignore' => 'Aan het negeren via hogerliggend boek',
+ 'watch_detail_parent_chapter' => 'Op de uitkijk via hogerliggend hoofdstuk',
+ 'watch_detail_parent_chapter_ignore' => 'Aan het negeren via hogerliggend hoofdstuk',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Fout opgetreden bij het verzenden van een test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'De URL komt niet overeen met de geconfigureerde toegestane SSR-hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nieuwe opmerking op pagina: :pageName',
+ 'new_comment_intro' => 'Een gebruiker heeft gereageerd op een pagina in :appName:',
+ 'new_page_subject' => 'Nieuwe pagina: :pageName',
+ 'new_page_intro' => 'Een nieuwe pagina is gemaakt in :appName:',
+ 'updated_page_subject' => 'Aangepaste pagina: :pageName',
+ 'updated_page_intro' => 'Een pagina werd aangepast in :appName:',
+ 'updated_page_debounce' => 'Om een stortvloed aan meldingen te voorkomen, zul je een tijdje geen meldingen ontvangen voor verdere bewerkingen van deze pagina door dezelfde redacteur.',
+
+ 'detail_page_name' => 'Pagina Naam:',
+ 'detail_commenter' => 'Reageerder:',
+ 'detail_comment' => 'Opmerking:',
+ 'detail_created_by' => 'Gemaakt Door:',
+ 'detail_updated_by' => 'Aangepast Door:',
+
+ 'action_view_comment' => 'Bekijk Opmerking',
+ 'action_view_page' => 'Bekijk Pagina',
+
+ 'footer_reason' => 'Deze melding is naar u verzonden omdat :link dit type activiteit voor dit artikel dekt.',
+ 'footer_reason_link' => 'je meldingsvoorkeuren',
+];
*/
return [
+ 'preferences' => 'Voorkeuren',
+
'shortcuts' => 'Snelkoppelingen',
'shortcuts_interface' => 'Toetsencombinaties voor de gebruikersinterface',
'shortcuts_toggle_desc' => 'Hier kunt u toetscombinaties voor de gebruikersinterface in- of uitschakelen voor navigatie en acties.',
'shortcuts_save' => 'Sla Toetsencombinaties Op',
'shortcuts_overlay_desc' => 'Opmerking: Wanneer toetsencombinaties zijn ingeschakeld, is een overlay beschikbaar door op "?" te drukken, die de momenteel beschikbare toetscombinaties voor acties op het scherm markeert.',
'shortcuts_update_success' => 'Toetsencombinatievoorkeuren zijn bijgewerkt!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Beheer toetsenbordsnelkoppelingen om door de gebruikersinterface van het systeem te navigeren.',
+
+ 'notifications' => 'Melding Voorkeuren',
+ 'notifications_desc' => 'Bepaal welke e-mailmeldingen je ontvangt wanneer bepaalde activiteiten in het systeem worden uitgevoerd.',
+ 'notifications_opt_own_page_changes' => 'Geef melding bij wijzigingen aan pagina\'s waarvan ik de eigenaar ben',
+ 'notifications_opt_own_page_comments' => 'Geef melding van opmerkingen op pagina\'s waarvan ik de eigenaar ben',
+ 'notifications_opt_comment_replies' => 'Geef melding van reacties op mijn opmerkingen',
+ 'notifications_save' => 'Voorkeuren opslaan',
+ 'notifications_update_success' => 'Voorkeuren voor meldingen zijn bijgewerkt!',
+ 'notifications_watched' => 'Gevolgde & Genegeerde Items',
+ 'notifications_watched_desc' => ' Hieronder staan de items waarvoor aangepaste \'Volg\'-voorkeuren zijn toegepast. Om je voorkeuren voor deze items bij te werken, bekijk je het item en zoek je naar de \'Volg\' opties in de zijbalk.',
+
+ 'profile_overview_desc' => ' Beheer de details van je gebruikersprofiel, inclusief de voorkeurstaal en verificatieopties.',
+];
'role_manage_settings' => 'Beheer app instellingen',
'role_export_content' => 'Exporteer inhoud',
'role_editor_change' => 'Wijzig pagina bewerker',
+ 'role_notifications' => 'Meldingen ontvangen & beheren',
'role_asset' => 'Asset Machtigingen',
'roles_system_warning' => 'Wees ervan bewust dat toegang tot een van de bovengenoemde drie machtigingen een gebruiker in staat kan stellen zijn eigen machtigingen of de machtigingen van anderen in het systeem kan wijzigen. Wijs alleen rollen toe met deze machtigingen aan vertrouwde gebruikers.',
'role_asset_desc' => 'Deze machtigingen bepalen de standaard toegang tot de assets binnen het systeem. Machtigingen op boeken, hoofdstukken en pagina\'s overschrijven deze instelling.',
'favourite_add_notification' => '":name" został dodany do Twoich ulubionych',
'favourite_remove_notification' => '":name" został usunięty z ulubionych',
+ // Watching
+ 'watch_update_level_notification' => 'Ustawienia obserwowania pomyślnie zaktualizowane',
+
// Auth
'auth_login' => 'zalogował się',
'auth_register' => 'zarejestrowany jako nowy użytkownik',
'recycle_bin_restore' => 'przywrócił z kosza',
'recycle_bin_destroy' => 'usunął z kosza',
- // Other
+ // Comments
'commented_on' => 'skomentował',
+ 'comment_create' => 'dodał komentarz',
+ 'comment_update' => 'zaktualizował komentarz',
+ 'comment_delete' => 'usunął komentarz',
+
+ // Other
'permissions_update' => 'zaktualizował uprawnienia',
];
'remove' => 'Usuń',
'add' => 'Dodaj',
'configure' => 'Konfiguruj',
+ 'manage' => 'Zarządzaj',
'fullscreen' => 'Pełny ekran',
'favourite' => 'Ulubione',
'unfavourite' => 'Usuń z ulubionych',
'shelves_permissions_updated' => 'Uprawnienia półki zostały zaktualizowane',
'shelves_permissions_active' => 'Uprawnienia półki są aktywne',
'shelves_permissions_cascade_warning' => 'Uprawnienia na półkach nie są automatycznie nakładane na zawartych w nich książkach. Dzieje się tak dlatego, że książka może istnieć na wielu półkach. Uprawnienia można jednak skopiować do książek podrzędnych, korzystając z opcji znajdującej się poniżej.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Uprawnienia tworzenia półki są używane tylko do kopiowania uprawnień do książek podrzędnych za pomocą poniższej czynności. Nie kontrolują możliwości tworzenia książek.',
'shelves_copy_permissions_to_books' => 'Skopiuj uprawnienia do książek',
'shelves_copy_permissions' => 'Skopiuj uprawnienia',
'shelves_copy_permissions_explain' => 'To spowoduje zastosowanie obecnych ustawień uprawnień tej półki na wszystkich książkach w niej zawartych. Przed aktywacją upewnij się, że wszelkie zmiany w uprawnieniach tej półki zostały zapisane.',
'pages_md_insert_drawing' => 'Wstaw rysunek',
'pages_md_show_preview' => 'Pokaż podgląd',
'pages_md_sync_scroll' => 'Synchronizuj przewijanie podglądu',
+ 'pages_drawing_unsaved' => 'Znaleziono niezapisany rysunek',
+ 'pages_drawing_unsaved_confirm' => 'Znaleziono niezapisane dane rysowania z poprzedniej nieudanej próby zapisu. Czy chcesz przywrócić i kontynuować edycję tego niezapisanego rysunku?',
'pages_not_in_chapter' => 'Strona nie została umieszczona w rozdziale',
'pages_move' => 'Przenieś stronę',
'pages_copy' => 'Skopiuj stronę',
'pages_copy_link' => 'Kopiuj link',
'pages_edit_content_link' => 'Przejdź do sekcji w edytorze',
'pages_pointer_enter_mode' => 'Aktywuj tryb wyboru sekcji',
- 'pages_pointer_label' => 'Page Section Options',
- 'pages_pointer_permalink' => 'Page Section Permalink',
- 'pages_pointer_include_tag' => 'Page Section Include Tag',
- 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
- 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink',
+ 'pages_pointer_label' => 'Sekcja opcji strony',
+ 'pages_pointer_permalink' => 'Sekcja odnośnika strony',
+ 'pages_pointer_include_tag' => 'Sekcja taga inkludującego',
+ 'pages_pointer_toggle_link' => 'Tryb bezpośredniego linku, naciśnij aby zmienić na tryb tagu do inkludowania',
+ 'pages_pointer_toggle_include' => 'Tryb tagu do inkludowania, naciśnij aby zmienić na tryb bezpośredniego linku',
'pages_permissions_active' => 'Uprawnienia strony są aktywne',
'pages_initial_revision' => 'Pierwsze wydanie',
'pages_references_update_revision' => 'Automatyczna aktualizacja wewnętrznych linków',
'references' => 'Odniesienia',
'references_none' => 'Brak śledzonych odwołań do tego elementu.',
'references_to_desc' => 'Poniżej znajdują się wszystkie znane strony w systemie, które odnoszą się do tego elementu.',
+
+ // Watch Options
+ 'watch' => 'Obserwuj',
+ 'watch_title_default' => 'Domyślne ustawienia',
+ 'watch_desc_default' => 'Przywróć do tylko domyślnych ustawień powiadomień.',
+ 'watch_title_ignore' => 'Ignoruj',
+ 'watch_desc_ignore' => 'Ignoruj wszystkie powiadomienia, w tym te z preferencji użytkownika.',
+ 'watch_title_new' => 'Nowe strony',
+ 'watch_desc_new' => 'Powiadom o utworzeniu nowej strony w tym elemencie.',
+ 'watch_title_updates' => 'Wszystkie aktualizacje strony',
+ 'watch_desc_updates' => 'Powiadom o wszystkich nowych stronach i zmianach strony.',
+ 'watch_desc_updates_page' => 'Powiadom o wszystkich zmianach strony.',
+ 'watch_title_comments' => 'Wszystkie aktualizacje strony i komentarze',
+ 'watch_desc_comments' => 'Powiadom o wszystkich nowych stronach, zmianach na stronie i nowych komentarzach.',
+ 'watch_desc_comments_page' => 'Powiadom o zmianach strony i nowych komentarzach.',
+ 'watch_change_default' => 'Zmień domyślne ustawienia powiadomień',
+ 'watch_detail_ignore' => 'Ignorowanie powiadomień',
+ 'watch_detail_new' => 'Obserwowanie nowych stron',
+ 'watch_detail_updates' => 'Obserwowanie nowych stron i aktualizacji',
+ 'watch_detail_comments' => 'Obserwowanie nowych stron, aktualizacji i komentarzy',
+ 'watch_detail_parent_book' => 'Obserwowanie przez książkę nadrzędną',
+ 'watch_detail_parent_book_ignore' => 'Ignorowanie przez książkę nadrzędną',
+ 'watch_detail_parent_chapter' => 'Obserwowanie przez rozdział nadrzędny',
+ 'watch_detail_parent_chapter_ignore' => 'Ignorowanie przez rozdział nadrzędny',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Błąd podczas wysyłania testowej wiadomości e-mail:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'Adres URL nie pasuje do skonfigurowanych dozwolonych hostów SSR',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Nowy komentarz na stronie: :pageName',
+ 'new_comment_intro' => 'Użytkownik skomentował stronę w :appName:',
+ 'new_page_subject' => 'Nowa strona: :pageName',
+ 'new_page_intro' => 'Nowa strona została utworzona w :appName:',
+ 'updated_page_subject' => 'Zaktualizowano stronę: :pageName',
+ 'updated_page_intro' => 'Strona została zaktualizowana w :appName:',
+ 'updated_page_debounce' => 'Aby zapobiec nadmiarowi powiadomień, przez jakiś czas nie będziesz otrzymywać powiadomień o dalszych edycjach tej strony przez tego samego edytora.',
+
+ 'detail_page_name' => 'Nazwa strony:',
+ 'detail_commenter' => 'Skomentował:',
+ 'detail_comment' => 'Komentarz:',
+ 'detail_created_by' => 'Utworzono przez:',
+ 'detail_updated_by' => 'Zaktualizowano przez:',
+
+ 'action_view_comment' => 'Pokaż komentarz',
+ 'action_view_page' => 'Wyświetl stronę',
+
+ 'footer_reason' => 'To powiadomienie zostało wysłane do Ciebie, ponieważ :link obejmuje ten typ aktywności dla tego elementu.',
+ 'footer_reason_link' => 'ustawienia powiadomień',
+];
*/
return [
+ 'preferences' => 'Preferencje',
+
'shortcuts' => 'Skróty',
'shortcuts_interface' => 'Interfejs Skrótów Klawiszowych',
'shortcuts_toggle_desc' => 'Tutaj możesz włączyć lub wyłączyć interfejs skrótów klawiszowych używanych do nawigacji i akcji.',
'shortcuts_save' => 'Zapisz skróty',
'shortcuts_overlay_desc' => 'Uwaga: Gdy skróty są włączone, przez naciśnięcie "?" może być otworzona nakładka pomocnicza, która podświetli dostępne skróty dla akcji widocznych obecnie na ekranie.',
'shortcuts_update_success' => 'Ustawienia skrótów zostały zaktualizowane!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Zarządzaj skrótami klawiaturowymi, które możesz użyć do nawigacji interfejsu użytkownika systemu.',
+
+ 'notifications' => 'Preferencje powiadomień',
+ 'notifications_desc' => 'Kontroluj otrzymywane powiadomienia e-mail, gdy określona aktywność jest wykonywana w systemie.',
+ 'notifications_opt_own_page_changes' => 'Powiadom o zmianach na stronach, których jestem właścicielem',
+ 'notifications_opt_own_page_comments' => 'Powiadom o komentarzach na stronach, których jestem właścicielem',
+ 'notifications_opt_comment_replies' => 'Powiadom o odpowiedziach na moje komentarze',
+ 'notifications_save' => 'Zapisz preferencje',
+ 'notifications_update_success' => 'Preferencje powiadomień zostały zaktualizowane!',
+ 'notifications_watched' => 'Obserwowane i ignorowane elementy',
+ 'notifications_watched_desc' => ' Poniżej znajdują się elementy, które mają własne preferencje obserwowania. Aby zaktualizować swoje preferencje, zobacz dany element, a następnie znajdź opcje obserwowania na pasku bocznym.',
+
+ 'profile_overview_desc' => ' Zarządzaj szczegółami swojego profilu użytkownika, w tym preferowanym językiem i opcjami uwierzytelniania.',
+];
'role_manage_settings' => 'Zarządzanie ustawieniami aplikacji',
'role_export_content' => 'Eksportuj zawartość',
'role_editor_change' => 'Zmień edytor strony',
+ 'role_notifications' => 'Odbieranie i zarządzanie powiadomieniami',
'role_asset' => 'Zarządzanie zasobami',
'roles_system_warning' => 'Pamiętaj, że dostęp do trzech powyższych uprawnień może pozwolić użytkownikowi na zmianę własnych uprawnień lub uprawnień innych osób w systemie. Przypisz tylko role z tymi uprawnieniami do zaufanych użytkowników.',
'role_asset_desc' => 'Te ustawienia kontrolują zarządzanie zasobami systemu. Uprawnienia książek, rozdziałów i stron nadpisują te ustawienia.',
'favourite_add_notification' => '":name" foi adicionado aos seus favoritos',
'favourite_remove_notification' => '":name" foi removido dos seus favoritos',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'sessão iniciada',
'auth_register' => 'registado como novo utilizador',
'recycle_bin_restore' => 'restaurado da reciclagem',
'recycle_bin_destroy' => 'removido da reciclagem',
- // Other
+ // Comments
'commented_on' => 'comentado a',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'permissões atualizadas',
];
'remove' => 'Remover',
'add' => 'Adicionar',
'configure' => 'Configurar',
+ 'manage' => 'Manage',
'fullscreen' => 'Ecrã completo',
'favourite' => 'Favorito',
'unfavourite' => 'Retirar Favorito',
'pages_md_insert_drawing' => 'Inserir Desenho',
'pages_md_show_preview' => 'Mostrar pré-visualização',
'pages_md_sync_scroll' => 'Sincronizar pré-visualização',
+ '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' => 'A página não está dentro de um capítulo',
'pages_move' => 'Mover Página',
'pages_copy' => 'Copiar Página',
'references' => 'Referências',
'references_none' => 'Não há referências registadas para este item.',
'references_to_desc' => 'Abaixo estão todas as páginas conhecidas do sistema que vinculam este item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erro lançado ao enviar um e-mail de teste:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Atalhos',
'shortcuts_interface' => 'Atalhos de Teclado',
'shortcuts_toggle_desc' => 'Aqui pode ativar ou desativar os atalhos de teclado do sistema, usados para navegação e ações.',
'shortcuts_save' => 'Salvar Atalhos',
'shortcuts_overlay_desc' => 'Nota: Quando os atalhos estão ativados, um balão de ajuda ficará disponível pressionando "?" destacando os atalhos disponíveis para ações atualmente visíveis na tela.',
'shortcuts_update_success' => 'As suas preferências de atalhos foram guardadas!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Gerir as configurações da aplicação',
'role_export_content' => 'Exportar conteúdo',
'role_editor_change' => 'Alterar editor de página',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Permissões de Ativos',
'roles_system_warning' => 'Esteja ciente de que o acesso a qualquer uma das três permissões acima pode permitir que um utilizador altere os seus próprios privilégios ou privilégios de outros no sistema. Apenas atribua cargos com essas permissões a utilizadores de confiança.',
'role_asset_desc' => 'Estas permissões controlam o acesso padrão para os ativos dentro do sistema. Permissões em Livros, Capítulos e Páginas serão sobrescritas por estas permissões.',
'page_restore' => 'restaurou a página',
'page_restore_notification' => 'Página restaurada com sucesso',
'page_move' => 'moveu a página',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'Página movida com sucesso',
// Chapters
'chapter_create' => 'criou o capítulo',
'chapter_delete' => 'excluiu o capítulo',
'chapter_delete_notification' => 'Capítulo excluída com sucesso',
'chapter_move' => 'moveu o capítulo',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'Capítulo excluído com sucesso',
// Books
'book_create' => 'criou o livro',
'bookshelf_delete_notification' => 'Prateleira excluída com sucesso',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => 'revisão restaurada',
+ 'revision_delete' => 'revisão excluída',
+ 'revision_delete_notification' => 'Revisão excluída com sucesso',
// Favourites
'favourite_add_notification' => '":name" foi adicionada aos seus favoritos',
'favourite_remove_notification' => '":name" foi removida dos seus favoritos',
+ // Watching
+ 'watch_update_level_notification' => 'Preferências de Observação atualizadas com sucesso',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => 'conectado',
+ 'auth_register' => 'registrado como novo usuário',
+ 'auth_password_reset_request' => 'redefinir senha do usuário solicitado',
+ 'auth_password_reset_update' => 'redefinir senha do usuário',
+ 'mfa_setup_method' => 'método MFA configurado',
'mfa_setup_method_notification' => 'Método de multi-fatores configurado com sucesso',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => 'Método MFA removido',
'mfa_remove_method_notification' => 'Método de multi-fatores removido com sucesso',
// Settings
- 'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'settings_update' => 'configurações atualizadas',
+ 'settings_update_notification' => 'Configurações atualizadas com sucesso',
+ 'maintenance_action_run' => 'Ação de manutenção executada',
// Webhooks
'webhook_create' => 'webhook criado',
'webhook_delete_notification' => 'Webhook excluido com sucesso',
// Users
- 'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
- 'user_update' => 'updated user',
+ 'user_create' => 'usuário criado',
+ 'user_create_notification' => 'Usuário criado com sucesso',
+ 'user_update' => 'usuário atualizado',
'user_update_notification' => 'Usuário atualizado com sucesso',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'usuário excluído',
'user_delete_notification' => 'Usuário removido com sucesso',
// API Tokens
- 'api_token_create' => 'created api token',
- 'api_token_create_notification' => 'API token successfully created',
- 'api_token_update' => 'updated api token',
- 'api_token_update_notification' => 'API token successfully updated',
- 'api_token_delete' => 'deleted api token',
- 'api_token_delete_notification' => 'API token successfully deleted',
+ 'api_token_create' => 'token de api criado',
+ 'api_token_create_notification' => 'Token de API criado com sucesso',
+ 'api_token_update' => 'token de API atualizado',
+ 'api_token_update_notification' => 'Token de API atualizado com sucesso',
+ 'api_token_delete' => 'token de api excluído',
+ 'api_token_delete_notification' => 'Token de API excluído com sucesso',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => 'função criada',
'role_create_notification' => 'Perfil criado com sucesso',
- 'role_update' => 'updated role',
+ 'role_update' => 'função atualizada',
'role_update_notification' => 'Perfil atualizado com sucesso',
- 'role_delete' => 'deleted role',
+ 'role_delete' => 'Excluir papel',
'role_delete_notification' => 'Perfil excluído com sucesso',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => 'lixeira esvaziada',
+ 'recycle_bin_restore' => 'restaurado da lixeira',
+ 'recycle_bin_destroy' => 'removido da lixeira',
- // Other
+ // Comments
'commented_on' => 'comentou em',
+ 'comment_create' => 'Adicionou comentário',
+ 'comment_update' => 'Atualizar descrição',
+ 'comment_delete' => 'Comentário deletado',
+
+ // Other
'permissions_update' => 'atualizou permissões',
];
// Buttons
'cancel' => 'Cancelar',
- 'close' => 'Close',
+ 'close' => 'Fechar',
'confirm' => 'Confirmar',
'back' => 'Voltar',
'save' => 'Salvar',
'remove' => 'Remover',
'add' => 'Adicionar',
'configure' => 'Configurar',
+ 'manage' => 'Administrar',
'fullscreen' => 'Tela cheia',
'favourite' => 'Favoritos',
'unfavourite' => 'Remover dos Favoritos',
// Image Manager
'image_select' => 'Selecionar Imagem',
- 'image_list' => 'Image List',
- 'image_details' => 'Image Details',
+ 'image_list' => 'Lista de imagens',
+ 'image_details' => 'Detalhes da Imagem',
'image_upload' => 'Fazer upload de imagem',
'image_intro' => 'Aqui você pode selecionar e gerenciar imagens que foram previamente enviadas para o sistema.',
'image_intro_upload' => 'Faça upload de uma imagem arrastando um arquivo de imagem para esta janela, ou usando o botão "Fazer upload de imagem" acima.',
'image_page_title' => 'visualizar imagens relacionadas a essa página',
'image_search_hint' => 'Pesquisar imagem por nome',
'image_uploaded' => 'Adicionada em :uploadedDate',
- 'image_uploaded_by' => 'Uploaded by :userName',
- 'image_uploaded_to' => 'Uploaded to :pageLink',
- 'image_updated' => 'Updated :updateDate',
+ 'image_uploaded_by' => 'Enviado por :userName',
+ 'image_uploaded_to' => 'Enviado para :pageLink',
+ 'image_updated' => 'Atualizou :updateDate',
'image_load_more' => 'Carregar Mais',
'image_image_name' => 'Nome da Imagem',
'image_delete_used' => 'Essa imagem é usada nas páginas abaixo.',
'image_upload_success' => 'Upload de imagem efetuado com sucesso',
'image_update_success' => 'Detalhes da imagem atualizados com sucesso',
'image_delete_success' => 'Imagem excluída com sucesso',
- 'image_replace' => 'Replace Image',
- 'image_replace_success' => 'Image file successfully updated',
+ 'image_replace' => 'Substituir imagem',
+ 'image_replace_success' => 'Arquivo de imagem atualizado com sucesso',
// Code Editor
'code_editor' => 'Editar Código',
'shelves_permissions_updated' => 'Permissões de prateleira atualizadas',
'shelves_permissions_active' => 'Permissões de prateleira ativas',
'shelves_permissions_cascade_warning' => 'As permissões nas prateleiras não são automaticamente em cascata para os livros contidos. Isso ocorre porque um livro pode existir em várias prateleiras. No entanto, as permissões podem ser copiadas para livros filhos usando a opção encontrada abaixo.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'As permissões de criação de prateleira são usadas apenas para copiar livros filhos usando a ação abaixo. Eles não controlam a capacidade de criar livros.',
'shelves_copy_permissions_to_books' => 'Copiar Permissões para Livros',
'shelves_copy_permissions' => 'Copiar Permissões',
'shelves_copy_permissions_explain' => 'Isso aplicará as configurações de permissão atuais desta estante a todos os livros contidos nela. Antes de ativar, verifique se todas as alterações nas permissões desta prateleira foram salvas.',
'pages_editing_page' => 'Editando Página',
'pages_edit_draft_save_at' => 'Rascunho salvo em ',
'pages_edit_delete_draft' => 'Excluir Rascunho',
- 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.',
+ 'pages_edit_delete_draft_confirm' => 'Tem certeza que deseja excluir as alterações nas páginas de rascunho? Todas as suas alterações, desde o último salvamento completo, serão perdidas e o editor será atualizado com o último estado de salvamento da página.',
'pages_edit_discard_draft' => 'Descartar Rascunho',
'pages_edit_switch_to_markdown' => 'Alternar para o Editor de Markdown',
'pages_edit_switch_to_markdown_clean' => '(Conteúdo Limpo)',
'pages_md_insert_drawing' => 'Inserir Desenho',
'pages_md_show_preview' => 'Mostrar pré-visualização',
'pages_md_sync_scroll' => 'Sincronizar pré-visualização',
+ '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' => 'Página não está dentro de um capítulo',
'pages_move' => 'Mover Página',
'pages_copy' => 'Copiar Página',
'pages_revisions_restore' => 'Restaurar',
'pages_revisions_none' => 'Essa página não tem revisões',
'pages_copy_link' => 'Copiar Link',
- 'pages_edit_content_link' => 'Jump to section in editor',
- 'pages_pointer_enter_mode' => 'Enter section select mode',
- 'pages_pointer_label' => 'Page Section Options',
- 'pages_pointer_permalink' => 'Page Section Permalink',
- 'pages_pointer_include_tag' => 'Page Section Include Tag',
- 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
- 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink',
+ 'pages_edit_content_link' => 'Ir para a seção do editor',
+ 'pages_pointer_enter_mode' => 'Entrar em modo de seleção de seção',
+ 'pages_pointer_label' => 'Opções de Seção de Página',
+ 'pages_pointer_permalink' => 'Seção de Página Permalink',
+ 'pages_pointer_include_tag' => 'Seção de Página Incluir Tag',
+ 'pages_pointer_toggle_link' => 'Modo permalink, pressione para mostrar a tag incluída',
+ 'pages_pointer_toggle_include' => 'Incluir o modo tag, pressione para mostrar permalink',
'pages_permissions_active' => 'Permissões de Página Ativas',
'pages_initial_revision' => 'Publicação Inicial',
'pages_references_update_revision' => 'Atualização automática do sistema de links internos',
'time_b' => 'nos últimos :minCount minutos',
'message' => ':start :time. Tome cuidado para não sobrescrever atualizações de outras pessoas!',
],
- 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content',
- 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content',
+ 'pages_draft_discarded' => 'Rascunho descartado! O editor foi atualizado com o conteúdo da página atual',
+ 'pages_draft_deleted' => 'Rascunho excluído! O editor foi atualizado com o conteúdo da página atual',
'pages_specific' => 'Página Específica',
'pages_is_template' => 'Modelo de Página',
'comment_new' => 'Novo Comentário',
'comment_created' => 'comentado :createDiff',
'comment_updated' => 'Editado :updateDiff por :username',
- 'comment_updated_indicator' => 'Updated',
+ 'comment_updated_indicator' => 'Atualizado',
'comment_deleted_success' => 'Comentário removido',
'comment_created_success' => 'Comentário adicionado',
'comment_updated_success' => 'Comentário editado',
'comment_delete_confirm' => 'Você tem certeza de que deseja excluir este comentário?',
'comment_in_reply_to' => 'Em resposta à :commentId',
- 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
+ 'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e gerenciados ao visualizar a página salva.',
// Revision
'revision_delete_confirm' => 'Tem certeza de que deseja excluir esta revisão?',
'references' => 'Referências',
'references_none' => 'Não há referências rastreadas para este item.',
'references_to_desc' => 'Abaixo estão todas as páginas conhecidas no sistema que vinculam a este item.',
+
+ // Watch Options
+ 'watch' => 'Acompanhar',
+ 'watch_title_default' => 'Preferências padrão',
+ 'watch_desc_default' => 'Reverter o acompanhamento apenas para suas preferências de notificação padrão.',
+ 'watch_title_ignore' => 'Ignorar',
+ 'watch_desc_ignore' => 'Ignorar todas as notificações, incluindo as de preferências de nível de usuário.',
+ 'watch_title_new' => 'Novas Páginas',
+ 'watch_desc_new' => 'Notificar quando qualquer nova página for criada dentro deste item.',
+ 'watch_title_updates' => 'Todas as atualizações da página',
+ 'watch_desc_updates' => 'Notificar sobre todas as novas páginas e alterações na página.',
+ 'watch_desc_updates_page' => 'Notificar sobre todas as alterações da página.',
+ 'watch_title_comments' => 'Todas as atualizações e comentários da página',
+ 'watch_desc_comments' => 'Notificar sobre todas as novas páginas, alterações de página e novos comentários.',
+ 'watch_desc_comments_page' => 'Notificar sobre alterações na página e novos comentários.',
+ 'watch_change_default' => 'Alterar preferências padrão de notificação',
+ 'watch_detail_ignore' => 'Ignorando notificações',
+ 'watch_detail_new' => 'Acompanhando para novas páginas',
+ 'watch_detail_updates' => 'Acompanhando novas páginas e atualizações',
+ 'watch_detail_comments' => 'Acompanhando novas páginas, atualizações e comentários',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Drawing & Images
'image_upload_error' => 'Um erro aconteceu enquanto o servidor tentava efetuar o upload da imagem',
'image_upload_type_error' => 'O tipo de imagem que está sendo enviada é inválido',
- 'image_upload_replace_type' => 'Image file replacements must be of the same type',
+ 'image_upload_replace_type' => 'Substituições de arquivos de imagem devem ser do mesmo tipo',
'drawing_data_not_found' => 'Dados de desenho não puderam ser carregados. Talvez o arquivo de desenho não exista mais ou você não tenha permissão para acessá-lo.',
// Attachments
// Pages
'page_draft_autosave_fail' => 'Falha ao tentar salvar o rascunho. Certifique-se que a conexão de internet está funcional antes de tentar salvar essa página',
- 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content',
+ 'page_draft_delete_fail' => 'Falha ao excluir o rascunho da página e buscar conteúdo salvo na página atual',
'page_custom_home_deletion' => 'Não é possível excluir uma página que está definida como página inicial',
// Entities
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erro encontrado ao enviar um e-mail de teste:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Novo comentário na página: :pageName',
+ 'new_comment_intro' => 'Um usuário comentou em uma página de :appName:',
+ 'new_page_subject' => 'Nova página: :pageName',
+ 'new_page_intro' => 'Uma nova página foi criada em :appName:',
+ 'updated_page_subject' => 'Página atualizada: :pageName',
+ 'updated_page_intro' => 'Uma página foi atualizada em :appName:',
+ 'updated_page_debounce' => 'Para prevenir notificações em massa, por enquanto notificações não serão enviadas para você para próximas edições nessa página pelo mesmo editor.',
+
+ 'detail_page_name' => 'Nome da Página:',
+ 'detail_commenter' => 'Comentador:',
+ 'detail_comment' => 'Comentário:',
+ 'detail_created_by' => 'Criado por: ',
+ 'detail_updated_by' => 'Atualizado por:',
+
+ 'action_view_comment' => 'Ver Comentário',
+ 'action_view_page' => 'Ver Página',
+
+ 'footer_reason' => 'Essa notificação foi enviada para você porque :link engloba esse tipo de atividade para este item.',
+ 'footer_reason_link' => 'suas preferências de notificação',
+];
*/
return [
+ 'preferences' => 'Preferências',
+
'shortcuts' => 'Atalhos',
'shortcuts_interface' => 'Atalhos de Teclado da Interface',
'shortcuts_toggle_desc' => 'Aqui você pode habilitar ou desabilitar os atalhos da interface do sistema de teclado, usados para navegação e ações.',
'shortcuts_save' => 'Salvar Atalhos',
'shortcuts_overlay_desc' => 'Observação: quando os atalhos estão ativados, uma sobreposição auxiliar está disponível pressionando "?" que destacará os atalhos disponíveis para ações atualmente visíveis na tela.',
'shortcuts_update_success' => 'As preferências de atalho foram atualizadas!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gerencie os atalhos de teclado que você pode usar para navegar na interface de usuário do sistema.',
+
+ 'notifications' => 'Preferências de notificação',
+ 'notifications_desc' => 'Controle as notificações por e-mail que você recebe quando uma determinada atividade é executada no sistema.',
+ 'notifications_opt_own_page_changes' => 'Notificar quando houver alterações em páginas que eu possuo',
+ 'notifications_opt_own_page_comments' => 'Notificar comentários nas páginas que eu possuo',
+ 'notifications_opt_comment_replies' => 'Notificar ao responder aos meus comentários',
+ 'notifications_save' => 'Salvar Preferências',
+ 'notifications_update_success' => 'Preferências de notificação foram atualizadas!',
+ 'notifications_watched' => 'Itens assistidos e ignorados',
+ 'notifications_watched_desc' => ' Abaixo estão os itens que possuem preferências de relógio personalizadas aplicadas. Para atualizar suas preferências para estes, veja o item e encontre as opções de relógio na barra lateral.',
+
+ 'profile_overview_desc' => ' Gerencie os detalhes do seu perfil de usuário, incluindo o idioma preferido e opções de autenticação.',
+];
'role_manage_settings' => 'Gerenciar configurações da aplicação',
'role_export_content' => 'Exportar conteúdo',
'role_editor_change' => 'Alterar página de edição',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Permissões de Ativos',
'roles_system_warning' => 'Esteja ciente de que o acesso a qualquer uma das três permissões acima pode permitir que um usuário altere seus próprios privilégios ou privilégios de outros usuários no sistema. Apenas atribua cargos com essas permissões para usuários confiáveis.',
'role_asset_desc' => 'Essas permissões controlam o acesso padrão para os ativos dentro do sistema. Permissões em Livros, Capítulos e Páginas serão sobrescritas por essas permissões.',
'favourite_add_notification' => '":name" a fost adăugat la favorite',
'favourite_remove_notification' => '":name" a fost eliminat din favorite',
+ // Watching
+ 'watch_update_level_notification' => 'Preferințele de urmărire actualizate cu succes',
+
// Auth
'auth_login' => 'autentificat',
'auth_register' => 'înregistrat ca utilizator nou',
'recycle_bin_restore' => 'restaurat din coșul de gunoi',
'recycle_bin_destroy' => 'eliminat din coșul de gunoi',
- // Other
+ // Comments
'commented_on' => 'a comentat la',
+ 'comment_create' => 'comentariu adăugat',
+ 'comment_update' => 'comentariu actualizat',
+ 'comment_delete' => 'comentariu șters',
+
+ // Other
'permissions_update' => 'a actualizat permisiunile',
];
'remove' => 'Elimină',
'add' => 'Adaugă',
'configure' => 'Configurează',
+ 'manage' => 'Gestionează',
'fullscreen' => 'Ecran complet',
'favourite' => 'Adaugă la favorite',
'unfavourite' => 'Șterge de la favorite',
'image_details' => 'Detalii imagine',
'image_upload' => 'Încarcă imaginea',
'image_intro' => 'Aici puteţi selecta şi gestiona imaginile care au fost încărcate anterior în sistem.',
- 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.',
+ 'image_intro_upload' => 'Încărcați o imagine nouă trăgând o imagine în această fereastră sau utilizând butonul "Încărcați Imaginea" de mai sus.',
'image_all' => 'Tot',
'image_all_title' => 'Vezi toate imaginile',
'image_book_title' => 'Vezi imaginile încărcate în această carte',
'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?',
'shelves_permissions' => 'Permisiuni raft',
'shelves_permissions_updated' => 'Permisiunile raftului au fost actualizate',
- 'shelves_permissions_active' => 'Shelf Permissions Active',
+ 'shelves_permissions_active' => 'Permisiuni raft active',
'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.',
'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
'shelves_copy_permissions_to_books' => 'Copiază permisiunile către cărți',
'books_sort_move_prev_book' => 'Move to Previous Book',
'books_sort_move_next_book' => 'Move to Next Book',
'books_sort_move_prev_chapter' => 'Move Into Previous Chapter',
- 'books_sort_move_next_chapter' => 'Move Into Next Chapter',
- 'books_sort_move_book_start' => 'Move to Start of Book',
- 'books_sort_move_book_end' => 'Move to End of Book',
+ 'books_sort_move_next_chapter' => 'Mergi la următorul capitol',
+ 'books_sort_move_book_start' => 'Sari la începutul cârtii',
+ 'books_sort_move_book_end' => 'Sari la sfârșitul cârtii',
'books_sort_move_before_chapter' => 'Move to Before Chapter',
'books_sort_move_after_chapter' => 'Move to After Chapter',
'books_copy' => 'Copiază cartea',
'pages_md_insert_drawing' => 'Inserează desen',
'pages_md_show_preview' => 'Arată previzualizarea',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Pagina nu este într-un capitol',
'pages_move' => 'Mută pagina',
'pages_copy' => 'Copiază pagina',
'references' => 'Referințe',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'Pagina Nouă',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'Toate actualizările paginii',
+ 'watch_desc_updates' => 'Notifică atunci când o pagină este editată sau creată.',
+ 'watch_desc_updates_page' => 'Notifică la toate modificările paginii.',
+ 'watch_title_comments' => 'Toate actualizările și comentariile paginii',
+ 'watch_desc_comments' => 'Notifică la toate paginile noi, editările de pagină și comentariile noi.',
+ 'watch_desc_comments_page' => 'Notifică la toate paginile noi, editările de pagină și comentariile noi.',
+ 'watch_change_default' => 'Schimbă preferințele implicite de notificare',
+ 'watch_detail_ignore' => 'Se ignoră notificările',
+ 'watch_detail_new' => 'Urmărire pagini noi',
+ 'watch_detail_updates' => 'Urmărire pagini noi şi actualizări',
+ 'watch_detail_comments' => 'Urmărire pagini noi, actualizări și comentarii',
+ 'watch_detail_parent_book' => 'Se uită prin cartea părinte',
+ 'watch_detail_parent_book_ignore' => 'Ignorare prin intermediul cărţii părinte',
+ 'watch_detail_parent_chapter' => 'Urmărire prin capitolul părinte',
+ 'watch_detail_parent_chapter_ignore' => 'Urmărire prin capitolul părinte',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Eroare la trimiterea unui e-mail de test:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Comentariu nou pe pagina: :pageName',
+ 'new_comment_intro' => 'Un utilizator a comentat pe o pagină în :appName:',
+ 'new_page_subject' => 'Pagină nouă: :pageName',
+ 'new_page_intro' => 'O nouă pagină a fost creată în :appName:',
+ 'updated_page_subject' => 'Pagina actualizată: :pageName',
+ 'updated_page_intro' => 'O nouă pagină a fost creată în :appName:',
+ 'updated_page_debounce' => 'Pentru a preveni notificări în masă, pentru un timp nu veți primi notificări suplimentare la această pagină de către același editor.',
+
+ 'detail_page_name' => 'Nume pagină:',
+ 'detail_commenter' => 'Cine a comentat:',
+ 'detail_comment' => 'Comentariu:',
+ 'detail_created_by' => 'Creat de:',
+ 'detail_updated_by' => 'Actualizat de:',
+
+ 'action_view_comment' => 'Vizualizați comentariul',
+ 'action_view_page' => 'Vezi pagina',
+
+ 'footer_reason' => 'Această notificare ți-a fost trimisă deoarece :link acoperă acest tip de activitate pentru acest articol.',
+ 'footer_reason_link' => 'preferințele dvs. de notificare',
+];
*/
return [
+ 'preferences' => 'Preferințe',
+
'shortcuts' => 'Scurtături',
'shortcuts_interface' => 'Comenzi rapide interfață',
'shortcuts_toggle_desc' => 'Aici puteți activa sau dezactiva scurtăturile interfeței folosite pentru navigare și acțiuni.',
'shortcuts_section_navigation' => 'Navigare',
'shortcuts_section_actions' => 'Acțiuni comune',
'shortcuts_save' => 'Salvează scurtăturile',
- 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
+ 'shortcuts_overlay_desc' => 'Notă: Când comenzile rapide sunt activate popup de ajutor este disponibilă prin apăsarea "?" care va evidenția scurtăturile disponibile pentru acțiunile vizibile în prezent pe ecran.',
'shortcuts_update_success' => 'Preferințele dumneavoastră au fost actualizate!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Gestionați scurtăturile de tastatură pe care le puteți utiliza pentru a naviga prin interfața.',
+
+ 'notifications' => 'Preferințe de notificare',
+ 'notifications_desc' => 'Controlați notificările prin e-mail pe care le primiți atunci când o anumită activitate este efectuată în sistem.',
+ 'notifications_opt_own_page_changes' => 'Notifică la comentarii pe paginile pe care le dețin',
+ 'notifications_opt_own_page_comments' => 'Notifică la comentarii pe paginile pe care le dețin',
+ 'notifications_opt_comment_replies' => 'Notifică la răspunsurile la comentariile mele',
+ 'notifications_save' => 'Salvează Preferințe',
+ 'notifications_update_success' => 'Preferințele de notificare au fost actualizate!',
+ 'notifications_watched' => 'Articole urmărite și ignorate',
+ 'notifications_watched_desc' => ' Mai jos sunt elementele care au fost aplicate preferințe personalizate. Pentru a actualiza preferințele pentru acestea, vizualizați elementul și apoi găsiți opțiunile de ceas în bara laterală.',
+
+ 'profile_overview_desc' => ' Gestionează detaliile profilului tău de utilizator, inclusiv opțiunile preferate de limbă și autentificare.',
+];
'app_custom_html_desc' => 'Orice conținut adăugat aici va fi inserat în partea de jos a secțiunii <head> a fiecărei pagini. Acest lucru este util pentru a suprascrie stilurile sau pentru a adăuga cod analitic.',
'app_custom_html_disabled_notice' => 'Conținutul headerului HTML personalizat este dezactivat pe această pagină de setări pentru a asigura că modificările pot fi inversate.',
'app_logo' => 'Logo aplicație',
- 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
+ 'app_logo_desc' => 'Acest lucru este folosit în bara de antet a aplicației, printre alte zone. Această imagine ar trebui să fie de 86px în înălțime. Imaginile mari vor fi scalate în jos.',
'app_icon' => 'Iconiță aplicație',
- 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+ 'app_icon_desc' => 'Această pictogramă este utilizată pentru tab-urile din browser și pictogramele de comenzi rapide. Aceasta ar trebui să fie o imagine PNG pătrată de 256px.',
'app_homepage' => 'Pagina principală a aplicației',
'app_homepage_desc' => 'Selectează o vizualizare pentru a afișa pe prima pagină în loc de vizualizarea implicită. Permisiunile paginii sunt ignorate pentru paginile selectate.',
'app_homepage_select' => 'Selectează o pagină',
'app_disable_comments_desc' => 'Dezactivează comentariile pentru toate paginile aplicației. <br> Comentariile existente nu sunt afișate.',
// Color settings
- 'color_scheme' => 'Application Color Scheme',
- 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
- 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
+ 'color_scheme' => 'Schema de culori a aplicației',
+ 'color_scheme_desc' => 'Setați culorile pe care să le utilizați în interfață. Culorile pot fi configurate separat pentru modurile întuneric şi lumină pentru a se potrivi cel mai bine cu tema şi a asigura lizibilitatea.',
+ 'ui_colors_desc' => 'Setaţi culoarea primară a aplicaţiei şi culoarea implicită a link-ului. Culoarea primară este utilizată în principal pentru banner-ul antet, butoane şi decoraţiunile interfeţei. Culoarea implicită a link-ului este utilizată pentru link-uri și acțiuni bazate pe text, atât în conținutul scris, cât și în interfața aplicației.',
'app_color' => 'Culoare primară',
'link_color' => 'Culoare link implicită',
'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
'roles' => 'Roluri',
'role_user_roles' => 'Roluri utilizator',
'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.',
- 'roles_x_users_assigned' => ':count user assigned|:count users assigned',
+ 'roles_x_users_assigned' => ':count utilizator atribuibil:count utilizatori alocați',
'roles_x_permissions_provided' => ':count permission|:count permissions',
'roles_assigned_users' => 'Utilizator alocat',
'roles_permissions_provided' => 'Permisiuni furnizate',
'role_manage_settings' => 'Gestionează setările aplicației',
'role_export_content' => 'Exportă conținut',
'role_editor_change' => 'Schimbă editorul de pagină',
+ 'role_notifications' => 'Primire și gestionare notificări',
'role_asset' => 'Permisiuni active',
'roles_system_warning' => 'Fi conștient de faptul că accesul la oricare dintre cele trei permisiuni de mai sus poate permite unui utilizator să își modifice propriile privilegii sau privilegiile altor persoane din sistem. Atribuie doar roluri cu aceste permisiuni utilizatorilor de încredere.',
'role_asset_desc' => 'Aceste permisiuni controlează accesul implicit la activele din sistem. Permisiunile pe Cărți, Capitole și Pagini vor suprascrie aceste permisiuni.',
// Webhooks
'webhooks' => 'Webhook-uri',
'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.',
- 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+ 'webhooks_x_trigger_events' => ':count declanșator eveniment:count evenimente de declanșare',
'webhooks_create' => 'Creează un nou Webhook',
'webhooks_none_created' => 'Nu au fost create webhook-uri.',
'webhooks_edit' => 'Editare Webhook',
'bookshelf_delete_notification' => 'Полка успешно удалена',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
+ 'revision_restore' => 'восстановил версию',
+ 'revision_delete' => 'удалил версию',
'revision_delete_notification' => 'Версия успешно удалена',
// Favourites
'favourite_add_notification' => '":name" добавлено в избранное',
'favourite_remove_notification' => '":name" удалено из избранного',
+ // Watching
+ 'watch_update_level_notification' => 'Настройки просмотра успешно обновлены',
+
// Auth
- 'auth_login' => 'logged in',
+ 'auth_login' => 'вошёл',
'auth_register' => 'зарегистрировался как новый пользователь',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
- 'mfa_setup_method_notification' => 'Ð\94вÑ\83Ñ\85Ñ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од авÑ\82оÑ\80изации успешно настроен',
- 'mfa_remove_method' => 'removed MFA method',
- 'mfa_remove_method_notification' => 'Ð\94вÑ\83Ñ\85Ñ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од авÑ\82оÑ\80изации успешно удален',
+ 'auth_password_reset_request' => 'запросил смену пароля пользователя',
+ 'auth_password_reset_update' => 'сбросил пароль пользователя',
+ 'mfa_setup_method' => 'ностроил метод МФА',
+ 'mfa_setup_method_notification' => 'Ð\9cногоÑ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од аÑ\83Ñ\82енÑ\82иÑ\84икации успешно настроен',
+ 'mfa_remove_method' => 'удалил метод МФА',
+ 'mfa_remove_method_notification' => 'Ð\9cногоÑ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од аÑ\83Ñ\82енÑ\82иÑ\84икации успешно удален',
// Settings
- 'settings_update' => 'updated settings',
+ 'settings_update' => 'обновил настройки',
'settings_update_notification' => 'Настройки успешно обновлены',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'maintenance_action_run' => 'запустил техническое обслуживание',
// Webhooks
'webhook_create' => 'создал вебхук',
'webhook_delete_notification' => 'Вебхук успешно удален',
// Users
- 'user_create' => 'created user',
+ 'user_create' => 'создал пользователя',
'user_create_notification' => 'Пользователь успешно создан',
- 'user_update' => 'updated user',
+ 'user_update' => 'обновил пользователя',
'user_update_notification' => 'Пользователь успешно обновлен',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'удалил пользователя',
'user_delete_notification' => 'Пользователь успешно удален',
// API Tokens
- 'api_token_create' => 'created api token',
+ 'api_token_create' => 'создал api token',
'api_token_create_notification' => 'API токен успешно создан',
- 'api_token_update' => 'updated api token',
+ 'api_token_update' => 'обновил api token',
'api_token_update_notification' => 'API токен успешно обновлен',
- 'api_token_delete' => 'deleted api token',
+ 'api_token_delete' => 'удалил api token',
'api_token_delete_notification' => 'API токен успешно удален',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => 'создал роль',
'role_create_notification' => 'Роль успешно создана',
- 'role_update' => 'updated role',
+ 'role_update' => 'обновил роль',
'role_update_notification' => 'Роль успешно обновлена',
- 'role_delete' => 'deleted role',
+ 'role_delete' => 'удалил роль',
'role_delete_notification' => 'Роль успешно удалена',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => 'очистил корзину',
+ 'recycle_bin_restore' => 'восстановлено из корзины',
+ 'recycle_bin_destroy' => 'удалено из корзины',
- // Other
+ // Comments
'commented_on' => 'прокомментировал',
+ 'comment_create' => 'добавил комментарий',
+ 'comment_update' => 'обновил комментарий',
+ 'comment_delete' => 'удалил комментарий',
+
+ // Other
'permissions_update' => 'обновил разрешения',
];
'user_invite_success_login' => 'Пароль установлен, теперь вы можете войти в систему, используя установленный пароль для доступа к :appName!',
// Multi-factor Authentication
- 'mfa_setup' => 'Ð\94вÑ\83Ñ\85факторная аутентификация',
- 'mfa_setup_desc' => 'Ð\94вÑ\83Ñ\85факторная аутентификация повышает степень безопасности вашей учетной записи.',
+ 'mfa_setup' => 'Ð\9cногофакторная аутентификация',
+ 'mfa_setup_desc' => 'Ð\9cногофакторная аутентификация повышает степень безопасности вашей учетной записи.',
'mfa_setup_configured' => 'Настроено',
'mfa_setup_reconfigure' => 'Перенастроить',
- 'mfa_setup_remove_confirmation' => 'Ð\92Ñ\8b Ñ\83веÑ\80енÑ\8b, Ñ\87Ñ\82о Ñ\85оÑ\82иÑ\82е Ñ\83далиÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 двÑ\83Ñ\85факторный метод аутентификации?',
+ 'mfa_setup_remove_confirmation' => 'Ð\92Ñ\8b Ñ\83веÑ\80енÑ\8b, Ñ\87Ñ\82о Ñ\85оÑ\82иÑ\82е Ñ\83далиÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 многофакторный метод аутентификации?',
'mfa_setup_action' => 'Настройка',
'mfa_backup_codes_usage_limit_warning' => 'У вас осталось менее 5 резервных кодов, пожалуйста, создайте и сохраните новый набор перед тем, как закончатся коды, чтобы предотвратить блокировку вашей учетной записи.',
'mfa_option_totp_title' => 'Мобильное приложение',
- 'mfa_option_totp_desc' => 'Ð\94лÑ\8f иÑ\81полÑ\8cзованиÑ\8f двÑ\83Ñ\85факторной аутентификации вам понадобится мобильное приложение, поддерживающее TOTP, например Google Authenticator, Authy или Microsoft Authenticator.',
+ 'mfa_option_totp_desc' => 'Ð\94лÑ\8f иÑ\81полÑ\8cзованиÑ\8f многофакторной аутентификации вам понадобится мобильное приложение, поддерживающее TOTP, например Google Authenticator, Authy или Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Резервные коды',
'mfa_option_backup_codes_desc' => 'Безопасно хранить набор одноразовых резервных кодов, которые вы можете ввести для проверки вашей личности.',
'mfa_gen_confirm_and_enable' => 'Подтвердить и включить',
'mfa_gen_backup_codes_download' => 'Скачать коды',
'mfa_gen_backup_codes_usage_warning' => 'Каждый код может быть использован только один раз',
'mfa_gen_totp_title' => 'Настройка мобильного приложения',
- 'mfa_gen_totp_desc' => 'Ð\94лÑ\8f иÑ\81полÑ\8cзованиÑ\8f двÑ\83Ñ\85факторной аутентификации вам понадобится мобильное приложение, поддерживающее TOTP, например Google Authenticator, Authy или Microsoft Authenticator.',
+ 'mfa_gen_totp_desc' => 'Ð\94лÑ\8f иÑ\81полÑ\8cзованиÑ\8f многофакторной аутентификации вам понадобится мобильное приложение, поддерживающее TOTP, например Google Authenticator, Authy или Microsoft Authenticator.',
'mfa_gen_totp_scan' => 'Отсканируйте QR-код, используя приложение для аутентификации.',
'mfa_gen_totp_verify_setup' => 'Проверить настройки',
'mfa_gen_totp_verify_setup_desc' => 'Проверьте, что все работает введя код, сгенерированный внутри вашего приложения для аутентификации, в поле ввода ниже:',
'mfa_verify_access' => 'Подтвердите доступ',
'mfa_verify_access_desc' => 'Ваша учетная запись требует подтверждения личности на дополнительном уровне верификации, прежде чем вам будет предоставлен доступ. Для продолжения подтвердите вход, используя один из настроенных методов.',
'mfa_verify_no_methods' => 'Методы не настроены',
- 'mfa_verify_no_methods_desc' => 'Ð\94лÑ\8f ваÑ\88ей Ñ\83Ñ\87еÑ\82ной запиÑ\81и не найденÑ\8b двÑ\83Ñ\85факторные методы аутентификации. Вам нужно настроить хотя бы один метод, прежде чем получить доступ.',
+ 'mfa_verify_no_methods_desc' => 'Ð\94лÑ\8f ваÑ\88ей Ñ\83Ñ\87еÑ\82ной запиÑ\81и не найденÑ\8b многофакторные методы аутентификации. Вам нужно настроить хотя бы один метод, прежде чем получить доступ.',
'mfa_verify_use_totp' => 'Проверить используя мобильное приложение',
'mfa_verify_use_backup_codes' => 'Проверить используя резервный код',
'mfa_verify_backup_code' => 'Резервный код',
'mfa_verify_backup_code_desc' => 'Введите один из оставшихся резервных кодов ниже:',
'mfa_verify_backup_code_enter_here' => 'Введите резервный код',
'mfa_verify_totp_desc' => 'Введите код, сгенерированный с помощью мобильного приложения, ниже:',
- 'mfa_setup_login_notification' => 'Ð\94вÑ\83Ñ\85Ñ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од настроен, пожалуйста, войдите снова, используя сконфигурированный метод.',
+ 'mfa_setup_login_notification' => 'Ð\9cногоÑ\84акÑ\82оÑ\80нÑ\8bй меÑ\82од аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86ии настроен, пожалуйста, войдите снова, используя сконфигурированный метод.',
];
// Buttons
'cancel' => 'Отмена',
- 'close' => 'Close',
+ 'close' => 'Закрыть',
'confirm' => 'Применить',
'back' => 'Назад',
'save' => 'Сохранить',
'remove' => 'Удалить',
'add' => 'Добавить',
'configure' => 'Настройка',
+ 'manage' => 'Управлять',
'fullscreen' => 'На весь экран',
'favourite' => 'Избранное',
'unfavourite' => 'Убрать из избранного',
// Image Manager
'image_select' => 'Выбрать изображение',
- 'image_list' => 'Image List',
- 'image_details' => 'Image Details',
+ 'image_list' => 'Список изображений',
+ 'image_details' => 'Сведения об изображении',
'image_upload' => 'Загрузить изображение',
'image_intro' => 'Здесь вы можете выбрать и управлять изображениями, которые были ранее загружены в систему.',
'image_intro_upload' => 'Загрузите новое изображение, перетянув файл в это окно, или с помощью кнопки "Загрузить изображение" выше.',
'image_page_title' => 'Просмотр всех изображений, загруженных на эту страницу',
'image_search_hint' => 'Поиск по названию изображения',
'image_uploaded' => 'Загружено :uploadedDate',
- 'image_uploaded_by' => 'Uploaded by :userName',
- 'image_uploaded_to' => 'Uploaded to :pageLink',
- 'image_updated' => 'Updated :updateDate',
+ 'image_uploaded_by' => 'Загружено :userName',
+ 'image_uploaded_to' => 'Загружено на :pageLink',
+ 'image_updated' => 'Обновлено :updateDate',
'image_load_more' => 'Загрузить еще',
'image_image_name' => 'Название изображения',
'image_delete_used' => 'Это изображение используется на странице ниже.',
'image_upload_success' => 'Изображение успешно загружено',
'image_update_success' => 'Детали изображения успешно обновлены',
'image_delete_success' => 'Изображение успешно удалено',
- 'image_replace' => 'Replace Image',
- 'image_replace_success' => 'Image file successfully updated',
+ 'image_replace' => 'Заменить изображение',
+ 'image_replace_success' => 'Файл изображения успешно обновлён',
// Code Editor
'code_editor' => 'Изменить код',
// Permissions and restrictions
'permissions' => 'Разрешения',
- 'permissions_desc' => 'Установите права доступа для переопределения прав по умолчанию, предоставленных ролями пользователей.',
- 'permissions_book_cascade' => 'РазÑ\80еÑ\88ениÑ\8f, установленные для книг, автоматически распространяются на дочерние главы и страницы, если для них не определены собственные разрешения.',
- 'permissions_chapter_cascade' => 'РазÑ\80еÑ\88ениÑ\8f, установленные для глав, автоматически распространяются на дочерние страницы, если для них не определены собственные разрешения.',
+ 'permissions_desc' => 'Установите права доступа для переопределения прав, предоставленных ролями пользователей по-умолчанию.',
+ 'permissions_book_cascade' => 'Ð\9fÑ\80ава доÑ\81Ñ\82Ñ\83па, установленные для книг, автоматически распространяются на дочерние главы и страницы, если для них не определены собственные разрешения.',
+ 'permissions_chapter_cascade' => 'Ð\9fÑ\80ава доÑ\81Ñ\82Ñ\83па, установленные для глав, автоматически распространяются на дочерние страницы, если для них не определены собственные разрешения.',
'permissions_save' => 'Сохранить разрешения',
'permissions_owner' => 'Владелец',
'permissions_role_everyone_else' => 'Все остальные',
'shelves_permissions_updated' => 'Доступы к полке обновлены',
'shelves_permissions_active' => 'Действующие разрешения полки',
'shelves_permissions_cascade_warning' => 'Разрешения на полки не наследуются автоматически содержащимся в них книгам. Это происходит потому, что книга может находиться на нескольких полках. Однако разрешения могут быть установлены для книг полки с помощью опции, приведенной ниже.',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => 'Разрешения полки на создание используется только для копирования разрешений на дочерние книги с помощью действия, описанного ниже. Они не контролируют возможность создавать книги.',
'shelves_copy_permissions_to_books' => 'Наследовать доступы книгам',
'shelves_copy_permissions' => 'Копировать доступы',
'shelves_copy_permissions_explain' => 'Это применит текущие настройки разрешений для этой полки ко всем книгам, содержащимся в ней. Перед активацией убедитесь, что все изменения разрешений этой полки были сохранены.',
'pages_editing_page' => 'Редактирование страницы',
'pages_edit_draft_save_at' => 'Черновик сохранён в ',
'pages_edit_delete_draft' => 'Удалить черновик',
- 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.',
+ 'pages_edit_delete_draft_confirm' => 'Вы уверены, что хотите удалить черновик с изменениями? Все изменения, внесенные вами с момента последнего полного сохранения, будут утеряны и редактор будет обновлен данными с последнего сохранения страницы.',
'pages_edit_discard_draft' => 'Отменить черновик',
'pages_edit_switch_to_markdown' => 'Переключиться на Markdown',
'pages_edit_switch_to_markdown_clean' => 'Только Markdown (с возможными потерями форматирования)',
'pages_md_insert_drawing' => 'Вставить рисунок',
'pages_md_show_preview' => 'Предпросмотр',
'pages_md_sync_scroll' => 'Синхронизировать прокрутку',
+ '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' => 'Страница не находится в главе',
'pages_move' => 'Переместить страницу',
'pages_copy' => 'Скопировать страницу',
'pages_revisions_created_by' => 'Создана',
'pages_revisions_date' => 'Дата версии',
'pages_revisions_number' => '#',
- 'pages_revisions_sort_number' => 'Номер ревизии',
+ 'pages_revisions_sort_number' => 'Номер версии',
'pages_revisions_numbered' => 'Версия #:id',
'pages_revisions_numbered_changes' => 'Изменения в версии #:id',
'pages_revisions_editor' => 'Тип редактора',
'pages_revisions_restore' => 'Восстановить',
'pages_revisions_none' => 'У этой страницы нет других версий',
'pages_copy_link' => 'Копировать ссылку',
- 'pages_edit_content_link' => 'Jump to section in editor',
+ 'pages_edit_content_link' => 'Перейти к разделу в редакторе',
'pages_pointer_enter_mode' => 'Enter section select mode',
- 'pages_pointer_label' => 'Page Section Options',
- 'pages_pointer_permalink' => 'Page Section Permalink',
- 'pages_pointer_include_tag' => 'Page Section Include Tag',
+ 'pages_pointer_label' => 'Настройки раздела страницы',
+ 'pages_pointer_permalink' => 'Постоянная ссылка на раздел страницы',
+ 'pages_pointer_include_tag' => 'Раздел страницы с тегом',
'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink',
'pages_permissions_active' => 'Действующие разрешения на страницу',
'time_b' => 'за последние :minCount минут',
'message' => ':start :time. Будьте осторожны, чтобы не перезаписывать друг друга!',
],
- 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content',
- 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content',
+ 'pages_draft_discarded' => 'Черновик сброшен! Редактор обновлен текущим содержимым страницы',
+ 'pages_draft_deleted' => 'Черновик удалён! Редактор обновлен текущим содержимым страницы',
'pages_specific' => 'Конкретная страница',
'pages_is_template' => 'Шаблон страницы',
'comment_new' => 'Новый комментарий',
'comment_created' => 'прокомментировал :createDiff',
'comment_updated' => 'Обновлен :updateDiff пользователем :username',
- 'comment_updated_indicator' => 'Updated',
+ 'comment_updated_indicator' => 'Обновлено',
'comment_deleted_success' => 'Комментарий удален',
'comment_created_success' => 'Комментарий добавлен',
'comment_updated_success' => 'Комментарий обновлен',
'comment_delete_confirm' => 'Удалить этот комментарий?',
'comment_in_reply_to' => 'В ответ на :commentId',
- 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
+ 'comment_editor_explain' => 'Вот комментарии, которые были оставлены на этой странице. Комментарии могут быть добавлены и управляться при просмотре сохраненной страницы.',
// Revision
'revision_delete_confirm' => 'Удалить эту версию?',
'references' => 'Ссылки',
'references_none' => 'Нет отслеживаемых ссылок на этот элемент.',
'references_to_desc' => 'Ниже показаны все известные страницы в системе, которые ссылаются на этот элемент.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Игнорирование уведомлений',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Drawing & Images
'image_upload_error' => 'Произошла ошибка при загрузке изображения',
'image_upload_type_error' => 'Неправильный тип загружаемого изображения',
- 'image_upload_replace_type' => 'Image file replacements must be of the same type',
+ 'image_upload_replace_type' => 'Замена файла изображения должна быть того же типа',
'drawing_data_not_found' => 'Данные чертежа не могут быть загружены. Возможно, файл чертежа больше не существует или у вас нет разрешения на доступ к нему.',
// Attachments
// Settings & Maintenance
'maintenance_test_email_failure' => 'Ошибка при отправке тестового письма:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'Новый комментарий на странице: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'Новая страница: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Имя страницы:',
+ 'detail_commenter' => 'Комментатор:',
+ 'detail_comment' => 'Комментарий:',
+ 'detail_created_by' => 'Создано:',
+ 'detail_updated_by' => 'Обновлено:',
+
+ 'action_view_comment' => 'Просмотреть комментарий',
+ 'action_view_page' => 'Посмотреть страницу',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'ваши настройки уведомлений',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Горячие клавиши',
'shortcuts_interface' => 'Горячие клавиши интерфейса',
'shortcuts_toggle_desc' => 'Здесь вы можете включить или отключить горячие клавиши системного интерфейса, используемые для навигации и действий.',
'shortcuts_save' => 'Сохранить горячие клавиши',
'shortcuts_overlay_desc' => 'Примечание: Когда горячие клавиши включены, вспомогательное наложение доступно через нажатие "?", которая будет подсвечивать доступные горячие клавиши для действий, видимых в настоящее время на экране.',
'shortcuts_update_success' => 'Настройки горячих клавиш были обновлены!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Управление настройками приложения',
'role_export_content' => 'Экспорт контента',
'role_editor_change' => 'Изменение редактора страниц',
+ 'role_notifications' => 'Получение и управление уведомлениями',
'role_asset' => 'Права доступа к материалам',
'roles_system_warning' => 'Имейте в виду, что доступ к любому из указанных выше трех разрешений может позволить пользователю изменить свои собственные привилегии или привилегии других пользователей системы. Назначать роли с этими правами можно только доверенным пользователям.',
'role_asset_desc' => 'Эти разрешения контролируют доступ по умолчанию к параметрам внутри системы. Разрешения на книги, главы и страницы перезапишут эти разрешения.',
'users_api_tokens_create' => 'Создать токен',
'users_api_tokens_expires' => 'Истекает',
'users_api_tokens_docs' => 'Документация',
- 'users_mfa' => 'Ð\94вÑ\83Ñ\85факторная аутентификация',
- 'users_mfa_desc' => 'Ð\94вÑ\83Ñ\85факторная аутентификация повышает степень безопасности вашей учетной записи.',
+ 'users_mfa' => 'Ð\9cногофакторная аутентификация',
+ 'users_mfa_desc' => 'Ð\9cногофакторная аутентификация повышает степень безопасности вашей учетной записи.',
'users_mfa_x_methods' => 'методов настроено :count|методов сконфигурировано :count',
'users_mfa_configure' => 'Настройка методов',
// Webhooks
'webhooks' => 'Вебхуки',
'webhooks_index_desc' => 'Webhooks - это способ посылать данные на внешние URL-адреса при возникновении определенных действий и событий в системе, которые позволяют интегрировать события с внешними платформами, такими как системы обмена сообщениями или уведомлениями.',
- 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+ 'webhooks_x_trigger_events' => ':count событие триггера|:count событий триггера',
'webhooks_create' => 'Создать вебхук',
'webhooks_none_created' => 'Вебхуки еще не созданы.',
'webhooks_edit' => 'Редактировать вебхук',
'favourite_add_notification' => '":name" bol pridaný medzi obľúbené',
'favourite_remove_notification' => '":name" bol odstránený z obľúbených',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'komentoval(a)',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'aktualizované oprávnenia',
];
'remove' => 'Odstrániť',
'add' => 'Pridať',
'configure' => 'Konfigurácia',
+ 'manage' => 'Manage',
'fullscreen' => 'Celá obrazovka',
'favourite' => 'Pridať do obľúbených',
'unfavourite' => 'Odstrániť z obľúbených',
'pages_md_insert_drawing' => 'Vložiť kresbu',
'pages_md_show_preview' => 'Zobraziť náhľad',
'pages_md_sync_scroll' => 'Posúvanie ukážky synchronizácie',
+ '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' => 'Stránka nie je v kapitole',
'pages_move' => 'Presunúť stránku',
'pages_copy' => 'Kpoírovať stránku',
'references' => 'Referencie',
'references_none' => 'Neexistujú žiadne sledované referencie na túto položku.',
'references_to_desc' => 'Nižšie sú zobrazené všetky známe stránky v systéme, ktoré odkazujú na túto položku.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Chyba pri odosielaní testovacieho e-mailu:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Skratky',
'shortcuts_interface' => 'Klávesové skratky rozhrania',
'shortcuts_toggle_desc' => 'Tu môžete povoliť alebo zakázať klávesové skratky systémového rozhrania, ktoré sa používajú na navigáciu a akcie.',
'shortcuts_save' => 'Uložiť skratky',
'shortcuts_overlay_desc' => 'Poznámka: Keď sú zapnuté skratky, pomocné prekrytie je dostupné stlačením „?", ktoré zvýrazní dostupné skratky akcií,, ktoré sú momentálne viditeľné na obrazovke.',
'shortcuts_update_success' => 'Predvoľby skratiek boli aktualizované!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Spravovať nastavenia aplikácie',
'role_export_content' => 'Exportovať obsah',
'role_editor_change' => 'Zmeniť editor stránky',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Oprávnenia majetku',
'roles_system_warning' => 'Uvedomte si, že prístup ku ktorémukoľvek z vyššie uvedených troch povolení môže používateľovi umožniť zmeniť svoje vlastné privilégiá alebo privilégiá ostatných v systéme. Roly s týmito povoleniami priraďujte iba dôveryhodným používateľom.',
'role_asset_desc' => 'Tieto oprávnenia regulujú prednastavený prístup k zdroju v systéme. Oprávnenia pre knihy, kapitoly a stránky majú vyššiu prioritu.',
'favourite_add_notification' => '":name" has been added to your favourites',
'favourite_remove_notification' => '":name" has been removed from your favourites',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'komentar na',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'pravice so posodobljene',
];
'remove' => 'Odstrani',
'add' => 'Dodaj',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Celozaslonski način',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Vstavi risbo',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Stran ni v poglavju',
'pages_move' => 'Premakni stran',
'pages_copy' => 'Kopiraj stran',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Napaka se je pojavila pri pošiljanju testne e-pošte:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Nastavitve za upravljanje',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Sistemska dovoljenja',
'roles_system_warning' => 'Zavedajte se, da lahko dostop do kateregakoli od zgornjih treh dovoljenj uporabniku omogoči, da spremeni lastne privilegije ali privilegije drugih v sistemu. Vloge s temi dovoljenji dodelite samo zaupanja vrednim uporabnikom.',
'role_asset_desc' => 'Ta dovoljenja nadzorujejo privzeti dostop do sredstev v sistemu. Dovoljenja za knjige, poglavja in strani bodo razveljavila ta dovoljenja.',
'favourite_add_notification' => '":name" har lagts till i dina favoriter',
'favourite_remove_notification' => '":name" har tagits bort från dina favoriter',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
+ 'auth_register' => 'registrerad som ny användare',
'auth_password_reset_request' => 'requested user password reset',
'auth_password_reset_update' => 'reset user password',
'mfa_setup_method' => 'configured MFA method',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'kommenterade',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'uppdaterade behörigheter',
];
'remember_me' => 'Kom ihåg mig',
'ldap_email_hint' => 'Vänligen ange en e-postadress att använda till kontot.',
'create_account' => 'Skapa konto',
- 'already_have_account' => 'Har du redan en användare?',
+ 'already_have_account' => 'Har du redan ett konto?',
'dont_have_account' => 'Har du ingen användare?',
'social_login' => 'Logga in genom socialt medie',
'social_registration' => 'Registrera dig genom socialt media',
// Buttons
'cancel' => 'Avbryt',
- 'close' => 'Close',
+ 'close' => 'Stäng',
'confirm' => 'Bekräfta',
'back' => 'Bakåt',
'save' => 'Spara',
'remove' => 'Radera',
'add' => 'Lägg till',
'configure' => 'Konfigurera',
+ 'manage' => 'Hantera',
'fullscreen' => 'Helskärm',
'favourite' => 'Favorit',
'unfavourite' => 'Ta bort favorit',
'pages_md_insert_drawing' => 'Infoga teckning',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Sidan ligger inte i något kapitel',
'pages_move' => 'Flytta sida',
'pages_copy' => 'Kopiera sida',
'references' => 'Referenser',
'references_none' => 'Det finns inga referenser kopplade till detta objekt.',
'references_to_desc' => 'Nedan visas alla kända sidor i systemet som länkar till detta objekt.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignorera',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Ett fel uppstod när ett test mail skulle skickas:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'Visa kommentar',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
- 'shortcuts' => 'Shortcuts',
+ 'preferences' => 'Preferences',
+
+ 'shortcuts' => 'Genvägar',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Hantera appinställningar',
'role_export_content' => 'Exportera innehåll',
'role_editor_change' => 'Ändra sidredigerare',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Tillgång till innehåll',
'roles_system_warning' => 'Var medveten om att åtkomst till någon av ovanstående tre behörigheter kan tillåta en användare att ändra sina egna rättigheter eller andras rättigheter i systemet. Tilldela endast roller med dessa behörigheter till betrodda användare.',
'role_asset_desc' => 'Det här är standardinställningarna för allt innehåll i systemet. Eventuella anpassade rättigheter på böcker, kapitel och sidor skriver över dessa inställningar.',
'favourite_add_notification' => '":name" favorilerinize eklendi',
'favourite_remove_notification' => '":name" favorilerinizden çıkarıldı',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'oturum açıldı',
'auth_register' => 'yeni kullanıcı olarak kayıt yapıldı',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'yorum yaptı',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'güncellenmiş izinler',
];
'remove' => 'Kaldır',
'add' => 'Ekle',
'configure' => 'Yapılandır',
+ 'manage' => 'Manage',
'fullscreen' => 'Tam Ekran',
'favourite' => 'Favoriye ekle',
'unfavourite' => 'Favorilerden çıkar',
'pages_md_insert_drawing' => 'Çizim Ekle',
'pages_md_show_preview' => 'Önizlemeyi göster',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Bu sayfa, bir bölüme ait değil',
'pages_move' => 'Sayfayı Taşı',
'pages_copy' => 'Sayfayı Kopyala',
'references' => 'Referanslar',
'references_none' => 'Bu öğeye ilişkin takip edilen bir referans bulunmamaktadır.',
'references_to_desc' => 'Aşağıda, sistemde bu öğeye bağlantı veren bilinen tüm sayfalar gösterilmektedir.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Test e-postası gönderilirken bir hata meydana geldi:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Kısayollar',
'shortcuts_interface' => 'Klavye Kısayollarını Görüntüle',
'shortcuts_toggle_desc' => 'Burada, gezinme ve eylemler için kullanılan klavye sistem arayüzü kısayollarını etkinleştirebilir veya devre dışı bırakabilirsiniz.',
'shortcuts_save' => 'Kısayolları Kaydet',
'shortcuts_overlay_desc' => 'Not: Kısayollar etkinleştirildiğinde, "?" tuşuna basılarak o anda ekranda görünen eylemler için mevcut kısayolları vurgulayan bir yardımcı yer paylaşımı kullanılabilir.',
'shortcuts_update_success' => 'Kısayol tercihleri güncellendi!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Uygulama ayarlarını yönet',
'role_export_content' => 'İçeriği dışa aktar',
'role_editor_change' => 'Yazı editörünü değiştir',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Varlık Yetkileri',
'roles_system_warning' => 'Yukarıdaki üç izinden herhangi birine erişimin, kullanıcının kendi ayrıcalıklarını veya sistemdeki diğerlerinin ayrıcalıklarını değiştirmesine izin verebileceğini unutmayın. Yalnızca bu izinlere sahip rolleri güvenilir kullanıcılara atayın.',
'role_asset_desc' => 'Bu izinler, sistem içindeki varlıklara varsayılan erişim izinlerini ayarlar. Kitaplar, bölümler ve sayfalar üzerindeki izinler, buradaki izinleri geçersiz kılar.',
'favourite_add_notification' => '":ім\'я" було додане до ваших улюлених',
'favourite_remove_notification' => '":ім\'я" було видалено з ваших улюблених',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'прокоментував',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'видалений коментар',
+
+ // Other
'permissions_update' => 'оновив дозволи',
];
'remove' => 'Видалити',
'add' => 'Додати',
'configure' => 'Налаштувати',
+ 'manage' => 'Manage',
'fullscreen' => 'На весь екран',
'favourite' => 'Улюблене',
'unfavourite' => 'Прибрати з обраного',
'pages_md_insert_drawing' => 'Вставити малюнок',
'pages_md_show_preview' => 'Показати попередній перегляд',
'pages_md_sync_scroll' => 'Синхронізація прокручування попереднього перегляду',
+ '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' => 'Сторінка не знаходиться в розділі',
'pages_move' => 'Перемістити сторінку',
'pages_copy' => 'Копіювати сторінку',
'references' => 'Посилання',
'references_none' => 'Немає відслідковуваних посилань для цього елемента.',
'references_to_desc' => 'Показані нижче всі відомі сторінки в системі, що посилаються на цей елемент.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Помилка під час надсилання тестового електронного листа:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Ярлики',
'shortcuts_interface' => 'Комбінації клавіш інтерфейсу',
'shortcuts_toggle_desc' => 'Тут ви можете увімкнути або вимкнути ярлики інтерфейсу клавіатури, які використовуються для навігації та дій.',
'shortcuts_save' => 'Зберегти ярлики',
'shortcuts_overlay_desc' => 'Примітка: якщо ярлики ввімкнено, допоміжне накладання доступне, натиснувши "?" який виділить доступні ярлики для дій, які зараз видно на екрані.',
'shortcuts_update_success' => 'Налаштування ярликів оновлено!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Керування налаштуваннями програми',
'role_export_content' => 'Вміст експорту',
'role_editor_change' => 'Змінити редактор сторінок',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Дозволи',
'roles_system_warning' => 'Майте на увазі, що доступ до будь-якого з вищезазначених трьох дозволів може дозволити користувачеві змінювати власні привілеї або привілеї інших в системі. Ролі з цими дозволами призначайте лише довіреним користувачам.',
'role_asset_desc' => 'Ці дозволи контролюють стандартні доступи всередині системи. Права на книги, розділи та сторінки перевизначать ці дозволи.',
'favourite_add_notification' => '":name" sevimlilaringizga qo\'shildi',
'favourite_remove_notification' => '":name" sevimlilaringizdan olib tashlandi',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => 'fikr qoldirdi',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => 'yangilangan huquqlar',
];
'remove' => 'Remove',
'add' => 'Add',
'configure' => 'Configure',
+ 'manage' => 'Manage',
'fullscreen' => 'Fullscreen',
'favourite' => 'Favourite',
'unfavourite' => 'Unfavourite',
'pages_md_insert_drawing' => 'Insert Drawing',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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',
'pages_move' => 'Move Page',
'pages_copy' => 'Copy Page',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Shortcuts',
'shortcuts_interface' => 'Interface Keyboard Shortcuts',
'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
'shortcuts_save' => 'Save Shortcuts',
'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
'shortcuts_update_success' => 'Shortcut preferences have been updated!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Manage app settings',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Asset Permissions',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
'page_restore' => 'đã khôi phục trang',
'page_restore_notification' => 'Trang đã được khôi phục thành công',
'page_move' => 'đã di chuyển trang',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => 'Đã di chuyển trang thành công',
// Chapters
'chapter_create' => 'đã tạo chương',
'chapter_delete' => 'đã xóa chương',
'chapter_delete_notification' => 'Chương đã được xóa thành công',
'chapter_move' => 'đã di chuyển chương',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => 'Đã chuyển chương thành công',
// Books
'book_create' => 'đã tạo sách',
'bookshelf_delete_notification' => 'Xoá giá sách thành công',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => 'đã khôi phục sửa đổi',
+ 'revision_delete' => 'đã xóa bản sửa đổi',
+ 'revision_delete_notification' => 'Bản sửa đổi đã được xóa thành công',
// Favourites
'favourite_add_notification' => '":name" đã được thêm vào danh sách yêu thích của bạn',
'favourite_remove_notification' => '":name" đã được gỡ khỏi danh sách yêu thích của bạn',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => 'đăng nhập',
+ 'auth_register' => 'đã đăng ký như người dùng mới',
+ 'auth_password_reset_request' => 'yêu cầu người dùng đặt lại mật khẩu',
+ 'auth_password_reset_update' => 'đặt lại mật khẩu người dùng',
+ 'mfa_setup_method' => 'đã định cấu hình phương thức MFA',
'mfa_setup_method_notification' => 'Cấu hình xác thực nhiều bước thành công',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => 'loại bỏ phương thức MFA',
'mfa_remove_method_notification' => 'Đã gỡ xác thực nhiều bước',
// Settings
- 'settings_update' => 'updated settings',
- 'settings_update_notification' => 'Settings successfully updated',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'settings_update' => 'cập nhật cài đặt',
+ 'settings_update_notification' => 'Cài đặt đã cập nhật thành công',
+ 'maintenance_action_run' => 'chạy hành động bảo trì',
// Webhooks
'webhook_create' => 'đã tạo webhook',
'webhook_delete_notification' => 'Webhook đã được xóa thành công',
// Users
- 'user_create' => 'created user',
- 'user_create_notification' => 'User successfully created',
- 'user_update' => 'updated user',
+ 'user_create' => 'người dùng đã tạo',
+ 'user_create_notification' => 'Người dùng được tạo thành công',
+ 'user_update' => 'người dùng được cập nhật',
'user_update_notification' => 'Người dùng được cập nhật thành công',
- 'user_delete' => 'deleted user',
+ 'user_delete' => 'người dùng đã bị xóa',
'user_delete_notification' => 'Người dùng đã được xóa thành công',
// API Tokens
'role_create_notification' => 'Vai trò mới đã được tạo thành công',
'role_update' => 'updated role',
'role_update_notification' => 'Vai trò đã được cập nhật thành công',
- 'role_delete' => 'deleted role',
+ 'role_delete' => 'đã xóa vai trò',
'role_delete_notification' => 'Vai trò đã được xóa thành công',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => 'làm trống thùng rác',
+ 'recycle_bin_restore' => 'khôi phục từ thùng rác',
+ 'recycle_bin_destroy' => 'đã xóa khỏi thùng rác',
- // Other
+ // Comments
'commented_on' => 'đã bình luận về',
+ 'comment_create' => 'thêm bình luận',
+ 'comment_update' => 'cập nhật bình luận',
+ 'comment_delete' => 'đã xóa bình luận',
+
+ // Other
'permissions_update' => 'các quyền đã được cập nhật',
];
// Buttons
'cancel' => 'Huỷ',
- 'close' => 'Close',
+ 'close' => 'Đóng',
'confirm' => 'Xác nhận',
'back' => 'Quay lại',
'save' => 'Lưu',
'remove' => 'Xóa bỏ',
'add' => 'Thêm',
'configure' => 'Cấu hình',
+ 'manage' => 'Manage',
'fullscreen' => 'Toàn màn hình',
'favourite' => 'Yêu thích',
'unfavourite' => 'Bỏ yêu thích',
// Image Manager
'image_select' => 'Chọn Ảnh',
- 'image_list' => 'Image List',
- 'image_details' => 'Image Details',
+ 'image_list' => 'Danh sách hình ảnh',
+ 'image_details' => 'Chi tiết hình ảnh',
'image_upload' => 'Tải ảnh lên',
'image_intro' => 'Bạn có thể lựa chọn và quản lý các hình ảnh đã được tải lên hệ thống từ trước ở đây.',
'image_intro_upload' => 'Tải lên ảnh mới bằng cách kéo và thả nó vào cửa sổ này, hoặc sử dụng nút tải ảnh ở bên trên.',
'image_page_title' => 'Xem các ảnh đã được tải lên trong trang này',
'image_search_hint' => 'Tìm kiếm ảnh bằng tên',
'image_uploaded' => 'Đã tải lên :uploadedDate',
- 'image_uploaded_by' => 'Uploaded by :userName',
- 'image_uploaded_to' => 'Uploaded to :pageLink',
- 'image_updated' => 'Updated :updateDate',
+ 'image_uploaded_by' => 'Tải lên bởi :userName',
+ 'image_uploaded_to' => 'Đã tải lên :pageLink',
+ 'image_updated' => 'Đã cập nhật :updateDate',
'image_load_more' => 'Hiện thêm',
'image_image_name' => 'Tên Ảnh',
'image_delete_used' => 'Ảnh này được sử dụng trong các trang dưới đây.',
'image_upload_success' => 'Ảnh đã tải lên thành công',
'image_update_success' => 'Chi tiết ảnh được cập nhật thành công',
'image_delete_success' => 'Ảnh đã được xóa thành công',
- 'image_replace' => 'Replace Image',
- 'image_replace_success' => 'Image file successfully updated',
+ 'image_replace' => 'Thay thế hình ảnh',
+ 'image_replace_success' => 'Đã cập nhật thành công tệp hình ảnh',
// Code Editor
'code_editor' => 'Sửa Mã',
'meta_updated' => 'Được cập nhật :timeLength',
'meta_updated_name' => 'Được cập nhật :timeLength bởi :user',
'meta_owned_name' => 'Được sở hữu bởi :user',
- 'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+ 'meta_reference_page_count' => 'Được tham chiếu trên :count page|Được tham chiếu trên :count pages',
'entity_select' => 'Chọn thực thể',
'entity_select_lack_permission' => 'Bạn không có quyền để chọn mục này',
'images' => 'Ảnh',
'permissions_owner' => 'Chủ sở hữu',
'permissions_role_everyone_else' => 'Những người khác',
'permissions_role_everyone_else_desc' => 'Đặt quyền cho tất cả vai trò không được ghi đè cụ thể.',
- 'permissions_role_override' => 'Override permissions for role',
- 'permissions_inherit_defaults' => 'Inherit defaults',
+ 'permissions_role_override' => 'Ghi đè quyền cho vai trò',
+ 'permissions_inherit_defaults' => 'Kế thừa giá trị mặc định',
// Search
'search_results' => 'Kết quả Tìm kiếm',
'shelves_save' => 'Lưu Giá',
'shelves_books' => 'Sách trên Giá này',
'shelves_add_books' => 'Thêm sách vào Giá này',
- 'shelves_drag_books' => 'Drag books below to add them to this shelf',
+ 'shelves_drag_books' => 'Kéo sách bên dưới để thêm vào kệ sách này',
'shelves_empty_contents' => 'Giá này không có sách nào',
'shelves_edit_and_assign' => 'Chỉnh sửa kệ để gán sách',
- 'shelves_edit_named' => 'Edit Shelf :name',
- 'shelves_edit' => 'Edit Shelf',
- 'shelves_delete' => 'Delete Shelf',
- 'shelves_delete_named' => 'Delete Shelf :name',
- 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.",
+ 'shelves_edit_named' => 'Chỉnh sửa kệ :name',
+ 'shelves_edit' => 'Chỉnh sửa kệ',
+ 'shelves_delete' => 'Xóa kệ',
+ 'shelves_delete_named' => 'Xóa kệ :name',
+ 'shelves_delete_explain' => "Thao tác này sẽ xóa kệ có tên ':name'. Sách chứa sẽ không bị xóa.",
'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?',
'shelves_permissions' => 'Shelf Permissions',
'shelves_permissions_updated' => 'Shelf Permissions Updated',
'pages_md_insert_drawing' => 'Chèn bản vẽ',
'pages_md_show_preview' => 'Show preview',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => 'Trang không nằm trong một chương',
'pages_move' => 'Di chuyển Trang',
'pages_copy' => 'Sao chép Trang',
'pages_copy_link' => 'Sao chép Liên kết',
'pages_edit_content_link' => 'Jump to section in editor',
'pages_pointer_enter_mode' => 'Enter section select mode',
- 'pages_pointer_label' => 'Page Section Options',
- 'pages_pointer_permalink' => 'Page Section Permalink',
- 'pages_pointer_include_tag' => 'Page Section Include Tag',
- 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
- 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink',
+ 'pages_pointer_label' => 'Tùy chọn phần trang',
+ 'pages_pointer_permalink' => 'Phần trang Liên kết cố định',
+ 'pages_pointer_include_tag' => 'Phần trang bao gồm thẻ',
+ 'pages_pointer_toggle_link' => 'Chế độ Liên kết cố định, Nhấn để hiển thị thẻ bao gồm',
+ 'pages_pointer_toggle_include' => 'Bao gồm chế độ thẻ, Nhấn để hiển thị liên kết cố định',
'pages_permissions_active' => 'Đang bật các quyền hạn từ Trang',
'pages_initial_revision' => 'Đăng bài mở đầu',
'pages_references_update_revision' => 'System auto-update of internal links',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => 'Lỗi khi gửi email thử:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => 'Lối tắt',
'shortcuts_interface' => 'Giao diện phím tắt',
'shortcuts_toggle_desc' => 'Tại đây, bạn có thể bật hoặc tắt các phím tắt trên giao diện hệ thống bàn phím, được sử dụng để điều hướng và thực hiện các thao tác.',
'shortcuts_save' => 'Lưu phím tắt',
'shortcuts_overlay_desc' => 'Lưu ý: Khi các phím tắt được bật, lớp phủ trợ giúp sẽ khả dụng bằng cách nhấn "?" sẽ làm nổi bật các lối tắt khả dụng cho các tác vụ hiện đang hiển thị trên màn hình.',
'shortcuts_update_success' => 'Các tùy chọn phím tắt đã được cập nhật!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => 'Quản lý cài đặt của ứng dụng',
'role_export_content' => 'Export content',
'role_editor_change' => 'Change page editor',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => 'Quyền tài sản (asset)',
'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
'role_asset_desc' => 'Các quyền này điều khiển truy cập mặc định tới tài sản (asset) nằm trong hệ thống. Quyền tại Sách, Chường và Trang se ghi đè các quyền này.',
'bookshelf_delete_notification' => '书架删除成功',
// Revisions
- 'revision_restore' => 'restored revision',
- 'revision_delete' => 'deleted revision',
- 'revision_delete_notification' => 'Revision successfully deleted',
+ 'revision_restore' => '已还原修改',
+ 'revision_delete' => '已删除修订',
+ 'revision_delete_notification' => '修订删除成功',
// Favourites
'favourite_add_notification' => '":name" 已添加到您的收藏',
'favourite_remove_notification' => '":name" 已从您的收藏中删除',
+ // Watching
+ 'watch_update_level_notification' => '关注偏好设置已更新成功',
+
// Auth
- 'auth_login' => 'logged in',
- 'auth_register' => 'registered as new user',
- 'auth_password_reset_request' => 'requested user password reset',
- 'auth_password_reset_update' => 'reset user password',
- 'mfa_setup_method' => 'configured MFA method',
+ 'auth_login' => '已登录',
+ 'auth_register' => '已注册为新用户',
+ 'auth_password_reset_request' => '已请求密码重置',
+ 'auth_password_reset_update' => '重置用户密码',
+ 'mfa_setup_method' => '已配置多重身份验证',
'mfa_setup_method_notification' => '多重身份认证设置成功',
- 'mfa_remove_method' => 'removed MFA method',
+ 'mfa_remove_method' => '已移除多重身份验证',
'mfa_remove_method_notification' => '多重身份认证已成功移除',
// Settings
- 'settings_update' => 'updated settings',
+ 'settings_update' => '设置已更新',
'settings_update_notification' => '设置更新成功',
- 'maintenance_action_run' => 'ran maintenance action',
+ 'maintenance_action_run' => '维护操作已运行',
// Webhooks
'webhook_create' => 'Webhook 已创建',
'webhook_delete_notification' => 'Webhook 删除成功',
// Users
- 'user_create' => 'created user',
+ 'user_create' => '用户已创建',
'user_create_notification' => '用户创建成功',
- 'user_update' => 'updated user',
+ 'user_update' => '用户已更新',
'user_update_notification' => '用户更新成功',
- 'user_delete' => 'deleted user',
+ 'user_delete' => '用户已删除',
'user_delete_notification' => '成功移除用户',
// API Tokens
- 'api_token_create' => 'created api token',
- 'api_token_create_notification' => 'API token successfully created',
- 'api_token_update' => 'updated api token',
- 'api_token_update_notification' => 'API token successfully updated',
- 'api_token_delete' => 'deleted api token',
- 'api_token_delete_notification' => 'API token successfully deleted',
+ 'api_token_create' => 'API 令牌已创建',
+ 'api_token_create_notification' => '成功创建 API 令牌',
+ 'api_token_update' => 'API 令牌已更新',
+ 'api_token_update_notification' => 'API 令牌更新成功',
+ 'api_token_delete' => '已删除 API 令牌',
+ 'api_token_delete_notification' => 'API 令牌删除成功',
// Roles
- 'role_create' => 'created role',
+ 'role_create' => '角色已创建',
'role_create_notification' => '角色创建成功',
- 'role_update' => 'updated role',
+ 'role_update' => '角色已更新',
'role_update_notification' => '角色更新成功',
- 'role_delete' => 'deleted role',
+ 'role_delete' => '角色已删除',
'role_delete_notification' => '角色删除成功',
// Recycle Bin
- 'recycle_bin_empty' => 'emptied recycle bin',
- 'recycle_bin_restore' => 'restored from recycle bin',
- 'recycle_bin_destroy' => 'removed from recycle bin',
+ 'recycle_bin_empty' => '回收站已清空',
+ 'recycle_bin_restore' => '已从回收站中恢复',
+ 'recycle_bin_destroy' => '已从回收站中移除',
- // Other
+ // Comments
'commented_on' => '评论',
+ 'comment_create' => '评论已添加',
+ 'comment_update' => '评论已更新',
+ 'comment_delete' => '评论已删除',
+
+ // Other
'permissions_update' => '权限已更新',
];
'remove' => '删除',
'add' => '添加',
'configure' => '配置',
+ 'manage' => '管理',
'fullscreen' => '全屏',
'favourite' => '收藏',
'unfavourite' => '取消收藏',
'shelves_permissions_updated' => '书架权限已更新',
'shelves_permissions_active' => '书架权限已启用',
'shelves_permissions_cascade_warning' => '书架上的权限不会自动应用到书架里的图书上,这是因为图书可以在多个书架上存在。使用下面的选项可以将权限复制到书架里的图书上。',
- 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.',
+ 'shelves_permissions_create' => '书架创建权限仅用于使用下面的操作将权限复制到子图书。这个权限不是用来控制创建书籍的。',
'shelves_copy_permissions_to_books' => '将权限复制到图书',
'shelves_copy_permissions' => '复制权限',
'shelves_copy_permissions_explain' => '此操作会将此书架的当前权限设置应用于其中包含的所有图书上。 启用前请确保已保存对此书架权限的任何更改。',
'pages_editing_page' => '正在编辑页面',
'pages_edit_draft_save_at' => '草稿保存于 ',
'pages_edit_delete_draft' => '删除草稿',
- 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.',
+ 'pages_edit_delete_draft_confirm' => '您确定要删除您的草稿页面更改吗?自上次完整保存以来的所有更改都将丢失,编辑器将更新为最新非草稿页面。',
'pages_edit_discard_draft' => '放弃草稿',
'pages_edit_switch_to_markdown' => '切换到 Markdown 编辑器',
'pages_edit_switch_to_markdown_clean' => '(整理内容)',
'pages_md_insert_drawing' => '插入图表',
'pages_md_show_preview' => '显示预览',
'pages_md_sync_scroll' => '同步预览滚动',
+ 'pages_drawing_unsaved' => '找到未保存的绘图',
+ 'pages_drawing_unsaved_confirm' => '从之前保存失败的绘图中发现了可恢复的数据。您想恢复并继续编辑这个未保存的绘图吗?',
'pages_not_in_chapter' => '本页面不在某章节中',
'pages_move' => '移动页面',
'pages_copy' => '复制页面',
'pages_revisions_none' => '此页面没有修订',
'pages_copy_link' => '复制链接',
'pages_edit_content_link' => '跳转到编辑器中的部分',
- 'pages_pointer_enter_mode' => 'Enter section select mode',
- 'pages_pointer_label' => 'Page Section Options',
- 'pages_pointer_permalink' => 'Page Section Permalink',
- 'pages_pointer_include_tag' => 'Page Section Include Tag',
- 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag',
- 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink',
+ 'pages_pointer_enter_mode' => '进入部分选择模式',
+ 'pages_pointer_label' => '页面部分选项',
+ 'pages_pointer_permalink' => '页面部分永久链接',
+ 'pages_pointer_include_tag' => '页面部分包含标签',
+ 'pages_pointer_toggle_link' => '永久链接模式,按下显示包含标签',
+ 'pages_pointer_toggle_include' => '包含标签模式,按下显示永久链接',
'pages_permissions_active' => '页面权限已启用',
'pages_initial_revision' => '初始发布',
'pages_references_update_revision' => '系统自动更新的内部链接',
'time_b' => '在最近 :minCount 分钟',
'message' => ':time :start。注意不要覆盖他人的更新!',
],
- 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content',
- 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content',
+ 'pages_draft_discarded' => '草稿已丢弃!编辑器已更新到当前页面内容',
+ 'pages_draft_deleted' => '草稿已删除!编辑器已更新为当前页面内容',
'pages_specific' => '具体页面',
'pages_is_template' => '页面模板',
'comment_updated_success' => '评论已更新',
'comment_delete_confirm' => '您确定要删除这条评论?',
'comment_in_reply_to' => '回复 :commentId',
- 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
+ 'comment_editor_explain' => '这里是此页面上留下的评论。查看已保存的页面时可以添加和管理评论。',
// Revision
'revision_delete_confirm' => '您确定要删除此修订版吗?',
'references' => '引用',
'references_none' => '没有跟踪到对此项目的引用。',
'references_to_desc' => '下面显示的是系统中所有已知链接到这个项目的页面。',
+
+ // Watch Options
+ 'watch' => '关注',
+ 'watch_title_default' => '默认偏好设置',
+ 'watch_desc_default' => '将关注设置恢复为默认通知偏好设置。',
+ 'watch_title_ignore' => '忽略',
+ 'watch_desc_ignore' => '忽略所有通知,包括来自用户级偏好的通知。',
+ 'watch_title_new' => '新页面',
+ 'watch_desc_new' => '在此项目中创建任何新页面时通知我。',
+ 'watch_title_updates' => '所有页面更新',
+ 'watch_desc_updates' => '在所有新页面和页面更改时通知我。',
+ 'watch_desc_updates_page' => '在有页面发生更改时通知我。',
+ 'watch_title_comments' => '所有页面更新和评论',
+ 'watch_desc_comments' => '在有新页面、页面更改和新评论时通知我。',
+ 'watch_desc_comments_page' => '在有页面更改和新评论时通知我。',
+ 'watch_change_default' => '更改默认通知偏好',
+ 'watch_detail_ignore' => '忽略通知',
+ 'watch_detail_new' => '已关注新页面',
+ 'watch_detail_updates' => '已关注新页面和更新',
+ 'watch_detail_comments' => '已关注新页面、更新和评论',
+ 'watch_detail_parent_book' => '已关注—继承自父图书',
+ 'watch_detail_parent_book_ignore' => '已忽略—继承自父图书',
+ 'watch_detail_parent_chapter' => '已关注—继承自父章节',
+ 'watch_detail_parent_chapter_ignore' => '已忽略—继承自父章节',
];
// Settings & Maintenance
'maintenance_test_email_failure' => '发送测试电子邮件时出现错误:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'URL 与已配置的 SSR 主机不匹配',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => '页面上有新评论::pageName',
+ 'new_comment_intro' => '一位用户在 :appName: 的页面上发表了评论',
+ 'new_page_subject' => '新页面::pageName',
+ 'new_page_intro' => ':appName: 中创建了一个新页面',
+ 'updated_page_subject' => '页面更新::pageName',
+ 'updated_page_intro' => ':appName: 中的一个页面已被更新',
+ 'updated_page_debounce' => '为了防止出现大量通知,一段时间内您不会收到同一编辑者再次编辑本页面的通知。',
+
+ 'detail_page_name' => '页面名称:',
+ 'detail_commenter' => '评论者:',
+ 'detail_comment' => '评论:',
+ 'detail_created_by' => '创建者:',
+ 'detail_updated_by' => '更新者:',
+
+ 'action_view_comment' => '查看评论',
+ 'action_view_page' => '查看页面',
+
+ 'footer_reason' => '向您发送此通知是因为 :link 涵盖了该项目的此类活动。',
+ 'footer_reason_link' => '个人通知偏好设置',
+];
*/
return [
+ 'preferences' => '偏好设置',
+
'shortcuts' => '快捷键',
'shortcuts_interface' => '界面键盘快捷键',
'shortcuts_toggle_desc' => '你可以启用或禁用键盘系统界面快捷键,这些快捷键用于导航和操作。',
'shortcuts_save' => '保存快捷键',
'shortcuts_overlay_desc' => '注意:当快捷键启用时,可以按"?"键来打开帮助,它将突出显示当前屏幕上可见操作的快捷键。',
'shortcuts_update_success' => '快捷键设置已更新!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => '管理可用于导航系统用户界面的快捷键。',
+
+ 'notifications' => '通知偏好',
+ 'notifications_desc' => '控制在系统内发生某些活动时您会收到的电子邮件通知。',
+ 'notifications_opt_own_page_changes' => '在我拥有的页面被修改时通知我',
+ 'notifications_opt_own_page_comments' => '在我拥有的页面上有新评论时通知我',
+ 'notifications_opt_comment_replies' => '在有人回复我的频率时通知我',
+ 'notifications_save' => '保存偏好设置',
+ 'notifications_update_success' => '通知偏好设置已更新!',
+ 'notifications_watched' => '已关注和忽略的项目',
+ 'notifications_watched_desc' => ' 下面是已应用自定义关注选项的项目。要更新您的关注设置,请查看该项目,然后在该项目的侧边栏中找到关注选项。',
+
+ 'profile_overview_desc' => ' 管理用户个人资料信息,包括首选语言和验证选项。',
+];
'role_manage_settings' => '管理 App 设置',
'role_export_content' => '导出内容',
'role_editor_change' => '更改页面编辑器',
+ 'role_notifications' => '管理和接收通知',
'role_asset' => '资源许可',
'roles_system_warning' => '请注意,拥有以上三个权限中的任何一个都会允许用户更改自己的权限或系统中其他人的权限。 请只将拥有这些权限的角色分配给你信任的用户。',
'role_asset_desc' => '对系统内资源的默认访问许可将由这些权限控制。单独设置在书籍、章节和页面上的权限将覆盖这里的权限设定。',
'page_restore' => '已還原頁面',
'page_restore_notification' => '頁面已還原成功',
'page_move' => '已移動頁面',
- 'page_move_notification' => 'Page successfully moved',
+ 'page_move_notification' => '頁面已成功移動',
// Chapters
'chapter_create' => '已建立章節',
'chapter_delete' => '已刪除章節',
'chapter_delete_notification' => '章節已刪除成功',
'chapter_move' => '已移動章節',
- 'chapter_move_notification' => 'Chapter successfully moved',
+ 'chapter_move_notification' => '章節已移動成功',
// Books
'book_create' => '已建立書本',
'favourite_add_notification' => '":name" 已加入到你的最愛',
'favourite_remove_notification' => '":name" 已從你的最愛移除',
+ // Watching
+ 'watch_update_level_notification' => 'Watch preferences successfully updated',
+
// Auth
'auth_login' => 'logged in',
'auth_register' => 'registered as new user',
'recycle_bin_restore' => 'restored from recycle bin',
'recycle_bin_destroy' => 'removed from recycle bin',
- // Other
+ // Comments
'commented_on' => '評論',
+ 'comment_create' => 'added comment',
+ 'comment_update' => 'updated comment',
+ 'comment_delete' => 'deleted comment',
+
+ // Other
'permissions_update' => '更新權限',
];
'remove' => '移除',
'add' => '新增',
'configure' => '配置',
+ 'manage' => 'Manage',
'fullscreen' => '全螢幕',
'favourite' => '最愛',
'unfavourite' => '取消最愛',
'pages_md_insert_drawing' => '插入繪圖',
'pages_md_show_preview' => '顯示預覽',
'pages_md_sync_scroll' => 'Sync preview scroll',
+ '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' => '頁面不在章節中',
'pages_move' => '移動頁面',
'pages_copy' => '複製頁面',
'references' => 'References',
'references_none' => 'There are no tracked references to this item.',
'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+
+ // Watch Options
+ 'watch' => 'Watch',
+ 'watch_title_default' => 'Default Preferences',
+ 'watch_desc_default' => 'Revert watching to just your default notification preferences.',
+ 'watch_title_ignore' => 'Ignore',
+ 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.',
+ 'watch_title_new' => 'New Pages',
+ 'watch_desc_new' => 'Notify when any new page is created within this item.',
+ 'watch_title_updates' => 'All Page Updates',
+ 'watch_desc_updates' => 'Notify upon all new pages and page changes.',
+ 'watch_desc_updates_page' => 'Notify upon all page changes.',
+ 'watch_title_comments' => 'All Page Updates & Comments',
+ 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.',
+ 'watch_desc_comments_page' => 'Notify upon page changes and new comments.',
+ 'watch_change_default' => 'Change default notification preferences',
+ 'watch_detail_ignore' => 'Ignoring notifications',
+ 'watch_detail_new' => 'Watching for new pages',
+ 'watch_detail_updates' => 'Watching new pages and updates',
+ 'watch_detail_comments' => 'Watching new pages, updates & comments',
+ 'watch_detail_parent_book' => 'Watching via parent book',
+ 'watch_detail_parent_book_ignore' => 'Ignoring via parent book',
+ 'watch_detail_parent_chapter' => 'Watching via parent chapter',
+ 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];
// Settings & Maintenance
'maintenance_test_email_failure' => '寄送測試電子郵件時發生錯誤:',
+ // HTTP errors
+ 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];
--- /dev/null
+<?php
+/**
+ * Text used for activity-based notifications.
+ */
+return [
+
+ 'new_comment_subject' => 'New comment on page: :pageName',
+ 'new_comment_intro' => 'A user has commented on a page in :appName:',
+ 'new_page_subject' => 'New page: :pageName',
+ 'new_page_intro' => 'A new page has been created in :appName:',
+ 'updated_page_subject' => 'Updated page: :pageName',
+ 'updated_page_intro' => 'A page has been updated in :appName:',
+ 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
+
+ 'detail_page_name' => 'Page Name:',
+ 'detail_commenter' => 'Commenter:',
+ 'detail_comment' => 'Comment:',
+ 'detail_created_by' => 'Created By:',
+ 'detail_updated_by' => 'Updated By:',
+
+ 'action_view_comment' => 'View Comment',
+ 'action_view_page' => 'View Page',
+
+ 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.',
+ 'footer_reason_link' => 'your notification preferences',
+];
*/
return [
+ 'preferences' => 'Preferences',
+
'shortcuts' => '快捷鍵',
'shortcuts_interface' => '介面鍵盤快捷鍵',
'shortcuts_toggle_desc' => '您可以在此處啟用或停用鍵盤系統介面快捷鍵,這些快捷鍵用於導覽與操作。',
'shortcuts_save' => '儲存快捷鍵',
'shortcuts_overlay_desc' => '注意:當快捷鍵啟用時,可以按下「?」來使用小幫手覆蓋畫面。這將會在目前的畫面上突顯可見動作的快捷鍵。',
'shortcuts_update_success' => '快捷鍵偏好設定已更新!',
-];
\ No newline at end of file
+ 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.',
+
+ 'notifications' => 'Notification Preferences',
+ 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
+ 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
+ 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
+ 'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
+ 'notifications_save' => 'Save Preferences',
+ 'notifications_update_success' => 'Notification preferences have been updated!',
+ 'notifications_watched' => 'Watched & Ignored Items',
+ 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.',
+
+ 'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+];
'role_manage_settings' => '管理應用程式設定',
'role_export_content' => '匯出內容',
'role_editor_change' => '重設頁面編輯器',
+ 'role_notifications' => 'Receive & manage notifications',
'role_asset' => '資源權限',
'roles_system_warning' => '請注意,有上述三項權限中的任一項的使用者都可以更改自己或系統中其他人的權限。有這些權限的角色只應分配給受信任的使用者。',
'role_asset_desc' => '對系統內資源的預設權限將由這裡的權限控制。若有單獨設定在書本、章節和頁面上的權限,將會覆寫這裡的權限設定。',
"packages": {
"": {
"dependencies": {
- "@codemirror/commands": "^6.2.2",
- "@codemirror/lang-css": "^6.1.1",
- "@codemirror/lang-html": "^6.4.3",
- "@codemirror/lang-javascript": "^6.1.6",
+ "@codemirror/commands": "^6.2.4",
+ "@codemirror/lang-css": "^6.2.1",
+ "@codemirror/lang-html": "^6.4.5",
+ "@codemirror/lang-javascript": "^6.1.9",
"@codemirror/lang-json": "^6.0.1",
- "@codemirror/lang-markdown": "^6.1.1",
+ "@codemirror/lang-markdown": "^6.2.0",
"@codemirror/lang-php": "^6.0.1",
"@codemirror/lang-xml": "^6.0.2",
- "@codemirror/language": "^6.6.0",
- "@codemirror/legacy-modes": "^6.3.2",
- "@codemirror/state": "^6.2.0",
- "@codemirror/theme-one-dark": "^6.1.1",
- "@codemirror/view": "^6.9.4",
- "@lezer/highlight": "^1.1.4",
+ "@codemirror/language": "^6.9.0",
+ "@codemirror/legacy-modes": "^6.3.3",
+ "@codemirror/state": "^6.2.1",
+ "@codemirror/theme-one-dark": "^6.1.2",
+ "@codemirror/view": "^6.16.0",
+ "@lezer/highlight": "^1.1.6",
"@ssddanbrown/codemirror-lang-smarty": "^1.0.0",
"@ssddanbrown/codemirror-lang-twig": "^1.0.0",
"codemirror": "^6.0.1",
+ "idb-keyval": "^6.2.1",
"markdown-it": "^13.0.1",
"markdown-it-task-lists": "^2.1.1",
"snabbdom": "^3.5.1",
"sortablejs": "^1.15.0"
},
"devDependencies": {
- "@lezer/generator": "^1.2.2",
+ "@lezer/generator": "^1.4.2",
"chokidar-cli": "^3.0",
- "esbuild": "^0.17.16",
- "eslint": "^8.38.0",
+ "esbuild": "^0.19",
+ "eslint": "^8.47.0",
"eslint-config-airbnb-base": "^15.0.0",
- "eslint-plugin-import": "^2.27.5",
+ "eslint-plugin-import": "^2.28.1",
"livereload": "^0.9.3",
"npm-run-all": "^4.1.5",
"punycode": "^2.3.0",
- "sass": "^1.62.0"
+ "sass": "^1.66.1"
+ }
+ },
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
}
},
"node_modules/@codemirror/autocomplete": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.5.1.tgz",
- "integrity": "sha512-/Sv9yJmqyILbZ26U4LBHnAtbikuVxWUp+rQ8BXuRGtxZfbfKOY/WPbsUtvSP2h0ZUZMlkxV/hqbKRFzowlA6xw==",
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz",
+ "integrity": "sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
}
},
"node_modules/@codemirror/commands": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.2.tgz",
- "integrity": "sha512-s9lPVW7TxXrI/7voZ+HmD/yiAlwAYn9PH5SUVSUhsxXHhv4yl5eZ3KLntSoTynfdgVYM0oIpccQEWRBQgmNZyw==",
+ "version": "6.2.4",
+ "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.4.tgz",
+ "integrity": "sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.2.0",
}
},
"node_modules/@codemirror/lang-css": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.1.1.tgz",
- "integrity": "sha512-P6jdNEHyRcqqDgbvHYyC9Wxkek0rnG3a9aVSRi4a7WrjPbQtBTaOmvYpXmm13zZMAatO4Oqpac+0QZs7sy+LnQ==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz",
+ "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
+ "@lezer/common": "^1.0.2",
"@lezer/css": "^1.0.0"
}
},
"node_modules/@codemirror/lang-html": {
- "version": "6.4.3",
- "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.3.tgz",
- "integrity": "sha512-VKzQXEC8nL69Jg2hvAFPBwOdZNvL8tMFOrdFwWpU+wc6a6KEkndJ/19R5xSaglNX6v2bttm8uIEFYxdQDcIZVQ==",
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.5.tgz",
+ "integrity": "sha512-dUCSxkIw2G+chaUfw3Gfu5kkN83vJQN8gfQDp9iEHsIZluMJA0YJveT12zg/28BJx+uPsbQ6VimKCgx3oJrZxA==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
}
},
"node_modules/@codemirror/lang-javascript": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.6.tgz",
- "integrity": "sha512-TTK28z+vJQY9GAefLTDDptI2LMqMfAiuTpt8s9SsNwocjVQ1v9yTzfReMf1hYhspQCdhfa7fdKnQJ78mKe/bHQ==",
+ "version": "6.1.9",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.9.tgz",
+ "integrity": "sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
}
},
"node_modules/@codemirror/lang-markdown": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.1.1.tgz",
- "integrity": "sha512-n87Ms6Y5UYb1UkFu8sRzTLfq/yyF1y2AYiWvaVdbBQi5WDj1tFk5N+AKA+WC0Jcjc1VxvrCCM0iizjdYYi9sFQ==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.0.tgz",
+ "integrity": "sha512-deKegEQVzfBAcLPqsJEa+IxotqPVwWZi90UOEvQbfa01NTAw8jNinrykuYPTULGUj+gha0ZG2HBsn4s5d64Qrg==",
"dependencies": {
+ "@codemirror/autocomplete": "^6.7.1",
"@codemirror/lang-html": "^6.0.0",
"@codemirror/language": "^6.3.0",
"@codemirror/state": "^6.0.0",
}
},
"node_modules/@codemirror/language": {
- "version": "6.6.0",
- "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
- "integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==",
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.0.tgz",
+ "integrity": "sha512-nFu311/0ne/qGuGCL3oKuktBgzVOaxCHZPZv1tLSZkNjPYxxvkjSbzno3MlErG2tgw1Yw1yF8BxMCegeMXqpiw==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
}
},
"node_modules/@codemirror/legacy-modes": {
- "version": "6.3.2",
- "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.3.2.tgz",
- "integrity": "sha512-ki5sqNKWzKi5AKvpVE6Cna4Q+SgxYuYVLAZFSsMjGBWx5qSVa+D+xipix65GS3f2syTfAD9pXKMX4i4p49eneQ==",
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.3.3.tgz",
+ "integrity": "sha512-X0Z48odJ0KIoh/HY8Ltz75/4tDYc9msQf1E/2trlxFaFFhgjpVHjZ/BCXe1Lk7s4Gd67LL/CeEEHNI+xHOiESg==",
"dependencies": {
"@codemirror/language": "^6.0.0"
}
}
},
"node_modules/@codemirror/state": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
- "integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.1.tgz",
+ "integrity": "sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw=="
},
"node_modules/@codemirror/theme-one-dark": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz",
- "integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
+ "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
}
},
"node_modules/@codemirror/view": {
- "version": "6.9.4",
- "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.9.4.tgz",
- "integrity": "sha512-Ov2H9gwlGUxiH94zWxlLtTlyogSFaQDIYjtSEcfzgh7MkKmKVchkmr4JbtR5zBev3jY5DVtKvUC8yjd1bKW55A==",
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.16.0.tgz",
+ "integrity": "sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==",
"dependencies": {
"@codemirror/state": "^6.1.4",
"style-mod": "^4.0.0",
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.17.tgz",
- "integrity": "sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz",
+ "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==",
"cpu": [
"arm"
],
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz",
- "integrity": "sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz",
+ "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==",
"cpu": [
"arm64"
],
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.17.tgz",
- "integrity": "sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz",
+ "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz",
- "integrity": "sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz",
+ "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==",
"cpu": [
"arm64"
],
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz",
- "integrity": "sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz",
+ "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz",
- "integrity": "sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz",
+ "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==",
"cpu": [
"arm64"
],
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz",
- "integrity": "sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz",
+ "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz",
- "integrity": "sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz",
+ "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==",
"cpu": [
"arm"
],
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz",
- "integrity": "sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz",
+ "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==",
"cpu": [
"arm64"
],
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz",
- "integrity": "sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz",
+ "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==",
"cpu": [
"ia32"
],
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz",
- "integrity": "sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz",
+ "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==",
"cpu": [
"loong64"
],
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz",
- "integrity": "sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz",
+ "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==",
"cpu": [
"mips64el"
],
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz",
- "integrity": "sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz",
+ "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==",
"cpu": [
"ppc64"
],
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz",
- "integrity": "sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz",
+ "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==",
"cpu": [
"riscv64"
],
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz",
- "integrity": "sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz",
+ "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==",
"cpu": [
"s390x"
],
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz",
- "integrity": "sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz",
+ "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz",
- "integrity": "sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz",
+ "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz",
- "integrity": "sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz",
+ "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz",
- "integrity": "sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz",
+ "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==",
"cpu": [
"x64"
],
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz",
- "integrity": "sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz",
+ "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==",
"cpu": [
"arm64"
],
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz",
- "integrity": "sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz",
+ "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==",
"cpu": [
"ia32"
],
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz",
- "integrity": "sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz",
+ "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==",
"cpu": [
"x64"
],
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
- "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz",
+ "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
- "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.5.1",
+ "espree": "^9.6.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
}
},
"node_modules/@eslint/js": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
- "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
+ "version": "8.47.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz",
+ "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
}
},
"node_modules/@lezer/generator": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.2.2.tgz",
- "integrity": "sha512-O//eH9jTPM1GnbZruuD23xU68Pkuragonn1DEIom4Kt/eJN/QFt7Vzvp1YjV/XBmoUKC+2ySPgrA5fMF9FMM2g==",
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.4.2.tgz",
+ "integrity": "sha512-6fRwiGnx6Sv2I25YKeC5Mw9LtIA3Yq4FmVdRk0NP+M8tCsLUHSJd4KzrEzYqW3SKqOR/ZRlGiw+HAXMRygyCZA==",
"dev": true,
"dependencies": {
"@lezer/common": "^1.0.2",
"@lezer/lr": "^1.3.0"
},
"bin": {
- "lezer-generator": "dist/lezer-generator.cjs"
+ "lezer-generator": "src/lezer-generator.cjs"
}
},
"node_modules/@lezer/highlight": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.4.tgz",
- "integrity": "sha512-IECkFmw2l7sFcYXrV8iT9GeY4W0fU4CxX0WMwhmhMIVjoDdD1Hr6q3G2NqVtLg/yVe5n7i4menG3tJ2r4eCrPQ==",
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz",
+ "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==",
"dependencies": {
"@lezer/common": "^1.0.0"
}
"dev": true
},
"node_modules/acorn": {
- "version": "8.8.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
- "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz",
+ "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4",
+ "es-shim-unscopables": "^1.0.0",
+ "get-intrinsic": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/array.prototype.flat": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
}
},
"node_modules/esbuild": {
- "version": "0.17.17",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.17.tgz",
- "integrity": "sha512-/jUywtAymR8jR4qsa2RujlAF7Krpt5VWi72Q2yuLD4e/hvtNcFQ0I1j8m/bxq238pf3/0KO5yuXNpuLx8BE1KA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz",
+ "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==",
"dev": true,
"hasInstallScript": true,
"bin": {
"node": ">=12"
},
"optionalDependencies": {
- "@esbuild/android-arm": "0.17.17",
- "@esbuild/android-arm64": "0.17.17",
- "@esbuild/android-x64": "0.17.17",
- "@esbuild/darwin-arm64": "0.17.17",
- "@esbuild/darwin-x64": "0.17.17",
- "@esbuild/freebsd-arm64": "0.17.17",
- "@esbuild/freebsd-x64": "0.17.17",
- "@esbuild/linux-arm": "0.17.17",
- "@esbuild/linux-arm64": "0.17.17",
- "@esbuild/linux-ia32": "0.17.17",
- "@esbuild/linux-loong64": "0.17.17",
- "@esbuild/linux-mips64el": "0.17.17",
- "@esbuild/linux-ppc64": "0.17.17",
- "@esbuild/linux-riscv64": "0.17.17",
- "@esbuild/linux-s390x": "0.17.17",
- "@esbuild/linux-x64": "0.17.17",
- "@esbuild/netbsd-x64": "0.17.17",
- "@esbuild/openbsd-x64": "0.17.17",
- "@esbuild/sunos-x64": "0.17.17",
- "@esbuild/win32-arm64": "0.17.17",
- "@esbuild/win32-ia32": "0.17.17",
- "@esbuild/win32-x64": "0.17.17"
+ "@esbuild/android-arm": "0.19.2",
+ "@esbuild/android-arm64": "0.19.2",
+ "@esbuild/android-x64": "0.19.2",
+ "@esbuild/darwin-arm64": "0.19.2",
+ "@esbuild/darwin-x64": "0.19.2",
+ "@esbuild/freebsd-arm64": "0.19.2",
+ "@esbuild/freebsd-x64": "0.19.2",
+ "@esbuild/linux-arm": "0.19.2",
+ "@esbuild/linux-arm64": "0.19.2",
+ "@esbuild/linux-ia32": "0.19.2",
+ "@esbuild/linux-loong64": "0.19.2",
+ "@esbuild/linux-mips64el": "0.19.2",
+ "@esbuild/linux-ppc64": "0.19.2",
+ "@esbuild/linux-riscv64": "0.19.2",
+ "@esbuild/linux-s390x": "0.19.2",
+ "@esbuild/linux-x64": "0.19.2",
+ "@esbuild/netbsd-x64": "0.19.2",
+ "@esbuild/openbsd-x64": "0.19.2",
+ "@esbuild/sunos-x64": "0.19.2",
+ "@esbuild/win32-arm64": "0.19.2",
+ "@esbuild/win32-ia32": "0.19.2",
+ "@esbuild/win32-x64": "0.19.2"
}
},
"node_modules/escape-string-regexp": {
}
},
"node_modules/eslint": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
- "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
+ "version": "8.47.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz",
+ "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.2",
- "@eslint/js": "8.38.0",
- "@humanwhocodes/config-array": "^0.11.8",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "^8.47.0",
+ "@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
+ "ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.4.0",
- "espree": "^9.5.1",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
- "grapheme-splitter": "^1.0.4",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
+ "optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
},
"bin": {
}
},
"node_modules/eslint-config-airbnb-base/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.27.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
- "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
+ "version": "2.28.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz",
+ "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==",
"dev": true,
"dependencies": {
"array-includes": "^3.1.6",
+ "array.prototype.findlastindex": "^1.2.2",
"array.prototype.flat": "^1.3.1",
"array.prototype.flatmap": "^1.3.1",
"debug": "^3.2.7",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.7",
- "eslint-module-utils": "^2.7.4",
+ "eslint-module-utils": "^2.8.0",
"has": "^1.0.3",
- "is-core-module": "^2.11.0",
+ "is-core-module": "^2.13.0",
"is-glob": "^4.0.3",
"minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.6",
+ "object.groupby": "^1.0.0",
"object.values": "^1.1.6",
- "resolve": "^1.22.1",
- "semver": "^6.3.0",
- "tsconfig-paths": "^3.14.1"
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.14.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/eslint-plugin-import/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-scope": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
- "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
}
},
"node_modules/eslint-visitor-keys": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
- "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/espree": {
- "version": "9.5.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
- "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
"dependencies": {
- "acorn": "^8.8.0",
+ "acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.0"
+ "eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
- "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
+ "has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
},
"funding": {
}
},
"node_modules/globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "version": "13.21.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
+ "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
- "node_modules/grapheme-splitter": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"node_modules/has": {
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
+ "node_modules/idb-keyval": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
+ "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
+ },
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
}
},
"node_modules/is-core-module": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz",
- "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
- "node_modules/js-sdsl": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
- "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
- "dev": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/js-sdsl"
- }
- },
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"node": ">= 0.4"
}
},
+ "node_modules/object.fromentries": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz",
+ "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz",
+ "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.21.2",
+ "get-intrinsic": "^1.2.1"
+ }
+ },
"node_modules/object.values": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
}
},
"node_modules/optionator": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
- "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
"dev": true,
"dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.3"
+ "type-check": "^0.4.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/sass": {
- "version": "1.62.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
- "integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
+ "version": "1.66.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz",
+ "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
}
},
"node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"bin": {
"semver": "bin/semver"
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\""
},
"devDependencies": {
- "@lezer/generator": "^1.2.2",
+ "@lezer/generator": "^1.4.2",
"chokidar-cli": "^3.0",
- "esbuild": "^0.17.16",
- "eslint": "^8.38.0",
+ "esbuild": "^0.19",
+ "eslint": "^8.47.0",
"eslint-config-airbnb-base": "^15.0.0",
- "eslint-plugin-import": "^2.27.5",
+ "eslint-plugin-import": "^2.28.1",
"livereload": "^0.9.3",
"npm-run-all": "^4.1.5",
"punycode": "^2.3.0",
- "sass": "^1.62.0"
+ "sass": "^1.66.1"
},
"dependencies": {
- "@codemirror/commands": "^6.2.2",
- "@codemirror/lang-css": "^6.1.1",
- "@codemirror/lang-html": "^6.4.3",
- "@codemirror/lang-javascript": "^6.1.6",
+ "@codemirror/commands": "^6.2.4",
+ "@codemirror/lang-css": "^6.2.1",
+ "@codemirror/lang-html": "^6.4.5",
+ "@codemirror/lang-javascript": "^6.1.9",
"@codemirror/lang-json": "^6.0.1",
- "@codemirror/lang-markdown": "^6.1.1",
+ "@codemirror/lang-markdown": "^6.2.0",
"@codemirror/lang-php": "^6.0.1",
"@codemirror/lang-xml": "^6.0.2",
- "@codemirror/language": "^6.6.0",
- "@codemirror/legacy-modes": "^6.3.2",
- "@codemirror/state": "^6.2.0",
- "@codemirror/theme-one-dark": "^6.1.1",
- "@codemirror/view": "^6.9.4",
- "@lezer/highlight": "^1.1.4",
+ "@codemirror/language": "^6.9.0",
+ "@codemirror/legacy-modes": "^6.3.3",
+ "@codemirror/state": "^6.2.1",
+ "@codemirror/theme-one-dark": "^6.1.2",
+ "@codemirror/view": "^6.16.0",
+ "@lezer/highlight": "^1.1.6",
"@ssddanbrown/codemirror-lang-smarty": "^1.0.0",
"@ssddanbrown/codemirror-lang-twig": "^1.0.0",
"codemirror": "^6.0.1",
+ "idb-keyval": "^6.2.1",
"markdown-it": "^13.0.1",
"markdown-it-task-lists": "^2.1.1",
"snabbdom": "^3.5.1",
<server name="APP_AUTO_LANG_PUBLIC" value="true"/>
<server name="APP_URL" value="http://bookstack.dev"/>
<server name="ALLOWED_IFRAME_HOSTS" value=""/>
+ <server name="ALLOWED_SSR_HOSTS" value="*"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="SESSION_DRIVER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
[](https://discord.gg/ztkBqR2)
[](https://fosstodon.org/@bookstack)
[](https://twitter.com/bookstack_app)
+[](https://foss.video/c/bookstack)
[](https://www.youtube.com/bookstackapp)
A platform for storing and organising information and documentation. Details for BookStack can be found on the official website at https://www.bookstackapp.com/.
#### Bronze Sponsors
<table><tbody><tr>
-<td><a href="https://www.stellarhosted.com/bookstack/" target="_blank">
- <img width="240" src="https://media.githubusercontent.com/media/BookStackApp/website/main/static/images/sponsors/stellarhosted.png" alt="Stellar Hosted">
+<td><a href="https://cloudabove.com/hosting" target="_blank">
+ <img width="240" src="https://media.githubusercontent.com/media/BookStackApp/website/main/static/images/sponsors/cloudabove.png" alt="Cloudabove">
</a></td>
<td><a href="https://www.practicali.be" target="_blank">
<img width="240" src="https://media.githubusercontent.com/media/BookStackApp/website/main/static/images/sponsors/practicali.png" alt="Practicali">
## 🌎 Translations
Translations for text within BookStack is managed through the [BookStack project on Crowdin](https://crowdin.com/project/bookstack). Some strings have colon-prefixed variables such as `:userName`. Leave these values as they are as they will be replaced at run-time. Crowdin is the preferred way to provide translations, otherwise the raw translations files can be found within the `resources/lang` path.
+Translations to original files, provided via pull request, will be fed back through Crowdin instead of being merged directly and therefore your changes & commits will not be reflected in git contribution history.
If you'd like a new language to be added to Crowdin, for you to be able to provide translations for, please [open a new issue here](https://github.com/BookStackApp/BookStack/issues/new?template=language_request.yml).
If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates).
-If you would like to report a security concern, details of doing so can [can be found here](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md).
+If you would like to report a security concern, details of doing so [can be found here](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md).
## ♿ Accessibility
* [OneLogin's SAML PHP Toolkit](https://github.com/onelogin/php-saml) - _[MIT](https://github.com/onelogin/php-saml/blob/master/LICENSE)_
* [League/CommonMark](https://commonmark.thephpleague.com/) - _[BSD-3-Clause](https://github.com/thephpleague/commonmark/blob/2.2/LICENSE)_
* [League/Flysystem](https://flysystem.thephpleague.com) - _[MIT](https://github.com/thephpleague/flysystem/blob/3.x/LICENSE)_
+* [League/html-to-markdown](https://github.com/thephpleague/html-to-markdown) - _[MIT](https://github.com/thephpleague/html-to-markdown/blob/master/LICENSE)_
+* [League/oauth2-client](https://oauth2-client.thephpleague.com/) - _[MIT](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE)_
* [pragmarx/google2fa](https://github.com/antonioribeiro/google2fa) - _[MIT](https://github.com/antonioribeiro/google2fa/blob/8.x/LICENSE.md)_
* [Bacon/BaconQrCode](https://github.com/Bacon/BaconQrCode) - _[BSD-2-Clause](https://github.com/Bacon/BaconQrCode/blob/master/LICENSE)_
* [phpseclib](https://github.com/phpseclib/phpseclib) - _[MIT](https://github.com/phpseclib/phpseclib/blob/master/LICENSE)_
* [Clockwork](https://github.com/itsgoingd/clockwork) - _[MIT](https://github.com/itsgoingd/clockwork/blob/master/LICENSE)_
* [PHPStan](https://phpstan.org/) & [Larastan](https://github.com/nunomaduro/larastan) - _[MIT](https://github.com/phpstan/phpstan/blob/master/LICENSE) and [MIT](https://github.com/nunomaduro/larastan/blob/master/LICENSE.md)_
* [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) - _[BSD 3-Clause](https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt)_
+* [JakeArchibald/IDB-Keyval](https://github.com/jakearchibald/idb-keyval) - _[Apache-2.0](https://github.com/jakearchibald/idb-keyval/blob/main/LICENCE)_
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><g><circle cx="10" cy="8" r="4"/><path d="M10.67,13.02C10.45,13.01,10.23,13,10,13c-2.42,0-4.68,0.67-6.61,1.82C2.51,15.34,2,16.32,2,17.35V20h9.26 C10.47,18.87,10,17.49,10,16C10,14.93,10.25,13.93,10.67,13.02z"/><path d="M20.75,16c0-0.22-0.03-0.42-0.06-0.63l1.14-1.01l-1-1.73l-1.45,0.49c-0.32-0.27-0.68-0.48-1.08-0.63L18,11h-2l-0.3,1.49 c-0.4,0.15-0.76,0.36-1.08,0.63l-1.45-0.49l-1,1.73l1.14,1.01c-0.03,0.21-0.06,0.41-0.06,0.63s0.03,0.42,0.06,0.63l-1.14,1.01 l1,1.73l1.45-0.49c0.32,0.27,0.68,0.48,1.08,0.63L16,21h2l0.3-1.49c0.4-0.15,0.76-0.36,1.08-0.63l1.45,0.49l1-1.73l-1.14-1.01 C20.72,16.42,20.75,16.22,20.75,16z M17,18c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S18.1,18,17,18z"/></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>
\ No newline at end of file
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
- <path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
</svg>
\ No newline at end of file
onSelect(this.toggle, event => {
event.stopPropagation();
+ event.preventDefault();
this.show(event);
if (event instanceof KeyboardEvent) {
keyboardNavHandler.focusNext();
// Internal State
this.parentId = null;
- this.formReplyText = this.formReplyLink.textContent;
+ this.formReplyText = this.formReplyLink?.textContent || '';
this.setupListeners();
}
setupListeners() {
- this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this));
- this.hideFormButton.addEventListener('click', this.hideForm.bind(this));
- this.addCommentButton.addEventListener('click', this.showForm.bind(this));
-
this.elem.addEventListener('page-comment-delete', () => {
this.updateCount();
this.hideForm();
});
if (this.form) {
+ this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this));
+ this.hideFormButton.addEventListener('click', this.hideForm.bind(this));
+ this.addCommentButton.addEventListener('click', this.showForm.bind(this));
this.form.addEventListener('submit', this.saveComment.bind(this));
}
}
this.showForm();
this.parentId = commentLocalId;
this.replyToRow.toggleAttribute('hidden', false);
- const replyLink = this.replyToRow.querySelector('a');
- replyLink.textContent = this.formReplyText.replace('1234', this.parentId);
- replyLink.href = `#comment${this.parentId}`;
+ this.formReplyLink.textContent = this.formReplyText.replace('1234', this.parentId);
+ this.formReplyLink.href = `#comment${this.parentId}`;
}
removeReplyTo() {
const selectionRange = this.#getSelectionRange();
- DrawIO.show(url, () => Promise.resolve(''), pngData => {
+ DrawIO.show(url, () => Promise.resolve(''), async pngData => {
const data = {
image: pngData,
uploaded_to: Number(this.editor.config.pageId),
};
- window.$http.post('/images/drawio', data).then(resp => {
+ try {
+ const resp = await window.$http.post('/images/drawio', data);
this.#insertDrawing(resp.data, selectionRange);
DrawIO.close();
- }).catch(err => {
+ } catch (err) {
this.handleDrawingUploadError(err);
- });
+ throw new Error(`Failed to save image with error: ${err}`);
+ }
});
}
const selectionRange = this.#getSelectionRange();
const drawingId = imgContainer.getAttribute('drawio-diagram');
- DrawIO.show(drawioUrl, () => DrawIO.load(drawingId), pngData => {
+ DrawIO.show(drawioUrl, () => DrawIO.load(drawingId), async pngData => {
const data = {
image: pngData,
uploaded_to: Number(this.editor.config.pageId),
};
- window.$http.post('/images/drawio', data).then(resp => {
+ try {
+ const resp = await window.$http.post('/images/drawio', data);
const newText = `<div drawio-diagram="${resp.data.id}"><img src="${resp.data.url}"></div>`;
const newContent = this.#getText().split('\n').map(line => {
if (line.indexOf(`drawio-diagram="${drawingId}"`) !== -1) {
}).join('\n');
this.#setText(newContent, selectionRange);
DrawIO.close();
- }).catch(err => {
+ } catch (err) {
this.handleDrawingUploadError(err);
- });
+ throw new Error(`Failed to save image with error: ${err}`);
+ }
});
}
// Docs: https://www.diagrams.net/doc/faq/embed-mode
+import * as store from './store';
let iFrame = null;
let lastApprovedOrigin;
-let onInit; let
- onSave;
+let onInit;
+let onSave;
+const saveBackupKey = 'last-drawing-save';
function drawPostMessage(data) {
iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin);
}
function drawEventExport(message) {
+ store.set(saveBackupKey, message.data);
if (onSave) {
- onSave(message.data);
+ onSave(message.data).then(() => {
+ store.del(saveBackupKey);
+ });
}
}
}
}
+/**
+ * Attempt to prompt and restore unsaved drawing content if existing.
+ * @returns {Promise<void>}
+ */
+async function attemptRestoreIfExists() {
+ const backupVal = await store.get(saveBackupKey);
+ const dialogEl = document.getElementById('unsaved-drawing-dialog');
+
+ if (!dialogEl) {
+ console.error('Missing expected unsaved-drawing dialog');
+ }
+
+ if (backupVal) {
+ /** @var {ConfirmDialog} */
+ const dialog = window.$components.firstOnElement(dialogEl, 'confirm-dialog');
+ const restore = await dialog.show();
+ if (restore) {
+ onInit = async () => backupVal;
+ }
+ }
+}
+
/**
* Show the draw.io editor.
+ * onSaveCallback must return a promise that resolves on successful save and errors on failure.
+ * onInitCallback must return a promise with the xml to load for the editor.
+ * Will attempt to provide an option to restore unsaved changes if found to exist.
* @param {String} drawioUrl
- * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor.
- * @param {Function} onSaveCallback - Is called with the drawing data on save.
+ * @param {Function<Promise<String>>} onInitCallback
+ * @param {Function<Promise>} onSaveCallback - Is called with the drawing data on save.
*/
-export function show(drawioUrl, onInitCallback, onSaveCallback) {
+export async function show(drawioUrl, onInitCallback, onSaveCallback) {
onInit = onInitCallback;
onSave = onSaveCallback;
+ await attemptRestoreIfExists();
+
iFrame = document.createElement('iframe');
iFrame.setAttribute('frameborder', '0');
window.addEventListener('message', drawReceive);
--- /dev/null
+export {get, set, del} from 'idb-keyval';
* leading edge, instead of the trailing.
* @attribution https://davidwalsh.name/javascript-debounce-function
* @param {Function} func
- * @param {Number} wait
+ * @param {Number} waitMs
* @param {Boolean} immediate
* @returns {Function}
*/
-export function debounce(func, wait, immediate) {
+export function debounce(func, waitMs, immediate) {
let timeout;
return function debouncedWrapper(...args) {
const context = this;
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
- timeout = setTimeout(later, wait);
+ timeout = setTimeout(later, waitMs);
if (callNow) func.apply(context, args);
};
}
const S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
return (`${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`);
}
+
+/**
+ * Create a promise that resolves after the given time.
+ * @param {int} timeMs
+ * @returns {Promise}
+ */
+export function wait(timeMs) {
+ return new Promise(res => {
+ setTimeout(res, timeMs);
+ });
+}
import {listenForDragAndPaste} from './drop-paste-handling';
import {getPrimaryToolbar, registerAdditionalToolbars} from './toolbars';
import {registerCustomIcons} from './icons';
+import {setupFilters} from './filters';
import {getPlugin as getCodeeditorPlugin} from './plugin-codeeditor';
import {getPlugin as getDrawioPlugin} from './plugin-drawio';
return headContentLines.slice(startLineIndex + 1, endLineIndex).join('\n');
}
-/**
- * Setup a serializer filter for <br> tags to ensure they're not rendered
- * within code blocks and that we use newlines there instead.
- * @param {Editor} editor
- */
-function setupBrFilter(editor) {
- editor.serializer.addNodeFilter('br', nodes => {
- for (const node of nodes) {
- if (node.parent && node.parent.name === 'code') {
- const newline = window.tinymce.html.Node.create('#text');
- newline.value = '\n';
- node.replace(newline);
- }
- }
- });
-}
-
/**
* @param {WysiwygConfigOptions} options
* @return {function(Editor)}
});
editor.on('PreInit', () => {
- setupBrFilter(editor);
+ setupFilters(editor);
});
// Custom handler hook
--- /dev/null
+/**
+ * Setup a serializer filter for <br> tags to ensure they're not rendered
+ * within code blocks and that we use newlines there instead.
+ * @param {Editor} editor
+ */
+function setupBrFilter(editor) {
+ editor.serializer.addNodeFilter('br', nodes => {
+ for (const node of nodes) {
+ if (node.parent && node.parent.name === 'code') {
+ const newline = window.tinymce.html.Node.create('#text');
+ newline.value = '\n';
+ node.replace(newline);
+ }
+ }
+ });
+}
+
+/**
+ * Remove accidentally added pointer elements that are within the content.
+ * These could have accidentally been added via getting caught in range
+ * selection within page content.
+ * @param {Editor} editor
+ */
+function setupPointerFilter(editor) {
+ editor.parser.addNodeFilter('div', nodes => {
+ for (const node of nodes) {
+ const id = node.attr('id') || '';
+ const nodeClass = node.attr('class') || '';
+ if (id === 'pointer' || nodeClass.includes('pointer')) {
+ node.remove();
+ }
+ }
+ });
+}
+
+/**
+ * Setup global default filters for the given editor instance.
+ * @param {Editor} editor
+ */
+export function setupFilters(editor) {
+ setupBrFilter(editor);
+ setupPointerFilter(editor);
+}
import * as DrawIO from '../services/drawio';
+import {wait} from '../services/util';
let pageEditor = null;
let currentNode = null;
}
async function updateContent(pngData) {
- const id = `image-${Math.random().toString(16).slice(2)}`;
const loadingImage = window.baseUrl('/loading.gif');
const handleUploadError = error => {
});
} catch (err) {
handleUploadError(err);
+ throw new Error(`Failed to save image with error: ${err}`);
}
return;
}
- setTimeout(async () => {
- pageEditor.insertContent(`<div drawio-diagram contenteditable="false"><img src="${loadingImage}" id="${id}"></div>`);
- DrawIO.close();
- try {
- const img = await DrawIO.upload(pngData, options.pageId);
- pageEditor.undoManager.transact(() => {
- pageEditor.dom.setAttrib(id, 'src', img.url);
- pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
- });
- } catch (err) {
- pageEditor.dom.remove(id);
- handleUploadError(err);
- }
- }, 5);
+ await wait(5);
+
+ const id = `drawing-${Math.random().toString(16).slice(2)}`;
+ const wrapId = `drawing-wrap-${Math.random().toString(16).slice(2)}`;
+ pageEditor.insertContent(`<div drawio-diagram contenteditable="false" id="${wrapId}"><img src="${loadingImage}" id="${id}"></div>`);
+ DrawIO.close();
+
+ try {
+ const img = await DrawIO.upload(pngData, options.pageId);
+ pageEditor.undoManager.transact(() => {
+ pageEditor.dom.setAttrib(id, 'src', img.url);
+ pageEditor.dom.setAttrib(wrapId, 'drawio-diagram', img.id);
+ });
+ } catch (err) {
+ pageEditor.dom.remove(wrapId);
+ handleUploadError(err);
+ throw new Error(`Failed to save image with error: ${err}`);
+ }
}
function drawingInit() {
max-width: 100%;
flex-grow: 1;
flex-basis: auto !important;
+ min-height: 0;
}
.editor-toolbar-label {
float: none !important;
#markdown-editor .markdown-editor-wrap:not(.active) {
flex-grow: 0;
flex: none;
- min-height: 0;
}
}
.screen-reader-only {
position: absolute;
- left: -10000px;
+ inset-inline-start: -10000px;
top: auto;
width: 1px;
height: 1px;
margin-inline-end: $-xl;
grid-template-columns: 1fr 4fr 1fr;
grid-template-areas: "a b c";
- grid-column-gap: $-xxl;
+ grid-column-gap: $-xl;
.tri-layout-right {
grid-area: c;
min-width: 0;
padding-inline-end: $-l;
}
}
+@include between($xxl, $xxxl) {
+ .tri-layout-container {
+ grid-template-columns: 1fr calc(940px + (2 * $-m)) 1fr;
+ grid-column-gap: $-s;
+ margin-inline-start: $-m;
+ margin-inline-end: $-m;
+ }
+}
@include between($l, $xxl) {
.tri-layout-left {
position: sticky;
@include lightDark(color, #555, #eee);
fill: currentColor;
text-align: start !important;
- max-height: 500px;
+ max-height: 80vh;
overflow-y: auto;
&.anchor-left {
inset-inline-end: auto;
&.wide {
min-width: 220px;
}
+ &.xl-limited {
+ width: 280px;
+ max-width: 100%;
+ }
.text-muted {
color: #999;
fill: #999;
white-space: nowrap;
line-height: 1.4;
cursor: pointer;
+ &.break-text {
+ white-space: normal;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ }
&:hover, &:focus {
text-decoration: none;
background-color: var(--color-primary-light);
}
.break-text {
+ white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
}
///////////////
// Screen breakpoints
+$xxxl: 1700px;
$xxl: 1400px;
$xl: 1100px;
$l: 1000px;
<div class="mb-xl">
<h5>{{ trans('common.details') }}</h5>
<div class="blended-links">
- @include('entities.meta', ['entity' => $book])
+ @include('entities.meta', ['entity' => $book, 'watchOptions' => $watchOptions])
@if($book->hasPermissions())
<div class="active-restriction">
@if(userCan('restrictions-manage', $book))
<hr class="primary-background">
- @if(signedInUser())
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
+ @include('entities.watch-action', ['entity' => $book])
+ @endif
+ @if(!user()->isGuest())
@include('entities.favourite-action', ['entity' => $book])
@endif
@if(userCan('content-export'))
@endif
@if(count($activity) > 0)
- <div class="mb-xl">
+ <div id="recent-activity" class="mb-xl">
<h5>{{ trans('entities.recent_activity') }}</h5>
@include('common.activity-list', ['activity' => $activity])
</div>
<div class="mb-xl">
<h5>{{ trans('common.details') }}</h5>
<div class="blended-links">
- @include('entities.meta', ['entity' => $chapter])
+ @include('entities.meta', ['entity' => $chapter, 'watchOptions' => $watchOptions])
@if($book->hasPermissions())
<div class="active-restriction">
<hr class="primary-background"/>
- @if(signedInUser())
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
+ @include('entities.watch-action', ['entity' => $chapter])
+ @endif
+ @if(!user()->isGuest())
@include('entities.favourite-action', ['entity' => $chapter])
@endif
@if(userCan('content-export'))
<div components="popup confirm-dialog"
- refs="confirm-dialog@popup {{ $ref }}"
+ @if($id ?? false) id="{{ $id }}" @endif
+ refs="confirm-dialog@popup {{ $ref ?? false }}"
class="popup-background">
<div class="popup-body very-small" tabindex="-1">
--- /dev/null
+<div class="dropdown-container" component="dropdown" option:dropdown:bubble-escapes="true">
+ <span class="user-name py-s hide-under-l" refs="dropdown@toggle"
+ aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
+ <img class="avatar" src="{{$user->getAvatar(30)}}" alt="{{ $user->name }}">
+ <span class="name">{{ $user->getShortName(9) }}</span> @icon('caret-down')
+ </span>
+ <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
+ <li>
+ <a href="{{ url('/favourites') }}" data-shortcut="favourites_view" class="icon-item">
+ @icon('star')
+ <div>{{ trans('entities.my_favourites') }}</div>
+ </a>
+ </li>
+ <li>
+ <a href="{{ $user->getProfileUrl() }}" data-shortcut="profile_view" class="icon-item">
+ @icon('user')
+ <div>{{ trans('common.view_profile') }}</div>
+ </a>
+ </li>
+ <li>
+ <a href="{{ $user->getEditUrl() }}" class="icon-item">
+ @icon('edit')
+ <div>{{ trans('common.edit_profile') }}</div>
+ </a>
+ </li>
+ <li>
+ <form action="{{ url(config('auth.method') === 'saml2' ? '/saml2/logout' : '/logout') }}"
+ method="post">
+ {{ csrf_field() }}
+ <button class="icon-item" data-shortcut="logout">
+ @icon('logout')
+ <div>{{ trans('auth.logout') }}</div>
+ </button>
+ </form>
+ </li>
+ <li><hr></li>
+ <li>
+ <a href="{{ url('/preferences') }}" class="icon-item">
+ @icon('user-preferences')
+ <div>{{ trans('preferences.preferences') }}</div>
+ </a>
+ </li>
+ <li>
+ @include('common.dark-mode-toggle', ['classes' => 'icon-item'])
+ </li>
+ </ul>
+</div>
\ No newline at end of file
</div>
<div class="flex-container-column items-center justify-center hide-under-l">
- @if (hasAppAccess())
+ @if (user()->hasAppAccess())
<form component="global-search" action="{{ url('/search') }}" method="GET" class="search-box" role="search" tabindex="0">
<button id="header-search-box-button"
refs="global-search@button"
<nav refs="header-mobile-toggle@menu" class="header-links">
<div class="links text-center">
- @if (hasAppAccess())
+ @if (user()->hasAppAccess())
<a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
@if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
<a href="{{ url('/shelves') }}" data-shortcut="shelves_view">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
@endif
<a href="{{ url('/books') }}" data-shortcut="books_view">@icon('books'){{ trans('entities.books') }}</a>
- @if(signedInUser() && userCan('settings-manage'))
+ @if(!user()->isGuest() && userCan('settings-manage'))
<a href="{{ url('/settings') }}" data-shortcut="settings_view">@icon('settings'){{ trans('settings.settings') }}</a>
@endif
- @if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
+ @if(!user()->isGuest() && userCan('users-manage') && !userCan('settings-manage'))
<a href="{{ url('/settings/users') }}" data-shortcut="settings_view">@icon('users'){{ trans('settings.users') }}</a>
@endif
@endif
- @if(!signedInUser())
+ @if(user()->isGuest())
@if(setting('registration-enabled') && config('auth.method') === 'standard')
<a href="{{ url('/register') }}">@icon('new-user'){{ trans('auth.sign_up') }}</a>
@endif
<a href="{{ url('/login') }}">@icon('login'){{ trans('auth.log_in') }}</a>
@endif
</div>
- @if(signedInUser())
- <?php $currentUser = user(); ?>
- <div class="dropdown-container" component="dropdown" option:dropdown:bubble-escapes="true">
- <span class="user-name py-s hide-under-l" refs="dropdown@toggle"
- aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
- <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
- <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
- </span>
- <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
- <li>
- <a href="{{ url('/favourites') }}" data-shortcut="favourites_view" class="icon-item">
- @icon('star')
- <div>{{ trans('entities.my_favourites') }}</div>
- </a>
- </li>
- <li>
- <a href="{{ $currentUser->getProfileUrl() }}" data-shortcut="profile_view" class="icon-item">
- @icon('user')
- <div>{{ trans('common.view_profile') }}</div>
- </a>
- </li>
- <li>
- <a href="{{ $currentUser->getEditUrl() }}" class="icon-item">
- @icon('edit')
- <div>{{ trans('common.edit_profile') }}</div>
- </a>
- </li>
- <li>
- <form action="{{ url(config('auth.method') === 'saml2' ? '/saml2/logout' : '/logout') }}"
- method="post">
- {{ csrf_field() }}
- <button class="icon-item" data-shortcut="logout">
- @icon('logout')
- <div>{{ trans('auth.logout') }}</div>
- </button>
- </form>
- </li>
- <li><hr></li>
- <li>
- <a href="{{ url('/preferences/shortcuts') }}" class="icon-item">
- @icon('shortcuts')
- <div>{{ trans('preferences.shortcuts') }}</div>
- </a>
- </li>
- <li>
- @include('common.dark-mode-toggle', ['classes' => 'icon-item'])
- </li>
- </ul>
- </div>
+ @if(!user()->isGuest())
+ @include('common.header-user-menu', ['user' => user()])
@endif
</nav>
@endphp
<form action="{{ url('/favourites/' . ($isFavourite ? 'remove' : 'add')) }}" method="POST">
{{ csrf_field() }}
- <input type="hidden" name="type" value="{{ get_class($entity) }}">
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
<input type="hidden" name="id" value="{{ $entity->id }}">
<button type="submit" data-shortcut="favourite" class="icon-list-item text-link">
<span>@icon($isFavourite ? 'star' : 'star-outline')</span>
--- /dev/null
+<a href="{{ $entity->getUrl() }}" class="flex-container-row items-center">
+ <span role="presentation"
+ class="icon flex-none text-{{$entity->getType()}}">@icon($entity->getType())</span>
+ <div class="flex text-{{ $entity->getType() }}">
+ {{ $entity->name }}
+ </div>
+</a>
\ No newline at end of file
</div>
</a>
@endif
+
+ @if($watchOptions?->canWatch())
+ @if($watchOptions->isWatching())
+ @include('entities.watch-controls', [
+ 'entity' => $entity,
+ 'watchLevel' => $watchOptions->getWatchLevel(),
+ 'label' => trans('entities.watch_detail_' . $watchOptions->getWatchLevel()),
+ 'ignoring' => $watchOptions->getWatchLevel() === 'ignore',
+ ])
+ @elseif($watchedParent = $watchOptions->getWatchedParent())
+ @include('entities.watch-controls', [
+ 'entity' => $entity,
+ 'watchLevel' => $watchOptions->getWatchLevel(),
+ 'label' => trans('entities.watch_detail_parent_' . $watchedParent->type . ($watchedParent->ignoring() ? '_ignore' : '')),
+ 'ignoring' => $watchedParent->ignoring(),
+ ])
+ @endif
+ @endif
</div>
\ No newline at end of file
--- /dev/null
+<form action="{{ url('/watching/update') }}" method="POST">
+ {{ csrf_field() }}
+ {{ method_field('PUT') }}
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
+ <input type="hidden" name="id" value="{{ $entity->id }}">
+ <button type="submit"
+ name="level"
+ value="updates"
+ class="icon-list-item text-link">
+ <span>@icon('watch')</span>
+ <span>{{ trans('entities.watch') }}</span>
+ </button>
+</form>
\ No newline at end of file
--- /dev/null
+<div component="dropdown"
+ class="dropdown-container block my-xxs">
+ <a refs="dropdown@toggle" href="#" class="entity-meta-item my-none">
+ @icon(($ignoring ? 'watch-ignore' : 'watch'))
+ <span>{{ $label }}</span>
+ </a>
+ <form action="{{ url('/watching/update') }}" method="POST">
+ {{ method_field('PUT') }}
+ {{ csrf_field() }}
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
+ <input type="hidden" name="id" value="{{ $entity->id }}">
+
+ <ul refs="dropdown@menu" class="dropdown-menu xl-limited anchor-left pb-none">
+ @foreach(\BookStack\Activity\WatchLevels::allSuitedFor($entity) as $option => $value)
+ <li>
+ <button name="level" value="{{ $option }}" class="icon-item">
+ @if($watchLevel === $option)
+ <span class="text-pos pt-m"
+ title="{{ trans('common.status_active') }}">@icon('check-circle')</span>
+ @else
+ <span title="{{ trans('common.status_inactive') }}"></span>
+ @endif
+ <div class="break-text">
+ <div class="mb-xxs"><strong>{{ trans('entities.watch_title_' . $option) }}</strong></div>
+ <div class="text-muted text-small">
+ @if(trans()->has('entities.watch_desc_' . $option . '_' . $entity->getMorphClass()))
+ {{ trans('entities.watch_desc_' . $option . '_' . $entity->getMorphClass()) }}
+ @else
+ {{ trans('entities.watch_desc_' . $option) }}
+ @endif
+ </div>
+ </div>
+ </button>
+ </li>
+ <li>
+ <hr class="my-none">
+ </li>
+ @endforeach
+ <li>
+ <a href="{{ url('/preferences/notifications') }}"
+ target="_blank"
+ class="text-item text-muted text-small break-text">{{ trans('entities.watch_change_default') }}</a>
+ </li>
+ </ul>
+ </form>
+</div>
\ No newline at end of file
@extends('layouts.simple')
@section('content')
-<div class="container mt-l">
+ <div class="container mt-l">
- <div class="card mb-xl px-l pb-l pt-l">
- <div class="grid half v-center">
- <div>
- @include('errors.parts.not-found-text', [
- 'title' => $message ?? trans('errors.404_page_not_found'),
- 'subtitle' => $subtitle ?? trans('errors.sorry_page_not_found'),
- 'details' => $details ?? trans('errors.sorry_page_not_found_permission_warning'),
- ])
- </div>
- <div class="text-right">
- @if(!signedInUser())
- <a href="{{ url('/login') }}" class="button outline">{{ trans('auth.log_in') }}</a>
- @endif
- <a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
+ <div class="card mb-xl px-l pb-l pt-l">
+ <div class="grid half v-center">
+ <div>
+ @include('errors.parts.not-found-text', [
+ 'title' => $message ?? trans('errors.404_page_not_found'),
+ 'subtitle' => $subtitle ?? trans('errors.sorry_page_not_found'),
+ 'details' => $details ?? trans('errors.sorry_page_not_found_permission_warning'),
+ ])
+ </div>
+ <div class="text-right">
+ @if(user()->isGuest())
+ <a href="{{ url('/login') }}" class="button outline">{{ trans('auth.log_in') }}</a>
+ @endif
+ <a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
+ </div>
</div>
- </div>
- </div>
+ </div>
- @if (setting('app-public') || !user()->isDefault())
- <div class="grid third gap-xxl">
- <div>
- <div class="card mb-xl">
- <h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
- <div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
+ @if (setting('app-public') || !user()->isGuest())
+ <div class="grid third gap-xxl">
+ <div>
+ <div class="card mb-xl">
+ <h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
+ <div class="px-m">
+ @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
+ </div>
</div>
</div>
- </div>
- <div>
- <div class="card mb-xl">
- <h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
- <div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
+ <div>
+ <div class="card mb-xl">
+ <h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
+ <div class="px-m">
+ @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
+ </div>
</div>
</div>
- </div>
- <div>
- <div class="card mb-xl">
- <h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
- <div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
+ <div>
+ <div class="card mb-xl">
+ <h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
+ <div class="px-m">
+ @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
+ </div>
</div>
</div>
</div>
- </div>
- @endif
-</div>
+ @endif
+ </div>
@stop
\ No newline at end of file
</div>
<div>
- <div id="recent-activity">
- <div class="card mb-xl">
- <h3 class="card-title">{{ trans('entities.recent_activity') }}</h3>
- @include('common.activity-list', ['activity' => $activity])
- </div>
+ <div id="recent-activity" class="card mb-xl">
+ <h3 class="card-title">{{ trans('entities.recent_activity') }}</h3>
+ @include('common.activity-list', ['activity' => $activity])
</div>
</div>
<!DOCTYPE html>
-<html lang="{{ config('app.lang') }}"
- dir="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+<html lang="{{ isset($locale) ? $locale->htmlLang() : config('app.default_locale') }}"
+ dir="{{ isset($locale) ? $locale->htmlDirection() : 'auto' }}"
class="{{ setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '' }}">
<head>
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
</div>
@yield('bottom')
- <script src="{{ versioned_asset('dist/app.js') }}" nonce="{{ $cspNonce }}"></script>
+ @if($cspNonce ?? false)
+ <script src="{{ versioned_asset('dist/app.js') }}" nonce="{{ $cspNonce }}"></script>
+ @endif
@yield('scripts')
@include('layouts.parts.base-body-end')
<!doctype html>
-<html lang="{{ config('app.lang') }}">
+<html lang="{{ $locale->htmlLang() }}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>@yield('title')</title>
<!DOCTYPE html>
-<html lang="{{ config('app.lang') }}"
- dir="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+<html lang="{{ isset($locale) ? $locale->htmlLang() : config('app.default_locale') }}"
+ dir="{{ isset($locale) ? $locale->htmlDirection() : 'auto' }}"
class="@yield('document-class')">
<head>
<title>{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}</title>
{{ trans('entities.pages_edit_delete_draft_confirm') }}
</p>
@endcomponent
+
+ {{--Saved Drawing--}}
+ @component('common.confirm-dialog', ['title' => trans('entities.pages_drawing_unsaved'), 'id' => 'unsaved-drawing-dialog'])
+ <p>
+ {{ trans('entities.pages_drawing_unsaved_confirm') }}
+ </p>
+ @endcomponent
</div>
\ No newline at end of file
<div id="markdown-editor" component="markdown-editor"
option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
- option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+ option:markdown-editor:text-direction="{{ $locale->htmlDirection() }}"
option:markdown-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
option:markdown-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
class="flex-fill flex code-fill">
@endpush
<div component="wysiwyg-editor"
- option:wysiwyg-editor:language="{{ config('app.lang') }}"
+ option:wysiwyg-editor:language="{{ $locale->htmlLang() }}"
option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
- option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+ option:wysiwyg-editor:text-direction="{{ $locale->htmlDirection() }}"
option:wysiwyg-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
option:wysiwyg-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
class="flex-fill flex">
<div id="revision-details" class="entity-details mb-xl">
<h5>{{ trans('common.details') }}</h5>
<div class="body text-small text-muted">
- @include('entities.meta', ['entity' => $revision])
+ @include('entities.meta', ['entity' => $revision, 'watchOptions' => null])
</div>
</div>
@stop
<div id="page-details" class="entity-details mb-xl">
<h5>{{ trans('common.details') }}</h5>
<div class="blended-links">
- @include('entities.meta', ['entity' => $page])
+ @include('entities.meta', ['entity' => $page, 'watchOptions' => $watchOptions])
@if($book->hasPermissions())
<div class="active-restriction">
<hr class="primary-background"/>
- @if(signedInUser())
+ @if($watchOptions->canWatch() && !$watchOptions->isWatching())
+ @include('entities.watch-action', ['entity' => $page])
+ @endif
+ @if(!user()->isGuest())
@include('entities.favourite-action', ['entity' => $page])
@endif
@if(userCan('content-export'))
<h6>{{ trans('entities.search_tags') }}</h6>
@include('search.parts.term-list', ['type' => 'tags', 'currentList' => $options->tags])
- @if(signedInUser())
+ @if(!user()->isGuest())
<h6>{{ trans('entities.search_options') }}</h6>
@component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
:</strong> {{ $activity->type }}</div>
<div class="flex-3 px-m py-xxs min-width-l">
@if($activity->entity)
- <a href="{{ $activity->entity->getUrl() }}" class="flex-container-row items-center">
- <span role="presentation"
- class="icon flex-none text-{{$activity->entity->getType()}}">@icon($activity->entity->getType())</span>
- <div class="flex text-{{ $activity->entity->getType() }}">
- {{ $activity->entity->name }}
- </div>
- </a>
+ @include('entities.icon-link', ['entity' => $activity->entity])
@elseif($activity->detail && $activity->isForEntity())
<div>
{{ trans('settings.audit_deleted_item') }} <br>
<div>@include('settings.roles.parts.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])</div>
<div>@include('settings.roles.parts.checkbox', ['permission' => 'content-export', 'label' => trans('settings.role_export_content')])</div>
<div>@include('settings.roles.parts.checkbox', ['permission' => 'editor-change', 'label' => trans('settings.role_editor_change')])</div>
+ <div>@include('settings.roles.parts.checkbox', ['permission' => 'receive-notifications', 'label' => trans('settings.role_notifications')])</div>
</div>
<div>
<div>@include('settings.roles.parts.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
<div id="details" class="mb-xl">
<h5>{{ trans('common.details') }}</h5>
<div class="blended-links">
- @include('entities.meta', ['entity' => $shelf])
+ @include('entities.meta', ['entity' => $shelf, 'watchOptions' => null])
@if($shelf->hasPermissions())
<div class="active-restriction">
@if(userCan('restrictions-manage', $shelf))
</div>
@if(count($activity) > 0)
- <div class="mb-xl">
+ <div id="recent-activity" class="mb-xl">
<h5>{{ trans('entities.recent_activity') }}</h5>
@include('common.activity-list', ['activity' => $activity])
</div>
</a>
@endif
- @if(signedInUser())
+ @if(!user()->isGuest())
<hr class="primary-background">
@include('entities.favourite-action', ['entity' => $shelf])
@endif
<div class="setting-list">
@include('users.parts.form')
- @include('users.parts.language-option-row', ['value' => old('setting.language') ?? config('app.default_locale')])
+ @include('users.parts.language-option-row', ['value' => old('language') ?? config('app.default_locale')])
</div>
<div class="form-group text-right">
<div class="grid half gap-xl">
<div>
- <label for="user-avatar" class="setting-list-label">{{ trans('settings.users_avatar') }}</label>
+ <label for="user-avatar"
+ class="setting-list-label">{{ trans('settings.users_avatar') }}</label>
<p class="small">{{ trans('settings.users_avatar_desc') }}</p>
</div>
<div>
</div>
</div>
- @include('users.parts.language-option-row', ['value' => setting()->getUser($user, 'language', config('app.default_locale'))])
+ @include('users.parts.language-option-row', ['value' => old('language') ?? $user->getLocale()->appLocale()])
</div>
<div class="text-right">
- <a href="{{ url(userCan('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+ <a href="{{ url(userCan('users-manage') ? "/settings/users" : "/") }}"
+ class="button outline">{{ trans('common.cancel') }}</a>
@if($authMethod !== 'system')
- <a href="{{ url("/settings/users/{$user->id}/delete") }}" class="button outline">{{ trans('settings.users_delete') }}</a>
+ <a href="{{ url("/settings/users/{$user->id}/delete") }}"
+ class="button outline">{{ trans('settings.users_delete') }}</a>
@endif
<button class="button" type="submit">{{ trans('common.save') }}</button>
</div>
</div>
<div class="text-m-right">
@if($user->id === user()->id)
- <a href="{{ url('/mfa/setup') }}" class="button outline">{{ trans('settings.users_mfa_configure') }}</a>
+ <a href="{{ url('/mfa/setup') }}"
+ class="button outline">{{ trans('settings.users_mfa_configure') }}</a>
@endif
</div>
</div>
class="button small outline">{{ trans('settings.users_social_disconnect') }}</button>
</form>
@else
- <a href="{{ url("/login/service/{$driver}") }}" aria-label="{{ trans('settings.users_social_connect') }} - {{ $driver }}"
+ <a href="{{ url("/login/service/{$driver}") }}"
+ aria-label="{{ trans('settings.users_social_connect') }} - {{ $driver }}"
class="button small outline">{{ trans('settings.users_social_connect') }}</a>
@endif
</div>
--- /dev/null
+@extends('layouts.simple')
+
+@section('body')
+ <div class="container small my-xl">
+
+ <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
+ <div class="flex min-width-m">
+ <h2 class="list-heading">{{ trans('preferences.shortcuts_interface') }}</h2>
+ <p class="text-muted">{{ trans('preferences.shortcuts_overview_desc') }}</p>
+ </div>
+ <div class="text-right">
+ <a href="{{ url('/preferences/shortcuts') }}" class="button outline">{{ trans('common.manage') }}</a>
+ </div>
+ </section>
+
+ @if(!user()->isGuest() && userCan('receive-notifications'))
+ <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
+ <div class="flex min-width-m">
+ <h2 class="list-heading">{{ trans('preferences.notifications') }}</h2>
+ <p class="text-muted">{{ trans('preferences.notifications_desc') }}</p>
+ </div>
+ <div class="text-right">
+ <a href="{{ url('/preferences/notifications') }}" class="button outline">{{ trans('common.manage') }}</a>
+ </div>
+ </section>
+ @endif
+
+ @if(!user()->isGuest())
+ <section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
+ <div class="flex min-width-m">
+ <h2 class="list-heading">{{ trans('settings.users_edit_profile') }}</h2>
+ <p class="text-muted">{{ trans('preferences.profile_overview_desc') }}</p>
+ </div>
+ <div class="text-right">
+ <a href="{{ user()->getEditUrl() }}" class="button outline">{{ trans('common.manage') }}</a>
+ </div>
+ </section>
+ @endif
+
+ </div>
+@stop
--- /dev/null
+@extends('layouts.simple')
+
+@section('body')
+ <div class="container small my-xl">
+
+ <section class="card content-wrap auto-height">
+ <form action="{{ url('/preferences/notifications') }}" method="post">
+ {{ method_field('put') }}
+ {{ csrf_field() }}
+
+ <h1 class="list-heading">{{ trans('preferences.notifications') }}</h1>
+ <p class="text-small text-muted">{{ trans('preferences.notifications_desc') }}</p>
+
+ <div class="flex-container-row wrap justify-space-between pb-m">
+ <div class="toggle-switch-list min-width-l">
+ <div>
+ @include('form.toggle-switch', [
+ 'name' => 'preferences[own-page-changes]',
+ 'value' => $preferences->notifyOnOwnPageChanges(),
+ 'label' => trans('preferences.notifications_opt_own_page_changes'),
+ ])
+ </div>
+ @if (!setting('app-disable-comments'))
+ <div>
+ @include('form.toggle-switch', [
+ 'name' => 'preferences[own-page-comments]',
+ 'value' => $preferences->notifyOnOwnPageComments(),
+ 'label' => trans('preferences.notifications_opt_own_page_comments'),
+ ])
+ </div>
+ <div>
+ @include('form.toggle-switch', [
+ 'name' => 'preferences[comment-replies]',
+ 'value' => $preferences->notifyOnCommentReplies(),
+ 'label' => trans('preferences.notifications_opt_comment_replies'),
+ ])
+ </div>
+ @endif
+ </div>
+
+ <div class="mt-auto">
+ <button class="button">{{ trans('preferences.notifications_save') }}</button>
+ </div>
+ </div>
+
+ </form>
+ </section>
+
+ <section class="card content-wrap auto-height">
+ <h2 class="list-heading">{{ trans('preferences.notifications_watched') }}</h2>
+ <p class="text-small text-muted">{{ trans('preferences.notifications_watched_desc') }}</p>
+
+ @if($watches->isEmpty())
+ <p class="text-muted italic">{{ trans('common.no_items') }}</p>
+ @else
+ <div class="item-list">
+ @foreach($watches as $watch)
+ <div class="flex-container-row justify-space-between item-list-row items-center wrap px-m py-s">
+ <div class="py-xs px-s min-width-m">
+ @include('entities.icon-link', ['entity' => $watch->watchable])
+ </div>
+ <div class="py-xs min-width-m text-m-right px-m">
+ @icon('watch' . ($watch->ignoring() ? '-ignore' : ''))
+ {{ trans('entities.watch_title_' . $watch->getLevelName()) }}
+ </div>
+ </div>
+ @endforeach
+ </div>
+ @endif
+
+ <div class="my-m">{{ $watches->links() }}</div>
+ </section>
+
+ </div>
+@stop
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html lang="{{ config('app.lang') }}">
+<html lang="{{ $locale->htmlLang() }}">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
$style = [
/* Layout ------------------------------ */
- 'body' => 'margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;',
+ 'body' => 'margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;color:#444444;',
'email-wrapper' => 'width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;',
/* Masthead ----------------------- */
'anchor' => 'color: '.setting('app-color').';overflow-wrap: break-word;word-wrap: break-word;word-break: break-all;word-break:break-word;',
'header-1' => 'margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;',
- 'paragraph' => 'margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;',
- 'paragraph-sub' => 'margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;',
+ 'paragraph' => 'margin-top: 0; color: #444444; font-size: 16px; line-height: 1.5em;',
+ 'paragraph-sub' => 'margin-top: 0; color: #444444; font-size: 12px; line-height: 1.5em;',
'paragraph-center' => 'text-align: center;',
/* Buttons ------------------------------ */
<!-- Outro -->
@foreach ($outroLines as $line)
- <p style="{{ $style['paragraph'] }}">
+ <p style="{{ $style['paragraph-sub'] }}">
{{ $line }}
</p>
@endforeach
<tr>
<td style="{{ $fontFamily }}">
<p style="{{ $style['paragraph-sub'] }}">
- {{ trans('common.email_action_help', ['actionText' => $actionText]) }}
+ {{ $locale->trans('common.email_action_help', ['actionText' => $actionText]) }}
</p>
<p style="{{ $style['paragraph-sub'] }}">
<p style="{{ $style['paragraph-sub'] }}">
© {{ date('Y') }}
<a style="{{ $style['anchor'] }}" href="{{ url('/') }}" target="_blank">{{ setting('app-name') }}</a>.
- {{ trans('common.email_rights') }}
+ {{ $locale->trans('common.email_rights') }}
</p>
</td>
</tr>
Route::post('/favourites/add', [ActivityControllers\FavouriteController::class, 'add']);
Route::post('/favourites/remove', [ActivityControllers\FavouriteController::class, 'remove']);
+ // Watching
+ Route::put('/watching/update', [ActivityControllers\WatchController::class, 'update']);
+
// Other Pages
Route::get('/', [HomeController::class, 'index']);
Route::get('/home', [HomeController::class, 'index']);
Route::delete('/settings/users/{id}', [UserControllers\UserController::class, 'destroy']);
// User Preferences
- Route::redirect('/preferences', '/');
+ Route::get('/preferences', [UserControllers\UserPreferencesController::class, 'index']);
Route::get('/preferences/shortcuts', [UserControllers\UserPreferencesController::class, 'showShortcuts']);
Route::put('/preferences/shortcuts', [UserControllers\UserPreferencesController::class, 'updateShortcuts']);
+ Route::get('/preferences/notifications', [UserControllers\UserPreferencesController::class, 'showNotifications']);
+ Route::put('/preferences/notifications', [UserControllers\UserPreferencesController::class, 'updateNotifications']);
Route::patch('/preferences/change-view/{type}', [UserControllers\UserPreferencesController::class, 'changeView']);
Route::patch('/preferences/change-sort/{type}', [UserControllers\UserPreferencesController::class, 'changeSort']);
Route::patch('/preferences/change-expansion/{type}', [UserControllers\UserPreferencesController::class, 'changeExpansion']);
use BookStack\Activity\Models\Webhook;
use BookStack\Activity\Tools\ActivityLogger;
use BookStack\Api\ApiToken;
-use BookStack\Entities\Models\PageRevision;
use BookStack\Users\Models\User;
-use Illuminate\Http\Client\Request;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Facades\Bus;
-use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class WebhookCallTest extends TestCase
public function test_webhook_runs_for_delete_actions()
{
+ // This test must not fake the queue/bus since this covers an issue
+ // around handling and serialization of items now deleted from the database.
$this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
- Http::fake([
- '*' => Http::response('', 500),
- ]);
+ $this->mockHttpClient([new Response(500)]);
$user = $this->users->newUser();
$resp = $this->asAdmin()->delete($user->getEditUrl());
public function test_failed_webhook_call_logs_error()
{
$logger = $this->withTestLogger();
- Http::fake([
- '*' => Http::response('', 500),
- ]);
+ $this->mockHttpClient([new Response(500)]);
$webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
$this->assertNull($webhook->last_errored_at);
public function test_webhook_call_exception_is_caught_and_logged()
{
- Http::shouldReceive('asJson')->andThrow(new \Exception('Failed to perform request'));
+ $this->mockHttpClient([new ConnectException('Failed to perform request', new \GuzzleHttp\Psr7\Request('GET', ''))]);
$logger = $this->withTestLogger();
$webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
$this->assertNotNull($webhook->last_errored_at);
}
+ public function test_webhook_uses_ssr_hosts_option_if_set()
+ {
+ config()->set('app.ssr_hosts', 'https://*.example.com');
+ $responses = $this->mockHttpClient();
+
+ $webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.co.uk'], ['all']);
+ $this->runEvent(ActivityType::ROLE_CREATE);
+ $this->assertEquals(0, $responses->requestCount());
+
+ $webhook->refresh();
+ $this->assertEquals('The URL does not match the configured allowed SSR hosts', $webhook->last_error);
+ $this->assertNotNull($webhook->last_errored_at);
+ }
+
public function test_webhook_call_data_format()
{
- Http::fake([
- '*' => Http::response('', 200),
- ]);
+ $responses = $this->mockHttpClient([new Response(200, [], '')]);
$webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
$page = $this->entities->page();
$editor = $this->users->editor();
$this->runEvent(ActivityType::PAGE_UPDATE, $page, $editor);
- Http::assertSent(function (Request $request) use ($editor, $page, $webhook) {
- $reqData = $request->data();
-
- return $request->isJson()
- && $reqData['event'] === 'page_update'
- && $reqData['text'] === ($editor->name . ' updated page "' . $page->name . '"')
- && is_string($reqData['triggered_at'])
- && $reqData['triggered_by']['name'] === $editor->name
- && $reqData['triggered_by_profile_url'] === $editor->getProfileUrl()
- && $reqData['webhook_id'] === $webhook->id
- && $reqData['webhook_name'] === $webhook->name
- && $reqData['url'] === $page->getUrl()
- && $reqData['related_item']['name'] === $page->name;
- });
+ $request = $responses->latestRequest();
+ $reqData = json_decode($request->getBody(), true);
+ $this->assertEquals('page_update', $reqData['event']);
+ $this->assertEquals(($editor->name . ' updated page "' . $page->name . '"'), $reqData['text']);
+ $this->assertIsString($reqData['triggered_at']);
+ $this->assertEquals($editor->name, $reqData['triggered_by']['name']);
+ $this->assertEquals($editor->getProfileUrl(), $reqData['triggered_by_profile_url']);
+ $this->assertEquals($webhook->id, $reqData['webhook_id']);
+ $this->assertEquals($webhook->name, $reqData['webhook_name']);
+ $this->assertEquals($page->getUrl(), $reqData['url']);
+ $this->assertEquals($page->name, $reqData['related_item']['name']);
}
protected function runEvent(string $event, $detail = '', ?User $user = null)
--- /dev/null
+<?php
+
+namespace Tests\Activity;
+
+use BookStack\Activity\ActivityType;
+use BookStack\Activity\Models\Comment;
+use BookStack\Activity\Notifications\Messages\BaseActivityNotification;
+use BookStack\Activity\Notifications\Messages\CommentCreationNotification;
+use BookStack\Activity\Notifications\Messages\PageCreationNotification;
+use BookStack\Activity\Notifications\Messages\PageUpdateNotification;
+use BookStack\Activity\Tools\ActivityLogger;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
+use BookStack\Activity\WatchLevels;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Settings\UserNotificationPreferences;
+use Illuminate\Support\Facades\Notification;
+use Tests\TestCase;
+
+class WatchTest extends TestCase
+{
+ public function test_watch_action_exists_on_entity_unless_active()
+ {
+ $editor = $this->users->editor();
+ $this->actingAs($editor);
+
+ $entities = [$this->entities->book(), $this->entities->chapter(), $this->entities->page()];
+ /** @var Entity $entity */
+ foreach ($entities as $entity) {
+ $resp = $this->get($entity->getUrl());
+ $this->withHtml($resp)->assertElementContains('form[action$="/watching/update"] button.icon-list-item', 'Watch');
+
+ $watchOptions = new UserEntityWatchOptions($editor, $entity);
+ $watchOptions->updateLevelByValue(WatchLevels::COMMENTS);
+
+ $resp = $this->get($entity->getUrl());
+ $this->withHtml($resp)->assertElementNotExists('form[action$="/watching/update"] button.icon-list-item');
+ }
+ }
+
+ public function test_watch_action_only_shows_with_permission()
+ {
+ $viewer = $this->users->viewer();
+ $this->actingAs($viewer);
+
+ $entities = [$this->entities->book(), $this->entities->chapter(), $this->entities->page()];
+ /** @var Entity $entity */
+ foreach ($entities as $entity) {
+ $resp = $this->get($entity->getUrl());
+ $this->withHtml($resp)->assertElementNotExists('form[action$="/watching/update"] button.icon-list-item');
+ }
+
+ $this->permissions->grantUserRolePermissions($viewer, ['receive-notifications']);
+
+ /** @var Entity $entity */
+ foreach ($entities as $entity) {
+ $resp = $this->get($entity->getUrl());
+ $this->withHtml($resp)->assertElementExists('form[action$="/watching/update"] button.icon-list-item');
+ }
+ }
+
+ public function test_watch_update()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->book();
+
+ $this->actingAs($editor)->get($book->getUrl());
+ $resp = $this->put('/watching/update', [
+ 'type' => $book->getMorphClass(),
+ 'id' => $book->id,
+ 'level' => 'comments'
+ ]);
+
+ $resp->assertRedirect($book->getUrl());
+ $this->assertSessionHas('success');
+ $this->assertDatabaseHas('watches', [
+ 'watchable_id' => $book->id,
+ 'watchable_type' => $book->getMorphClass(),
+ 'user_id' => $editor->id,
+ 'level' => WatchLevels::COMMENTS,
+ ]);
+
+ $resp = $this->put('/watching/update', [
+ 'type' => $book->getMorphClass(),
+ 'id' => $book->id,
+ 'level' => 'default'
+ ]);
+ $resp->assertRedirect($book->getUrl());
+ $this->assertDatabaseMissing('watches', [
+ 'watchable_id' => $book->id,
+ 'watchable_type' => $book->getMorphClass(),
+ 'user_id' => $editor->id,
+ ]);
+ }
+
+ public function test_watch_update_fails_for_guest()
+ {
+ $this->setSettings(['app-public' => 'true']);
+ $guest = $this->users->guest();
+ $this->permissions->grantUserRolePermissions($guest, ['receive-notifications']);
+ $book = $this->entities->book();
+
+ $resp = $this->put('/watching/update', [
+ 'type' => $book->getMorphClass(),
+ 'id' => $book->id,
+ 'level' => 'comments'
+ ]);
+
+ $this->assertPermissionError($resp);
+ $guest->unsetRelations();
+ }
+
+ public function test_watch_detail_display_reflects_state()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->bookHasChaptersAndPages();
+ $chapter = $book->chapters()->first();
+ $page = $chapter->pages()->first();
+
+ (new UserEntityWatchOptions($editor, $book))->updateLevelByValue(WatchLevels::UPDATES);
+
+ $this->actingAs($editor)->get($book->getUrl())->assertSee('Watching new pages and updates');
+ $this->get($chapter->getUrl())->assertSee('Watching via parent book');
+ $this->get($page->getUrl())->assertSee('Watching via parent book');
+
+ (new UserEntityWatchOptions($editor, $chapter))->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->get($chapter->getUrl())->assertSee('Watching new pages, updates & comments');
+ $this->get($page->getUrl())->assertSee('Watching via parent chapter');
+
+ (new UserEntityWatchOptions($editor, $page))->updateLevelByValue(WatchLevels::UPDATES);
+ $this->get($page->getUrl())->assertSee('Watching new pages and updates');
+ }
+
+ public function test_watch_detail_ignore_indicator_cascades()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->bookHasChaptersAndPages();
+ (new UserEntityWatchOptions($editor, $book))->updateLevelByValue(WatchLevels::IGNORE);
+
+ $this->actingAs($editor)->get($book->getUrl())->assertSee('Ignoring notifications');
+ $this->get($book->chapters()->first()->getUrl())->assertSee('Ignoring via parent book');
+ $this->get($book->pages()->first()->getUrl())->assertSee('Ignoring via parent book');
+ }
+
+ public function test_watch_option_menu_shows_current_active_state()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->book();
+ $options = new UserEntityWatchOptions($editor, $book);
+
+ $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl()));
+ $respHtml->assertElementNotExists('form[action$="/watching/update"] svg[data-icon="check-circle"]');
+
+ $options->updateLevelByValue(WatchLevels::COMMENTS);
+ $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl()));
+ $respHtml->assertElementExists('form[action$="/watching/update"] button[value="comments"] svg[data-icon="check-circle"]');
+
+ $options->updateLevelByValue(WatchLevels::IGNORE);
+ $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl()));
+ $respHtml->assertElementExists('form[action$="/watching/update"] button[value="ignore"] svg[data-icon="check-circle"]');
+ }
+
+ public function test_watch_option_menu_limits_options_for_pages()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->bookHasChaptersAndPages();
+ (new UserEntityWatchOptions($editor, $book))->updateLevelByValue(WatchLevels::IGNORE);
+
+ $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl()));
+ $respHtml->assertElementExists('form[action$="/watching/update"] button[name="level"][value="new"]');
+
+ $respHtml = $this->withHtml($this->get($book->pages()->first()->getUrl()));
+ $respHtml->assertElementExists('form[action$="/watching/update"] button[name="level"][value="updates"]');
+ $respHtml->assertElementNotExists('form[action$="/watching/update"] button[name="level"][value="new"]');
+ }
+
+ public function test_notify_own_page_changes()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['own-page-changes' => 'true']);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin();
+ $this->entities->updatePage($entities['page'], ['name' => 'My updated page', 'html' => 'Hello']);
+ $notifications->assertSentTo($editor, PageUpdateNotification::class);
+ }
+
+ public function test_notify_own_page_comments()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['own-page-comments' => 'true']);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment'
+ ]);
+ $notifications->assertSentTo($editor, CommentCreationNotification::class);
+ }
+
+ public function test_notify_comment_replies()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['comment-replies' => 'true']);
+
+ // Create some existing comments to pad IDs to help potentially error
+ // on mis-identification of parent via ids used.
+ Comment::factory()->count(5)
+ ->for($entities['page'], 'entity')
+ ->create(['created_by' => $this->users->admin()->id]);
+
+ $notifications = Notification::fake();
+
+ $this->actingAs($editor)->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment'
+ ]);
+ $comment = $entities['page']->comments()->orderBy('id', 'desc')->first();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment response',
+ 'parent_id' => $comment->local_id,
+ ]);
+ $notifications->assertSentTo($editor, CommentCreationNotification::class);
+ }
+
+ public function test_notify_watch_parent_book_ignore()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $prefs = new UserNotificationPreferences($editor);
+ $watches->updateLevelByValue(WatchLevels::IGNORE);
+ $prefs->updateFromSettingsArray(['own-page-changes' => 'true', 'own-page-comments' => true]);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment response',
+ ]);
+ $this->entities->updatePage($entities['page'], ['name' => 'My updated page', 'html' => 'Hello']);
+ $notifications->assertNothingSent();
+ }
+
+ public function test_notify_watch_parent_book_comments()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+
+ // Comment post
+ $this->actingAs($admin)->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment response',
+ ]);
+
+ $notifications->assertSentTo($editor, function (CommentCreationNotification $notification) use ($editor, $admin, $entities) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'New comment on page: ' . $entities['page']->getShortName()
+ && str_contains($mailContent, 'View Comment')
+ && str_contains($mailContent, 'Page Name: ' . $entities['page']->name)
+ && str_contains($mailContent, 'Commenter: ' . $admin->name)
+ && str_contains($mailContent, 'Comment: My new comment response');
+ });
+ }
+
+ public function test_notify_watch_parent_book_updates()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::UPDATES);
+
+ $this->actingAs($admin);
+ $this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
+
+ $notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'Updated page: Updated page'
+ && str_contains($mailContent, 'View Page')
+ && str_contains($mailContent, 'Page Name: Updated page')
+ && str_contains($mailContent, 'Updated By: ' . $admin->name)
+ && str_contains($mailContent, 'you won\'t be sent notifications for further edits to this page by the same editor');
+ });
+
+ // Test debounce
+ $notifications = Notification::fake();
+ $this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
+ $notifications->assertNothingSentTo($editor);
+ }
+
+ public function test_notify_watch_parent_book_new()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::NEW);
+
+ $this->actingAs($admin)->get($entities['chapter']->getUrl('/create-page'));
+ $page = $entities['chapter']->pages()->where('draft', '=', true)->first();
+ $this->post($page->getUrl(), ['name' => 'My new page', 'html' => 'My new page content']);
+
+ $notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'New page: My new page'
+ && str_contains($mailContent, 'View Page')
+ && str_contains($mailContent, 'Page Name: My new page')
+ && str_contains($mailContent, 'Created By: ' . $admin->name);
+ });
+ }
+
+ public function test_notifications_sent_in_right_language()
+ {
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ setting()->putUser($editor, 'language', 'de');
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+
+ $activities = [
+ ActivityType::PAGE_CREATE => $entities['page'],
+ ActivityType::PAGE_UPDATE => $entities['page'],
+ ActivityType::COMMENT_CREATE => (new Comment([]))->forceFill(['entity_id' => $entities['page']->id, 'entity_type' => $entities['page']->getMorphClass()]),
+ ];
+
+ $notifications = Notification::fake();
+ $logger = app()->make(ActivityLogger::class);
+ $this->actingAs($admin);
+
+ foreach ($activities as $activityType => $detail) {
+ $logger->add($activityType, $detail);
+ }
+
+ $sent = $notifications->sentNotifications()[get_class($editor)][$editor->id];
+ $this->assertCount(3, $sent);
+
+ foreach ($sent as $notificationInfo) {
+ $notification = $notificationInfo[0]['notification'];
+ $this->assertInstanceOf(BaseActivityNotification::class, $notification);
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ $this->assertStringContainsString('Name der Seite:', $mailContent);
+ $this->assertStringContainsString('Diese Benachrichtigung wurde', $mailContent);
+ $this->assertStringContainsString('Sollte es beim Anklicken der Schaltfläche', $mailContent);
+ }
+ }
+
+ public function test_notifications_not_sent_if_lacking_view_permission_for_related_item()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->permissions->disableEntityInheritedPermissions($page);
+
+ $this->asAdmin()->post("/comment/{$page->id}", [
+ 'text' => 'My new comment response',
+ ])->assertOk();
+
+ $notifications->assertNothingSentTo($editor);
+ }
+
+ public function test_watches_deleted_on_user_delete()
+ {
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->assertDatabaseHas('watches', ['user_id' => $editor->id]);
+
+ $this->asAdmin()->delete($editor->getEditUrl());
+
+ $this->assertDatabaseMissing('watches', ['user_id' => $editor->id]);
+ }
+
+ public function test_watches_deleted_on_item_delete()
+ {
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->assertDatabaseHas('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
+
+ $this->entities->destroy($page);
+
+ $this->assertDatabaseMissing('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
+ }
+}
'value' => 'tagvalue',
],
],
+ 'priority' => 15,
];
$resp = $this->postJson($this->baseEndpoint, $details);
'value' => 'freshtagval',
],
],
+ 'priority' => 15,
];
$resp = $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details);
'value' => 'tagvalue',
],
],
+ 'priority' => 15,
];
$resp = $this->postJson($this->baseEndpoint, $details);
'value' => 'freshtagval',
],
],
+ 'priority' => 15,
];
$resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
namespace Tests\Api;
+use BookStack\Access\Notifications\UserInviteNotification;
use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Activity as ActivityModel;
use BookStack\Entities\Models\Entity;
use BookStack\Facades\Activity;
-use BookStack\Notifications\UserInvite;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Hash;
$resp->assertStatus(200);
/** @var User $user */
- Notification::assertSentTo($user, UserInvite::class);
+ Notification::assertSentTo($user, UserInviteNotification::class);
}
public function test_create_name_and_email_validation()
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
-use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Illuminate\Testing\TestResponse;
use Tests\Helpers\OidcJwtHelper;
'auth.method' => 'oidc',
'auth.defaults.guard' => 'oidc',
'oidc.name' => 'SingleSignOn-Testing',
- 'oidc.display_name_claims' => ['name'],
+ 'oidc.display_name_claims' => 'name',
'oidc.client_id' => OidcJwtHelper::defaultClientId(),
'oidc.client_secret' => 'testpass',
'oidc.jwt_public_key' => $this->keyFilePath,
$this->post('/oidc/login');
$state = session()->get('oidc_state');
- $transactions = &$this->mockHttpClient([$this->getMockAuthorizationResponse([
+ $transactions = $this->mockHttpClient([$this->getMockAuthorizationResponse([
'sub' => 'benny1010101',
])]);
// App calls token endpoint to get id token
$resp = $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state);
$resp->assertRedirect('/');
- $this->assertCount(1, $transactions);
- /** @var Request $tokenRequest */
- $tokenRequest = $transactions[0]['request'];
+ $this->assertEquals(1, $transactions->requestCount());
+ $tokenRequest = $transactions->latestRequest();
$this->assertEquals('https://oidc.local/token', (string) $tokenRequest->getUri());
$this->assertEquals('POST', $tokenRequest->getMethod());
$this->assertEquals('Basic ' . base64_encode(OidcJwtHelper::defaultClientId() . ':testpass'), $tokenRequest->getHeader('Authorization')[0]);
{
$this->withAutodiscovery();
- $transactions = &$this->mockHttpClient([
+ $transactions = $this->mockHttpClient([
$this->getAutoDiscoveryResponse(),
$this->getJwksResponse(),
]);
$this->runLogin();
$this->assertTrue(auth()->check());
- /** @var Request $discoverRequest */
- $discoverRequest = $transactions[0]['request'];
- /** @var Request $discoverRequest */
- $keysRequest = $transactions[1]['request'];
+ $discoverRequest = $transactions->requestAt(0);
+ $keysRequest = $transactions->requestAt(1);
$this->assertEquals('GET', $keysRequest->getMethod());
$this->assertEquals('GET', $discoverRequest->getMethod());
$this->assertEquals(OidcJwtHelper::defaultIssuer() . '/.well-known/openid-configuration', $discoverRequest->getUri());
{
$this->withAutodiscovery();
- $transactions = &$this->mockHttpClient([
+ $transactions = $this->mockHttpClient([
$this->getAutoDiscoveryResponse(),
$this->getJwksResponse(),
$this->getAutoDiscoveryResponse([
// Initial run
$this->post('/oidc/login');
- $this->assertCount(2, $transactions);
+ $this->assertEquals(2, $transactions->requestCount());
// Second run, hits cache
$this->post('/oidc/login');
- $this->assertCount(2, $transactions);
+ $this->assertEquals(2, $transactions->requestCount());
// Third run, different issuer, new cache key
config()->set(['oidc.issuer' => 'https://auto.example.com']);
$this->post('/oidc/login');
- $this->assertCount(4, $transactions);
+ $this->assertEquals(4, $transactions->requestCount());
}
public function test_auth_login_with_autodiscovery_with_keys_that_do_not_have_alg_property()
$this->assertEquals('xXBennyTheGeezXx', $user->external_auth_id);
}
+ public function test_auth_uses_mulitple_display_name_claims_if_configured()
+ {
+ config()->set(['oidc.display_name_claims' => 'first_name|last_name']);
+
+ $this->runLogin([
+ 'sub' => 'benny1010101',
+ 'first_name' => 'Benny',
+ 'last_name' => 'Jenkins'
+ ]);
+
+ $this->assertDatabaseHas('users', [
+ 'name' => 'Benny Jenkins',
+ ]);
+ }
+
public function test_login_group_sync()
{
config()->set([
namespace Tests\Auth;
-use BookStack\Notifications\ConfirmEmail;
+use BookStack\Access\Notifications\ConfirmEmailNotification;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\DB;
// Ensure notification sent
/** @var User $dbUser */
$dbUser = User::query()->where('email', '=', $user->email)->first();
- Notification::assertSentTo($dbUser, ConfirmEmail::class);
+ Notification::assertSentTo($dbUser, ConfirmEmailNotification::class);
// Test access and resend confirmation email
$resp = $this->post('/login', ['email' => $user->email, 'password' => $user->password]);
// Get confirmation and confirm notification matches
$emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
- Notification::assertSentTo($dbUser, ConfirmEmail::class, function ($notification, $channels) use ($emailConfirmation) {
+ Notification::assertSentTo($dbUser, ConfirmEmailNotification::class, function ($notification, $channels) use ($emailConfirmation) {
return $notification->token === $emailConfirmation->token;
});
namespace Tests\Auth;
-use BookStack\Notifications\ResetPassword;
+use BookStack\Access\Notifications\ResetPasswordNotification;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
/** @var User $user */
- Notification::assertSentTo($user, ResetPassword::class);
- $n = Notification::sent($user, ResetPassword::class);
+ Notification::assertSentTo($user, ResetPasswordNotification::class);
+ $n = Notification::sent($user, ResetPasswordNotification::class);
$this->get('/password/reset/' . $n->first()->token)
->assertOk()
$resp = $this->followingRedirects()->post('/password/email', [
'email' => $editor->email,
]);
- Notification::assertTimesSent(1, ResetPassword::class);
+ Notification::assertTimesSent(1, ResetPasswordNotification::class);
$resp->assertSee('A password reset link will be sent to ' . $editor->email . ' if that email address is found in the system.');
}
}
namespace Tests\Auth;
+use BookStack\Access\Notifications\UserInviteNotification;
use BookStack\Access\UserInviteService;
-use BookStack\Notifications\UserInvite;
use BookStack\Users\Models\User;
use Carbon\Carbon;
use Illuminate\Notifications\Messages\MailMessage;
$newUser = User::query()->where('email', '=', $email)->orderBy('id', 'desc')->first();
- Notification::assertSentTo($newUser, UserInvite::class);
+ Notification::assertSentTo($newUser, UserInviteNotification::class);
$this->assertDatabaseHas('user_invites', [
'user_id' => $newUser->id,
]);
$resp->assertRedirect('/settings/users');
$newUser = User::query()->where('email', '=', $email)->orderBy('id', 'desc')->first();
- Notification::assertSentTo($newUser, UserInvite::class, function ($notification, $channels, $notifiable) {
+ Notification::assertSentTo($newUser, UserInviteNotification::class, function ($notification, $channels, $notifiable) {
/** @var MailMessage $mail */
$mail = $notification->toMail($notifiable);
$this->artisan('bookstack:cleanup-images -v')
->expectsOutput('Dry run, no images have been deleted')
- ->expectsOutput('1 images found that would have been deleted')
+ ->expectsOutput('1 image(s) found that would have been deleted')
->expectsOutputToContain($image->path)
->assertExitCode(0);
$this->artisan('bookstack:cleanup-images --force')
->expectsOutputToContain('This operation is destructive and is not guaranteed to be fully accurate')
->expectsConfirmation('Are you sure you want to proceed?', 'yes')
- ->expectsOutput('1 images deleted')
+ ->expectsOutput('1 image(s) deleted')
->assertExitCode(0);
$this->assertDatabaseMissing('images', ['id' => $image->id]);
$this->assertDatabaseHas('images', ['id' => $image->id]);
}
+
+ public function test_command_force_no_interaction_run()
+ {
+ $page = $this->entities->page();
+ $image = Image::factory()->create(['uploaded_to' => $page->id]);
+
+ $this->artisan('bookstack:cleanup-images --force --no-interaction')
+ ->expectsOutputToContain('This operation is destructive and is not guaranteed to be fully accurate')
+ ->expectsOutput('1 image(s) deleted')
+ ->assertExitCode(0);
+
+ $this->assertDatabaseMissing('images', ['id' => $image->id]);
+ }
}
--- /dev/null
+<?php
+
+namespace Tests\Commands;
+
+use BookStack\Uploads\Image;
+use BookStack\Users\Models\User;
+use GuzzleHttp\Psr7\Response;
+use Illuminate\Database\Eloquent\Collection;
+use Tests\TestCase;
+
+class RefreshAvatarCommandTest extends TestCase
+{
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ config()->set([
+ 'services.disable_services' => false,
+ 'services.avatar_url' => 'https://avatars.example.com?a=b',
+ ]);
+ }
+
+ public function test_command_errors_if_avatar_fetch_disabled()
+ {
+ config()->set(['services.avatar_url' => false]);
+
+ $this->artisan('bookstack:refresh-avatar')
+ ->expectsOutputToContain("Avatar fetching is disabled on this instance")
+ ->assertExitCode(1);
+ }
+
+ public function test_command_requires_email_or_id_option()
+ {
+ $this->artisan('bookstack:refresh-avatar')
+ ->expectsOutputToContain("Either a --id=<number> or --email=<email> option must be provided")
+ ->assertExitCode(1);
+ }
+
+ public function test_command_runs_with_provided_email()
+ {
+ $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
+
+ $user = $this->users->viewer();
+ $this->assertFalse($user->avatar()->exists());
+
+ $this->artisan("bookstack:refresh-avatar --email={$user->email} -f")
+ ->expectsQuestion('Are you sure you want to proceed?', true)
+ ->expectsOutput("[ID: {$user->id}] {$user->email} - Updated")
+ ->expectsOutputToContain('This will destroy any existing avatar images these users have, and attempt to fetch new avatar images from avatars.example.com')
+ ->assertExitCode(0);
+
+ $this->assertEquals('https://avatars.example.com?a=b', $requests->latestRequest()->getUri());
+
+ $user->refresh();
+ $this->assertTrue($user->avatar()->exists());
+ }
+
+ public function test_command_runs_with_provided_id()
+ {
+ $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
+
+ $user = $this->users->viewer();
+ $this->assertFalse($user->avatar()->exists());
+
+ $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
+ ->expectsQuestion('Are you sure you want to proceed?', true)
+ ->expectsOutput("[ID: {$user->id}] {$user->email} - Updated")
+ ->assertExitCode(0);
+
+ $this->assertEquals('https://avatars.example.com?a=b', $requests->latestRequest()->getUri());
+
+ $user->refresh();
+ $this->assertTrue($user->avatar()->exists());
+ }
+
+ public function test_command_runs_with_provided_id_error_upstream()
+ {
+ $requests = $this->mockHttpClient([new Response(404)]);
+
+ $user = $this->users->viewer();
+ $this->assertFalse($user->avatar()->exists());
+
+ $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
+ ->expectsQuestion('Are you sure you want to proceed?', true)
+ ->expectsOutput("[ID: {$user->id}] {$user->email} - Not updated")
+ ->assertExitCode(1);
+
+ $this->assertEquals(1, $requests->requestCount());
+ $this->assertFalse($user->avatar()->exists());
+ }
+
+ public function test_saying_no_to_confirmation_does_not_refresh_avatar()
+ {
+ $user = $this->users->viewer();
+
+ $this->assertFalse($user->avatar()->exists());
+ $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
+ ->expectsQuestion('Are you sure you want to proceed?', false)
+ ->assertExitCode(0);
+ $this->assertFalse($user->avatar()->exists());
+ }
+
+ public function test_giving_non_existing_user_shows_error_message()
+ {
+ ->assertExitCode(1);
+ }
+
+ public function test_command_runs_all_users_without_avatars_dry_run()
+ {
+ $users = User::query()->where('image_id', '=', 0)->get();
+
+ $this->artisan('bookstack:refresh-avatar --users-without-avatars')
+ ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
+ ->expectsOutput("[ID: {$users[0]->id}] {$users[0]->email} - Not updated")
+ ->expectsOutput('Dry run, no avatars were updated.')
+ ->assertExitCode(0);
+ }
+
+ public function test_command_runs_all_users_without_avatars_with_none_to_update()
+ {
+ $requests = $this->mockHttpClient();
+ $image = Image::factory()->create();
+ User::query()->update(['image_id' => $image->id]);
+
+ $this->artisan('bookstack:refresh-avatar --users-without-avatars -f')
+ ->expectsOutput('0 user(s) found to update avatars for.')
+ ->assertExitCode(0);
+
+ $this->assertEquals(0, $requests->requestCount());
+ }
+
+ public function test_command_runs_all_users_without_avatars()
+ {
+ /** @var Collection|User[] $users */
+ $users = User::query()->where('image_id', '=', 0)->get();
+
+ $pendingCommand = $this->artisan('bookstack:refresh-avatar --users-without-avatars -f');
+ $pendingCommand
+ ->expectsOutput($users->count() . ' user(s) found to update avatars for.')
+ ->expectsQuestion('Are you sure you want to proceed?', true);
+
+ $responses = [];
+ foreach ($users as $user) {
+ $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
+ $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
+ }
+ $requests = $this->mockHttpClient($responses);
+
+ $pendingCommand->assertExitCode(0);
+ $pendingCommand->run();
+
+ $this->assertEquals(0, User::query()->where('image_id', '=', 0)->count());
+ $this->assertEquals($users->count(), $requests->requestCount());
+ }
+
+ public function test_saying_no_to_confirmation_all_users_without_avatars()
+ {
+ $requests = $this->mockHttpClient();
+
+ $this->artisan('bookstack:refresh-avatar --users-without-avatars -f')
+ ->expectsQuestion('Are you sure you want to proceed?', false)
+ ->assertExitCode(0);
+
+ $this->assertEquals(0, $requests->requestCount());
+ }
+
+ public function test_command_runs_all_users_dry_run()
+ {
+ $users = User::query()->where('image_id', '=', 0)->get();
+
+ $this->artisan('bookstack:refresh-avatar --all')
+ ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
+ ->expectsOutput("[ID: {$users[0]->id}] {$users[0]->email} - Not updated")
+ ->expectsOutput('Dry run, no avatars were updated.')
+ ->assertExitCode(0);
+ }
+
+ public function test_command_runs_update_all_users_avatar()
+ {
+ /** @var Collection|User[] $users */
+ $users = User::query()->get();
+
+ $pendingCommand = $this->artisan('bookstack:refresh-avatar --all -f');
+ $pendingCommand
+ ->expectsOutput($users->count() . ' user(s) found to update avatars for.')
+ ->expectsQuestion('Are you sure you want to proceed?', true);
+
+ $responses = [];
+ foreach ($users as $user) {
+ $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
+ $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
+ }
+ $requests = $this->mockHttpClient($responses);
+
+ $pendingCommand->assertExitCode(0);
+ $pendingCommand->run();
+
+ $this->assertEquals(0, User::query()->where('image_id', '=', 0)->count());
+ $this->assertEquals($users->count(), $requests->requestCount());
+ }
+
+ public function test_command_runs_update_all_users_avatar_errors()
+ {
+ /** @var Collection|User[] $users */
+ $users = array_values(User::query()->get()->all());
+
+ $pendingCommand = $this->artisan('bookstack:refresh-avatar --all -f');
+ $pendingCommand
+ ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
+ ->expectsQuestion('Are you sure you want to proceed?', true);
+
+ $responses = [];
+ foreach ($users as $index => $user) {
+ if ($index === 0) {
+ $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Not updated");
+ $responses[] = new Response(404);
+ continue;
+ }
+
+ $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
+ $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
+ }
+
+ $requests = $this->mockHttpClient($responses);
+
+ $pendingCommand->assertExitCode(1);
+ $pendingCommand->run();
+
+ $userWithAvatars = User::query()->where('image_id', '!=', 0)->count();
+ $this->assertEquals(count($users) - 1, $userWithAvatars);
+ $this->assertEquals(count($users), $requests->requestCount());
+ }
+
+ public function test_saying_no_to_confirmation_update_all_users_avatar()
+ {
+ $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
+
+ $this->artisan('bookstack:refresh-avatar --all -f')
+ ->expectsQuestion('Are you sure you want to proceed?', false)
+ ->assertExitCode(0);
+
+ $this->assertEquals(0, $requests->requestCount());
+ }
+}
$respHtml = $this->withHtml($this->get($page->getUrl('/edit')));
$respHtml->assertElementContains('.comment-box .content', 'My great comment to see in the editor');
}
+
+ public function test_comment_creator_name_truncated()
+ {
+ [$longNamedUser] = $this->users->newUserWithRole(['name' => 'Wolfeschlegelsteinhausenbergerdorff'], ['comment-create-all', 'page-view-all']);
+ $page = $this->entities->page();
+
+ $comment = Comment::factory()->make();
+ $this->actingAs($longNamedUser)->postJson("/comment/$page->id", $comment->getAttributes());
+
+ $pageResp = $this->asAdmin()->get($page->getUrl());
+ $pageResp->assertSee('Wolfeschlegels…');
+ }
}
$page = $this->entities->page();
$this->createRevisions($page, 2);
- $pageView = $this->get($page->getUrl());
+ $pageView = $this->asViewer()->get($page->getUrl());
$pageView->assertSee('Revision #' . $page->revision_count);
}
namespace Tests\Entity;
use BookStack\Search\SearchOptions;
+use Illuminate\Http\Request;
use Tests\TestCase;
class SearchOptionsTest extends TestCase
$this->assertEquals(['is_tree' => ''], $options->filters);
}
+ public function test_from_string_properly_parses_escaped_quotes()
+ {
+ $options = SearchOptions::fromString('"\"cat\"" surprise "\"\"" "\"donkey" "\""');
+
+ $this->assertEquals(['"cat"', '""', '"donkey', '"'], $options->exacts);
+ }
+
public function test_to_string_includes_all_items_in_the_correct_format()
{
$expected = 'cat "dog" [tag=good] {is_tree}';
}
}
+ public function test_to_string_escapes_quotes_as_expected()
+ {
+ $options = new SearchOptions();
+ $options->exacts = ['"cat"', '""', '"donkey', '"'];
+
+ $output = $options->toString();
+ $this->assertEquals('"\"cat\"" "\"\"" "\"donkey" "\""', $output);
+ }
+
public function test_correct_filter_values_are_set_from_string()
{
$opts = SearchOptions::fromString('{is_tree} {name:dan} {cat:happy}');
'cat' => 'happy',
], $opts->filters);
}
+ public function test_it_cannot_parse_out_empty_exacts()
+ {
+ $options = SearchOptions::fromString('"" test ""');
+
+ $this->assertEmpty($options->exacts);
+ $this->assertCount(1, $options->searches);
+ }
+
+ public function test_from_request_properly_parses_exacts_from_search_terms()
+ {
+ $request = new Request([
+ 'search' => 'biscuits "cheese" "" "baked beans"'
+ ]);
+
+ $options = SearchOptions::fromRequest($request);
+ $this->assertEquals(["biscuits"], $options->searches);
+ $this->assertEquals(['"cheese"', '""', '"baked', 'beans"'], $options->exacts);
+ }
}
$resp = $this->actingAs($editor)->get($page->getUrl());
$this->withHtml($resp)->assertElementContains('button', 'Favourite');
- $this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/add"]');
+ $this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/add"] input[name="type"][value="page"]');
$resp = $this->post('/favourites/add', [
- 'type' => get_class($page),
+ 'type' => $page->getMorphClass(),
'id' => $page->id,
]);
$resp->assertRedirect($page->getUrl());
$this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/remove"]');
$resp = $this->post('/favourites/remove', [
- 'type' => get_class($page),
+ 'type' => $page->getMorphClass(),
'id' => $page->id,
]);
$resp->assertRedirect($page->getUrl());
$this->actingAs($user)->get($book->getUrl());
$resp = $this->post('/favourites/add', [
- 'type' => get_class($book),
+ 'type' => $book->getMorphClass(),
'id' => $book->id,
]);
$resp->assertRedirect($book->getUrl());
use BookStack\Entities\Repos\BookshelfRepo;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Entities\Repos\PageRepo;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Builder;
return $draftPage;
}
+ /**
+ * Fully destroy the given entity from the system, bypassing the recycle bin
+ * stage. Still runs through main app deletion logic.
+ */
+ public function destroy(Entity $entity)
+ {
+ $trash = app()->make(TrashCan::class);
+ $trash->destroyEntity($entity);
+ }
+
/**
* @param Entity|Entity[] $entities
*/
return $user;
}
+ /**
+ * Get the system "guest" user.
+ */
+ public function guest(): User
+ {
+ return User::getGuest();
+ }
+
/**
* Create a new fresh user without any relations.
*/
namespace Tests;
use BookStack\Activity\ActivityType;
+use BookStack\Translation\LocaleManager;
class LanguageTest extends TestCase
{
$this->langs = array_diff(scandir(lang_path('')), ['..', '.']);
}
- public function test_locales_config_key_set_properly()
+ public function test_locales_list_set_properly()
{
- $configLocales = config('app.locales');
- sort($configLocales);
+ $appLocales = $this->app->make(LocaleManager::class)->getAllAppLocales();
+ sort($appLocales);
sort($this->langs);
- $this->assertEquals(implode(':', $configLocales), implode(':', $this->langs), 'app.locales configuration variable does not match those found in lang files');
+ $this->assertEquals(implode(':', $this->langs), implode(':', $appLocales), 'app.locales configuration variable does not match those found in lang files');
}
// Not part of standard phpunit test runs since we sometimes expect non-added langs.
}
}
- public function test_rtl_config_set_if_lang_is_rtl()
+ public function test_views_use_rtl_if_rtl_language_is_set()
{
- $this->asEditor();
- $this->assertFalse(config('app.rtl'), 'App RTL config should be false by default');
+ $this->asEditor()->withHtml($this->get('/'))->assertElementExists('html[dir="ltr"]');
+
setting()->putUser($this->users->editor(), 'language', 'ar');
- $this->get('/');
- $this->assertTrue(config('app.rtl'), 'App RTL config should have been set to true by middleware');
+
+ $this->withHtml($this->get('/'))->assertElementExists('html[dir="rtl"]');
}
public function test_unknown_lang_does_not_break_app()
$resp = $this->post($chapter->getUrl('/create-guest-page'), ['name' => 'My guest page']);
$resp->assertRedirect($chapter->book->getUrl('/page/my-guest-page/edit'));
- $user = User::getDefault();
+ $user = $this->users->guest();
$this->assertDatabaseHas('pages', [
'name' => 'My guest page',
'chapter_id' => $chapter->id,
public function test_public_view_can_take_on_other_roles()
{
$this->setSettings(['app-public' => 'true']);
- $newRole = $this->users->attachNewRole(User::getDefault(), []);
+ $newRole = $this->users->attachNewRole($this->users->guest(), []);
$page = $this->entities->page();
$this->permissions->disableEntityInheritedPermissions($page);
$this->permissions->addEntityPermission($page, ['view', 'update'], $newRole);
$this->withHtml($resp)->assertLinkExists($page->getUrl('/edit'));
}
+
+ public function test_public_user_cannot_view_or_update_their_profile()
+ {
+ $this->setSettings(['app-public' => 'true']);
+ $guest = $this->users->guest();
+
+ $resp = $this->get($guest->getEditUrl());
+ $this->assertPermissionError($resp);
+
+ $resp = $this->put($guest->getEditUrl(), ['name' => 'My new guest name']);
+ $this->assertPermissionError($resp);
+ }
}
namespace Tests\Settings;
-use BookStack\Notifications\TestEmail;
+use BookStack\Settings\TestEmailNotification;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
$sendReq->assertRedirect('/settings/maintenance#image-cleanup');
$this->assertSessionHas('success', 'Email sent to ' . $admin->email);
- Notification::assertSentTo($admin, TestEmail::class);
+ Notification::assertSentTo($admin, TestEmailNotification::class);
}
public function test_send_test_email_failure_displays_error_notification()
$this->permissions->grantUserRolePermissions($user, ['settings-manage']);
$sendReq = $this->actingAs($user)->post('/settings/maintenance/send-test-email');
- Notification::assertSentTo($user, TestEmail::class);
+ Notification::assertSentTo($user, TestEmailNotification::class);
}
}
namespace Tests;
use BookStack\Entities\Models\Entity;
+use BookStack\Http\HttpClientHistory;
+use BookStack\Http\HttpRequestService;
use BookStack\Settings\SettingService;
-use BookStack\Uploads\HttpFetcher;
-use GuzzleHttp\Client;
-use GuzzleHttp\Handler\MockHandler;
-use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Middleware;
+use BookStack\Users\Models\User;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Testing\Assert as PHPUnit;
-use Mockery;
use Monolog\Handler\TestHandler;
use Monolog\Logger;
-use Psr\Http\Client\ClientInterface;
use Ssddanbrown\AssertHtml\TestsHtml;
use Tests\Helpers\EntityProvider;
use Tests\Helpers\FileProvider;
}
/**
- * Mock the HttpFetcher service and return the given data on fetch.
+ * Mock the http client used in BookStack http calls.
*/
- protected function mockHttpFetch($returnData, int $times = 1)
+ protected function mockHttpClient(array $responses = []): HttpClientHistory
{
- $mockHttp = Mockery::mock(HttpFetcher::class);
- $this->app[HttpFetcher::class] = $mockHttp;
- $mockHttp->shouldReceive('fetch')
- ->times($times)
- ->andReturn($returnData);
- }
-
- /**
- * Mock the http client used in BookStack.
- * Returns a reference to the container which holds all history of http transactions.
- *
- * @link https://docs.guzzlephp.org/en/stable/testing.html#history-middleware
- */
- protected function &mockHttpClient(array $responses = []): array
- {
- $container = [];
- $history = Middleware::history($container);
- $mock = new MockHandler($responses);
- $handlerStack = new HandlerStack($mock);
- $handlerStack->push($history);
- $this->app[ClientInterface::class] = new Client(['handler' => $handlerStack]);
-
- return $container;
+ return $this->app->make(HttpRequestService::class)->mockClient($responses);
}
/**
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Tools\PageContent;
+use BookStack\Exceptions\ThemeException;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
use Illuminate\Console\Command;
-use Illuminate\Http\Client\Request as HttpClientRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
-use Illuminate\Support\Facades\Http;
-use League\CommonMark\ConfigurableEnvironmentInterface;
use League\CommonMark\Environment\Environment;
class ThemeTest extends TestCase
});
}
+ public function test_theme_functions_loads_errors_are_caught_and_logged()
+ {
+ $this->usingThemeFolder(function ($themeFolder) {
+ $functionsFile = theme_path('functions.php');
+ file_put_contents($functionsFile, "<?php\n\\BookStack\\Biscuits::eat();");
+
+ $this->expectException(ThemeException::class);
+ $this->expectExceptionMessageMatches('/Failed loading theme functions file at ".*?" with error: Class "BookStack\\\\Biscuits" not found/');
+
+ $this->runWithEnv('APP_THEME', $themeFolder, fn() => null);
+ });
+ }
+
public function test_event_commonmark_environment_configure()
{
$callbackCalled = false;
};
Theme::listen(ThemeEvents::WEBHOOK_CALL_BEFORE, $callback);
- Http::fake([
- '*' => Http::response('', 200),
- ]);
+ $responses = $this->mockHttpClient([new \GuzzleHttp\Psr7\Response(200, [], '')]);
$webhook = new Webhook(['name' => 'Test webhook', 'endpoint' => 'https://example.com']);
$webhook->save();
$this->assertEquals($webhook->id, $args[1]->id);
$this->assertEquals($detail->id, $args[2]->id);
- Http::assertSent(function (HttpClientRequest $request) {
- return $request->isJson() && $request->data()['test'] === 'hello!';
- });
+ $this->assertEquals(1, $responses->requestCount());
+ $request = $responses->latestRequest();
+ $reqData = json_decode($request->getBody(), true);
+ $this->assertEquals('hello!', $reqData['test']);
}
public function test_event_activity_logged()
--- /dev/null
+<?php
+
+namespace Tests\Unit;
+
+use BookStack\Exceptions\HttpFetchException;
+use BookStack\Util\SsrUrlValidator;
+use Tests\TestCase;
+
+class SsrUrlValidatorTest extends TestCase
+{
+ public function test_allowed()
+ {
+ $testMap = [
+ // Single values
+ ['config' => '', 'url' => '', 'result' => false],
+ ['config' => '', 'url' => 'https://example.com', 'result' => false],
+ ['config' => ' ', 'url' => 'https://example.com', 'result' => false],
+ ['config' => '*', 'url' => '', 'result' => false],
+ ['config' => '*', 'url' => 'https://example.com', 'result' => true],
+ ['config' => 'https://*', 'url' => 'https://example.com', 'result' => true],
+ ['config' => 'http://*', 'url' => 'https://example.com', 'result' => false],
+ ['config' => 'https://*example.com', 'url' => 'https://example.com', 'result' => true],
+ ['config' => 'https://*ample.com', 'url' => 'https://example.com', 'result' => true],
+ ['config' => 'https://*.example.com', 'url' => 'https://example.com', 'result' => false],
+ ['config' => 'https://*.example.com', 'url' => 'https://test.example.com', 'result' => true],
+ ['config' => '*//example.com', 'url' => 'https://example.com', 'result' => true],
+ ['config' => '*//example.com', 'url' => 'http://example.com', 'result' => true],
+ ['config' => '*//example.co', 'url' => 'http://example.co.uk', 'result' => false],
+ ['config' => '*//example.co/bookstack', 'url' => 'https://example.co/bookstack/a/path', 'result' => true],
+ ['config' => '*//example.co*', 'url' => 'https://example.co.uk/bookstack/a/path', 'result' => true],
+ ['config' => 'https://example.com', 'url' => 'https://example.com/a/b/c?test=cat', 'result' => true],
+ ['config' => 'https://example.com', 'url' => 'https://example.co.uk', 'result' => false],
+
+ // Escapes
+ ['config' => 'https://(.*?).com', 'url' => 'https://example.com', 'result' => false],
+ ['config' => 'https://example.com', 'url' => 'https://example.co.uk#https://example.com', 'result' => false],
+
+ // Multi values
+ ['config' => '*//example.org *//example.com', 'url' => 'https://example.com', 'result' => true],
+ ['config' => '*//example.org *//example.com', 'url' => 'https://example.com/a/b/c?test=cat#hello', 'result' => true],
+ ['config' => '*.example.org *.example.com', 'url' => 'https://example.co.uk', 'result' => false],
+ ['config' => ' *.example.org *.example.com ', 'url' => 'https://example.co.uk', 'result' => false],
+ ['config' => '* *.example.com', 'url' => 'https://example.co.uk', 'result' => true],
+ ['config' => '*//example.org *//example.com *//example.co.uk', 'url' => 'https://example.co.uk', 'result' => true],
+ ['config' => '*//example.org *//example.com *//example.co.uk', 'url' => 'https://example.net', 'result' => false],
+ ];
+
+ foreach ($testMap as $test) {
+ $result = (new SsrUrlValidator($test['config']))->allowed($test['url']);
+ $this->assertEquals($test['result'], $result, "Failed asserting url '{$test['url']}' with config '{$test['config']}' results " . ($test['result'] ? 'true' : 'false'));
+ }
+ }
+
+ public function test_enssure_allowed()
+ {
+ $result = (new SsrUrlValidator('https://example.com'))->ensureAllowed('https://example.com');
+ $this->assertNull($result);
+
+ $this->expectException(HttpFetchException::class);
+ (new SsrUrlValidator('https://example.com'))->ensureAllowed('https://test.example.com');
+ }
+}
namespace Tests\Uploads;
use BookStack\Exceptions\HttpFetchException;
-use BookStack\Uploads\HttpFetcher;
use BookStack\Uploads\UserAvatars;
use BookStack\Users\Models\User;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\Psr7\Response;
use Tests\TestCase;
class AvatarTest extends TestCase
return User::query()->where('email', '=', $user->email)->first();
}
- protected function assertImageFetchFrom(string $url)
- {
- $http = $this->mock(HttpFetcher::class);
-
- $http->shouldReceive('fetch')
- ->once()->with($url)
- ->andReturn($this->files->pngImageData());
- }
-
- protected function deleteUserImage(User $user)
+ protected function deleteUserImage(User $user): void
{
$this->files->deleteAtRelativePath($user->avatar->path);
}
public function test_gravatar_fetched_on_user_create()
{
- config()->set([
- 'services.disable_services' => false,
- ]);
+ $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
+ config()->set(['services.disable_services' => false]);
$user = User::factory()->make();
- $this->assertImageFetchFrom('https://www.gravatar.com/avatar/' . md5(strtolower($user->email)) . '?s=500&d=identicon');
$user = $this->createUserRequest($user);
$this->assertDatabaseHas('images', [
'created_by' => $user->id,
]);
$this->deleteUserImage($user);
+
+ $expectedUri = 'https://www.gravatar.com/avatar/' . md5(strtolower($user->email)) . '?s=500&d=identicon';
+ $this->assertEquals($expectedUri, $requests->latestRequest()->getUri());
}
public function test_custom_url_used_if_set()
$user = User::factory()->make();
$url = 'https://example.com/' . urlencode(strtolower($user->email)) . '/' . md5(strtolower($user->email)) . '/500';
- $this->assertImageFetchFrom($url);
+ $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
$user = $this->createUserRequest($user);
+ $this->assertEquals($url, $requests->latestRequest()->getUri());
$this->deleteUserImage($user);
}
public function test_avatar_not_fetched_if_no_custom_url_and_services_disabled()
{
- config()->set([
- 'services.disable_services' => true,
- ]);
-
+ config()->set(['services.disable_services' => true]);
$user = User::factory()->make();
-
- $http = $this->mock(HttpFetcher::class);
- $http->shouldNotReceive('fetch');
+ $requests = $this->mockHttpClient([new Response()]);
$this->createUserRequest($user);
+
+ $this->assertEquals(0, $requests->requestCount());
}
public function test_avatar_not_fetched_if_avatar_url_option_set_to_false()
]);
$user = User::factory()->make();
-
- $http = $this->mock(HttpFetcher::class);
- $http->shouldNotReceive('fetch');
+ $requests = $this->mockHttpClient([new Response()]);
$this->createUserRequest($user);
+
+ $this->assertEquals(0, $requests->requestCount());
}
public function test_no_failure_but_error_logged_on_failed_avatar_fetch()
{
- config()->set([
- 'services.disable_services' => false,
- ]);
+ config()->set(['services.disable_services' => false]);
- $http = $this->mock(HttpFetcher::class);
- $http->shouldReceive('fetch')->andThrow(new HttpFetchException());
+ $this->mockHttpClient([new ConnectException('Failed to connect', new Request('GET', ''))]);
$logger = $this->withTestLogger();
$user = User::factory()->make();
$avatar = app()->make(UserAvatars::class);
- $url = 'http_malformed_url/' . urlencode(strtolower($user->email)) . '/' . md5(strtolower($user->email)) . '/500';
$logger = $this->withTestLogger();
+ $this->mockHttpClient([new ConnectException('Could not resolve host http_malformed_url', new Request('GET', ''))]);
$avatar->fetchAndAssignToUser($user);
+ $url = 'http_malformed_url/' . urlencode(strtolower($user->email)) . '/' . md5(strtolower($user->email)) . '/500';
$this->assertTrue($logger->hasError('Failed to save user avatar image'));
$exception = $logger->getRecords()[0]['context']['exception'];
- $this->assertEquals(new HttpFetchException(
- 'Cannot get image from ' . $url,
- 6,
- (new HttpFetchException('Could not resolve host: http_malformed_url', 6))
- ), $exception);
+ $this->assertInstanceOf(HttpFetchException::class, $exception);
+ $this->assertEquals('Cannot get image from ' . $url, $exception->getMessage());
+ $this->assertEquals('Could not resolve host http_malformed_url', $exception->getPrevious()->getMessage());
}
}
public function test_guest_profile_shows_limited_form()
{
- $guest = User::getDefault();
+ $guest = $this->users->guest();
$resp = $this->asAdmin()->get('/settings/users/' . $guest->id);
$resp->assertSee('Guest');
$this->withHtml($resp)->assertElementNotExists('#password');
public function test_guest_profile_cannot_be_deleted()
{
- $guestUser = User::getDefault();
+ $guestUser = $this->users->guest();
$resp = $this->asAdmin()->get('/settings/users/' . $guestUser->id . '/delete');
$resp->assertSee('Delete User');
$resp->assertSee('Guest');
{
$langs = ['en', 'fr', 'hr'];
foreach ($langs as $lang) {
- config()->set('app.locale', $lang);
+ config()->set('app.default_locale', $lang);
$resp = $this->asAdmin()->get('/settings/users/create');
$this->withHtml($resp)->assertElementExists('select[name="language"] option[value="' . $lang . '"][selected]');
}
namespace Tests\User;
+use BookStack\Activity\Tools\UserEntityWatchOptions;
+use BookStack\Activity\WatchLevels;
use Tests\TestCase;
class UserPreferencesTest extends TestCase
{
+ public function test_index_view()
+ {
+ $resp = $this->asEditor()->get('/preferences');
+ $resp->assertOk();
+ $resp->assertSee('Interface Keyboard Shortcuts');
+ $resp->assertSee('Edit Profile');
+ }
+
+ public function test_index_view_accessible_but_without_profile_and_notifications_for_guest_user()
+ {
+ $this->setSettings(['app-public' => 'true']);
+ $this->permissions->grantUserRolePermissions($this->users->guest(), ['receive-notifications']);
+ $resp = $this->get('/preferences');
+ $resp->assertOk();
+ $resp->assertSee('Interface Keyboard Shortcuts');
+ $resp->assertDontSee('Edit Profile');
+ $resp->assertDontSee('Notification');
+ }
public function test_interface_shortcuts_updating()
{
$this->asEditor();
$this->withHtml($this->get('/'))->assertElementExists('body[component="shortcuts"]');
}
+ public function test_notification_routes_requires_notification_permission()
+ {
+ $viewer = $this->users->viewer();
+ $resp = $this->actingAs($viewer)->get('/preferences/notifications');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->put('/preferences/notifications');
+ $this->assertPermissionError($resp);
+
+ $this->permissions->grantUserRolePermissions($viewer, ['receive-notifications']);
+ $resp = $this->get('/preferences/notifications');
+ $resp->assertOk();
+ $resp->assertSee('Notification Preferences');
+ }
+
+ public function test_notification_preferences_updating()
+ {
+ $editor = $this->users->editor();
+
+ // View preferences with defaults
+ $resp = $this->actingAs($editor)->get('/preferences/notifications');
+ $resp->assertSee('Notification Preferences');
+
+ $html = $this->withHtml($resp);
+ $html->assertFieldHasValue('preferences[comment-replies]', 'false');
+
+ // Update preferences
+ $resp = $this->put('/preferences/notifications', [
+ 'preferences' => ['comment-replies' => 'true'],
+ ]);
+
+ $resp->assertRedirect('/preferences/notifications');
+ $resp->assertSessionHas('success', 'Notification preferences have been updated!');
+
+ // View updates to preferences page
+ $resp = $this->get('/preferences/notifications');
+ $html = $this->withHtml($resp);
+ $html->assertFieldHasValue('preferences[comment-replies]', 'true');
+ }
+
+ public function test_notification_preferences_show_watches()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->book();
+
+ $options = new UserEntityWatchOptions($editor, $book);
+ $options->updateLevelByValue(WatchLevels::COMMENTS);
+
+ $resp = $this->actingAs($editor)->get('/preferences/notifications');
+ $resp->assertSee($book->name);
+ $resp->assertSee('All Page Updates & Comments');
+
+ $options->updateLevelByValue(WatchLevels::DEFAULT);
+
+ $resp = $this->actingAs($editor)->get('/preferences/notifications');
+ $resp->assertDontSee($book->name);
+ $resp->assertDontSee('All Page Updates & Comments');
+ }
+
+ public function test_notification_preferences_dont_error_on_deleted_items()
+ {
+ $editor = $this->users->editor();
+ $book = $this->entities->book();
+
+ $options = new UserEntityWatchOptions($editor, $book);
+ $options->updateLevelByValue(WatchLevels::COMMENTS);
+
+ $this->actingAs($editor)->delete($book->getUrl());
+ $book->refresh();
+ $this->assertNotNull($book->deleted_at);
+
+ $resp = $this->actingAs($editor)->get('/preferences/notifications');
+ $resp->assertOk();
+ $resp->assertDontSee($book->name);
+ }
+
+ public function test_notification_preferences_not_accessible_to_guest()
+ {
+ $this->setSettings(['app-public' => 'true']);
+ $guest = $this->users->guest();
+ $this->permissions->grantUserRolePermissions($guest, ['receive-notifications']);
+
+ $resp = $this->get('/preferences/notifications');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->put('/preferences/notifications', [
+ 'preferences' => ['comment-replies' => 'true'],
+ ]);
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_notification_comment_options_only_exist_if_comments_active()
+ {
+ $resp = $this->asEditor()->get('/preferences/notifications');
+ $resp->assertSee('Notify upon comments');
+ $resp->assertSee('Notify upon replies');
+
+ setting()->put('app-disable-comments', true);
+
+ $resp = $this->get('/preferences/notifications');
+ $resp->assertDontSee('Notify upon comments');
+ $resp->assertDontSee('Notify upon replies');
+ }
+
public function test_update_sort_preference()
{
$editor = $this->users->editor();
$this->withHtml($home)->assertElementExists('.dark-mode');
}
+ public function test_dark_mode_toggle_endpoint_changes_to_light_when_dark_by_default()
+ {
+ config()->set('setting-defaults.user.dark-mode-enabled', true);
+ $editor = $this->users->editor();
+
+ $this->assertEquals(true, setting()->getUser($editor, 'dark-mode-enabled'));
+ $prefChange = $this->actingAs($editor)->patch('/preferences/toggle-dark-mode');
+ $prefChange->assertRedirect();
+ $this->assertEquals(false, setting()->getUser($editor, 'dark-mode-enabled'));
+
+ $home = $this->get('/');
+ $this->withHtml($home)->assertElementNotExists('.dark-mode');
+ $home->assertDontSee('Light Mode');
+ $home->assertSee('Dark Mode');
+ }
+
public function test_books_view_type_preferences_when_list()
{
$editor = $this->users->editor();
public function test_select_requires_logged_in_user()
{
$this->setSettings(['app-public' => true]);
- $defaultUser = User::getDefault();
- $this->permissions->grantUserRolePermissions($defaultUser, ['users-manage']);
+ $this->permissions->grantUserRolePermissions($this->users->guest(), ['users-manage']);
$resp = $this->get('/search/users/select?search=a');
$this->assertPermissionError($resp);