Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Brown
c01217827c MFA: Started exploring hardware key support 2026-06-01 13:08:58 +01:00
150 changed files with 2702 additions and 2124 deletions

View File

@@ -22,7 +22,7 @@ jobs:
uses: https://github.com/shivammathur/setup-php@v2
with:
php-version: 8.5
extensions: gd, mbstring, json, curl, xml, dom, mysql, ldap
extensions: gd, mbstring, json, curl, xml, mysql, ldap
- name: Get Composer Cache Directory
id: composer-cache

View File

@@ -23,14 +23,6 @@ jobs:
services:
mysql:
image: docker.io/library/mariadb:12.2.2-noble
options: --tmpfs /var/lib/mysql:rw
cmd:
- --innodb-flush-log-at-trx-commit=0
- --innodb-flush-method=O_DIRECT
- --innodb-doublewrite=0
- --innodb-buffer-pool-size=256M
- --skip-log-bin
- --sync-binlog=0
env:
MARIADB_USER: bookstack-test
MARIADB_PASSWORD: bookstack-test
@@ -43,7 +35,7 @@ jobs:
uses: https://github.com/shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: gd, mbstring, json, curl, xml, dom, mysql, ldap
extensions: gd, mbstring, json, curl, xml, mysql, ldap
- name: Get Composer Cache Directory
id: composer-cache

View File

@@ -21,7 +21,7 @@ jobs:
matrix:
php: ['8.2', '8.3', '8.4', '8.5']
env:
phpextensions: gd, mbstring, json, curl, xml, dom, mysql, ldap, gmp
phpextensions: gd, mbstring, json, curl, xml, mysql, ldap, gmp
phpextensioncachekey: cache-v1
steps:
- uses: https://code.forgejo.org/actions/checkout@v6

View File

@@ -1,33 +0,0 @@
name: update-snyk
on:
workflow_dispatch:
push:
paths:
- 'composer*'
- 'package*'
branches:
- 'development'
- 'release'
jobs:
update:
runs-on: docker
container:
image: docker.io/library/node:24-trixie
steps:
- uses: https://code.forgejo.org/actions/checkout@v6
- name: Update Snyk for monitoring - Composer
uses: https://github.com/snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: snyk monitor --file=composer.lock --project-name=bookstack-${{forgejo.ref_name}}-composer
- name: Update Snyk for monitoring - NPM
uses: https://github.com/snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: snyk monitor --file=package-lock.json --project-name=bookstack-${{forgejo.ref_name}}-npm

View File

@@ -544,7 +544,3 @@ FelixFrizzy :: German
Pedro de Mattia (pdmtt) :: Portuguese, Brazilian
lonestan :: Russian
Paul Kernstock (kernstock) :: German
brtbr :: German; German Informal
Ricardo Covelo (covelo12) :: Portuguese
Bojan Maksimovic (PolarniMeda) :: Serbian (Cyrillic)
Dian Prawira (wiradian84) :: Indonesian

6
.gitignore vendored
View File

@@ -9,10 +9,10 @@ Homestead.yaml
.idea
npm-debug.log
yarn-error.log
/public/dist/*.map
/public/dist
/public/plugins
/public/css/*.map
/public/js/*.map
/public/css
/public/js
/public/bower
/public/build/
/public/favicon.ico

View File

@@ -8,7 +8,6 @@ use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Facades\Activity;
use BookStack\Http\Controller;
use BookStack\Util\UrlComparison;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
@@ -187,8 +186,7 @@ class LoginController extends Controller
{
// Store the previous location for redirect after login
$previous = url()->previous('');
$comparison = new UrlComparison($previous, url('/'));
$isPreviousFromInstance = $comparison->originsMatch() && $comparison->pathsOverlap();
$isPreviousFromInstance = str_starts_with($previous, url('/'));
if (!$previous || !setting('app-public') || !$isPreviousFromInstance) {
return;
}

View File

@@ -0,0 +1,133 @@
<?php
namespace BookStack\Access\Controllers;
use BookStack\Access\LoginService;
use BookStack\Access\Mfa\BackupCodeService;
use BookStack\Access\Mfa\MfaSession;
use BookStack\Access\Mfa\MfaValue;
use BookStack\Access\Mfa\MfaVerificationLimiter;
use BookStack\Activity\ActivityType;
use BookStack\Exceptions\NotFoundException;
use BookStack\Http\Controller;
use Cose\Algorithms;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\Denormalizer\WebauthnSerializerFactory;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialOptions;
use Webauthn\PublicKeyCredentialParameters;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialUserEntity;
class MfaHardwareKeyController extends Controller
{
use HandlesPartialLogins;
protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-hardware-key-secret';
public function __construct(
protected MfaVerificationLimiter $limiter,
) {
}
/**
* Show a view that allows configuration of a hardware key.
*/
public function generate(BackupCodeService $codeService)
{
$domain = parse_url(url('/'), PHP_URL_HOST);
$rpEntity = PublicKeyCredentialRpEntity::create(
setting('app-name'),
$domain,
);
$user = $this->currentOrLastAttemptedUser();
// TODO - Ensure user ID is somewhat long and random
// See: https://webauthn-doc.spomky-labs.com/prerequisites/user-entity
// Maybe hash of (app-id from settings + user id)
// Or maybe a UUID/random_string which we connect via DB entry?
$userEntity = PublicKeyCredentialUserEntity::create(
$user->slug,
$user->id,
$user->name,
);
$publicKeyCredentialParametersList = [
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256K), // More interesting algorithm
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256), // ||
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_RS256), // ||
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_PS256), // \/
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ED256), // Less interesting algorithm
];
$credentialOptions = PublicKeyCredentialCreationOptions::create(
$rpEntity,
$userEntity,
random_bytes(32),
$publicKeyCredentialParametersList,
hints: [
PublicKeyCredentialOptions::HINT_SECURITY_KEY,
],
timeout: 60000, // 1 minute
authenticatorSelection: AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
)
);
$attestationStatementSupportManager = AttestationStatementSupportManager::create();
$attestationStatementSupportManager->add(NoneAttestationStatementSupport::create());
$factory = new WebauthnSerializerFactory($attestationStatementSupportManager);
$serializer = $factory->create();
$jsonOptions = $serializer->serialize(
$credentialOptions,
'json',
[
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
JsonEncode::OPTIONS => JSON_THROW_ON_ERROR,
]
);
// TODO - Change
$this->setPageTitle(trans('auth.mfa_gen_hardware_key_title'));
return view('mfa.hardware-key-generate', [
'options' => $jsonOptions,
]);
}
/**
* Confirm the setup of a hardware key, storing the key value against the user.
*
* @throws Exception
*/
public function confirm()
{
return redirect('/mfa/setup');
}
/**
* Verify the MFA method submission on check.
*
* @throws NotFoundException
* @throws ValidationException
*/
public function verify(Request $request, MfaSession $mfaSession, LoginService $loginService)
{
$user = $this->currentOrLastAttemptedUser();
$this->limiter->incrementAttempts($user, $request);
if ($this->limiter->hasHitLimit($user, $request)) {
$this->clearLastAttemptedUser();
$this->limiter->throwException();
}
}
}

View File

@@ -23,13 +23,14 @@ class MfaValue extends Model
const METHOD_TOTP = 'totp';
const METHOD_BACKUP_CODES = 'backup_codes';
const METHOD_HARDWARE_KEY = 'hardware_key';
/**
* Get all the MFA methods available.
*/
public static function allMethods(): array
{
return [self::METHOD_TOTP, self::METHOD_BACKUP_CODES];
return [self::METHOD_TOTP, self::METHOD_BACKUP_CODES, self::METHOD_HARDWARE_KEY];
}
/**

View File

@@ -59,7 +59,8 @@ class CommentController extends Controller
'html' => ['required', 'string'],
]);
$comment = $this->commentRepo->getVisibleById($commentId);
$comment = $this->commentRepo->getById($commentId);
$this->checkOwnablePermission(Permission::PageView, $comment->entity);
$this->checkOwnablePermission(Permission::CommentUpdate, $comment);
$comment = $this->commentRepo->update($comment, $input['html']);
@@ -75,7 +76,8 @@ class CommentController extends Controller
*/
public function archive(int $id)
{
$comment = $this->commentRepo->getVisibleById($id);
$comment = $this->commentRepo->getById($id);
$this->checkOwnablePermission(Permission::PageView, $comment->entity);
if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) {
$this->showPermissionError();
}
@@ -94,7 +96,8 @@ class CommentController extends Controller
*/
public function unarchive(int $id)
{
$comment = $this->commentRepo->getVisibleById($id);
$comment = $this->commentRepo->getById($id);
$this->checkOwnablePermission(Permission::PageView, $comment->entity);
if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) {
$this->showPermissionError();
}
@@ -113,7 +116,7 @@ class CommentController extends Controller
*/
public function destroy(int $id)
{
$comment = $this->commentRepo->getVisibleById($id);
$comment = $this->commentRepo->getById($id);
$this->checkOwnablePermission(Permission::CommentDelete, $comment);
$this->commentRepo->delete($comment);

View File

@@ -3,7 +3,6 @@
namespace BookStack\App\Providers;
use BookStack\Uploads\ImageService;
use BookStack\Util\UrlFilter;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
@@ -22,8 +21,10 @@ class ValidationRuleServiceProvider extends ServiceProvider
Validator::extend('safe_url', function ($attribute, $value, $parameters, $validator) {
$cleanLinkName = strtolower(trim($value));
$filter = new UrlFilter($cleanLinkName);
return $filter->isAllowed();
$isJs = str_starts_with($cleanLinkName, 'javascript:');
$isData = str_starts_with($cleanLinkName, 'data:');
return !$isJs && !$isData;
});
}
}

View File

@@ -7,7 +7,6 @@ use BookStack\Theming\ThemeModule;
use BookStack\Theming\ThemeModuleException;
use BookStack\Theming\ThemeModuleManager;
use BookStack\Theming\ThemeModuleZip;
use BookStack\Util\UrlComparison;
use GuzzleHttp\Psr7\Request;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
@@ -200,6 +199,7 @@ class InstallModuleCommand extends Command
{
$httpRequests = app()->make(HttpRequestService::class);
$client = $httpRequests->buildClient(30, ['stream' => true]);
$originalUrl = parse_url($location);
$currentLocation = $location;
$maxRedirects = 3;
$redirectCount = 0;
@@ -212,11 +212,12 @@ class InstallModuleCommand extends Command
if ($statusCode >= 300 && $statusCode < 400 && $redirectCount < $maxRedirects) {
$redirectLocation = $resp->getHeaderLine('Location');
if ($redirectLocation) {
$comparison = new UrlComparison($location, $redirectLocation);
$redirectOriginMatches = $comparison->originsMatch();
$redirectUrl = parse_url($redirectLocation);
$redirectOriginMatches = ($originalUrl['host'] ?? '') === ($redirectUrl['host'] ?? '')
&& ($originalUrl['scheme'] ?? '') === ($redirectUrl['scheme'] ?? '')
&& ($originalUrl['port'] ?? '') === ($redirectUrl['port'] ?? '');
if (!$redirectOriginMatches) {
$redirectUrl = parse_url($redirectLocation);
$redirectOrigin = ($redirectUrl['scheme'] ?? '') . '://' . ($redirectUrl['host'] ?? '') . (isset($redirectUrl['port']) ? ':' . $redirectUrl['port'] : '');
$this->info("The download URL is redirecting to a different site: {$redirectOrigin}");
$shouldContinue = $this->confirm("Do you trust downloading the module from this site?");

View File

@@ -25,7 +25,6 @@ class Handler extends ExceptionHandler
protected $dontReport = [
NotFoundException::class,
StoppedAuthenticationException::class,
NotifyException::class,
];
/**

View File

@@ -6,22 +6,18 @@ use Exception;
use Illuminate\Contracts\Support\Responsable;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
* An exception that is thrown to notify the user of something which went wrong.
* Typically these should be translated messages since they will be shown to the end user
* via a pop up notification error message in the UI.
*
* This exception is not intended to be used for internal system/application errors,
* and therefore will not be logged by the exception handler.
*/
class NotifyException extends Exception implements Responsable, HttpExceptionInterface
{
public function __construct(
string $message,
public string $redirectLocation = '/',
protected int $status = 500
) {
public $message;
public string $redirectLocation;
protected int $status;
public function __construct(string $message, string $redirectLocation = '/', int $status = 500)
{
$this->message = $message;
$this->redirectLocation = $redirectLocation;
$this->status = $status;
parent::__construct();
}

View File

@@ -290,7 +290,7 @@ class SearchRunner
$query->where('name', '=', $tagParts['name']);
}
if (is_numeric($tagParts['value']) && is_finite($tagParts['value']) && $tagParts['operator'] !== 'like') {
if (is_numeric($tagParts['value']) && $tagParts['operator'] !== 'like') {
// We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
// search the value as a string which prevents being able to do number-based operations
// on the tag values. We ensure it has a numeric value and then cast it just to be sure.

View File

@@ -4,12 +4,37 @@ namespace BookStack\Sorting;
class BookSortMapItem
{
public function __construct(
public int $id,
public int $sort,
public int|null $parentChapterId,
public string $type,
public int $parentBookId,
) {
/**
* @var int
*/
public $id;
/**
* @var int
*/
public $sort;
/**
* @var ?int
*/
public $parentChapterId;
/**
* @var string
*/
public $type;
/**
* @var int
*/
public $parentBookId;
public function __construct(int $id, int $sort, ?int $parentChapterId, string $type, int $parentBookId)
{
$this->id = $id;
$this->sort = $sort;
$this->parentChapterId = $parentChapterId;
$this->type = $type;
$this->parentBookId = $parentBookId;
}
}

View File

@@ -168,7 +168,7 @@ class BookSorter
$model->priority = $sortMapItem->sort;
}
if ($priorityChanged || $chapterChanged) {
if ($chapterChanged || $priorityChanged) {
$model::withoutTimestamps(fn () => $model->save());
}
}

View File

@@ -10,7 +10,6 @@ use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\HasCreatorAndUpdater;
use BookStack\Users\Models\OwnableInterface;
use BookStack\Users\Models\User;
use BookStack\Util\UrlFilter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -72,7 +71,7 @@ class Attachment extends Model implements OwnableInterface
public function getUrl($openInline = false): string
{
if ($this->external && !str_starts_with($this->path, 'http')) {
return (new UrlFilter($this->path))->clean();
return $this->path;
}
return url('/attachments/' . $this->id . ($openInline ? '?open=true' : ''));

View File

@@ -11,7 +11,6 @@ use BookStack\Http\Controller;
use BookStack\Permissions\Permission;
use BookStack\Uploads\Attachment;
use BookStack\Uploads\AttachmentService;
use BookStack\Util\UrlFilter;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Http\Request;
@@ -108,9 +107,6 @@ class AttachmentController extends Controller
{
/** @var Attachment $attachment */
$attachment = Attachment::query()->findOrFail($attachmentId);
$this->checkOwnablePermission(Permission::PageView, $attachment->page);
$this->checkOwnablePermission(Permission::PageUpdate, $attachment->page);
$this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment);
try {
$this->validate($request, [
@@ -124,6 +120,10 @@ class AttachmentController extends Controller
]), 422);
}
$this->checkOwnablePermission(Permission::PageView, $attachment->page);
$this->checkOwnablePermission(Permission::PageUpdate, $attachment->page);
$this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment);
$attachment = $this->attachmentService->updateFile($attachment, [
'name' => $request->input('attachment_edit_name'),
'link' => $request->input('attachment_edit_url'),
@@ -142,10 +142,6 @@ class AttachmentController extends Controller
public function attachLink(Request $request)
{
$pageId = $request->input('attachment_link_uploaded_to');
$page = $this->pageQueries->findVisibleByIdOrFail($pageId);
$this->checkPermission(Permission::AttachmentCreateAll);
$this->checkOwnablePermission(Permission::PageUpdate, $page);
try {
$this->validate($request, [
@@ -160,6 +156,11 @@ class AttachmentController extends Controller
]), 422);
}
$page = $this->pageQueries->findVisibleByIdOrFail($pageId);
$this->checkPermission(Permission::AttachmentCreateAll);
$this->checkOwnablePermission(Permission::PageUpdate, $page);
$attachmentName = $request->input('attachment_link_name');
$link = $request->input('attachment_link_url');
$this->attachmentService->saveNewFromLink($attachmentName, $link, intval($pageId));
@@ -222,8 +223,7 @@ class AttachmentController extends Controller
}
if ($attachment->external) {
$url = (new UrlFilter($attachment->path))->clean();
return redirect($url);
return redirect($attachment->path);
}
$fileName = $attachment->getFileName();

View File

@@ -1,16 +1,13 @@
<?php
namespace BookStack\Util\HtmlPurifier;
namespace BookStack\Util;
use BookStack\App\AppVersion;
use BookStack\Util\HtmlPurifier\Filters\UriLimitFileProtocolToAnchors;
use BookStack\Util\UrlFilter;
use HTMLPurifier;
use HTMLPurifier_Config;
use HTMLPurifier_DefinitionCache_Serializer;
use HTMLPurifier_HTML5Config;
use HTMLPurifier_HTMLDefinition;
use HTMLPurifier_URIDefinition;
/**
* Provides a configured HTML Purifier instance.
@@ -36,12 +33,7 @@ class ConfiguredHtmlPurifier
$htmlDef = $config->getDefinition('HTML', true, true);
if ($htmlDef instanceof HTMLPurifier_HTMLDefinition) {
$this->configureHtmlDefinition($htmlDef);
}
$uriDef = $config->getDefinition('URI', true, true);
if ($uriDef instanceof HTMLPurifier_URIDefinition) {
$this->configureUriDefinition($uriDef);
$this->configureDefinition($htmlDef);
}
$this->purifier = new HTMLPurifier($config);
@@ -85,18 +77,21 @@ class ConfiguredHtmlPurifier
$config->set('Attr.ID.HTML5', true);
$config->set('Output.FixInnerHTML', false);
$config->set('URI.SafeIframeRegexp', '%^(http://|https://|//)%');
$allowedSchemes = UrlFilter::getAllowedSchemes();
$allowedSchemesSetting = [];
foreach ($allowedSchemes as $scheme) {
$allowedSchemesSetting[$scheme] = true;
}
$config->set('URI.AllowedSchemes', $allowedSchemesSetting);
$config->set('URI.AllowedSchemes', [
'http' => true,
'https' => true,
'mailto' => true,
'ftp' => true,
'nntp' => true,
'news' => true,
'tel' => true,
'file' => true,
]);
// $config->set('Cache.DefinitionImpl', null); // Disable cache during testing
}
protected function configureHtmlDefinition(HTMLPurifier_HTMLDefinition $definition): void
public function configureDefinition(HTMLPurifier_HTMLDefinition $definition): void
{
// Allow the object element
$definition->addElement(
@@ -154,15 +149,6 @@ class ConfiguredHtmlPurifier
// Allow mention-ids on links
$definition->addAttribute('a', 'data-mention-user-id', 'Number');
// Set up custom handler for srcset to limit accepted types
$definition->addAttribute('img', 'srcset', new SrcsetAttrDef());
$definition->addAttribute('source', 'srcset', new SrcsetAttrDef());
}
protected function configureUriDefinition(HTMLPurifier_URIDefinition $definition): void
{
$definition->registerFilter(new UriLimitFileProtocolToAnchors());
}
public function purify(string $html): string

View File

@@ -2,7 +2,6 @@
namespace BookStack\Util;
use BookStack\Util\HtmlPurifier\ConfiguredHtmlPurifier;
use DOMAttr;
use DOMElement;
use DOMNodeList;

View File

@@ -1,53 +0,0 @@
<?php
namespace BookStack\Util\HtmlPurifier\Filters;
use HTMLPurifier_Config;
use HTMLPurifier_Context;
use HTMLPurifier_URI;
use HTMLPurifier_URIFilter;
/**
* Limits file:// URIs to only be used on anchor tags href attributes.
* This prevents use on iframes/embeds/images where they can be used to load external
* content on the network, triggering calls which may include NTLM auth hashes when in
* certain windows based environments.
*/
class UriLimitFileProtocolToAnchors extends HTMLPurifier_URIFilter
{
/**
* @type string
*/
public $name = 'LimitFileProtocolToAnchors';
/**
* @type bool
*/
public $always_load = true;
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function filter(&$uri, $config, $context)
{
// Ensure we're only filtering file:// URIs'
if ($uri->scheme !== 'file') {
return true;
}
$token = $context->get('CurrentToken', true);
$attr = $context->get('CurrentAttr', true);
// Only allow if used on hrefs on anchor tags
$isAnchor = $token && $token->name === 'a';
$isHref = $attr === 'href';
if ($isAnchor && $isHref) {
return true;
}
return false;
}
}

View File

@@ -1,26 +0,0 @@
<?php
namespace BookStack\Util\HtmlPurifier;
use HTMLPurifier_AttrDef;
/**
* Custom attribute definition to filter out potentially dangerous
* values from the srcset attribute.
*/
class SrcsetAttrDef extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context)
{
$lower = strtolower($string);
$nonAllowed = ['javascript:', 'vbscript:', 'data:', 'file:'];
foreach ($nonAllowed as $nonAllowedString) {
if (str_contains($lower, $nonAllowedString)) {
return false;
}
}
return $string;
}
}

View File

@@ -1,36 +0,0 @@
<?php
namespace BookStack\Util;
class UrlComparison
{
public function __construct(
protected readonly string $a,
protected readonly string $b,
) {
}
/**
* Check if the two URLs have the same origin.
*/
public function originsMatch(): bool
{
$aParts = parse_url($this->a);
$bParts = parse_url($this->b);
return ($aParts['host'] ?? '') === ($bParts['host'] ?? '')
&& ($aParts['scheme'] ?? '') === ($bParts['scheme'] ?? '')
&& ($aParts['port'] ?? '') === ($bParts['port'] ?? '');
}
/**
* Check if there's some overlap between the two URLs' paths.
*/
public function pathsOverlap(): bool
{
$aPath = parse_url($this->a, PHP_URL_PATH) ?? '';
$bPath = parse_url($this->b, PHP_URL_PATH) ?? '';
return str_starts_with($aPath, $bPath) || str_starts_with($bPath, $aPath);
}
}

View File

@@ -1,103 +0,0 @@
<?php
declare(strict_types=1);
namespace BookStack\Util;
/**
* Helps filter URLs to prevent use of undesired schemes.
* Also parses and rebuilds the URL to ensure it's valid.
*/
class UrlFilter
{
protected static array $allowedSchemes = ['http', 'https', 'mailto', 'tel', 'file', 'ftp', 'nntp', 'news'];
protected string $url;
public function __construct(string $url)
{
$this->url = trim($url);
}
/**
* Check if the URL is allowed to be generally used as a link
* in the application. This does not assure the original URL string
* provided is safe as-is. Ensure you use the clean method to produce
* a URL that is considered safe to use.
*/
public function isAllowed(): bool
{
$urlParts = parse_url($this->url);
if (!$urlParts) {
return false;
}
// Extra check to help avoid scenarios where non-standard characters are used in the scheme
// to work around parse_url handling with URLs which may be interpreted by the browser differently.
if (str_contains($this->url, ':') && !preg_match('/^[a-z]+:/i', $this->url)) {
return false;
}
if (isset($urlParts['scheme'])) {
return in_array(strtolower($urlParts['scheme']), self::$allowedSchemes);
}
return true;
}
/**
* Clean the URL to ensure it's valid and only uses the allowed schemes.
* If the URL is not allowed, return a placeholder.
*/
public function clean(): string
{
if (!$this->isAllowed()) {
return '#badlink';
}
$urlParts = parse_url($this->url);
if (!$urlParts) {
return '#badlink';
}
$url = '';
if (isset($urlParts['scheme']) || isset($urlParts['host'])) {
$scheme = strtolower($urlParts['scheme'] ?? 'https');
$url = $scheme . ':' . (isset($urlParts['host']) ? '//' : '');
}
if (isset($urlParts['user']) || isset($urlParts['pass'])) {
$url .= $urlParts['user'] ?? '';
if (isset($urlParts['pass'])) {
$url .= ':' . $urlParts['pass'];
}
$url .= '@';
}
if (isset($urlParts['host'])) {
$url .= $urlParts['host'];
}
if (isset($urlParts['port'])) {
$url .= ':' . $urlParts['port'];
}
if (isset($urlParts['path'])) {
$url .= $urlParts['path'];
}
if (isset($urlParts['query'])) {
$url .= '?' . $urlParts['query'];
}
if (isset($urlParts['fragment'])) {
$url .= '#' . $urlParts['fragment'];
}
return $url;
}
/**
* Get schemes that are allowed to be used in content links.
*/
public static function getAllowedSchemes(): array
{
return self::$allowedSchemes;
}
}

View File

@@ -40,6 +40,7 @@
"socialiteproviders/okta": "^4.2",
"socialiteproviders/twitch": "^5.3",
"ssddanbrown/htmldiff": "^2.0.0",
"web-auth/webauthn-lib": "^5.3",
"xemlock/htmlpurifier-html5": "^0.1.12"
},
"require-dev": {

1497
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
1021b927781747de28641b5135ae31befef7c9aedc82de9178d6d412b1327d97
22e02ee72d21ff719c1073abbec8302f8e2096ba6d072e133051064ed24b45b1

View File

@@ -482,8 +482,8 @@ electron-to-chromium
License: ISC
License File: node_modules/electron-to-chromium/LICENSE
Copyright: Copyright 2018 Kilian Valkhof
Source: git+https://github.com/Kilian/electron-to-chromium.git
Link: git+https://github.com/Kilian/electron-to-chromium.git
Source: git+https://github.com/kilian/electron-to-chromium.git
Link: git+https://github.com/kilian/electron-to-chromium.git
-----------
emittery
License: MIT
@@ -860,7 +860,7 @@ License: MIT
License File: node_modules/handlebars/LICENSE
Copyright: Copyright (C) 2011-2019 by Yehuda Katz
Source: https://github.com/handlebars-lang/handlebars.js.git
Link: https://handlebarsjs.com/
Link: https://www.handlebarsjs.com/
-----------
has-bigints
License: MIT
@@ -1663,6 +1663,13 @@ Copyright: Copyright (c) Stephen Sugden <**@*************.***> (stephensugden.co
Source: grncdr/merge-stream
Link: grncdr/merge-stream
-----------
micromatch
License: MIT
License File: node_modules/micromatch/LICENSE
Copyright: Copyright (c) 2014-present, Jon Schlinkert.
Source: micromatch/micromatch
Link: https://github.com/micromatch/micromatch
-----------
mimic-fn
License: MIT
License File: node_modules/mimic-fn/license
@@ -3567,6 +3574,13 @@ Copyright: Copyright (c) 2017-present Devon Govett
Source: https://github.com/parcel-bundler/watcher.git
Link: https://github.com/parcel-bundler/watcher.git
-----------
@parcel/watcher-linux-x64-musl
License: MIT
License File: node_modules/@parcel/watcher-linux-x64-musl/LICENSE
Copyright: Copyright (c) 2017-present Devon Govett
Source: https://github.com/parcel-bundler/watcher.git
Link: https://github.com/parcel-bundler/watcher.git
-----------
@parcel/watcher
License: MIT
License File: node_modules/@parcel/watcher/LICENSE
@@ -3589,8 +3603,8 @@ Link: https://github.com/un-ts/pkgr/blob/master/packages/core
License: MIT
License File: node_modules/@sinclair/typebox/license
Copyright: Copyright (c) 2017-2026 Haydn Paterson
Source: https://github.com/sinclairzx81/sinclair-typebox
Link: https://github.com/sinclairzx81/sinclair-typebox
Source: https://github.com/sinclairzx81/typebox-legacy
Link: https://github.com/sinclairzx81/typebox-legacy
-----------
@sinonjs/commons
License: BSD-3-Clause
@@ -3803,3 +3817,8 @@ Link: https://github.com/ungap/structured-clone#readme
License: MIT
Source: git+https://github.com/unrs/unrs-resolver.git
Link: https://github.com/unrs/unrs-resolver#readme
-----------
@unrs/resolver-binding-linux-x64-musl
License: MIT
Source: git+https://github.com/unrs/unrs-resolver.git
Link: https://github.com/unrs/unrs-resolver#readme

View File

@@ -110,7 +110,7 @@ License: BSD-3-Clause
License File: vendor/firebase/php-jwt/LICENSE
Copyright: Copyright (c) 2011, Neuman Vong
Source: https://github.com/googleapis/php-jwt.git
Link: https://github.com/googleapis/php-jwt
Link: https://github.com/firebase/php-jwt
-----------
fruitcake/php-cors
License: MIT

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -99,8 +99,8 @@ return [
'user_update_notification' => 'Uživatel byl úspěšně aktualizován',
'user_delete' => 'odstranil uživatele',
'user_delete_notification' => 'Uživatel byl úspěšně odstraněn',
'user_mfa_reset' => 'obnovit vícefaktorové ověření pro uživatele',
'user_mfa_reset_notification' => 'Obnovení metod vícefaktorového ověřování',
'user_mfa_reset' => 'reset MFA for user',
'user_mfa_reset_notification' => 'Multi-factor authentication methods reset',
// API Tokens
'api_token_create' => 'API token byl vytvořen',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'Neplatné přihlašovací údaje.',
'throttle' => 'Příliš mnoho pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.',
'mfa_throttle' => '{0}Příliš mnoho pokusů o vícefázové ověření. Zkuste to prosím znovu za :seconds sekund.|{1}Příliš mnoho pokusů o vícefázové ověření. Zkuste to prosím znovu za :seconds sekundu.|[2,4]Příliš mnoho pokusů o vícefázové ověření. Zkuste to prosím znovu za :seconds sekundy.|[5,*]Příliš mnoho pokusů o vícefázové ověření. Zkuste to prosím znovu za :seconds sekund.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Registrace',

View File

@@ -173,7 +173,7 @@ return [
'books_sort_desc' => 'Pro přeuspořádání obsahu přesuňte kapitoly a stránky v knize. Mohou být přidány další knihy, které umožní snadný přesun kapitol a stránek mezi knihami. Volitelně lze nastavit pravidlo automatického řazení, aby se při změnách automaticky seřadil obsah této knihy.',
'books_sort_auto_sort' => 'Možnost automatického řazení',
'books_sort_auto_sort_active' => 'Aktivní automatické řazení: :sortName',
'books_sort_auto_sort_creation_hint' => 'Pravidla řazení mohou být vytvořena v nastavení "Seznamy a řazení" uživatelem s příslušnými oprávněními.',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'Seřadit knihu :bookName',
'books_sort_name' => 'Seřadit podle názvu',
'books_sort_created' => 'Seřadit podle data vytvoření',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Skrýt/Zobrazit postranní panel',
'page_contents' => 'Obsah stránky',
'page_contents_none' => 'Na stránce nejsou žádné nadpisy.',
'page_contents_info' => 'Obsah se generuje ze všech použitých nadpisů na stránce.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Štítky stránky',
'chapter_tags' => 'Štítky kapitoly',
'book_tags' => 'Štítky knihy',

View File

@@ -207,7 +207,7 @@ return [
'role_all' => 'Vše',
'role_own' => 'Vlastní',
'role_controlled_by_asset' => 'Řídí se obsahem, do kterého jsou nahrávány',
'role_controlled_by_page_delete' => 'Řídí se právem k odstranění stránky',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => 'Uložit roli',
'role_users' => 'Uživatelé mající tuto roli',
'role_users_none' => 'Žádný uživatel nemá tuto roli',
@@ -260,13 +260,13 @@ return [
'users_api_tokens_create' => 'Vytvořit Token',
'users_api_tokens_expires' => 'Vyprší',
'users_api_tokens_docs' => 'Dokumentace API',
'users_mfa' => 'Vícefaktorové ověření',
'users_mfa' => 'Vícefázové ověření',
'users_mfa_desc' => 'Nastavit vícefaktorové ověřování jako další vrstvu zabezpečení vašeho uživatelského účtu.',
'users_mfa_x_methods' => ':count nastavená metoda|:count nastavených metod',
'users_mfa_configure' => 'Konfigurovat metody',
'users_mfa_reset' => 'Obnova metod vícefaktorového ověření',
'users_mfa_reset_desc' => 'Tímto se obnoví a vymažou všechny nakonfigurované metody vícefaktorového ověřování pro tohoto uživatele. Pokud některá z jeho rolí vyžaduje vícefaktorové ověřování, bude při příštím přihlášení vyzván k nastavení nových metod.',
'users_mfa_reset_confirm' => 'Opravdu chcete obnovit vícefaktorové ověřování pro tohoto uživatele?',
'users_mfa_reset' => 'Reset Multi-Factor Authentication Methods',
'users_mfa_reset_desc' => 'This will reset and clear all configured multi-factor authentication methods for this user. If multi-factor authentication is required by any of their roles, they\'ll be prompted to configure new methods on their next login.',
'users_mfa_reset_confirm' => 'Are you sure you want to reset multi-factor authentication for this user?',
// API Tokens
'user_api_token_create' => 'Vytvořit API Token',
@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'De indtastede brugeroplysninger stemmer ikke overens med vores registreringer.',
'throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds sekunder.',
'mfa_throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds sekunder.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Registrer',

View File

@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Sidebjælke til/fra',
'page_contents' => 'Sideindhold',
'page_contents_none' => 'Ingen overskrifter blev fundet i sidens indhold.',
'page_contents_info' => 'Indholdsmenuen er genereret fra alle kursformater, der bruges på siden.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Sidetags',
'chapter_tags' => 'Kapiteltags',
'book_tags' => 'Bogtags',

View File

@@ -366,9 +366,8 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'Thailandsk',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',
'uk' => 'Українська',
'uz' => 'Ozbekcha',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'Diese Anmeldedaten stimmen nicht mit unseren Aufzeichnungen überein.',
'throttle' => 'Zu viele Anmeldeversuche. Bitte versuchen Sie es in :seconds Sekunden erneut.',
'mfa_throttle' => 'Zu viele Multi-Faktor-Verifizierungsversuche. Bitte versuchen Sie es in :seconds Sekunden erneut.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Registrieren',

View File

@@ -173,7 +173,7 @@ return [
'books_sort_desc' => 'Verschieben Sie Kapitel und Seiten innerhalb eines Buches, um dessen Inhalt neu zu ordnen. Es können weitere Bücher hinzugefügt werden, wodurch Kapitel und Seiten problemlos zwischen den Büchern verschoben werden können. Optional kann eine automatische Sortierregel festgelegt werden, um den Inhalt dieses Buches bei Änderungen automatisch zu sortieren.',
'books_sort_auto_sort' => 'Automatische Sortierfunktionsoption',
'books_sort_auto_sort_active' => 'Automatische Sortierung aktiv: :sortName',
'books_sort_auto_sort_creation_hint' => 'Regeln für die automatische Sortierung können von einem Benutzer mit den entsprechenden Berechtigungen im Einstellungsbereich "Listen & Sortieren" erstellt werden.',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'Buch ":bookName" sortieren',
'books_sort_name' => 'Sortieren nach Namen',
'books_sort_created' => 'Sortieren nach Erstellungsdatum',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Seitenleiste umschalten',
'page_contents' => 'Seiteninhalt',
'page_contents_none' => 'Es wurden keine Überschriften im Seiteninhalt gefunden.',
'page_contents_info' => 'Das Inhaltsmenü wird aus allen auf der Seite verwendeten Überschriften generiert.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Seiten-Schlagwörter',
'chapter_tags' => 'Kapitel-Schlagwörter',
'book_tags' => 'Buch-Schlagwörter',

View File

@@ -207,7 +207,7 @@ return [
'role_all' => 'Alle',
'role_own' => 'Eigene',
'role_controlled_by_asset' => 'Abhängig von dem Asset, in das sie hochgeladen werden',
'role_controlled_by_page_delete' => 'Kontrolliert durch die Berechtigung zum Löschen einer Seite',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => 'Rolle speichern',
'role_users' => 'Dieser Rolle zugeordnete Benutzer',
'role_users_none' => 'Derzeit sind diesem Rollentyp keine Benutzer zugewiesen',
@@ -265,7 +265,7 @@ return [
'users_mfa_x_methods' => ':count Methode konfiguriert|:count Methoden konfiguriert',
'users_mfa_configure' => 'Methoden konfigurieren',
'users_mfa_reset' => 'Setze Multifaktor-Authentifizierung zurück',
'users_mfa_reset_desc' => 'Dies wird alle konfigurierten Multifaktor-Authentifizierungsmethoden für diesen Nutzer zurücksetzen. Falls Multifaktor-Authentifizierung für eine seiner Rollen erforderlich ist, wird der Nutzer aufgefordert, neue Methoden beim nächsten Login zu konfigurieren.',
'users_mfa_reset_desc' => 'Dies wird alle konfigurierten Multifaktor-Authentifizierungsmethoden für diesen Nutzer zurücksetzen. Falls Multifaktor-Authentifizierung für eine seiner Rollen erforderlich ist, werden sie aufgefordert, neue Methoden beim nächsten Login zu konfigurieren.',
'users_mfa_reset_confirm' => 'Sind Sie sicher, dass Sie diese Multi-Faktor-Authentifizierungsmethode für diesen Nutzer zurücksetzen möchten?',
// API Tokens
@@ -366,7 +366,6 @@ return [
'ru' => 'Russisch',
'sk' => 'Slowenisch',
'sl' => 'Slowenisch',
'sr' => 'Српски',
'sv' => 'Schwedisch',
'th' => 'ภาษาไทย',
'tr' => 'Türkisch',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'Die eingegebenen Anmeldedaten sind ungültig.',
'throttle' => 'Zu viele Anmeldeversuche. Bitte versuche es in :seconds Sekunden erneut.',
'mfa_throttle' => 'Zu viele Multi-Faktor-Verifizierungsversuche. Bitte versuche es in :seconds Sekunden erneut.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Registrieren',

View File

@@ -173,7 +173,7 @@ return [
'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt zu reorganisieren. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert. Optional kann eine automatische Sortierregel erstellt werden, um den Inhalt dieses Buches nach Änderungen automatisch zu sortieren.',
'books_sort_auto_sort' => 'Auto-Sortieroption',
'books_sort_auto_sort_active' => 'Automatische Sortierung aktiv: :sortName',
'books_sort_auto_sort_creation_hint' => 'Regeln für die automatische Sortierung können von einem Benutzer mit den entsprechenden Berechtigungen im Einstellungsbereich "Listen & Sortieren" erstellt werden.',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'Buch ":bookName" sortieren',
'books_sort_name' => 'Sortieren nach Namen',
'books_sort_created' => 'Sortieren nach Erstellungsdatum',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Seitenleiste umschalten',
'page_contents' => 'Seiteninhalt',
'page_contents_none' => 'Es wurden keine Überschriften im Seiteninhalt gefunden.',
'page_contents_info' => 'Das Inhaltsmenü wird aus allen auf der Seite verwendeten Überschriften generiert.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Seiten-Schlagwörter',
'chapter_tags' => 'Kapitel-Schlagwörter',
'book_tags' => 'Buch-Schlagwörter',

View File

@@ -208,7 +208,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung
'role_all' => 'Alle',
'role_own' => 'Eigene',
'role_controlled_by_asset' => 'Berechtigungen werden vom Uploadziel bestimmt',
'role_controlled_by_page_delete' => 'Kontrolliert durch die Berechtigung zum Löschen einer Seite',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => 'Rolle speichern',
'role_users' => 'Dieser Rolle zugeordnete Benutzer',
'role_users_none' => 'Bisher sind dieser Rolle keine Benutzer zugeordnet',
@@ -266,8 +266,8 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung
'users_mfa_x_methods' => ':count Methode konfiguriert|:count Methoden konfiguriert',
'users_mfa_configure' => 'Methoden konfigurieren',
'users_mfa_reset' => 'Setze Multifaktor-Authentifizierung zurück',
'users_mfa_reset_desc' => 'Dies wird alle konfigurierten Multifaktor-Authentifizierungsmethoden für diesen Nutzer zurücksetzen. Falls Multifaktor-Authentifizierung für eine seiner Rollen erforderlich ist, wird der Nutzer aufgefordert, neue Methoden beim nächsten Login zu konfigurieren.',
'users_mfa_reset_confirm' => 'Bist du sicher, dass du diese Multi-Faktor-Authentifizierungsmethode für diesen Nutzer zurücksetzen möchtest?',
'users_mfa_reset_desc' => 'Dies wird alle konfigurierten Multifaktor-Authentifizierungsmethoden für diesen Nutzer zurücksetzen. Falls Multifaktor-Authentifizierung für eine seiner Rollen erforderlich ist, werden sie aufgefordert, neue Methoden beim nächsten Login zu konfigurieren.',
'users_mfa_reset_confirm' => 'Sind Sie sicher, dass Sie diese Multi-Faktor-Authentifizierungsmethode für diesen Nutzer zurücksetzen möchten?',
// API Tokens
'user_api_token_create' => 'Neuen API-Token erstellen',
@@ -367,7 +367,6 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung
'ru' => 'Russisch',
'sk' => 'Slowenisch',
'sl' => 'Slowenisch',
'sr' => 'Српски',
'sv' => 'Schwedisch',
'th' => 'ภาษาไทย',
'tr' => 'Türkisch',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Ruso',
'sk' => 'Eslovaco',
'sl' => 'Esloveno',
'sr' => 'Српски',
'sv' => 'Sueco',
'th' => 'ภาษาไทย',
'tr' => 'Turco',

View File

@@ -367,7 +367,6 @@ return [
'ru' => 'Ruso',
'sk' => 'Eslovaco',
'sl' => 'Esloveno',
'sr' => 'Српски',
'sv' => 'Sueco',
'th' => 'ภาษาไทย',
'tr' => 'Turco',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский (vene keel)',
'sk' => 'Slovensky',
'sl' => 'Sloveenia',
'sr' => 'Српски',
'sv' => 'Rootsi',
'th' => 'ภาษาไทย',
'tr' => 'Türgi',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Russe',
'sk' => 'Slovaque',
'sl' => 'Slovène',
'sr' => 'Српски',
'sv' => 'Suédois',
'th' => 'ภาษาไทย',
'tr' => 'Turc',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,9 +366,8 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'Thai',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',
'uk' => 'Українська',
'uz' => 'Ozbekcha',

View File

@@ -99,7 +99,7 @@ return [
'user_update_notification' => 'Pengguna berhasil diperbarui',
'user_delete' => 'pengguna yang dihapus',
'user_delete_notification' => 'Pengguna berhasil dihapus',
'user_mfa_reset' => 'atur ulang MFA untuk pengguna',
'user_mfa_reset' => 'reset MFA for user',
'user_mfa_reset_notification' => 'Multi-factor authentication methods reset',
// API Tokens

View File

@@ -8,8 +8,8 @@
return [
// General editor terms
'general' => 'Umum',
'advanced' => 'Tingkat lanjut',
'none' => 'Tidak Satupun',
'advanced' => 'Lanjutan',
'none' => 'Tidak Ada',
'cancel' => 'Batal',
'save' => 'Simpan',
'close' => 'Tutup',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,9 +366,8 @@ return [
'ru' => 'Russo',
'sk' => 'Sloveno',
'sl' => 'Sloveno',
'sr' => 'Српски',
'sv' => 'Svedese',
'th' => 'Thailandese',
'th' => 'ภาษาไทย',
'tr' => 'Turco',
'uk' => 'Ucraino',
'uz' => 'Ozbekcha',

View File

@@ -173,7 +173,7 @@ return [
'books_sort_desc' => 'ブック内のチャプタおよびページを移動して内容を再編成できます。他のブックを並べて、ブック間でチャプタやページを簡単に移動することもできます。オプションで自動ソートルールを設定すると、変更時にブックの内容を自動的にソートすることができます。',
'books_sort_auto_sort' => '自動ソートオプション',
'books_sort_auto_sort_active' => '自動ソート有効: :sortName',
'books_sort_auto_sort_creation_hint' => '自動ソートオプションのルールは、関連する権限を持ったユーザーによって設定の「一覧とソート」エリアで作成できます。',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'ブック「:bookName」を並べ替え',
'books_sort_name' => '名前で並べ替え',
'books_sort_created' => '作成日で並べ替え',

View File

@@ -207,7 +207,7 @@ return [
'role_all' => '全て',
'role_own' => '自身',
'role_controlled_by_asset' => 'このアセットに対し、右記の操作を許可:',
'role_controlled_by_page_delete' => 'ページ削除権限を適用',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => '役割を保存',
'role_users' => 'この役割を持つユーザー',
'role_users_none' => 'この役割が付与されたユーザーはいません',
@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -331,7 +331,7 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Zijbalk Tonen/Verbergen',
'page_contents' => 'Pagina Inhoud',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Pagina Labels',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский (Russisch)',
'sk' => 'Slovensky (Slowaaks)',
'sl' => 'Slovenščina (Sloveens)',
'sr' => 'Српски',
'sv' => 'Svenska (Zweeds)',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe (Turks)',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -99,8 +99,8 @@ return [
'user_update_notification' => 'Użytkownik zaktualizowany pomyślnie',
'user_delete' => 'usunięto użytkownika',
'user_delete_notification' => 'Użytkownik pomyślnie usunięty',
'user_mfa_reset' => 'zresetuj MFA dla użytkownika',
'user_mfa_reset_notification' => 'Przywracanie metod uwierzytelniania wieloskładnikowego',
'user_mfa_reset' => 'reset MFA for user',
'user_mfa_reset_notification' => 'Multi-factor authentication methods reset',
// API Tokens
'api_token_create' => 'utworzono token API',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'Wprowadzone poświadczenia są nieprawidłowe.',
'throttle' => 'Zbyt wiele prób logowania. Spróbuj ponownie za :seconds s.',
'mfa_throttle' => 'Zbyt wiele prób weryfikacji wieloskładnikowej. Spróbuj ponownie za :seconds sekund.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Zarejestruj się',

View File

@@ -170,10 +170,10 @@ return [
'books_search_this' => 'Wyszukaj w tej książce',
'books_navigation' => 'Nawigacja po książce',
'books_sort' => 'Sortuj zawartość książki',
'books_sort_desc' => 'Przenieś rozdziały i strony w książce w celu reorganizacji jej treści. Można dodać inne książki, które umożliwiają łatwe przenoszenie rozdziałów i stron między książkami. Opcjonalnie reguła automatycznego sortowania może być ustawiona, aby automatycznie sortować zawartość tej książki po jej zmianach.',
'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.',
'books_sort_auto_sort' => 'Opcja automatycznego sortowania',
'books_sort_auto_sort_active' => 'Automatyczne sortowanie aktywne: :sortName',
'books_sort_auto_sort_creation_hint' => 'Reguły opcji automatycznego sortowania mogą być tworzone w obszarze ustawień "Listy i sortowanie" przez użytkownika z odpowiednimi uprawnieniami.',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'Sortuj książkę :bookName',
'books_sort_name' => 'Sortuj według nazwy',
'books_sort_created' => 'Sortuj według daty utworzenia',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Przełącz pasek boczny',
'page_contents' => 'Zawartość strony',
'page_contents_none' => 'Nie znaleziono nagłówków w treści strony.',
'page_contents_info' => 'Menu zawartości jest generowane z dowolnych formatów nagłówków używanych na stronie.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Tagi strony',
'chapter_tags' => 'Tagi rozdziału',
'book_tags' => 'Tagi książki',

View File

@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Podany sekret dla tego API jest nieprawidłowy',
'api_user_no_api_permission' => 'Właściciel używanego tokenu API nie ma uprawnień do wykonywania zapytań do API',
'api_user_token_expired' => 'Token uwierzytelniania wygasł',
'api_cookie_auth_only_get' => 'Tylko żądania GET są dozwolone podczas korzystania z API z uwierzytelniania opartego na plikach cookie',
'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Błąd podczas wysyłania testowej wiadomości e-mail:',

View File

@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Rozdziały na początku',
'sort_rule_op_chapters_last' => 'Rozdziały na końcu',
'sorting_page_limits' => 'Limity wyświetlania per strona',
'sorting_page_limits_desc' => 'Ustaw ile elementów pokazywać na stronie w różnych listach w systemie. Zazwyczaj mniejsza ilość będzie bardziej wydajna, podczas gdy większa ilość unika konieczności kliknięcia na wiele stron. Zaleca się stosowanie wielokrotności 6 razy.',
'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Konserwacja',
@@ -207,7 +207,7 @@ return [
'role_all' => 'Wszyscy',
'role_own' => 'Własne',
'role_controlled_by_asset' => 'Kontrolowane przez zasób, do którego zostały udostępnione',
'role_controlled_by_page_delete' => 'Kontrolowane przez uprawnienia do usuwania stron',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => 'Zapisz rolę',
'role_users' => 'Użytkownicy w tej roli',
'role_users_none' => 'Brak użytkowników zapisanych do tej roli',
@@ -264,9 +264,9 @@ return [
'users_mfa_desc' => 'Skonfiguruj uwierzytelnianie wieloskładnikowe jako dodatkową warstwę bezpieczeństwa dla swojego konta użytkownika.',
'users_mfa_x_methods' => ':count metoda skonfigurowana|:count metody skonfigurowane',
'users_mfa_configure' => 'Konfiguruj metody',
'users_mfa_reset' => 'Resetuj metody uwierzytelniania wieloetapowego',
'users_mfa_reset_desc' => 'Spowoduje to zresetowanie i wyczyszczenie wszystkich skonfigurowanych metod uwierzytelniania wieloetapowego dla tego użytkownika. Jeśli uwierzytelnianie wieloetapowe jest wymagane przez dowolną z ich ról, zostaną poproszone o skonfigurowanie nowych metod przy następnym logowaniu.',
'users_mfa_reset_confirm' => 'Czy na pewno chcesz zresetować uwierzytelnianie wieloskładnikowe dla tego użytkownika?',
'users_mfa_reset' => 'Reset Multi-Factor Authentication Methods',
'users_mfa_reset_desc' => 'This will reset and clear all configured multi-factor authentication methods for this user. If multi-factor authentication is required by any of their roles, they\'ll be prompted to configure new methods on their next login.',
'users_mfa_reset_confirm' => 'Are you sure you want to reset multi-factor authentication for this user?',
// API Tokens
'user_api_token_create' => 'Utwórz klucz API',
@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -99,8 +99,8 @@ return [
'user_update_notification' => 'Utilizador atualizado com sucesso',
'user_delete' => 'utilizador eliminado',
'user_delete_notification' => 'Utilizador removido com sucesso',
'user_mfa_reset' => 'reiniciar a MFA para o utilizador',
'user_mfa_reset_notification' => 'Reinicialização dos métodos de autenticação multifatorial',
'user_mfa_reset' => 'reset MFA for user',
'user_mfa_reset_notification' => 'Multi-factor authentication methods reset',
// API Tokens
'api_token_create' => 'token API criado',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'Estas credenciais não coincidem com os nossos registos.',
'throttle' => 'Demasiadas tentativas de acesso. Tente novamente em :seconds segundos.',
'mfa_throttle' => 'Foram efetuadas demasiadas tentativas de verificação multifatorial. Por favor, tente novamente daqui a :seconds segundos.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Registar',

View File

@@ -48,7 +48,7 @@ return [
'superscript' => 'Superior à linha',
'subscript' => 'Inferior à linha',
'text_color' => 'Cor do texto',
'highlight_color' => 'Cor de destaque',
'highlight_color' => 'Highlight color',
'custom_color' => 'Cor personalizada',
'remove_color' => 'Remover cor',
'background_color' => 'Cor de fundo',
@@ -83,9 +83,9 @@ return [
'table_properties' => 'Propriedades da tabela',
'table_properties_title' => 'Propriedades da Tabela',
'delete_table' => 'Eliminar tabela',
'table_clear_formatting' => 'Limpar formatação de tabela',
'resize_to_contents' => 'Redimensionar para o conteúdo',
'row_header' => 'Cabeçalho da linha',
'table_clear_formatting' => 'Clear table formatting',
'resize_to_contents' => 'Resize to contents',
'row_header' => 'Row header',
'insert_row_before' => 'Inserir linha antes',
'insert_row_after' => 'Inserir linha depois',
'delete_row' => 'Eliminar linha',
@@ -149,7 +149,7 @@ return [
'url' => 'URL',
'text_to_display' => 'Texto a ser exibido',
'title' => 'Título',
'browse_links' => 'Procurar ligações',
'browse_links' => 'Browse links',
'open_link' => 'Abrir ligação',
'open_link_in' => 'Abrir ligação em...',
'open_link_current' => 'Janela atual',
@@ -167,7 +167,7 @@ return [
'about_title' => 'Sobre o Editor WYSIWYG',
'editor_license' => 'Editor da licença de direitos autorais',
'editor_lexical_license' => 'Este editor é criado como um fork do :lexicaLink que é distribuído sob a licença MIT.',
'editor_lexical_license_link' => 'Detalhes da licença completa podem ser encontrados aqui.',
'editor_lexical_license_link' => 'Full license details can be found here.',
'editor_tiny_license' => 'Este editor foi criado com :tinyLink que é fornecido sob a licença MIT.',
'editor_tiny_license_link' => 'Os dados relativos aos direitos de autor e à licença do TinyMCE podem ser encontrados aqui.',
'save_continue' => 'Salvar página e continuar',

View File

@@ -46,27 +46,27 @@ return [
'import' => 'Importar',
'import_validate' => 'Validar Importação',
'import_desc' => 'Importar livros, capítulos e páginas usando uma exportação ZIP portátil da mesma ou uma instância diferente. Selecione um arquivo ZIP para prosseguir. Após o carregamento e validação do arquivo, conseguirá configurar e confirmar a importação na próxima visualização.',
'import_zip_select' => 'Selecione o ficheiro ZIP para enviar',
'import_zip_validation_errors' => 'Foram detetados erros ao validar o ficheiro ZIP fornecido:',
'import_pending' => 'Aguardando importação',
'import_pending_none' => 'Nenhuma importação foi iniciada.',
'import_continue' => 'Continuar importação',
'import_continue_desc' => 'Verifique o conteúdo a importar a partir do ficheiro ZIP carregado. Quando estiver pronto, execute a importação para adicionar o seu conteúdo a este sistema. O ficheiro ZIP de importação carregado será automaticamente removido após a importação bem-sucedida.',
'import_details' => 'Detalhes da importação',
'import_run' => 'Executar Importação',
'import_size' => ':size Tamanho do ZIP importado',
'import_uploaded_at' => 'Carregado :relativeTime',
'import_uploaded_by' => 'Carregado por',
'import_location' => 'Local de Importação',
'import_location_desc' => 'Selecione um local de destino para o seu conteúdo importado. Terá de dispor das permissões necessárias para criar conteúdo no local que escolher.',
'import_delete_confirm' => 'Tem a certeza que pretende eliminar a importação?',
'import_zip_select' => 'Select ZIP file to upload',
'import_zip_validation_errors' => 'Errors were detected while validating the provided ZIP file:',
'import_pending' => 'Pending Imports',
'import_pending_none' => 'No imports have been started.',
'import_continue' => 'Continue Import',
'import_continue_desc' => 'Review the content due to be imported from the uploaded ZIP file. When ready, run the import to add its contents to this system. The uploaded ZIP import file will be automatically removed on successful import.',
'import_details' => 'Import Details',
'import_run' => 'Run Import',
'import_size' => ':size Import ZIP Size',
'import_uploaded_at' => 'Uploaded :relativeTime',
'import_uploaded_by' => 'Uploaded by',
'import_location' => 'Import Location',
'import_location_desc' => 'Select a target location for your imported content. You\'ll need the relevant permissions to create within the location you choose.',
'import_delete_confirm' => 'Are you sure you want to delete this import?',
'import_delete_desc' => 'Isto irá eliminar o arquivo ZIP de importação enviado e não pode ser desfeito.',
'import_errors' => 'Erros de Importação',
'import_errors_desc' => 'Ocorreram os seguintes erros durante a tentativa de importação:',
'breadcrumb_siblings_for_page' => 'Navegar itens do mesmo nível por página',
'breadcrumb_siblings_for_chapter' => 'Navegar itens do mesmo nível por capítulo',
'breadcrumb_siblings_for_book' => 'Navegar itens do mesmo nível por livro',
'breadcrumb_siblings_for_bookshelf' => 'Navegar itens do mesmo nível por estante',
'import_errors' => 'Import Errors',
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
// Permissions and restrictions
'permissions' => 'Permissões',
@@ -172,8 +172,8 @@ return [
'books_sort' => 'Ordenar Conteúdos do Livro',
'books_sort_desc' => 'Mova capítulos e páginas de um livro para reorganizar o seu conteúdo. É possível acrescentar outros livros, o que permite uma movimentação fácil de capítulos e páginas entre livros. Opcionalmente, uma regra de organização automática pode ser definida para classificar automaticamente o conteúdo deste livro após alterações.',
'books_sort_auto_sort' => '',
'books_sort_auto_sort_active' => 'Ordenação automática ativada: :sortName',
'books_sort_auto_sort_creation_hint' => 'As regras da opção de ordenação automática podem ser criadas na área de configurações "Listas e Ordenação" por um utilizador com as permissões necessárias.',
'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName',
'books_sort_auto_sort_creation_hint' => 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.',
'books_sort_named' => 'Ordenar Livro :bookName',
'books_sort_name' => 'Ordenar por Nome',
'books_sort_created' => 'Ordenar por Data de Criação',
@@ -235,7 +235,7 @@ return [
'pages_delete_draft' => 'Eliminar Rascunho de Página',
'pages_delete_success' => 'Página eliminada',
'pages_delete_draft_success' => 'Rascunho de página eliminado',
'pages_delete_warning_template' => 'Esta página é atualmente utilizada como modelo de página predefinido para livros ou capítulos. Após a eliminação desta página, estes livros ou capítulos deixarão de ter um modelo de página predefinido atribuído.',
'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.',
'pages_delete_confirm' => 'Tem certeza que deseja eliminar a página?',
'pages_delete_draft_confirm' => 'Tem certeza que deseja eliminar o rascunho de página?',
'pages_editing_named' => 'A Editar a Página :pageName',
@@ -252,8 +252,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Conteúdo Limitado)',
'pages_edit_switch_to_markdown_stable' => '(Conteúdo Estável)',
'pages_edit_switch_to_wysiwyg' => 'Alternar para o editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Mudar para o novo WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(Em fase de testes beta)',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)',
'pages_edit_set_changelog' => 'Relatar Alterações',
'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por si',
'pages_edit_enter_changelog' => 'Inserir Alterações',
@@ -273,7 +273,7 @@ return [
'pages_md_insert_drawing' => 'Inserir Desenho',
'pages_md_show_preview' => 'Mostrar pré-visualização',
'pages_md_sync_scroll' => 'Sincronizar pré-visualização',
'pages_md_plain_editor' => 'Editor de texto simples',
'pages_md_plain_editor' => 'Plaintext editor',
'pages_drawing_unsaved' => 'Encontrado um rascunho não guardado',
'pages_drawing_unsaved_confirm' => 'Dados de um rascunho não guardado foi encontrado de um tentativa anteriormente falhada. Deseja restaurar e continuar a edição desse rascunho?',
'pages_not_in_chapter' => 'A página não está dentro de um capítulo',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => 'Alternar barra lateral',
'page_contents' => 'Conteúdo da Página',
'page_contents_none' => 'Não foram encontrados títulos no conteúdo da página.',
'page_contents_info' => 'O índice é gerado a partir dos formatos de título utilizados na página.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Etiquetas de Página',
'chapter_tags' => 'Etiquetas do Capítulo',
'book_tags' => 'Etiquetas do Livro',
@@ -401,11 +401,11 @@ return [
'comment' => 'Comentário',
'comments' => 'Comentários',
'comment_add' => 'Adicionar Comentário',
'comment_none' => 'Não há comentários para apresentar',
'comment_none' => 'No comments to display',
'comment_placeholder' => 'Digite aqui os seus comentários',
'comment_thread_count' => ':count Tópico de comentários|:count Tópicos de comentários',
'comment_archived_count' => ':count Arquivado',
'comment_archived_threads' => 'Tópicos Arquivados',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
'comment_archived_count' => ':count Archived',
'comment_archived_threads' => 'Archived Threads',
'comment_save' => 'Guardar comentário',
'comment_new' => 'Comentário Novo',
'comment_created' => 'comentado :createDiff',
@@ -414,14 +414,14 @@ return [
'comment_deleted_success' => 'Comentário removido',
'comment_created_success' => 'Comentário adicionado',
'comment_updated_success' => 'Comentário editado',
'comment_archive_success' => 'Comentário arquivado',
'comment_unarchive_success' => 'Comentário não arquivado',
'comment_view' => 'Ver comentário',
'comment_jump_to_thread' => 'Ir para o tópico',
'comment_archive_success' => 'Comment archived',
'comment_unarchive_success' => 'Comment un-archived',
'comment_view' => 'View comment',
'comment_jump_to_thread' => 'Jump to thread',
'comment_delete_confirm' => 'Tem a certeza de que deseja eliminar este comentário?',
'comment_in_reply_to' => 'Em resposta à :commentId',
'comment_reference' => 'Referência',
'comment_reference_outdated' => '(Desatualizado)',
'comment_reference' => 'Reference',
'comment_reference_outdated' => '(Outdated)',
'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e geridos ao visualizar a página guardada.',
// Revision
@@ -452,7 +452,7 @@ return [
// References
'references' => 'Referências',
'references_none' => 'Não há referências registadas para este item.',
'references_to_desc' => 'A seguir, encontra-se uma lista de todo o conteúdo conhecido no sistema associado a este item.',
'references_to_desc' => 'Listed below is all the known content in the system that links to this item.',
// Watch Options
'watch' => 'Ver',
@@ -470,11 +470,11 @@ return [
'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' => 'Ignorar notificações',
'watch_detail_new' => 'A observar novas páginas',
'watch_detail_updates' => 'A observar novas páginas e atualizações',
'watch_detail_comments' => 'A observar novas páginas, atualizações e comentários',
'watch_detail_parent_book' => 'A observar via livro pai',
'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' => 'A ignorar através do livro pai',
'watch_detail_parent_chapter' => 'A observar via capítulo pai',
'watch_detail_parent_chapter_ignore' => 'A ignorar via capítulo pai',
'watch_detail_parent_chapter' => 'Watching via parent chapter',
'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter',
];

View File

@@ -10,7 +10,7 @@ return [
// Auth
'error_user_exists_different_creds' => 'Um utilizador com o endereço de e-mail :email já existe mas com credenciais diferentes.',
'auth_pre_register_theme_prevention' => 'Não foi possível registar a conta de utilizador com os detalhes fornecidos',
'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details',
'email_already_confirmed' => 'E-mail já foi confirmado. Tente iniciar sessão.',
'email_confirmation_invalid' => 'Este token de confirmação não é válido ou já foi utilizado. Por favor, tente registar-se novamente.',
'email_confirmation_expired' => 'O token de confirmação já expirou. Um novo e-mail foi enviado.',
@@ -37,7 +37,7 @@ return [
'social_driver_not_found' => 'Social driver não encontrado',
'social_driver_not_configured' => 'Os seus parâmetros sociais de :socialAccount não estão corretamente configurados.',
'invite_token_expired' => 'Este link de convite expirou. Alternativamente, pode tentar redefinir a senha da sua conta.',
'login_user_not_found' => 'Não foi possível encontrar um utilizador para esta ação.',
'login_user_not_found' => 'A user for this action could not be found.',
// System
'path_not_writable' => 'O caminho do arquivo :filePath não pôde ser carregado. Certifique-se de que tem permissões de escrita no servidor.',
@@ -51,9 +51,9 @@ return [
'image_upload_error' => 'Ocorreu um erro no carregamento da imagem',
'image_upload_type_error' => 'O tipo de imagem enviada é inválida',
'image_upload_replace_type' => 'A imagem de substituição deverá ser do mesmo tipo que a anterior',
'image_upload_memory_limit' => 'Não foi possível processar o carregamento da imagem e/ou criar miniaturas devido a limites de recursos do sistema.',
'image_thumbnail_memory_limit' => 'Não foi possível criar variações de tamanho de imagem devido a limites de recursos do sistema.',
'image_gallery_thumbnail_memory_limit' => 'Não foi possível criar miniaturas da galeria devido a limites de recursos do sistema.',
'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.',
'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.',
'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.',
'drawing_data_not_found' => 'Dados de desenho não puderam ser carregados. Talvez o arquivo de desenho não exista mais ou não tenha permissão para aceder-lhe.',
// Attachments
@@ -78,7 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'Não pode excluir o único administrador',
'users_cannot_delete_guest' => 'Não pode excluir o usuário convidado',
'users_could_not_send_invite' => 'Não foi possível criar o utilizador, pois o envio do endereço eletrónico de convite falhou',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'Este cargo não pode ser editado',
@@ -106,17 +106,17 @@ return [
'back_soon' => 'Voltaremos em breve.',
// Import
'import_zip_cant_read' => 'Não foi possível ler o ficheiro ZIP.',
'import_zip_cant_decode_data' => 'Não foi possível encontrar nem descodificar o conteúdo do ficheiro ZIP data.json.',
'import_zip_no_data' => 'Os dados do ficheiro ZIP não contêm o conteúdo esperado de livro, capítulo ou página.',
'import_zip_data_too_large' => 'O conteúdo do ficheiro ZIP data.json excede o tamanho máximo de upload definido para a aplicação.',
'import_validation_failed' => 'A importação do ficheiro ZIP não foi validada devido a erros:',
'import_zip_failed_notification' => 'Não foi possível importar o ficheiro ZIP.',
'import_perms_books' => 'Não dispõe das permissões necessárias para criar livros.',
'import_perms_chapters' => 'Não dispõe das permissões necessárias para criar capítulos.',
'import_perms_pages' => 'Não dispõe das permissões necessárias para criar páginas.',
'import_perms_images' => 'Não dispõe das permissões necessárias para criar imagens.',
'import_perms_attachments' => 'Não dispõe das permissões necessárias para criar anexos.',
'import_zip_cant_read' => 'Could not read ZIP file.',
'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.',
'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.',
'import_zip_data_too_large' => 'ZIP data.json content exceeds the configured application maximum upload size.',
'import_validation_failed' => 'Import ZIP failed to validate with errors:',
'import_zip_failed_notification' => 'Failed to import ZIP file.',
'import_perms_books' => 'You are lacking the required permissions to create books.',
'import_perms_chapters' => 'You are lacking the required permissions to create chapters.',
'import_perms_pages' => 'You are lacking the required permissions to create pages.',
'import_perms_images' => 'You are lacking the required permissions to create images.',
'import_perms_attachments' => 'You are lacking the required permission to create attachments.',
// API errors
'api_no_authorization_found' => 'Nenhum token de autorização encontrado na requisição',
@@ -125,11 +125,11 @@ return [
'api_incorrect_token_secret' => 'O segredo fornecido para o token de API usado está incorreto',
'api_user_no_api_permission' => 'O proprietário do token de API utilizado não tem permissão para fazer requisições de API',
'api_user_token_expired' => 'O token de autenticação expirou',
'api_cookie_auth_only_get' => 'Ao utilizar a API com autenticação baseada em “cookies”, apenas são permitidos pedidos GET',
'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erro lançado ao enviar um e-mail de teste:',
// HTTP errors
'http_ssr_url_no_match' => 'O URL não corresponde aos "hosts" SSR permitidos configurados',
'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts',
];

View File

@@ -11,11 +11,11 @@ return [
'updated_page_subject' => 'Página atualizada: :pageName',
'updated_page_intro' => 'Uma página foi atualizada em :appName:',
'updated_page_debounce' => 'Para evitar um grande volume de notificações, durante algum tempo não serão enviadas notificações de edições futuras para esta página através do mesmo editor.',
'comment_mention_subject' => 'Foi mencionado num comentário na página: :pageName',
'comment_mention_intro' => 'Foi mencionado num comentário no :appName:',
'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
'detail_page_name' => 'Nome da Página:',
'detail_page_path' => 'Caminho da página:',
'detail_page_path' => 'Page Path:',
'detail_commenter' => 'Comentador:',
'detail_comment' => 'Comentário:',
'detail_created_by' => 'Criado Por:',

View File

@@ -23,7 +23,7 @@ return [
'notifications_desc' => 'Controlar as notificações via correio eletrónico quando certas atividades são executadas pelo sistema.',
'notifications_opt_own_page_changes' => 'Notificar quando páginas que possuo sofrem alterações',
'notifications_opt_own_page_comments' => 'Notificar quando comentam páginas que possuo',
'notifications_opt_comment_mentions' => 'Notificar-me quando for mencionado num comentário',
'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
'notifications_opt_comment_replies' => 'Notificar respostas aos meus comentários',
'notifications_save' => 'Guardar preferências',
'notifications_update_success' => 'Preferências de notificação foram atualizadas!',
@@ -43,7 +43,7 @@ return [
'profile_email_no_permission' => 'Infelizmente você não tem permissão para alterar seu correio eletrônico. Se você quiser mudar isso, você precisa pedir a um administrador para alterar por você.',
'profile_avatar_desc' => 'Selecione uma imagem que será usada para lhe representar aos outros usuários do sistema. Idealmente, esta imagem deve ser quadrada e sobre 256px em largura e altura.',
'profile_admin_options' => 'Opções de administrador',
'profile_admin_options_desc' => 'Poderá encontrar opções adicionais de nível de administrador, como as destinadas a gerir a atribuição de funções, na sua conta de utilizador, na secção "Definições > Utilizadores" da aplicação.',
'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.',
'delete_account' => 'Excluir Conta',
'delete_my_account' => 'Excluir a Minha Conta',

View File

@@ -75,36 +75,36 @@ return [
'reg_confirm_restrict_domain_placeholder' => 'Nenhuma restrição definida',
// Sorting Settings
'sorting' => 'Listas e ordenação',
'sorting_book_default' => 'Regra de ordenação padrão dos livros',
'sorting_book_default_desc' => 'Selecione a regra de ordenação predefinida a aplicar aos novos livros. Isto não afetará os livros existentes e pode ser substituído individualmente para cada livro.',
'sorting_rules' => 'Regras de Ordenação',
'sorting_rules_desc' => 'Trata-se de operações de ordenação predefinidas que podem ser aplicadas ao conteúdo do sistema.',
'sort_rule_assigned_to_x_books' => 'Atribuído a: :count Livro|Atribuído a: :count Livros',
'sort_rule_create' => 'Criar Regra de Ordenação',
'sort_rule_edit' => 'Editar Regra de Ordenação',
'sort_rule_delete' => 'Eliminar Regra de Ordenação',
'sort_rule_delete_desc' => 'Remova esta regra de ordenação do sistema. Os livros que utilizam esta ordenação voltarão a ser ordenados manualmente.',
'sort_rule_delete_warn_books' => 'Esta regra de ordenação é atualmente utilizada em :count livro(s). Tem a certeza de que deseja eliminar isto?',
'sort_rule_delete_warn_default' => 'Esta regra de ordenação é atualmente utilizada em livros. Tem a certeza de que deseja eliminar isto?',
'sort_rule_details' => 'Detalhes de Regras de Ordenação',
'sort_rule_details_desc' => 'Defina um nome para esta regra de ordenação, que aparecerá nas listas quando os utilizadores selecionarem uma opção de ordenação.',
'sort_rule_operations' => 'Operações de Ordenação',
'sort_rule_operations_desc' => 'Configure as ações de ordenação a executar, selecionando-as na lista de operações disponíveis. Quando utilizadas, as operações serão aplicadas por ordem, de cima para baixo. Quaisquer alterações efetuadas aqui serão aplicadas a todos os livros atribuídos quando se guardar.',
'sort_rule_available_operations' => 'Operações Disponíveis',
'sort_rule_available_operations_empty' => 'Não operações pendentes',
'sort_rule_configured_operations' => 'Operações Configuradas',
'sort_rule_configured_operations_empty' => 'Operações de arrastar/adicionar a partir da lista "Operações Disponíveis"',
'sorting' => 'Lists & Sorting',
'sorting_book_default' => 'Default Book Sort Rule',
'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.',
'sorting_rules' => 'Sort Rules',
'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.',
'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books',
'sort_rule_create' => 'Create Sort Rule',
'sort_rule_edit' => 'Edit Sort Rule',
'sort_rule_delete' => 'Delete Sort Rule',
'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.',
'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?',
'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?',
'sort_rule_details' => 'Sort Rule Details',
'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.',
'sort_rule_operations' => 'Sort Operations',
'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.',
'sort_rule_available_operations' => 'Available Operations',
'sort_rule_available_operations_empty' => 'No operations remaining',
'sort_rule_configured_operations' => 'Configured Operations',
'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list',
'sort_rule_op_asc' => '(Asc)',
'sort_rule_op_desc' => '(Desc)',
'sort_rule_op_name' => 'Nome - Alfabético',
'sort_rule_op_name_numeric' => 'Nome - Numérico',
'sort_rule_op_created_date' => 'Data de criação',
'sort_rule_op_updated_date' => 'Data de atualização',
'sort_rule_op_chapters_first' => 'Capítulos: Primeiro',
'sort_rule_op_chapters_last' => 'Capítulos: Últimos',
'sorting_page_limits' => 'Limites de Exibição por Página',
'sorting_page_limits_desc' => 'Defina o número de itens a apresentar por página nas várias listas do sistema. Normalmente, um número mais baixo proporciona melhor desempenho, enquanto um número mais elevado evita a necessidade de percorrer várias páginas. É recomendado utilizar um múltiplo de 6.',
'sort_rule_op_name' => 'Name - Alphabetical',
'sort_rule_op_name_numeric' => 'Name - Numeric',
'sort_rule_op_created_date' => 'Created Date',
'sort_rule_op_updated_date' => 'Updated Date',
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Manutenção',
@@ -141,7 +141,7 @@ return [
'recycle_bin_contents_empty' => 'A reciclagem está atualmente vazia',
'recycle_bin_empty' => 'Esvaziar Reciclagem',
'recycle_bin_empty_confirm' => 'Isto irá destruir permanentemente todos os itens na reciclagem inclusive o conteúdo de cada item. Tem certeza de que a deseja esvaziar?',
'recycle_bin_destroy_confirm' => 'Esta ação irá eliminar definitivamente este item do sistema, com quaisquer elementos secundários listados abaixo, e não será possível recuperar este conteúdo. Tem a certeza de que deseja eliminar definitivamente este item?',
'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
'recycle_bin_destroy_list' => 'Itens a serem Destruídos',
'recycle_bin_restore_list' => 'Itens a serem Restaurados',
'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para o seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na reciclagem, o item pai também precisará de ser restaurado.',
@@ -194,20 +194,20 @@ return [
'role_access_api' => 'Aceder à API do sistema',
'role_manage_settings' => 'Gerir as configurações da aplicação',
'role_export_content' => 'Exportar conteúdo',
'role_import_content' => 'Importar conteúdo',
'role_import_content' => 'Import content',
'role_editor_change' => 'Alterar editor de página',
'role_notifications' => 'Receber e gerir notificações',
'role_permission_note_users_and_roles' => 'Tecnicamente, estas permissões também permitirão visualizar e pesquisar utilizadores e papéis no sistema.',
'role_notifications' => 'Receive & manage notifications',
'role_permission_note_users_and_roles' => 'These permissions will technically also provide visibility & searching of users & roles in the system.',
'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.',
'role_asset_admins' => 'Os administradores recebem automaticamente acesso a todo o conteúdo, mas estas opções podem mostrar ou ocultar as opções da Interface de Usuário.',
'role_asset_image_view_note' => 'Isto está relacionado com a visibilidade do gerenciador de imagens. O acesso real dos arquivos de imagem enviados dependerá da opção de armazenamento de imagens do sistema.',
'role_asset_users_note' => 'Tecnicamente, estas permissões também permitirão visualizar e pesquisar utilizadores no sistema.',
'role_asset_users_note' => 'These permissions will technically also provide visibility & searching of users in the system.',
'role_all' => 'Todos',
'role_own' => 'Próprio',
'role_controlled_by_asset' => 'Controlado pelo ativo para o qual eles são enviados',
'role_controlled_by_page_delete' => 'Controlado pelas permissões de eliminação de páginas',
'role_controlled_by_page_delete' => 'Controlled by page delete permissions',
'role_save' => 'Guardar Cargo',
'role_users' => 'Utilizadores com este cargo',
'role_users_none' => 'Nenhum utilizador está atualmente vinculado a este cargo',
@@ -229,8 +229,8 @@ return [
'users_send_invite_text' => 'Pode escolher enviar a este utilizador um convite por e-mail que o possibilitará definir a sua própria palavra-passe, ou defina você mesmo uma.',
'users_send_invite_option' => 'Enviar convite por e-mail',
'users_external_auth_id' => 'ID de Autenticação Externa',
'users_external_auth_id_desc' => 'Quando se utiliza um sistema de autenticação externo (como SAML2, OIDC ou LDAP), este é o ID que associa este utilizador do BookStack à conta do sistema de autenticação. Pode ignorar este campo se estiver a utilizar a autenticação padrão baseada no e-mail.',
'users_password_warning' => 'Preencha os campos abaixo apenas se pretender alterar a palavra-passe deste utilizador.',
'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.',
'users_password_warning' => 'Only fill the below if you would like to change the password for this user.',
'users_system_public' => 'Este utilizador representa quaisquer convidados que visitam a aplicação. Não pode ser utilizado para efetuar autenticação, mas é automaticamente atribuído.',
'users_delete' => 'Eliminar Utilizador',
'users_delete_named' => 'Eliminar :userName',
@@ -246,7 +246,7 @@ return [
'users_preferred_language' => 'Linguagem de Preferência',
'users_preferred_language_desc' => 'Esta opção irá alterar o idioma utilizado para a interface de utilizador da aplicação. Isto não afetará nenhum conteúdo criado por utilizadores.',
'users_social_accounts' => 'Contas Sociais',
'users_social_accounts_desc' => 'Ver o estado das contas sociais associadas a este utilizador. As contas sociais podem ser utilizadas em complemento ao sistema de autenticação principal para aceder ao sistema.',
'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.',
'users_social_accounts_info' => 'Aqui pode ligar outras contas para acesso mais rápido. Desligar uma conta não retira a possibilidade de acesso usando-a. Para revogar o acesso ao perfil através da conta social, você deverá fazê-lo na sua conta social.',
'users_social_connect' => 'Contas Associadas',
'users_social_disconnect' => 'Dissociar Conta',
@@ -255,7 +255,7 @@ return [
'users_social_connected' => 'A conta:socialAccount foi associada com sucesso ao seu perfil.',
'users_social_disconnected' => 'A conta:socialAccount foi dissociada com sucesso de seu perfil.',
'users_api_tokens' => 'Tokens de API',
'users_api_tokens_desc' => 'Crie e faça a gestão dos tokens de acesso utilizados para autenticação na API REST do BookStack. As permissões para a API são geridas através do utilizador a quem o token pertence.',
'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.',
'users_api_tokens_none' => 'Nenhum token de API foi criado para este utilizador',
'users_api_tokens_create' => 'Criar Token',
'users_api_tokens_expires' => 'Expira',
@@ -264,9 +264,9 @@ return [
'users_mfa_desc' => 'Configure a autenticação multi-fatores como uma camada extra de segurança para sua conta de utilizador.',
'users_mfa_x_methods' => ':count método configurado|:count métodos configurados',
'users_mfa_configure' => 'Configurar Métodos',
'users_mfa_reset' => 'Redefinir métodos de autenticação multifator',
'users_mfa_reset_desc' => 'Isto irá reiniciar e eliminar todos os métodos de autenticação multifator configurados para este utilizador. Se a autenticação multifator for exigida por alguma das suas funções, ser-lhe-á solicitado que configure novos métodos no seu próximo início de sessão.',
'users_mfa_reset_confirm' => 'Tem a certeza de que deseja repor a autenticação multifator para este utilizador?',
'users_mfa_reset' => 'Reset Multi-Factor Authentication Methods',
'users_mfa_reset_desc' => 'This will reset and clear all configured multi-factor authentication methods for this user. If multi-factor authentication is required by any of their roles, they\'ll be prompted to configure new methods on their next login.',
'users_mfa_reset_confirm' => 'Are you sure you want to reset multi-factor authentication for this user?',
// API Tokens
'user_api_token_create' => 'Criar Token de API',
@@ -316,13 +316,13 @@ return [
'webhooks_last_error_message' => 'Última mensagem de erro:',
// Licensing
'licenses' => 'Licenças',
'licenses_desc' => 'Esta página apresenta informações sobre as licenças do BookStack, bem como sobre os projetos e bibliotecas utilizados no BookStack. Muitos dos projetos aqui listados só podem ser utilizados num contexto de desenvolvimento.',
'licenses_bookstack' => 'Licença de BookStack',
'licenses_php' => 'Licenças de Bibliotecas PHP',
'licenses_js' => 'Licenças de Bibliotecas de JavaScript',
'licenses_other' => 'Outras Licenças',
'license_details' => 'Detalhes de Licença',
'licenses' => 'Licenses',
'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.',
'licenses_bookstack' => 'BookStack License',
'licenses_php' => 'PHP Library Licenses',
'licenses_js' => 'JavaScript Library Licenses',
'licenses_other' => 'Other Licenses',
'license_details' => 'License Details',
//! If editing translations files directly please ignore this in all
//! languages apart from en. Content will be auto-copied from en.
@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -105,11 +105,11 @@ return [
'url' => 'O formato da URL :attribute é inválido.',
'uploaded' => 'O arquivo não pôde ser carregado. O servidor pode não aceitar arquivos deste tamanho.',
'zip_file' => 'O :attribute deve referenciar um ficheiro dentro do ZIP.',
'zip_file_size' => 'O ficheiro :attribute não deve exceder :size MB.',
'zip_file_mime' => 'O :attribute deve referenciar um ficheiro do tipo :validTypes, encontrado em :foundType.',
'zip_model_expected' => 'Era esperado um objeto de dados, mas foi encontrado “:type”.',
'zip_unique' => 'O :attribute deve ser único para o tipo de objeto dentro do ficheiro ZIP.',
'zip_file' => 'The :attribute needs to reference a file within the ZIP.',
'zip_file_size' => 'The file :attribute must not exceed :size MB.',
'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.',
'zip_model_expected' => 'Data object expected but ":type" found.',
'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.',
// Custom validation lines
'custom' => [

View File

@@ -99,8 +99,8 @@ return [
'user_update_notification' => 'Usuário atualizado com sucesso',
'user_delete' => 'usuário excluído',
'user_delete_notification' => 'Usuário removido com sucesso',
'user_mfa_reset' => 'redefinir MFA para usuário',
'user_mfa_reset_notification' => 'Redefinir métodos de autenticação de múltiplos fatores',
'user_mfa_reset' => 'reset MFA for user',
'user_mfa_reset_notification' => 'Multi-factor authentication methods reset',
// API Tokens
'api_token_create' => 'token de API criado',

View File

@@ -8,7 +8,7 @@ return [
'failed' => 'As credenciais fornecidas não puderam ser validadas em nossos registros.',
'throttle' => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.',
'mfa_throttle' => 'Muitas tentativas de verificação de multifatores. Por favor tente novamente em :seconds segundos.',
'mfa_throttle' => 'Too many multi-factor verification attempts. Please try again in :seconds seconds.',
// Login & Register
'sign_up' => 'Criar Conta',

View File

@@ -219,7 +219,7 @@ return [
'chapters_permissions_active' => 'Permissões de Capítulo Ativas',
'chapters_permissions_success' => 'Permissões de Capítulo Atualizadas',
'chapters_search_this' => 'Pesquisar neste Capítulo',
'chapter_sort_book' => 'Ordenar livro',
'chapter_sort_book' => 'Classificar livro',
// Pages
'page' => 'Página',
@@ -331,9 +331,9 @@ return [
// Editor Sidebar
'toggle_sidebar' => '',
'page_contents' => 'Conteúdos da página',
'page_contents_none' => 'Nenhum título foi encontrado no conteúdo da página.',
'page_contents_info' => 'O menu de conteúdo é gerado a partir de qualquer formato de cabeçalho usado na página.',
'page_contents' => 'Page Contents',
'page_contents_none' => 'No headings were found in the page content.',
'page_contents_info' => 'The contents menu is generated from any heading formats used in the page.',
'page_tags' => 'Marcadores de Página',
'chapter_tags' => 'Marcadores de Capítulo',
'book_tags' => 'Marcadores de Livro',

View File

@@ -183,7 +183,7 @@ return [
'role_details' => 'Detalhes do Perfil',
'role_name' => 'Nome do Perfil',
'role_desc' => 'Breve Descrição do Perfil',
'role_mfa_enforced' => 'Requer Autenticação Multifator',
'role_mfa_enforced' => 'Requer Autenticação Multi-fator',
'role_external_auth_id' => 'IDs de Autenticação Externa',
'role_system' => 'Permissões do Sistema',
'role_manage_users' => 'Gerenciar usuários',
@@ -260,13 +260,13 @@ return [
'users_api_tokens_create' => 'Criar Token',
'users_api_tokens_expires' => 'Expira',
'users_api_tokens_docs' => 'Documentação da API',
'users_mfa' => 'Autenticação Multifator',
'users_mfa_desc' => 'A autenticação multifator adiciona uma camada extra de segurança à sua conta.',
'users_mfa' => 'Autenticação de Múltiplos Fatores',
'users_mfa_desc' => 'A autenticação multi-fator adiciona outra camada de segurança à sua conta.',
'users_mfa_x_methods' => ':count método configurado|:count métodos configurados',
'users_mfa_configure' => 'Configurar Métodos',
'users_mfa_reset' => 'Redefiner Métodos de Autenticação Multifator',
'users_mfa_reset_desc' => 'Isto irá redefinir e limpar todos os métodos de autenticação multifator configurados para este usuário. Se a autenticação multifator for exigida por qualquer uma de suas funções, você será solicitado a configurar novos métodos em seu próximo login.',
'users_mfa_reset_confirm' => 'Você tem certeza que deseja remover o método de autenticação multifator?',
'users_mfa_reset' => 'Reset Multi-Factor Authentication Methods',
'users_mfa_reset_desc' => 'This will reset and clear all configured multi-factor authentication methods for this user. If multi-factor authentication is required by any of their roles, they\'ll be prompted to configure new methods on their next login.',
'users_mfa_reset_confirm' => 'Are you sure you want to reset multi-factor authentication for this user?',
// API Tokens
'user_api_token_create' => 'Criar Token de API',
@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -366,7 +366,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'Slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

View File

@@ -367,7 +367,6 @@ return [
'ru' => 'Русский',
'sk' => 'Slovensky',
'sl' => 'slovenščina',
'sr' => 'Српски',
'sv' => 'Svenska',
'th' => 'ภาษาไทย',
'tr' => 'Türkçe',

Some files were not shown because too many files have changed in this diff Show More