APP_KEY=SomeRandomString
# Application URL
-# Remove the hash below and set a URL if using BookStack behind
-# a proxy or if using a third-party authentication option.
# This must be the root URL that you want to host BookStack on.
-# All URL's in BookStack will be generated using this value.
-#APP_URL=https://example.com
+# All URLs in BookStack will be generated using this value
+# to ensure URLs generated are consistent and secure.
+# If you change this in the future you may need to run a command
+# to update stored URLs in the database. Command example:
+# php artisan bookstack:update-url https://old.example.com https://new.example.com
+APP_URL=https://example.com
# Database details
DB_HOST=localhost
# Can be 'smtp' or 'sendmail'
MAIL_DRIVER=smtp
-# Mail sender options
-MAIL_FROM_NAME=BookStack
+# Mail sender details
+MAIL_FROM_NAME="BookStack"
# SMTP mail options
# Contents of the robots.txt file can be overridden, making this option obsolete.
ALLOW_ROBOTS=null
+# A list of hosts that BookStack can be iframed within.
+# Space separated if multiple. BookStack host domain is auto-inferred.
+# For Example: ALLOWED_IFRAME_HOSTS="https://example.com https://a.example.com"
+# Setting this option will also auto-adjust cookies to be SameSite=None.
+ALLOWED_IFRAME_HOSTS=null
+
# The default and maximum item-counts for listing API requests.
API_DEFAULT_ITEM_COUNT=100
API_MAX_ITEM_COUNT=500
--- /dev/null
+# These are supported funding model platforms
+
+github: [ssddanbrown]
--- /dev/null
+---
+name: New API Endpoint or Feature
+about: Request a new endpoint or API feature be added
+labels: ":nut_and_bolt: API Request"
+---
+
+#### API Endpoint or Feature
+
+Clearly describe what you'd like to have added to the API.
+
+#### Use-Case
+
+Explain the use-case that you're working-on that requires the above request.
+
+#### Additional Context
+
+If required, add any other context about the feature request here.
\ No newline at end of file
Marco (cdrfun) :: German
10935336 :: Chinese Simplified
孟繁阳 (FanyangMeng) :: Chinese Simplified
+Andrej Močan (andrejm) :: Slovenian
+gilane9_ :: Arabic
+Raed alnahdi (raednahdi) :: Arabic
+Xiphoseer :: German
+MerlinSVK (merlinsvk) :: Slovak
+Kauê Sena (kaue.sena.ks) :: Portuguese, Brazilian
+MatthieuParis :: French
+Douradinho :: Portuguese, Brazilian
+Gaku Yaguchi (tama11) :: Japanese
+johnroyer :: Chinese Traditional
+jackaaa :: Chinese Traditional
namespace BookStack\Actions;
use BookStack\Auth\User;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Support\Str;
/**
- * @property string $key
+ * @property string $type
* @property User $user
* @property Entity $entity
- * @property string $extra
+ * @property string $detail
* @property string $entity_type
* @property int $entity_id
* @property int $user_id
- * @property int $book_id
*/
class Activity extends Model
{
/**
* Get the user this activity relates to.
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function user()
+ public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
- * Returns text from the language files, Looks up by using the
- * activity key.
+ * Returns text from the language files, Looks up by using the activity key.
*/
- public function getText()
+ public function getText(): string
{
- return trans('activities.' . $this->key);
+ return trans('activities.' . $this->type);
+ }
+
+ /**
+ * Check if this activity is intended to be for an entity.
+ */
+ public function isForEntity(): bool
+ {
+ return Str::startsWith($this->type, [
+ 'page_', 'chapter_', 'book_', 'bookshelf_'
+ ]);
}
/**
*/
public function isSimilarTo(Activity $activityB): bool
{
- return [$this->key, $this->entity_type, $this->entity_id] === [$activityB->key, $activityB->entity_type, $activityB->entity_id];
+ return [$this->type, $this->entity_type, $this->entity_id] === [$activityB->type, $activityB->entity_type, $activityB->entity_id];
}
}
use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Interfaces\Loggable;
+use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
-use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class ActivityService
{
protected $activity;
- protected $user;
protected $permissionService;
- /**
- * ActivityService constructor.
- */
public function __construct(Activity $activity, PermissionService $permissionService)
{
$this->activity = $activity;
$this->permissionService = $permissionService;
- $this->user = user();
}
/**
- * Add activity data to database.
+ * Add activity data to database for an entity.
*/
- public function add(Entity $entity, string $activityKey, ?int $bookId = null)
+ public function addForEntity(Entity $entity, string $type)
{
- $activity = $this->newActivityForUser($activityKey, $bookId);
+ $activity = $this->newActivityForUser($type);
$entity->activity()->save($activity);
- $this->setNotification($activityKey);
+ $this->setNotification($type);
}
/**
- * Adds a activity history with a message, without binding to a entity.
+ * Add a generic activity event to the database.
+ * @param string|Loggable $detail
*/
- public function addMessage(string $activityKey, string $message, ?int $bookId = null)
+ public function add(string $type, $detail = '')
{
- $this->newActivityForUser($activityKey, $bookId)->forceFill([
- 'extra' => $message
- ])->save();
+ if ($detail instanceof Loggable) {
+ $detail = $detail->logDescriptor();
+ }
- $this->setNotification($activityKey);
+ $activity = $this->newActivityForUser($type);
+ $activity->detail = $detail;
+ $activity->save();
+ $this->setNotification($type);
}
/**
* Get a new activity instance for the current user.
*/
- protected function newActivityForUser(string $key, ?int $bookId = null): Activity
+ protected function newActivityForUser(string $type): Activity
{
return $this->activity->newInstance()->forceFill([
- 'key' => strtolower($key),
- 'user_id' => $this->user->id,
- 'book_id' => $bookId ?? 0,
+ 'type' => strtolower($type),
+ 'user_id' => user()->id,
]);
}
* and instead uses the 'extra' field with the entities name.
* Used when an entity is deleted.
*/
- public function removeEntity(Entity $entity): Collection
+ public function removeEntity(Entity $entity)
{
- $activities = $entity->activity()->get();
$entity->activity()->update([
- 'extra' => $entity->name,
- 'entity_id' => 0,
- 'entity_type' => '',
+ 'detail' => $entity->name,
+ 'entity_id' => null,
+ 'entity_type' => null,
]);
- return $activities;
}
/**
*/
public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
{
+ /** @var [string => int[]] $queryIds */
+ $queryIds = [$entity->getMorphClass() => [$entity->id]];
+
if ($entity->isA('book')) {
- $query = $this->activity->newQuery()->where('book_id', '=', $entity->id);
- } else {
- $query = $this->activity->newQuery()->where('entity_type', '=', $entity->getMorphClass())
- ->where('entity_id', '=', $entity->id);
+ $queryIds[(new Chapter)->getMorphClass()] = $entity->chapters()->visible()->pluck('id');
+ }
+ if ($entity->isA('book') || $entity->isA('chapter')) {
+ $queryIds[(new Page)->getMorphClass()] = $entity->pages()->visible()->pluck('id');
}
- $activity = $this->permissionService
- ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
- ->orderBy('created_at', 'desc')
+ $query = $this->activity->newQuery();
+ $query->where(function (Builder $query) use ($queryIds) {
+ foreach ($queryIds as $morphClass => $idArr) {
+ $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
+ $innerQuery->where('entity_type', '=', $morphClass)
+ ->whereIn('entity_id', $idArr);
+ });
+ }
+ });
+
+ $activity = $query->orderBy('created_at', 'desc')
->with(['entity' => function (Relation $query) {
$query->withTrashed();
}, 'user.avatar'])
/**
* Flashes a notification message to the session if an appropriate message is available.
*/
- protected function setNotification(string $activityKey)
+ protected function setNotification(string $type)
{
- $notificationTextKey = 'activities.' . $activityKey . '_notification';
+ $notificationTextKey = 'activities.' . $type . '_notification';
if (trans()->has($notificationTextKey)) {
$message = trans($notificationTextKey);
session()->flash('success', $message);
--- /dev/null
+<?php namespace BookStack\Actions;
+
+class ActivityType
+{
+ const PAGE_CREATE = 'page_create';
+ const PAGE_UPDATE = 'page_update';
+ const PAGE_DELETE = 'page_delete';
+ const PAGE_RESTORE = 'page_restore';
+ const PAGE_MOVE = 'page_move';
+
+ const CHAPTER_CREATE = 'chapter_create';
+ const CHAPTER_UPDATE = 'chapter_update';
+ const CHAPTER_DELETE = 'chapter_delete';
+ const CHAPTER_MOVE = 'chapter_move';
+
+ const BOOK_CREATE = 'book_create';
+ const BOOK_UPDATE = 'book_update';
+ const BOOK_DELETE = 'book_delete';
+ const BOOK_SORT = 'book_sort';
+
+ const BOOKSHELF_CREATE = 'bookshelf_create';
+ const BOOKSHELF_UPDATE = 'bookshelf_update';
+ const BOOKSHELF_DELETE = 'bookshelf_delete';
+
+ const COMMENTED_ON = 'commented_on';
+ const PERMISSIONS_UPDATE = 'permissions_update';
+
+ const SETTINGS_UPDATE = 'settings_update';
+ const MAINTENANCE_ACTION_RUN = 'maintenance_action_run';
+
+ const RECYCLE_BIN_EMPTY = 'recycle_bin_empty';
+ const RECYCLE_BIN_RESTORE = 'recycle_bin_restore';
+ const RECYCLE_BIN_DESTROY = 'recycle_bin_destroy';
+
+ const USER_CREATE = 'user_create';
+ const USER_UPDATE = 'user_update';
+ const USER_DELETE = 'user_delete';
+
+ const API_TOKEN_CREATE = 'api_token_create';
+ const API_TOKEN_UPDATE = 'api_token_update';
+ const API_TOKEN_DELETE = 'api_token_delete';
+
+ const ROLE_CREATE = 'role_create';
+ const ROLE_UPDATE = 'role_update';
+ const ROLE_DELETE = 'role_delete';
+
+ const AUTH_PASSWORD_RESET = 'auth_password_reset_request';
+ const AUTH_PASSWORD_RESET_UPDATE = 'auth_password_reset_update';
+ const AUTH_LOGIN = 'auth_login';
+ const AUTH_REGISTER = 'auth_register';
+}
\ No newline at end of file
<?php namespace BookStack\Actions;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property string text
* @property int|null parent_id
* @property int local_id
*/
-class Comment extends Ownable
+class Comment extends Model
{
+ use HasCreatorAndUpdater;
+
protected $fillable = ['text', 'parent_id'];
protected $appends = ['created', 'updated'];
/**
* Get the entity that this comment belongs to
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
- public function entity()
+ public function entity(): MorphTo
{
return $this->morphTo('entity');
}
/**
* Check if a comment has been updated since creation.
- * @return bool
*/
- public function isUpdated()
+ public function isUpdated(): bool
{
return $this->updated_at->timestamp > $this->created_at->timestamp;
}
<?php namespace BookStack\Actions;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
use League\CommonMark\CommonMarkConverter;
+use BookStack\Facades\Activity as ActivityService;
/**
* Class CommentRepo
$comment->parent_id = $parent_id;
$entity->comments()->save($comment);
+ ActivityService::addForEntity($entity, ActivityType::COMMENTED_ON);
return $comment;
}
use BookStack\Model;
-/**
- * Class Attribute
- * @package BookStack
- */
class Tag extends Model
{
protected $fillable = ['name', 'value', 'order'];
- protected $hidden = ['id', 'entity_id', 'entity_type'];
+ protected $hidden = ['id', 'entity_id', 'entity_type', 'created_at', 'updated_at'];
/**
* Get the entity that this tag belongs to
<?php namespace BookStack\Actions;
use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
use DB;
use Illuminate\Support\Collection;
<?php namespace BookStack\Actions;
use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Entity;
use BookStack\Entities\EntityProvider;
use DB;
use Illuminate\Support\Collection;
/**
* Add a view to the given entity.
- * @param \BookStack\Entities\Entity $entity
+ * @param \BookStack\Entities\Models\Entity $entity
* @return int
*/
public function add(Entity $entity)
$query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
}
- return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
+ return $query->with('viewable')
+ ->skip($skipCount)
+ ->take($count)
+ ->get()
+ ->pluck('viewable')
+ ->filter();
}
/**
<?php namespace BookStack\Api;
use BookStack\Http\Controllers\Api\ApiController;
+use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use ReflectionClass;
protected $reflectionClasses = [];
protected $controllerClasses = [];
+ /**
+ * Load the docs form the cache if existing
+ * otherwise generate and store in the cache.
+ */
+ public static function generateConsideringCache(): Collection
+ {
+ $appVersion = trim(file_get_contents(base_path('version')));
+ $cacheKey = 'api-docs::' . $appVersion;
+ if (Cache::has($cacheKey) && config('app.env') === 'production') {
+ $docs = Cache::get($cacheKey);
+ } else {
+ $docs = (new static())->generate();
+ Cache::put($cacheKey, $docs, 60 * 24);
+ }
+ return $docs;
+ }
+
/**
* Generate API documentation.
*/
- public function generate(): Collection
+ protected function generate(): Collection
{
$apiRoutes = $this->getFlatApiRoutes();
$apiRoutes = $this->loadDetailsFromControllers($apiRoutes);
/**
* Load body params and their rules by inspecting the given class and method name.
- * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ * @throws BindingResolutionException
*/
protected function getBodyParamsFromClass(string $className, string $methodName): ?array
{
<?php namespace BookStack\Api;
use BookStack\Auth\User;
+use BookStack\Interfaces\Loggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Carbon;
-class ApiToken extends Model
+/**
+ * Class ApiToken
+ * @property int $id
+ * @property string $token_id
+ * @property string $secret
+ * @property string $name
+ * @property Carbon $expires_at
+ * @property User $user
+ */
+class ApiToken extends Model implements Loggable
{
protected $fillable = ['name', 'expires_at'];
protected $casts = [
{
return Carbon::now()->addYears(100)->format('Y-m-d');
}
+
+ /**
+ * @inheritdoc
+ */
+ public function logDescriptor(): string
+ {
+ return "({$this->id}) {$this->name}; User: {$this->user->logDescriptor()}";
+ }
}
* guard with 'remember' functionality removed. Basic auth and event emission
* has also been removed to keep this simple. Designed to be extended by external
* Auth Guards.
- *
- * @package Illuminate\Auth
*/
class ExternalBaseSessionGuard implements StatefulGuard
{
* into the default laravel 'Guard' auth flow. Instead most of the logic is done
* via the Saml2 controller & Saml2Service. This class provides a safer, thin
* version of SessionGuard.
- *
- * @package BookStack\Auth\Access\Guards
*/
class Saml2SessionGuard extends ExternalBaseSessionGuard
{
* Class Ldap
* An object-orientated thin abstraction wrapper for common PHP LDAP functions.
* Allows the standard LDAP functions to be mocked for testing.
- * @package BookStack\Services
*/
class Ldap
{
<?php namespace BookStack\Auth\Access;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\SocialAccount;
use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
use Exception;
class RegistrationService
$newUser->socialAccounts()->save($socialAccount);
}
+ Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
+
// Start email confirmation flow if required
if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
$newUser->save();
<?php namespace BookStack\Auth\Access;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\User;
use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\SamlException;
use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
use Exception;
use Illuminate\Support\Str;
use OneLogin\Saml2\Auth;
}
auth()->login($user);
+ Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
return $user;
}
}
<?php namespace BookStack\Auth\Access;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\SocialAccount;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\SocialDriverNotConfigured;
use BookStack\Exceptions\SocialSignInAccountNotUsed;
use BookStack\Exceptions\UserRegistrationException;
+use BookStack\Facades\Activity;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\Factory as Socialite;
use Laravel\Socialite\Contracts\Provider;
// Simply log the user into the application.
if (!$isLoggedIn && $socialAccount !== null) {
auth()->login($socialAccount->user);
+ Activity::add(ActivityType::AUTH_LOGIN, $socialAccount);
return redirect()->intended('/');
}
<?php namespace BookStack\Auth\Permissions;
use BookStack\Auth\Role;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
use BookStack\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use BookStack\Auth\Permissions;
use BookStack\Auth\Role;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Entity;
use BookStack\Entities\EntityProvider;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* Prepare the local entity cache and ensure it's empty
- * @param \BookStack\Entities\Entity[] $entities
+ * @param \BookStack\Entities\Models\Entity[] $entities
*/
protected function readyEntityCache($entities = [])
{
/**
* Get a chapter via ID, Checks local cache
* @param $chapterId
- * @return \BookStack\Entities\Book
+ * @return \BookStack\Entities\Models\Book
*/
protected function getChapter($chapterId)
{
});
// Chunk through all bookshelves
- $this->entityProvider->bookshelf->newQuery()->withTrashed()->select(['id', 'restricted', 'created_by'])
+ $this->entityProvider->bookshelf->newQuery()->withTrashed()->select(['id', 'restricted', 'owned_by'])
->chunk(50, function ($shelves) use ($roles) {
$this->buildJointPermissionsForShelves($shelves, $roles);
});
protected function bookFetchQuery()
{
return $this->entityProvider->book->withTrashed()->newQuery()
- ->select(['id', 'restricted', 'created_by'])->with(['chapters' => function ($query) {
- $query->withTrashed()->select(['id', 'restricted', 'created_by', 'book_id']);
+ ->select(['id', 'restricted', 'owned_by'])->with(['chapters' => function ($query) {
+ $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id']);
}, 'pages' => function ($query) {
- $query->withTrashed()->select(['id', 'restricted', 'created_by', 'book_id', 'chapter_id']);
+ $query->withTrashed()->select(['id', 'restricted', 'owned_by', 'book_id', 'chapter_id']);
}]);
}
/**
* Rebuild the entity jointPermissions for a particular entity.
- * @param \BookStack\Entities\Entity $entity
+ * @param \BookStack\Entities\Models\Entity $entity
* @throws \Throwable
*/
public function buildJointPermissionsForEntity(Entity $entity)
});
// Chunk through all bookshelves
- $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'created_by'])
+ $this->entityProvider->bookshelf->newQuery()->select(['id', 'restricted', 'owned_by'])
->chunk(50, function ($shelves) use ($roles) {
$this->buildJointPermissionsForShelves($shelves, $roles);
});
/**
* Delete all of the entity jointPermissions for a list of entities.
- * @param \BookStack\Entities\Entity[] $entities
+ * @param \BookStack\Entities\Models\Entity[] $entities
* @throws \Throwable
*/
protected function deleteManyJointPermissionsForEntities($entities)
/**
* Get the actions related to an entity.
- * @param \BookStack\Entities\Entity $entity
+ * @param \BookStack\Entities\Models\Entity $entity
* @return array
*/
protected function getActions(Entity $entity)
/**
* Create an array of data with the information of an entity jointPermissions.
* Used to build data for bulk insertion.
- * @param \BookStack\Entities\Entity $entity
+ * @param \BookStack\Entities\Models\Entity $entity
* @param Role $role
* @param $action
* @param $permissionAll
'action' => $action,
'has_permission' => $permissionAll,
'has_permission_own' => $permissionOwn,
- 'created_by' => $entity->getRawAttribute('created_by')
+ 'owned_by' => $entity->getRawAttribute('owned_by')
];
}
/**
* Checks if an entity has a restriction set upon it.
- * @param Ownable $ownable
- * @param $permission
- * @return bool
+ * @param HasCreatorAndUpdater|HasOwner $ownable
*/
- public function checkOwnableUserAccess(Ownable $ownable, $permission)
+ public function checkOwnableUserAccess(Model $ownable, string $permission): bool
{
$explodedPermission = explode('-', $permission);
- $baseQuery = $ownable->where('id', '=', $ownable->id);
+ $baseQuery = $ownable->newQuery()->where('id', '=', $ownable->id);
$action = end($explodedPermission);
$this->currentAction = $action;
$allPermission = $this->currentUser() && $this->currentUser()->can($permission . '-all');
$ownPermission = $this->currentUser() && $this->currentUser()->can($permission . '-own');
$this->currentAction = 'view';
- $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->created_by;
+ $ownerField = ($ownable instanceof Entity) ? 'owned_by' : 'created_by';
+ $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->$ownerField;
return ($allPermission || ($isOwner && $ownPermission));
}
$query->where('has_permission', '=', 1)
->orWhere(function ($query2) use ($userId) {
$query2->where('has_permission_own', '=', 1)
- ->where('created_by', '=', $userId);
+ ->where('owned_by', '=', $userId);
});
});
/**
* Check if an entity has restrictions set on itself or its
* parent tree.
- * @param \BookStack\Entities\Entity $entity
+ * @param \BookStack\Entities\Models\Entity $entity
* @param $action
* @return bool|mixed
*/
$query->where('has_permission', '=', true)
->orWhere(function ($query) {
$query->where('has_permission_own', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
});
$query->where('has_permission', '=', true)
->orWhere(function (Builder $query) {
$query->where('has_permission_own', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
});
$query->where('draft', '=', false)
->orWhere(function (Builder $query) {
$query->where('draft', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
}
/**
* Add restrictions for a generic entity
* @param string $entityType
- * @param Builder|\BookStack\Entities\Entity $query
+ * @param Builder|\BookStack\Entities\Models\Entity $query
* @param string $action
* @return Builder
*/
$query->where('draft', '=', false)
->orWhere(function ($query) {
$query->where('draft', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
}
->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
});
->where(function ($query) {
$query->where('has_permission', '=', true)->orWhere(function ($query) {
$query->where('has_permission_own', '=', true)
- ->where('created_by', '=', $this->currentUser()->id);
+ ->where('owned_by', '=', $this->currentUser()->id);
});
});
});
<?php namespace BookStack\Auth\Permissions;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\Role;
use BookStack\Exceptions\PermissionsException;
+use BookStack\Facades\Activity;
use Exception;
use Illuminate\Database\Eloquent\Collection;
-use Illuminate\Support\Str;
class PermissionsRepo
{
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
$this->assignRolePermissions($role, $permissions);
$this->permissionService->buildJointPermissionForRole($role);
+ Activity::add(ActivityType::ROLE_CREATE, $role);
return $role;
}
$role->fill($roleData);
$role->save();
$this->permissionService->buildJointPermissionForRole($role);
+ Activity::add(ActivityType::ROLE_UPDATE, $role);
}
/**
* Assign an list of permission names to an role.
*/
- public function assignRolePermissions(Role $role, array $permissionNameArray = [])
+ protected function assignRolePermissions(Role $role, array $permissionNameArray = [])
{
$permissions = [];
$permissionNameArray = array_values($permissionNameArray);
}
$this->permissionService->deleteJointPermissionsForRole($role);
+ Activity::add(ActivityType::ROLE_DELETE, $role);
$role->delete();
}
}
use BookStack\Auth\Permissions\JointPermission;
use BookStack\Auth\Permissions\RolePermission;
+use BookStack\Interfaces\Loggable;
use BookStack\Model;
use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property string $external_auth_id
* @property string $system_name
*/
-class Role extends Model
+class Role extends Model implements Loggable
{
protected $fillable = ['display_name', 'description', 'external_auth_id'];
/**
* The roles that belong to the role.
*/
- public function users()
+ public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->orderBy('name', 'asc');
}
/**
* The RolePermissions that belong to the role.
*/
- public function permissions()
+ public function permissions(): BelongsToMany
{
return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id');
}
{
return static::query()->where('system_name', '!=', 'admin')->get();
}
+
+ /**
+ * @inheritdoc
+ */
+ public function logDescriptor(): string
+ {
+ return "({$this->id}) {$this->display_name}";
+ }
}
<?php namespace BookStack\Auth;
+use BookStack\Interfaces\Loggable;
use BookStack\Model;
-class SocialAccount extends Model
+/**
+ * Class SocialAccount
+ * @property string $driver
+ * @property User $user
+ */
+class SocialAccount extends Model implements Loggable
{
protected $fillable = ['user_id', 'driver', 'driver_id', 'timestamps'];
{
return $this->belongsTo(User::class);
}
+
+ /**
+ * @inheritDoc
+ */
+ public function logDescriptor(): string
+ {
+ return "{$this->driver}; {$this->user->logDescriptor()}";
+ }
}
<?php namespace BookStack\Auth;
use BookStack\Api\ApiToken;
+use BookStack\Interfaces\Loggable;
use BookStack\Model;
use BookStack\Notifications\ResetPassword;
use BookStack\Uploads\Image;
use Carbon\Carbon;
+use Exception;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Collection;
/**
* Class User
- * @package BookStack\Auth
* @property string $id
* @property string $name
* @property string $email
* @property string $external_auth_id
* @property string $system_name
*/
-class User extends Model implements AuthenticatableContract, CanResetPasswordContract
+class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable
{
use Authenticatable, CanResetPassword, Notifiable;
*/
protected $fillable = ['name', 'email'];
+ protected $casts = ['last_activity_at' => 'datetime'];
+
/**
* The attributes excluded from the model's JSON form.
* @var array
/**
* This holds the user's permissions when loaded.
- * @var array
+ * @var ?Collection
*/
protected $permissions;
}
}
+ /**
+ * Check if the user has a particular permission.
+ */
+ public function can(string $permissionName): bool
+ {
+ if ($this->email === 'guest') {
+ return false;
+ }
+
+ return $this->permissions()->contains($permissionName);
+ }
+
/**
* Get all permissions belonging to a the current user.
- * @param bool $cache
- * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
- public function permissions($cache = true)
+ protected function permissions(): Collection
{
- if (isset($this->permissions) && $cache) {
+ if (isset($this->permissions)) {
return $this->permissions;
}
- $this->load('roles.permissions');
- $permissions = $this->roles->map(function ($role) {
- return $role->permissions;
- })->flatten()->unique();
- $this->permissions = $permissions;
- return $permissions;
+
+ $this->permissions = $this->newQuery()->getConnection()->table('role_user', 'ru')
+ ->select('role_permissions.name as name')->distinct()
+ ->leftJoin('permission_role', 'ru.role_id', '=', 'permission_role.role_id')
+ ->leftJoin('role_permissions', 'permission_role.permission_id', '=', 'role_permissions.id')
+ ->where('ru.user_id', '=', $this->id)
+ ->get()
+ ->pluck('name');
+
+ return $this->permissions;
}
/**
- * Check if the user has a particular permission.
- * @param $permissionName
- * @return bool
+ * Clear any cached permissions on this instance.
*/
- public function can($permissionName)
+ public function clearPermissionCache()
{
- if ($this->email === 'guest') {
- return false;
- }
- return $this->permissions()->pluck('name')->contains($permissionName);
+ $this->permissions = null;
}
/**
/**
* Get the social account associated with this user.
- * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ * @return HasMany
*/
public function socialAccounts()
{
try {
$avatar = $this->avatar ? url($this->avatar->getThumb($size, $size, false)) : $default;
- } catch (\Exception $err) {
+ } catch (Exception $err) {
$avatar = $default;
}
return $avatar;
/**
* Get the avatar for the user.
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ * @return BelongsTo
*/
public function avatar()
{
return $this->hasMany(ApiToken::class);
}
+ /**
+ * Get the last activity time for this user.
+ */
+ public function scopeWithLastActivityAt(Builder $query)
+ {
+ $query->addSelect(['activities.created_at as last_activity_at'])
+ ->leftJoinSub(function (\Illuminate\Database\Query\Builder $query) {
+ $query->from('activities')->select('user_id')
+ ->selectRaw('max(created_at) as created_at')
+ ->groupBy('user_id');
+ }, 'activities', 'users.id', '=', 'activities.user_id');
+ }
+
/**
* Get the url for editing this user.
*/
{
$this->notify(new ResetPassword($token));
}
+
+ /**
+ * @inheritdoc
+ */
+ public function logDescriptor(): string
+ {
+ return "({$this->id}) {$this->name}";
+ }
}
<?php namespace BookStack\Auth;
use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\UserUpdateException;
use BookStack\Uploads\Image;
+use BookStack\Uploads\UserAvatars;
use Exception;
use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Pagination\LengthAwarePaginator;
use Images;
use Log;
class UserRepo
{
-
- protected $user;
- protected $role;
+ protected $userAvatar;
/**
* UserRepo constructor.
*/
- public function __construct(User $user, Role $role)
+ public function __construct(UserAvatars $userAvatar)
{
- $this->user = $user;
- $this->role = $role;
+ $this->userAvatar = $userAvatar;
}
/**
*/
public function getByEmail(string $email): ?User
{
- return $this->user->where('email', '=', $email)->first();
+ return User::query()->where('email', '=', $email)->first();
}
/**
- * @param int $id
- * @return User
+ * Get a user by their ID.
*/
- public function getById($id)
+ public function getById(int $id): User
{
- return $this->user->newQuery()->findOrFail($id);
+ return User::query()->findOrFail($id);
}
/**
* Get all the users with their permissions.
- * @return Builder|static
*/
- public function getAllUsers()
+ public function getAllUsers(): Collection
{
- return $this->user->with('roles', 'avatar')->orderBy('name', 'asc')->get();
+ return User::query()->with('roles', 'avatar')->orderBy('name', 'asc')->get();
}
/**
* Get all the users with their permissions in a paginated format.
- * @param int $count
- * @param $sortData
- * @return Builder|static
*/
- public function getAllUsersPaginatedAndSorted($count, $sortData)
+ public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
{
- $query = $this->user->with('roles', 'avatar')->orderBy($sortData['sort'], $sortData['order']);
+ $sort = $sortData['sort'];
+
+ $query = User::query()->select(['*'])
+ ->withLastActivityAt()
+ ->with(['roles', 'avatar'])
+ ->orderBy($sort, $sortData['order']);
if ($sortData['search']) {
$term = '%' . $sortData['search'] . '%';
/**
* Assign a user to a system-level role.
- * @param User $user
- * @param $systemRoleName
* @throws NotFoundException
*/
- public function attachSystemRole(User $user, $systemRoleName)
+ public function attachSystemRole(User $user, string $systemRoleName)
{
- $role = $this->role->newQuery()->where('system_name', '=', $systemRoleName)->first();
- if ($role === null) {
+ $role = Role::getSystemRole($systemRoleName);
+ if (is_null($role)) {
throw new NotFoundException("Role '{$systemRoleName}' not found");
}
$user->attachRole($role);
/**
* Checks if the give user is the only admin.
- * @param User $user
- * @return bool
*/
- public function isOnlyAdmin(User $user)
+ public function isOnlyAdmin(User $user): bool
{
if (!$user->hasSystemRole('admin')) {
return false;
}
- $adminRole = $this->role->getSystemRole('admin');
- if ($adminRole->users->count() > 1) {
+ $adminRole = Role::getSystemRole('admin');
+ if ($adminRole->users()->count() > 1) {
return false;
}
+
return true;
}
/**
* Set the assigned user roles via an array of role IDs.
- * @param User $user
- * @param array $roles
* @throws UserUpdateException
*/
public function setUserRoles(User $user, array $roles)
/**
* Check if the given user is the last admin and their new roles no longer
* contains the admin role.
- * @param User $user
- * @param array $newRoles
- * @return bool
*/
protected function demotingLastAdmin(User $user, array $newRoles) : bool
{
if ($this->isOnlyAdmin($user)) {
- $adminRole = $this->role->getSystemRole('admin');
+ $adminRole = Role::getSystemRole('admin');
if (!in_array(strval($adminRole->id), $newRoles)) {
return true;
}
*/
public function create(array $data, bool $emailConfirmed = false): User
{
- return $this->user->forceCreate([
+ $details = [
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'email_confirmed' => $emailConfirmed,
'external_auth_id' => $data['external_auth_id'] ?? '',
- ]);
+ ];
+ return User::query()->forceCreate($details);
}
/**
* Remove the given user from storage, Delete all related content.
- * @param User $user
* @throws Exception
*/
- public function destroy(User $user)
+ public function destroy(User $user, ?int $newOwnerId = null)
{
$user->socialAccounts()->delete();
$user->apiTokens()->delete();
$user->delete();
// Delete user profile images
- $profileImages = Image::where('type', '=', 'user')->where('uploaded_to', '=', $user->id)->get();
+ $profileImages = Image::query()->where('type', '=', 'user')
+ ->where('uploaded_to', '=', $user->id)
+ ->get();
+
foreach ($profileImages as $image) {
Images::destroy($image);
}
+
+ if (!empty($newOwnerId)) {
+ $newOwner = User::query()->find($newOwnerId);
+ if (!is_null($newOwner)) {
+ $this->migrateOwnership($user, $newOwner);
+ }
+ }
+ }
+
+ /**
+ * Migrate ownership of items in the system from one user to another.
+ */
+ protected function migrateOwnership(User $fromUser, User $toUser)
+ {
+ $entities = (new EntityProvider)->all();
+ foreach ($entities as $instance) {
+ $instance->newQuery()->where('owned_by', '=', $fromUser->id)
+ ->update(['owned_by' => $toUser->id]);
+ }
}
/**
* Get the latest activity for a user.
- * @param User $user
- * @param int $count
- * @param int $page
- * @return array
*/
- public function getActivity(User $user, $count = 20, $page = 0)
+ public function getActivity(User $user, int $count = 20, int $page = 0): array
{
return Activity::userActivity($user, $count, $page);
}
/**
* Get the roles in the system that are assignable to a user.
- * @return mixed
*/
- public function getAllRoles()
+ public function getAllRoles(): Collection
{
- return $this->role->newQuery()->orderBy('display_name', 'asc')->get();
+ return Role::query()->orderBy('display_name', 'asc')->get();
}
/**
* Get an avatar image for a user and set it as their avatar.
* Returns early if avatars disabled or not set in config.
- * @param User $user
- * @return bool
*/
- public function downloadAndAssignUserAvatar(User $user)
+ public function downloadAndAssignUserAvatar(User $user): void
{
- if (!Images::avatarFetchEnabled()) {
- return false;
- }
-
try {
- $avatar = Images::saveUserAvatar($user);
- $user->avatar()->associate($avatar);
- $user->save();
- return true;
+ $this->userAvatar->fetchAndAssignToUser($user);
} catch (Exception $e) {
Log::error('Failed to save user avatar image');
- return false;
}
}
}
// and used by BookStack in URL generation.
'url' => env('APP_URL', '') === 'http://bookstack.dev' ? '' : env('APP_URL', ''),
+ // A list of hosts that BookStack can be iframed within.
+ // Space separated if multiple. BookStack host domain is auto-inferred.
+ 'iframe_hosts' => env('ALLOWED_IFRAME_HOSTS', null),
+
// Application timezone for back-end date functions.
'timezone' => env('APP_TIMEZONE', 'UTC'),
'locale' => env('APP_LANG', 'en'),
// Locales available
- 'locales' => ['en', 'ar', 'bg', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'it', 'ja', 'ko', 'nl', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl', 'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
+ 'locales' => ['en', 'ar', 'bg', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'it', 'ja', 'ko', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl', 'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
// Application Fallback Locale
'fallback_locale' => 'en',
BookStack\Providers\EventServiceProvider::class,
BookStack\Providers\RouteServiceProvider::class,
BookStack\Providers\CustomFacadeProvider::class,
+ BookStack\Providers\CustomValidationServiceProvider::class,
],
/*
'root' => storage_path(),
],
- 'ftp' => [
- 'driver' => 'ftp',
- 'host' => 'ftp.example.com',
- 'username' => 'your-username',
- 'password' => 'your-password',
- ],
-
's3' => [
'driver' => 's3',
'key' => env('STORAGE_S3_KEY', 'your-key'),
'use_path_style_endpoint' => env('STORAGE_S3_ENDPOINT', null) !== null,
],
- 'rackspace' => [
- 'driver' => 'rackspace',
- 'username' => 'your-username',
- 'key' => 'your-key',
- 'container' => 'your-container',
- 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
- 'region' => 'IAD',
- 'url_type' => 'publicURL',
- ],
-
],
];
<?php
+use \Illuminate\Support\Str;
+
/**
* Session configuration options.
*
// By setting this option to true, session cookies will only be sent back
// to the server if the browser has a HTTPS connection. This will keep
// the cookie from being sent to you if it can not be done securely.
- 'secure' => env('SESSION_SECURE_COOKIE', false),
+ 'secure' => env('SESSION_SECURE_COOKIE', null)
+ ?? Str::startsWith(env('APP_URL'), 'https:'),
// HTTP Access Only
// Setting this value to true will prevent JavaScript from accessing the
// This option determines how your cookies behave when cross-site requests
// take place, and can be used to mitigate CSRF attacks. By default, we
// do not enable this as other CSRF protection services are in place.
- // Options: lax, strict
- 'same_site' => null,
+ // Options: lax, strict, none
+ 'same_site' => 'lax',
];
* @var string
*/
protected $signature = 'bookstack:cleanup-images
- {--a|all : Include images that are used in page revisions}
- {--f|force : Actually run the deletions}
+ {--a|all : Also delete images that are only used in old revisions}
+ {--f|force : Actually run the deletions, Defaults to a dry-run}
';
/**
namespace BookStack\Console\Commands;
-use BookStack\Entities\PageRevision;
+use BookStack\Entities\Models\PageRevision;
use Illuminate\Console\Command;
class ClearRevisions extends Command
namespace BookStack\Console\Commands;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Repos\BookshelfRepo;
use Illuminate\Console\Command;
/**
* Create a new command instance.
- *
- * @param UserRepo $userRepo
*/
public function __construct(UserRepo $userRepo)
{
namespace BookStack\Console\Commands;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Tools\SearchIndex;
use DB;
use Illuminate\Console\Command;
*/
protected $description = 'Re-index all content for searching';
- protected $searchService;
+ protected $searchIndex;
/**
* Create a new command instance.
- *
- * @param SearchService $searchService
*/
- public function __construct(SearchService $searchService)
+ public function __construct(SearchIndex $searchIndex)
{
parent::__construct();
- $this->searchService = $searchService;
+ $this->searchIndex = $searchIndex;
}
/**
$connection = DB::getDefaultConnection();
if ($this->option('database') !== null) {
DB::setDefaultConnection($this->option('database'));
- $this->searchService->setConnection(DB::connection($this->option('database')));
}
- $this->searchService->indexAllEntities();
+ $this->searchIndex->indexAllEntities();
DB::setDefaultConnection($connection);
$this->comment('Search index regenerated');
}
<?php namespace BookStack\Entities;
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\ShelfContext;
use Illuminate\View\View;
class BreadcrumbsViewComposer
/**
* BreadcrumbsViewComposer constructor.
- * @param EntityContext $entityContextManager
+ * @param ShelfContext $entityContextManager
*/
- public function __construct(EntityContext $entityContextManager)
+ public function __construct(ShelfContext $entityContextManager)
{
$this->entityContextManager = $entityContextManager;
}
+++ /dev/null
-<?php namespace BookStack\Entities;
-
-use Illuminate\Support\Collection;
-
-/**
- * Class Chapter
- * @property Collection<Page> $pages
- * @package BookStack\Entities
- */
-class Chapter extends BookChild
-{
- public $searchFactor = 1.3;
-
- protected $fillable = ['name', 'description', 'priority', 'book_id'];
- protected $hidden = ['restricted', 'pivot'];
-
- /**
- * Get the pages that this chapter contains.
- * @param string $dir
- * @return mixed
- */
- public function pages($dir = 'ASC')
- {
- return $this->hasMany(Page::class)->orderBy('priority', $dir);
- }
-
- /**
- * Get the url of this chapter.
- * @param string|bool $path
- * @return string
- */
- public function getUrl($path = false)
- {
- $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
- $fullPath = '/books/' . urlencode($bookSlug) . '/chapter/' . urlencode($this->slug);
-
- if ($path !== false) {
- $fullPath .= '/' . trim($path, '/');
- }
-
- return url($fullPath);
- }
-
- /**
- * Get an excerpt of this chapter's description to the specified length or less.
- * @param int $length
- * @return string
- */
- public function getExcerpt(int $length = 100)
- {
- $description = $this->text ?? $this->description;
- return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
- }
-
- /**
- * Check if this chapter has any child pages.
- * @return bool
- */
- public function hasChildren()
- {
- return count($this->pages) > 0;
- }
-
- /**
- * Get the visible pages in this chapter.
- */
- public function getVisiblePages(): Collection
- {
- return $this->pages()->visible()
- ->orderBy('draft', 'desc')
- ->orderBy('priority', 'asc')
- ->get();
- }
-}
<?php namespace BookStack\Entities;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
+
/**
* Class EntityProvider
*
* Provides access to the core entity models.
* Wrapped up in this provider since they are often used together
* so this is a neater alternative to injecting all in individually.
- *
- * @package BookStack\Entities
*/
class EntityProvider
{
*/
public $pageRevision;
- /**
- * EntityProvider constructor.
- */
- public function __construct(
- Bookshelf $bookshelf,
- Book $book,
- Chapter $chapter,
- Page $page,
- PageRevision $pageRevision
- ) {
- $this->bookshelf = $bookshelf;
- $this->book = $book;
- $this->chapter = $chapter;
- $this->page = $page;
- $this->pageRevision = $pageRevision;
+
+ public function __construct()
+ {
+ $this->bookshelf = new Bookshelf();
+ $this->book = new Book();
+ $this->chapter = new Chapter();
+ $this->page = new Page();
+ $this->pageRevision = new PageRevision();
}
/**
* Fetch all core entity types as an associated array
* with their basic names as the keys.
- * @return [string => Entity]
+ * @return array<Entity>
*/
public function all(): array
{
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Uploads\Image;
use Exception;
* @property string $description
* @property int $image_id
* @property Image|null $cover
- * @package BookStack\Entities
*/
class Book extends Entity implements HasCoverImage
{
public $searchFactor = 2;
protected $fillable = ['name', 'description'];
- protected $hidden = ['restricted', 'pivot', 'image_id'];
+ protected $hidden = ['restricted', 'pivot', 'image_id', 'deleted_at'];
/**
* Get the url for this book.
- * @param string|bool $path
- * @return string
*/
- public function getUrl($path = false)
+ public function getUrl(string $path = ''): string
{
- if ($path !== false) {
- return url('/books/' . urlencode($this->slug) . '/' . trim($path, '/'));
- }
- return url('/books/' . urlencode($this->slug));
+ return url('/books/' . implode('/', [urlencode($this->slug), trim($path, '/')]));
}
/**
$chapters = $this->chapters()->visible()->get();
return $pages->concat($chapters)->sortBy('priority')->sortByDesc('draft');
}
-
- /**
- * Get an excerpt of this book's description to the specified length or less.
- * @param int $length
- * @return string
- */
- public function getExcerpt(int $length = 100)
- {
- $description = $this->description;
- return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
- }
}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Book;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property Book $book
* @method Builder whereSlugs(string $bookSlug, string $childSlug)
*/
-class BookChild extends Entity
+abstract class BookChild extends Entity
{
/**
/**
* Get the book this page sits in.
- * @return BelongsTo
*/
public function book(): BelongsTo
{
- return $this->belongsTo(Book::class);
+ return $this->belongsTo(Book::class)->withTrashed();
}
/**
$this->save();
$this->refresh();
- // Update related activity
- $this->activity()->update(['book_id' => $newBookId]);
-
// Update all child pages if a chapter
if ($this instanceof Chapter) {
foreach ($this->pages as $page) {
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Uploads\Image;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
protected $fillable = ['name', 'description', 'image_id'];
- protected $hidden = ['restricted', 'image_id'];
+ protected $hidden = ['restricted', 'image_id', 'deleted_at'];
/**
* Get the books in this shelf.
/**
* Get the url for this bookshelf.
- * @param string|bool $path
- * @return string
*/
- public function getUrl($path = false)
+ public function getUrl(string $path = ''): string
{
- if ($path !== false) {
- return url('/shelves/' . urlencode($this->slug) . '/' . trim($path, '/'));
- }
- return url('/shelves/' . urlencode($this->slug));
+ return url('/shelves/' . implode('/', [urlencode($this->slug), trim($path, '/')]));
}
/**
return 'cover_shelf';
}
- /**
- * Get an excerpt of this book's description to the specified length or less.
- * @param int $length
- * @return string
- */
- public function getExcerpt(int $length = 100)
- {
- $description = $this->description;
- return mb_strlen($description) > $length ? mb_substr($description, 0, $length-3) . '...' : $description;
- }
-
/**
* Check if this shelf contains the given book.
* @param Book $book
--- /dev/null
+<?php namespace BookStack\Entities\Models;
+
+use Illuminate\Support\Collection;
+
+/**
+ * Class Chapter
+ * @property Collection<Page> $pages
+ */
+class Chapter extends BookChild
+{
+ public $searchFactor = 1.3;
+
+ protected $fillable = ['name', 'description', 'priority', 'book_id'];
+ protected $hidden = ['restricted', 'pivot', 'deleted_at'];
+
+ /**
+ * Get the pages that this chapter contains.
+ * @param string $dir
+ * @return mixed
+ */
+ public function pages($dir = 'ASC')
+ {
+ return $this->hasMany(Page::class)->orderBy('priority', $dir);
+ }
+
+ /**
+ * Get the url of this chapter.
+ */
+ public function getUrl($path = ''): string
+ {
+ $parts = [
+ 'books',
+ urlencode($this->getAttribute('bookSlug') ?? $this->book->slug),
+ 'chapter',
+ urlencode($this->slug),
+ trim($path, '/'),
+ ];
+
+ return url('/' . implode('/', $parts));
+ }
+
+ /**
+ * Get the visible pages in this chapter.
+ */
+ public function getVisiblePages(): Collection
+ {
+ return $this->pages()->visible()
+ ->orderBy('draft', 'desc')
+ ->orderBy('priority', 'asc')
+ ->get();
+ }
+}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Interfaces\Loggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
-class Deletion extends Model
+class Deletion extends Model implements Loggable
{
/**
return $record;
}
+ public function logDescriptor(): string
+ {
+ $deletable = $this->deletable()->first();
+ return "Deletion ({$this->id}) for {$deletable->getType()} ({$deletable->id}) {$deletable->name}";
+ }
}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Actions\Activity;
use BookStack\Actions\Comment;
use BookStack\Actions\View;
use BookStack\Auth\Permissions\EntityPermission;
use BookStack\Auth\Permissions\JointPermission;
+use BookStack\Entities\Tools\SearchIndex;
+use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Facades\Permissions;
-use BookStack\Ownable;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
+use BookStack\Traits\HasOwner;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
* @method static Entity|Builder hasPermission(string $permission)
* @method static Builder withLastView()
* @method static Builder withViewCount()
- *
- * @package BookStack\Entities
*/
-class Entity extends Ownable
+abstract class Entity extends Model
{
use SoftDeletes;
+ use HasCreatorAndUpdater;
+ use HasOwner;
/**
* @var string - Name of property where the main text content is found
/**
* Get the entities that are visible to the current user.
*/
- public function scopeVisible(Builder $query)
+ public function scopeVisible(Builder $query): Builder
{
return $this->scopeHasPermission($query, 'view');
}
/**
* Compares this entity to another given entity.
* Matches by comparing class and id.
- * @param $entity
- * @return bool
*/
- public function matches($entity)
+ public function matches(Entity $entity): bool
{
return [get_class($this), $this->id] === [get_class($entity), $entity->id];
}
/**
- * Checks if an entity matches or contains another given entity.
- * @param Entity $entity
- * @return bool
+ * Checks if the current entity matches or contains the given.
*/
- public function matchesOrContains(Entity $entity)
+ public function matchesOrContains(Entity $entity): bool
{
- $matches = [get_class($this), $this->id] === [get_class($entity), $entity->id];
-
- if ($matches) {
+ if ($this->matches($entity)) {
return true;
}
/**
* Gets the activity objects for this entity.
- * @return MorphMany
*/
- public function activity()
+ public function activity(): MorphMany
{
return $this->morphMany(Activity::class, 'entity')
->orderBy('created_at', 'desc');
/**
* Get View objects for this entity.
*/
- public function views()
+ public function views(): MorphMany
{
return $this->morphMany(View::class, 'viewable');
}
/**
* Get the Tag models that have been user assigned to this entity.
- * @return MorphMany
*/
- public function tags()
+ public function tags(): MorphMany
{
return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
}
/**
* Get the comments for an entity
- * @param bool $orderByCreated
- * @return MorphMany
*/
- public function comments($orderByCreated = true)
+ public function comments(bool $orderByCreated = true): MorphMany
{
$query = $this->morphMany(Comment::class, 'entity');
return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
/**
* Get the related search terms.
- * @return MorphMany
*/
- public function searchTerms()
+ public function searchTerms(): MorphMany
{
return $this->morphMany(SearchTerm::class, 'entity');
}
/**
* Get this entities restrictions.
*/
- public function permissions()
+ public function permissions(): MorphMany
{
return $this->morphMany(EntityPermission::class, 'restrictable');
}
/**
* Check if this entity has a specific restriction set against it.
- * @param $role_id
- * @param $action
- * @return bool
*/
- public function hasRestriction($role_id, $action)
+ public function hasRestriction(int $role_id, string $action): bool
{
return $this->permissions()->where('role_id', '=', $role_id)
->where('action', '=', $action)->count() > 0;
}
/**
- * Get entity type.
- * @return mixed
+ * Get the entity type as a simple lowercase word.
*/
- public static function getType()
+ public static function getType(): string
{
- return strtolower(static::getClassName());
- }
-
- /**
- * Get an instance of an entity of the given type.
- * @param $type
- * @return Entity
- */
- public static function getEntityInstance($type)
- {
- $types = ['Page', 'Book', 'Chapter', 'Bookshelf'];
- $className = str_replace([' ', '-', '_'], '', ucwords($type));
- if (!in_array($className, $types)) {
- return null;
- }
-
- return app('BookStack\\Entities\\' . $className);
+ $className = array_slice(explode('\\', static::class), -1, 1)[0];
+ return strtolower($className);
}
/**
/**
* Get the body text of this entity.
- * @return mixed
*/
- public function getText()
+ public function getText(): string
{
- return $this->{$this->textField};
+ return $this->{$this->textField} ?? '';
}
/**
* Get an excerpt of this entity's descriptive content to the specified length.
- * @param int $length
- * @return mixed
*/
- public function getExcerpt(int $length = 100)
+ public function getExcerpt(int $length = 100): string
{
$text = $this->getText();
+
if (mb_strlen($text) > $length) {
$text = mb_substr($text, 0, $length-3) . '...';
}
+
return trim($text);
}
/**
* Get the url of this entity
- * @param $path
- * @return string
*/
- public function getUrl($path = '/')
- {
- return $path;
- }
+ abstract public function getUrl(string $path = '/'): string;
/**
* Get the parent entity if existing.
*/
public function indexForSearch()
{
- $searchService = app()->make(SearchService::class);
- $searchService->indexEntity(clone $this);
+ app(SearchIndex::class)->indexEntity(clone $this);
}
/**
*/
public function refreshSlug(): string
{
- $generator = new SlugGenerator($this);
- $this->slug = $generator->generate();
+ $this->slug = (new SlugGenerator)->generate($this);
return $this->slug;
}
}
<?php
-namespace BookStack\Entities;
+namespace BookStack\Entities\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
+use BookStack\Entities\Tools\PageContent;
use BookStack\Uploads\Attachment;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
public $textField = 'text';
- protected $hidden = ['html', 'markdown', 'text', 'restricted', 'pivot'];
+ protected $hidden = ['html', 'markdown', 'text', 'restricted', 'pivot', 'deleted_at'];
+
+ protected $casts = [
+ 'draft' => 'boolean',
+ 'template' => 'boolean',
+ ];
/**
* Get the entities that are visible to the current user.
*/
- public function scopeVisible(Builder $query)
+ public function scopeVisible(Builder $query): Builder
{
$query = Permissions::enforceDraftVisiblityOnQuery($query);
return parent::scopeVisible($query);
}
/**
- * Get the url for this page.
- * @param string|bool $path
- * @return string
+ * Get the url of this page.
*/
- public function getUrl($path = false)
+ public function getUrl($path = ''): string
{
- $bookSlug = $this->getAttribute('bookSlug') ? $this->getAttribute('bookSlug') : $this->book->slug;
- $midText = $this->draft ? '/draft/' : '/page/';
- $idComponent = $this->draft ? $this->id : urlencode($this->slug);
-
- $url = '/books/' . urlencode($bookSlug) . $midText . $idComponent;
- if ($path !== false) {
- $url .= '/' . trim($path, '/');
- }
+ $parts = [
+ 'books',
+ urlencode($this->getAttribute('bookSlug') ?? $this->book->slug),
+ $this->draft ? 'draft' : 'page',
+ $this->draft ? $this->id : urlencode($this->slug),
+ trim($path, '/'),
+ ];
- return url($url);
+ return url('/' . implode('/', $parts));
}
/**
{
return $this->revisions()->first();
}
+
+ /**
+ * Get this page for JSON display.
+ */
+ public function forJsonDisplay(): Page
+ {
+ $refreshed = $this->refresh()->unsetRelations()->load(['tags', 'createdBy', 'updatedBy', 'ownedBy']);
+ $refreshed->setHidden(array_diff($refreshed->getHidden(), ['html', 'markdown']));
+ $refreshed->html = (new PageContent($refreshed))->render();
+ return $refreshed;
+ }
}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Auth\User;
+use BookStack\Entities\Models\Page;
use BookStack\Model;
use Carbon\Carbon;
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Models;
use BookStack\Model;
namespace BookStack\Entities\Repos;
+use BookStack\Actions\ActivityType;
use BookStack\Actions\TagRepo;
-use BookStack\Entities\Book;
-use BookStack\Entities\Entity;
-use BookStack\Entities\HasCoverImage;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\HasCoverImage;
use BookStack\Exceptions\ImageUploadException;
+use BookStack\Facades\Activity;
use BookStack\Uploads\ImageRepo;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
protected $imageRepo;
- /**
- * BaseRepo constructor.
- * @param $tagRepo
- */
public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo)
{
$this->tagRepo = $tagRepo;
$entity->forceFill([
'created_by' => user()->id,
'updated_by' => user()->id,
+ 'owned_by' => user()->id,
]);
$entity->refreshSlug();
$entity->save();
$entity->save();
}
}
-
- /**
- * Update the permissions of an entity.
- */
- public function updatePermissions(Entity $entity, bool $restricted, Collection $permissions = null)
- {
- $entity->restricted = $restricted;
- $entity->permissions()->delete();
-
- if (!is_null($permissions)) {
- $entityPermissionData = $permissions->flatMap(function ($restrictions, $roleId) {
- return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
- return [
- 'role_id' => $roleId,
- 'action' => strtolower($action),
- ] ;
- });
- });
-
- $entity->permissions()->createMany($entityPermissionData);
- }
-
- $entity->save();
- $entity->rebuildPermissions();
- }
}
<?php namespace BookStack\Entities\Repos;
+use BookStack\Actions\ActivityType;
use BookStack\Actions\TagRepo;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
+use BookStack\Facades\Activity;
use BookStack\Uploads\ImageRepo;
use Exception;
-use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
/**
* BookRepo constructor.
- * @param $tagRepo
*/
public function __construct(BaseRepo $baseRepo, TagRepo $tagRepo, ImageRepo $imageRepo)
{
*/
public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator
{
- return Book::visible()->orderBy($sort, $order)->paginate($count);
+ return Book::visible()->with('cover')->orderBy($sort, $order)->paginate($count);
}
/**
{
$book = new Book();
$this->baseRepo->create($book, $input);
+ Activity::addForEntity($book, ActivityType::BOOK_CREATE);
return $book;
}
public function update(Book $book, array $input): Book
{
$this->baseRepo->update($book, $input);
+ Activity::addForEntity($book, ActivityType::BOOK_UPDATE);
return $book;
}
$this->baseRepo->updateCoverImage($book, $coverImage, $removeImage);
}
- /**
- * Update the permissions of a book.
- */
- public function updatePermissions(Book $book, bool $restricted, Collection $permissions = null)
- {
- $this->baseRepo->updatePermissions($book, $restricted, $permissions);
- }
-
/**
* Remove a book from the system.
* @throws Exception
{
$trashCan = new TrashCan();
$trashCan->softDestroyBook($book);
+ Activity::addForEntity($book, ActivityType::BOOK_DELETE);
+
$trashCan->autoClearOld();
}
}
<?php namespace BookStack\Entities\Repos;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\NotFoundException;
+use BookStack\Facades\Activity;
use Exception;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\UploadedFile;
/**
* BookshelfRepo constructor.
- * @param $baseRepo
*/
public function __construct(BaseRepo $baseRepo)
{
public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator
{
return Bookshelf::visible()
- ->with('visibleBooks')
+ ->with(['visibleBooks', 'cover'])
->orderBy($sort, $order)
->paginate($count);
}
$shelf = new Bookshelf();
$this->baseRepo->create($shelf, $input);
$this->updateBooks($shelf, $bookIds);
+ Activity::addForEntity($shelf, ActivityType::BOOKSHELF_CREATE);
return $shelf;
}
/**
- * Create a new shelf in the system.
+ * Update an existing shelf in the system using the given input.
*/
public function update(Bookshelf $shelf, array $input, ?array $bookIds): Bookshelf
{
$this->updateBooks($shelf, $bookIds);
}
+ Activity::addForEntity($shelf, ActivityType::BOOKSHELF_UPDATE);
return $shelf;
}
$this->baseRepo->updateCoverImage($shelf, $coverImage, $removeImage);
}
- /**
- * Update the permissions of a bookshelf.
- */
- public function updatePermissions(Bookshelf $shelf, bool $restricted, Collection $permissions = null)
- {
- $this->baseRepo->updatePermissions($shelf, $restricted, $permissions);
- }
-
/**
* Copy down the permissions of the given shelf to all child books.
*/
{
$trashCan = new TrashCan();
$trashCan->softDestroyShelf($shelf);
+ Activity::addForEntity($shelf, ActivityType::BOOKSHELF_DELETE);
$trashCan->autoClearOld();
}
}
<?php namespace BookStack\Entities\Repos;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\NotFoundException;
+use BookStack\Facades\Activity;
use Exception;
use Illuminate\Support\Collection;
$chapter->book_id = $parentBook->id;
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
$this->baseRepo->create($chapter, $input);
+ Activity::addForEntity($chapter, ActivityType::CHAPTER_CREATE);
return $chapter;
}
public function update(Chapter $chapter, array $input): Chapter
{
$this->baseRepo->update($chapter, $input);
+ Activity::addForEntity($chapter, ActivityType::CHAPTER_UPDATE);
return $chapter;
}
- /**
- * Update the permissions of a chapter.
- */
- public function updatePermissions(Chapter $chapter, bool $restricted, Collection $permissions = null)
- {
- $this->baseRepo->updatePermissions($chapter, $restricted, $permissions);
- }
-
/**
* Remove a chapter from the system.
* @throws Exception
{
$trashCan = new TrashCan();
$trashCan->softDestroyChapter($chapter);
+ Activity::addForEntity($chapter, ActivityType::CHAPTER_DELETE);
$trashCan->autoClearOld();
}
throw new MoveOperationException('Chapters can only be moved into books');
}
+ /** @var Book $parent */
$parent = Book::visible()->where('id', '=', $entityId)->first();
if ($parent === null) {
throw new MoveOperationException('Book to move chapter into not found');
$chapter->changeBook($parent->id);
$chapter->rebuildPermissions();
+ Activity::addForEntity($chapter, ActivityType::CHAPTER_MOVE);
+
return $parent;
}
}
<?php namespace BookStack\Entities\Repos;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Managers\TrashCan;
-use BookStack\Entities\Page;
-use BookStack\Entities\PageRevision;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
use BookStack\Exceptions\PermissionsException;
+use BookStack\Facades\Activity;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
* Get a page by ID.
* @throws NotFoundException
*/
- public function getById(int $id): Page
+ public function getById(int $id, array $relations = ['book']): Page
{
- $page = Page::visible()->with(['book'])->find($id);
+ $page = Page::visible()->with($relations)->find($id);
if (!$page) {
throw new NotFoundException(trans('errors.page_not_found'));
$page = (new Page())->forceFill([
'name' => trans('entities.pages_initial_name'),
'created_by' => user()->id,
+ 'owned_by' => user()->id,
'updated_by' => user()->id,
'draft' => true,
]);
public function publishDraft(Page $draft, array $input): Page
{
$this->baseRepo->update($draft, $input);
- if (isset($input['template']) && userCan('templates-manage')) {
- $draft->template = ($input['template'] === 'true');
- }
+ $this->updateTemplateStatusAndContentFromInput($draft, $input);
- $pageContent = new PageContent($draft);
- $pageContent->setNewHTML($input['html']);
$draft->draft = false;
$draft->revision_count = 1;
$draft->priority = $this->getNewPriority($draft);
$this->savePageRevision($draft, trans('entities.pages_initial_revision'));
$draft->indexForSearch();
- return $draft->refresh();
+ $draft->refresh();
+
+ Activity::addForEntity($draft, ActivityType::PAGE_CREATE);
+ return $draft;
}
/**
// Hold the old details to compare later
$oldHtml = $page->html;
$oldName = $page->name;
+ $oldMarkdown = $page->markdown;
- if (isset($input['template']) && userCan('templates-manage')) {
- $page->template = ($input['template'] === 'true');
- }
-
- $pageContent = new PageContent($page);
- $pageContent->setNewHTML($input['html']);
+ $this->updateTemplateStatusAndContentFromInput($page, $input);
$this->baseRepo->update($page, $input);
// Update with new details
$page->revision_count++;
-
- if (setting('app-editor') !== 'markdown') {
- $page->markdown = '';
- }
-
$page->save();
// Remove all update drafts for this user & page.
$this->getUserDraftQuery($page)->delete();
// Save a revision after updating
- $summary = $input['summary'] ?? null;
- if ($oldHtml !== $input['html'] || $oldName !== $input['name'] || $summary !== null) {
+ $summary = trim($input['summary'] ?? "");
+ $htmlChanged = isset($input['html']) && $input['html'] !== $oldHtml;
+ $nameChanged = isset($input['name']) && $input['name'] !== $oldName;
+ $markdownChanged = isset($input['markdown']) && $input['markdown'] !== $oldMarkdown;
+ if ($htmlChanged || $nameChanged || $markdownChanged || $summary) {
$this->savePageRevision($page, $summary);
}
+ Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
return $page;
}
+ protected function updateTemplateStatusAndContentFromInput(Page $page, array $input)
+ {
+ if (isset($input['template']) && userCan('templates-manage')) {
+ $page->template = ($input['template'] === 'true');
+ }
+
+ $pageContent = new PageContent($page);
+ if (!empty($input['markdown'] ?? '')) {
+ $pageContent->setNewMarkdown($input['markdown']);
+ } else {
+ $pageContent->setNewHTML($input['html']);
+ }
+ }
+
/**
* Saves a page revision into the system.
*/
- protected function savePageRevision(Page $page, string $summary = null)
+ protected function savePageRevision(Page $page, string $summary = null): PageRevision
{
$revision = new PageRevision($page->getAttributes());
- if (setting('app-editor') !== 'markdown') {
- $revision->markdown = '';
- }
-
$revision->page_id = $page->id;
$revision->slug = $page->slug;
$revision->book_slug = $page->book->slug;
{
// If the page itself is a draft simply update that
if ($page->draft) {
- $page->fill($input);
if (isset($input['html'])) {
- $content = new PageContent($page);
- $content->setNewHTML($input['html']);
+ (new PageContent($page))->setNewHTML($input['html']);
}
+ $page->fill($input);
$page->save();
return $page;
}
{
$trashCan = new TrashCan();
$trashCan->softDestroyPage($page);
+ Activity::addForEntity($page, ActivityType::PAGE_DELETE);
$trashCan->autoClearOld();
}
public function restoreRevision(Page $page, int $revisionId): Page
{
$page->revision_count++;
- $this->savePageRevision($page);
-
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
+
$page->fill($revision->toArray());
$content = new PageContent($page);
- $content->setNewHTML($revision->html);
+
+ if (!empty($revision->markdown)) {
+ $content->setNewMarkdown($revision->markdown);
+ } else {
+ $content->setNewHTML($revision->html);
+ }
+
$page->updated_by = user()->id;
$page->refreshSlug();
$page->save();
-
$page->indexForSearch();
+
+ $summary = trans('entities.pages_revision_restored_from', ['id' => strval($revisionId), 'summary' => $revision->summary]);
+ $this->savePageRevision($page, $summary);
+
+ Activity::addForEntity($page, ActivityType::PAGE_RESTORE);
return $page;
}
* @throws MoveOperationException
* @throws PermissionsException
*/
- public function move(Page $page, string $parentIdentifier): Book
+ public function move(Page $page, string $parentIdentifier): Entity
{
$parent = $this->findParentByIdentifier($parentIdentifier);
if ($parent === null) {
$page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id);
$page->rebuildPermissions();
- return ($parent instanceof Book ? $parent : $parent->book);
+ Activity::addForEntity($page, ActivityType::PAGE_MOVE);
+ return $parent;
}
/**
return $parentClass::visible()->where('id', '=', $entityId)->first();
}
- /**
- * Update the permissions of a page.
- */
- public function updatePermissions(Page $page, bool $restricted, Collection $permissions = null)
- {
- $this->baseRepo->updatePermissions($page, $restricted, $permissions);
- }
-
/**
* Change the page's parent to the given entity.
*/
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Book;
-use BookStack\Entities\BookChild;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
use BookStack\Exceptions\SortOperationException;
use Illuminate\Support\Collection;
/**
* BookContents constructor.
- * @param $book
*/
public function __construct(Book $book)
{
$pages->groupBy('chapter_id')->each(function ($pages, $chapter_id) use ($chapterMap, &$lonePages) {
$chapter = $chapterMap->get($chapter_id);
if ($chapter) {
- $chapter->setAttribute('pages', collect($pages)->sortBy($this->bookChildSortFunc()));
+ $chapter->setAttribute('visible_pages', collect($pages)->sortBy($this->bookChildSortFunc()));
} else {
$lonePages = $lonePages->concat($pages);
}
});
+ $chapters->whereNull('visible_pages')->each(function (Chapter $chapter) {
+ $chapter->setAttribute('visible_pages', collect([]));
+ });
+
$all->each(function (Entity $entity) use ($renderPages) {
$entity->setRelation('book', $this->book);
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Uploads\ImageService;
use DomPDF;
use Exception;
use SnappyPDF;
use Throwable;
-class ExportService
+class ExportFormatter
{
protected $imageService;
protected function containHtml(string $htmlContent): string
{
$imageTagsOutput = [];
- preg_match_all("/\<img.*src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
+ preg_match_all("/\<img.*?src\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $imageTagsOutput);
// Replace image src with base64 encoded image strings
if (isset($imageTagsOutput[0]) && count($imageTagsOutput[0]) > 0) {
{
$text = $chapter->name . "\n\n";
$text .= $chapter->description . "\n\n";
- foreach ($chapter->pages as $page) {
+ foreach ($chapter->getVisiblePages() as $page) {
$text .= $this->pageToPlainText($page);
}
return $text;
*/
public function bookToPlainText(Book $book): string
{
- $bookTree = (new BookContents($book))->getTree(false, true);
+ $bookTree = (new BookContents($book))->getTree(false, false);
$text = $book->name . "\n\n";
foreach ($bookTree as $bookChild) {
if ($bookChild->isA('chapter')) {
--- /dev/null
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ConfigurableEnvironmentInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\Extension\Strikethrough\StrikethroughDelimiterProcessor;
+
+class CustomStrikeThroughExtension implements ExtensionInterface
+{
+
+ public function register(ConfigurableEnvironmentInterface $environment)
+ {
+ $environment->addDelimiterProcessor(new StrikethroughDelimiterProcessor());
+ $environment->addInlineRenderer(Strikethrough::class, new CustomStrikethroughRenderer());
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ElementRendererInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\HtmlElement;
+use League\CommonMark\Inline\Element\AbstractInline;
+use League\CommonMark\Inline\Renderer\InlineRendererInterface;
+
+/**
+ * This is a somewhat clone of the League\CommonMark\Extension\Strikethrough\StrikethroughRender
+ * class but modified slightly to use <s> HTML tags instead of <del> in order to
+ * match front-end markdown-it rendering.
+ */
+class CustomStrikethroughRenderer implements InlineRendererInterface
+{
+ public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
+ {
+ if (!($inline instanceof Strikethrough)) {
+ throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline));
+ }
+
+ return new HtmlElement('s', $inline->getData('attributes', []), $htmlRenderer->renderInlines($inline->children()));
+ }
+}
\ No newline at end of file
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\Markdown\CustomStrikeThroughExtension;
use DOMDocument;
use DOMNodeList;
use DOMXPath;
+use League\CommonMark\CommonMarkConverter;
+use League\CommonMark\Environment;
+use League\CommonMark\Extension\Table\TableExtension;
+use League\CommonMark\Extension\TaskList\TaskListExtension;
class PageContent
{
{
$this->page->html = $this->formatHtml($html);
$this->page->text = $this->toPlainText();
+ $this->page->markdown = '';
+ }
+
+ /**
+ * Update the content of the page with new provided Markdown content.
+ */
+ public function setNewMarkdown(string $markdown)
+ {
+ $this->page->markdown = $markdown;
+ $html = $this->markdownToHtml($markdown);
+ $this->page->html = $this->formatHtml($html);
+ $this->page->text = $this->toPlainText();
+ }
+
+ /**
+ * Convert the given Markdown content to a HTML string.
+ */
+ protected function markdownToHtml(string $markdown): string
+ {
+ $environment = Environment::createCommonMarkEnvironment();
+ $environment->addExtension(new TableExtension());
+ $environment->addExtension(new TaskListExtension());
+ $environment->addExtension(new CustomStrikeThroughExtension());
+ $converter = new CommonMarkConverter([], $environment);
+ return $converter->convertToHtml($markdown);
}
/**
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Page;
-use BookStack\Entities\PageRevision;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Models\PageRevision;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
--- /dev/null
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use BookStack\Facades\Activity;
+use Illuminate\Http\Request;
+use Illuminate\Support\Collection;
+
+class PermissionsUpdater
+{
+
+ /**
+ * Update an entities permissions from a permission form submit request.
+ */
+ public function updateFromPermissionsForm(Entity $entity, Request $request)
+ {
+ $restricted = $request->get('restricted') === 'true';
+ $permissions = $request->get('restrictions', null);
+ $ownerId = $request->get('owned_by', null);
+
+ $entity->restricted = $restricted;
+ $entity->permissions()->delete();
+
+ if (!is_null($permissions)) {
+ $entityPermissionData = $this->formatPermissionsFromRequestToEntityPermissions($permissions);
+ $entity->permissions()->createMany($entityPermissionData);
+ }
+
+ if (!is_null($ownerId)) {
+ $this->updateOwnerFromId($entity, intval($ownerId));
+ }
+
+ $entity->save();
+ $entity->rebuildPermissions();
+
+ Activity::addForEntity($entity, ActivityType::PERMISSIONS_UPDATE);
+ }
+
+ /**
+ * Update the owner of the given entity.
+ * Checks the user exists in the system first.
+ * Does not save the model, just updates it.
+ */
+ protected function updateOwnerFromId(Entity $entity, int $newOwnerId)
+ {
+ $newOwner = User::query()->find($newOwnerId);
+ if (!is_null($newOwner)) {
+ $entity->owned_by = $newOwner->id;
+ }
+ }
+
+ /**
+ * Format permissions provided from a permission form to be
+ * EntityPermission data.
+ */
+ protected function formatPermissionsFromRequestToEntityPermissions(array $permissions): Collection
+ {
+ return collect($permissions)->flatMap(function ($restrictions, $roleId) {
+ return collect($restrictions)->keys()->map(function ($action) use ($roleId) {
+ return [
+ 'role_id' => $roleId,
+ 'action' => strtolower($action),
+ ] ;
+ });
+ });
+ }
+}
--- /dev/null
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\SearchTerm;
+use Illuminate\Support\Collection;
+
+class SearchIndex
+{
+ /**
+ * @var SearchTerm
+ */
+ protected $searchTerm;
+
+ /**
+ * @var EntityProvider
+ */
+ protected $entityProvider;
+
+
+ public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider)
+ {
+ $this->searchTerm = $searchTerm;
+ $this->entityProvider = $entityProvider;
+ }
+
+
+ /**
+ * Index the given entity.
+ */
+ public function indexEntity(Entity $entity)
+ {
+ $this->deleteEntityTerms($entity);
+ $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+ $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
+ $terms = array_merge($nameTerms, $bodyTerms);
+ foreach ($terms as $index => $term) {
+ $terms[$index]['entity_type'] = $entity->getMorphClass();
+ $terms[$index]['entity_id'] = $entity->id;
+ }
+ $this->searchTerm->newQuery()->insert($terms);
+ }
+
+ /**
+ * Index multiple Entities at once
+ * @param Entity[] $entities
+ */
+ protected function indexEntities(array $entities)
+ {
+ $terms = [];
+ foreach ($entities as $entity) {
+ $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+ $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
+ foreach (array_merge($nameTerms, $bodyTerms) as $term) {
+ $term['entity_id'] = $entity->id;
+ $term['entity_type'] = $entity->getMorphClass();
+ $terms[] = $term;
+ }
+ }
+
+ $chunkedTerms = array_chunk($terms, 500);
+ foreach ($chunkedTerms as $termChunk) {
+ $this->searchTerm->newQuery()->insert($termChunk);
+ }
+ }
+
+ /**
+ * Delete and re-index the terms for all entities in the system.
+ */
+ public function indexAllEntities()
+ {
+ $this->searchTerm->newQuery()->truncate();
+
+ foreach ($this->entityProvider->all() as $entityModel) {
+ $selectFields = ['id', 'name', $entityModel->textField];
+ $entityModel->newQuery()
+ ->withTrashed()
+ ->select($selectFields)
+ ->chunk(1000, function (Collection $entities) {
+ $this->indexEntities($entities->all());
+ });
+ }
+ }
+
+ /**
+ * Delete related Entity search terms.
+ */
+ public function deleteEntityTerms(Entity $entity)
+ {
+ $entity->searchTerms()->delete();
+ }
+
+ /**
+ * Create a scored term array from the given text.
+ */
+ protected function generateTermArrayFromText(string $text, int $scoreAdjustment = 1): array
+ {
+ $tokenMap = []; // {TextToken => OccurrenceCount}
+ $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
+ $token = strtok($text, $splitChars);
+
+ while ($token !== false) {
+ if (!isset($tokenMap[$token])) {
+ $tokenMap[$token] = 0;
+ }
+ $tokenMap[$token]++;
+ $token = strtok($splitChars);
+ }
+
+ $terms = [];
+ foreach ($tokenMap as $token => $count) {
+ $terms[] = [
+ 'term' => $token,
+ 'score' => $count * $scoreAdjustment
+ ];
+ }
+
+ return $terms;
+ }
+}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
use Illuminate\Http\Request;
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
-class SearchService
+class SearchRunner
{
- /**
- * @var SearchTerm
- */
- protected $searchTerm;
/**
* @var EntityProvider
*/
protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
- /**
- * SearchService constructor.
- */
- public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
+
+ public function __construct(EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
{
- $this->searchTerm = $searchTerm;
$this->entityProvider = $entityProvider;
$this->db = $db;
$this->permissionService = $permissionService;
}
- /**
- * Set the database connection
- */
- public function setConnection(Connection $connection)
- {
- $this->db = $connection;
- }
-
/**
* Search all entities in the system.
* The provided count is for each entity to search,
$search = $this->buildEntitySearchQuery($opts, $entityType)->where('book_id', '=', $bookId)->take(20)->get();
$results = $results->merge($search);
}
+
return $results->sortByDesc('score')->take(20);
}
/**
- * Search a book for entities
+ * Search a chapter for entities
*/
public function searchChapter(int $chapterId, string $searchString): Collection
{
* matching instead of the items themselves.
* @return \Illuminate\Database\Eloquent\Collection|int|static[]
*/
- public function searchEntityTable(SearchOptions $searchOpts, string $entityType = 'page', int $page = 1, int $count = 20, string $action = 'view', bool $getCount = false)
+ protected function searchEntityTable(SearchOptions $searchOpts, string $entityType = 'page', int $page = 1, int $count = 20, string $action = 'view', bool $getCount = false)
{
$query = $this->buildEntitySearchQuery($searchOpts, $entityType, $action);
if ($getCount) {
// Handle normal search terms
if (count($searchOpts->searches) > 0) {
- $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
+ $rawScoreSum = $this->db->raw('SUM(score) as score');
+ $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', $rawScoreSum);
$subQuery->where('entity_type', '=', $entity->getMorphClass());
$subQuery->where(function (Builder $query) use ($searchOpts) {
foreach ($searchOpts->searches as $inputTerm) {
$query->orWhere('term', 'like', $inputTerm .'%');
}
})->groupBy('entity_type', 'entity_id');
- $entitySelect->join(\DB::raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
+ $entitySelect->join($this->db->raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
$join->on('id', '=', 'entity_id');
})->selectRaw($entity->getTable().'.*, s.score')->orderBy('score', 'desc');
$entitySelect->mergeBindings($subQuery);
}
// Handle exact term matching
- if (count($searchOpts->exacts) > 0) {
- $entitySelect->where(function (EloquentBuilder $query) use ($searchOpts, $entity) {
- foreach ($searchOpts->exacts as $inputTerm) {
- $query->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
- $query->where('name', 'like', '%'.$inputTerm .'%')
- ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
- });
- }
+ foreach ($searchOpts->exacts as $inputTerm) {
+ $entitySelect->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
+ $query->where('name', 'like', '%'.$inputTerm .'%')
+ ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
});
}
return $query;
}
- /**
- * Index the given entity.
- */
- public function indexEntity(Entity $entity)
- {
- $this->deleteEntityTerms($entity);
- $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
- $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
- $terms = array_merge($nameTerms, $bodyTerms);
- foreach ($terms as $index => $term) {
- $terms[$index]['entity_type'] = $entity->getMorphClass();
- $terms[$index]['entity_id'] = $entity->id;
- }
- $this->searchTerm->newQuery()->insert($terms);
- }
-
- /**
- * Index multiple Entities at once
- * @param \BookStack\Entities\Entity[] $entities
- */
- protected function indexEntities($entities)
- {
- $terms = [];
- foreach ($entities as $entity) {
- $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
- $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
- foreach (array_merge($nameTerms, $bodyTerms) as $term) {
- $term['entity_id'] = $entity->id;
- $term['entity_type'] = $entity->getMorphClass();
- $terms[] = $term;
- }
- }
-
- $chunkedTerms = array_chunk($terms, 500);
- foreach ($chunkedTerms as $termChunk) {
- $this->searchTerm->newQuery()->insert($termChunk);
- }
- }
-
- /**
- * Delete and re-index the terms for all entities in the system.
- */
- public function indexAllEntities()
- {
- $this->searchTerm->truncate();
-
- foreach ($this->entityProvider->all() as $entityModel) {
- $selectFields = ['id', 'name', $entityModel->textField];
- $entityModel->newQuery()
- ->withTrashed()
- ->select($selectFields)
- ->chunk(1000, function ($entities) {
- $this->indexEntities($entities);
- });
- }
- }
-
- /**
- * Delete related Entity search terms.
- * @param Entity $entity
- */
- public function deleteEntityTerms(Entity $entity)
- {
- $entity->searchTerms()->delete();
- }
-
- /**
- * Create a scored term array from the given text.
- * @param $text
- * @param float|int $scoreAdjustment
- * @return array
- */
- protected function generateTermArrayFromText($text, $scoreAdjustment = 1)
- {
- $tokenMap = []; // {TextToken => OccurrenceCount}
- $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
- $token = strtok($text, $splitChars);
-
- while ($token !== false) {
- if (!isset($tokenMap[$token])) {
- $tokenMap[$token] = 0;
- }
- $tokenMap[$token]++;
- $token = strtok($splitChars);
- }
-
- $terms = [];
- foreach ($tokenMap as $token => $count) {
- $terms[] = [
- 'term' => $token,
- 'score' => $count * $scoreAdjustment
- ];
- }
- return $terms;
- }
-
-
-
-
/**
* Custom entity search filters
*/
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use Illuminate\Session\Store;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
-class EntityContext
+class ShelfContext
{
- protected $session;
-
protected $KEY_SHELF_CONTEXT_ID = 'context_bookshelf_id';
- /**
- * EntityContextManager constructor.
- */
- public function __construct(Store $session)
- {
- $this->session = $session;
- }
-
/**
* Get the current bookshelf context for the given book.
*/
public function getContextualShelfForBook(Book $book): ?Bookshelf
{
- $contextBookshelfId = $this->session->get($this->KEY_SHELF_CONTEXT_ID, null);
+ $contextBookshelfId = session()->get($this->KEY_SHELF_CONTEXT_ID, null);
if (!is_int($contextBookshelfId)) {
return null;
/**
* Store the current contextual shelf ID.
- * @param int $shelfId
*/
public function setShelfContext(int $shelfId)
{
- $this->session->put($this->KEY_SHELF_CONTEXT_ID, $shelfId);
+ session()->put($this->KEY_SHELF_CONTEXT_ID, $shelfId);
}
/**
*/
public function clearShelfContext()
{
- $this->session->forget($this->KEY_SHELF_CONTEXT_ID);
+ session()->forget($this->KEY_SHELF_CONTEXT_ID);
}
}
--- /dev/null
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use Illuminate\Support\Collection;
+
+class SiblingFetcher
+{
+
+ /**
+ * Search among the siblings of the entity of given type and id.
+ */
+ public function fetch(string $entityType, int $entityId): Collection
+ {
+ $entity = (new EntityProvider)->get($entityType)->visible()->findOrFail($entityId);
+ $entities = [];
+
+ // Page in chapter
+ if ($entity->isA('page') && $entity->chapter) {
+ $entities = $entity->chapter->getVisiblePages();
+ }
+
+ // Page in book or chapter
+ if (($entity->isA('page') && !$entity->chapter) || $entity->isA('chapter')) {
+ $entities = $entity->book->getDirectChildren();
+ }
+
+ // Book
+ // Gets just the books in a shelf if shelf is in context
+ if ($entity->isA('book')) {
+ $contextShelf = (new ShelfContext)->getContextualShelfForBook($entity);
+ if ($contextShelf) {
+ $entities = $contextShelf->visibleBooks()->get();
+ } else {
+ $entities = Book::visible()->get();
+ }
+ }
+
+ // Shelve
+ if ($entity->isA('bookshelf')) {
+ $entities = Bookshelf::visible()->get();
+ }
+
+ return $entities;
+ }
+}
-<?php namespace BookStack\Entities;
+<?php namespace BookStack\Entities\Tools;
+use BookStack\Entities\Models\Entity;
use Illuminate\Support\Str;
class SlugGenerator
{
- protected $entity;
-
- /**
- * SlugGenerator constructor.
- * @param $entity
- */
- public function __construct(Entity $entity)
- {
- $this->entity = $entity;
- }
-
/**
* Generate a fresh slug for the given entity.
* The slug will generated so it does not conflict within the same parent item.
*/
- public function generate(): string
+ public function generate(Entity $entity): string
{
- $slug = $this->formatNameAsSlug($this->entity->name);
- while ($this->slugInUse($slug)) {
+ $slug = $this->formatNameAsSlug($entity->name);
+ while ($this->slugInUse($slug, $entity)) {
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
}
return $slug;
* Check if a slug is already in-use for this
* type of model within the same parent.
*/
- protected function slugInUse(string $slug): bool
+ protected function slugInUse(string $slug, Entity $entity): bool
{
- $query = $this->entity->newQuery()->where('slug', '=', $slug);
+ $query = $entity->newQuery()->where('slug', '=', $slug);
- if ($this->entity instanceof BookChild) {
- $query->where('book_id', '=', $this->entity->book_id);
+ if ($entity instanceof BookChild) {
+ $query->where('book_id', '=', $entity->book_id);
}
- if ($this->entity->id) {
- $query->where('id', '!=', $this->entity->id);
+ if ($entity->id) {
+ $query->where('id', '!=', $entity->id);
}
return $query->count() > 0;
-<?php namespace BookStack\Entities\Managers;
+<?php namespace BookStack\Entities\Tools;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Deletion;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Models\Entity;
use BookStack\Entities\EntityProvider;
-use BookStack\Entities\HasCoverImage;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\HasCoverImage;
+use BookStack\Entities\Models\Page;
use BookStack\Exceptions\NotifyException;
use BookStack\Facades\Activity;
use BookStack\Uploads\AttachmentService;
*/
public function getTrashedCounts(): array
{
- $provider = app(EntityProvider::class);
$counts = [];
/** @var Entity $instance */
- foreach ($provider->all() as $key => $instance) {
+ foreach ((new EntityProvider)->all() as $key => $instance) {
$counts[$key] = $instance->newQuery()->onlyTrashed()->count();
}
$count++;
};
- if ($entity->isA('chapter') || $entity->isA('book')) {
+ if ($entity instanceof Chapter || $entity instanceof Book) {
$entity->pages()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
}
- if ($entity->isA('book')) {
+ if ($entity instanceof Book) {
$entity->chapters()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
}
/**
* Destroy the given entity.
+ * @throws Exception
*/
protected function destroyEntity(Entity $entity): int
{
- if ($entity->isA('page')) {
+ if ($entity instanceof Page) {
return $this->destroyPage($entity);
}
- if ($entity->isA('chapter')) {
+ if ($entity instanceof Chapter) {
return $this->destroyChapter($entity);
}
- if ($entity->isA('book')) {
+ if ($entity instanceof Book) {
return $this->destroyBook($entity);
}
- if ($entity->isA('shelf')) {
+ if ($entity instanceof Bookshelf) {
return $this->destroyShelf($entity);
}
}
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
-class ApiController extends Controller
+abstract class ApiController extends Controller
{
protected $rules = [];
<?php namespace BookStack\Http\Controllers\Api;
use BookStack\Api\ApiDocsGenerator;
-use Cache;
-use Illuminate\Support\Collection;
class ApiDocsController extends ApiController
{
*/
public function display()
{
- $docs = $this->getDocs();
+ $docs = ApiDocsGenerator::generateConsideringCache();
+ $this->setPageTitle(trans('settings.users_api_tokens_docs'));
return view('api-docs.index', [
'docs' => $docs,
]);
/**
* Show a JSON view of the API docs data.
*/
- public function json() {
- $docs = $this->getDocs();
- return response()->json($docs);
- }
-
- /**
- * Get the base docs data.
- * Checks and uses the system cache for quick re-fetching.
- */
- protected function getDocs(): Collection
+ public function json()
{
- $appVersion = trim(file_get_contents(base_path('version')));
- $cacheKey = 'api-docs::' . $appVersion;
- if (Cache::has($cacheKey) && config('app.env') === 'production') {
- $docs = Cache::get($cacheKey);
- } else {
- $docs = (new ApiDocsGenerator())->generate();
- Cache::put($cacheKey, $docs, 60*24);
- }
-
- return $docs;
+ $docs = ApiDocsGenerator::generateConsideringCache();
+ return response()->json($docs);
}
}
<?php namespace BookStack\Http\Controllers\Api;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Exceptions\NotifyException;
-use BookStack\Facades\Activity;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
],
];
- /**
- * BooksApiController constructor.
- */
public function __construct(BookRepo $bookRepo)
{
$this->bookRepo = $bookRepo;
{
$books = Book::visible();
return $this->apiListingResponse($books, [
- 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'image_id',
+ 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', 'image_id',
]);
}
$requestData = $this->validate($request, $this->rules['create']);
$book = $this->bookRepo->create($requestData);
- Activity::add($book, 'book_create', $book->id);
-
return response()->json($book);
}
*/
public function read(string $id)
{
- $book = Book::visible()->with(['tags', 'cover', 'createdBy', 'updatedBy'])->findOrFail($id);
+ $book = Book::visible()->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy'])->findOrFail($id);
return response()->json($book);
}
$requestData = $this->validate($request, $this->rules['update']);
$book = $this->bookRepo->update($book, $requestData);
- Activity::add($book, 'book_update', $book->id);
return response()->json($book);
}
/**
- * Delete a single book from the system.
- * @throws NotifyException
- * @throws BindingResolutionException
+ * Delete a single book.
+ * This will typically send the book to the recycle bin.
+ * @throws \Exception
*/
public function delete(string $id)
{
$this->checkOwnablePermission('book-delete', $book);
$this->bookRepo->destroy($book);
- Activity::addMessage('book_delete', $book->name);
-
return response('', 204);
}
}
\ No newline at end of file
<?php namespace BookStack\Http\Controllers\Api;
-use BookStack\Entities\Book;
-use BookStack\Entities\ExportService;
-use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\ExportFormatter;
use Throwable;
class BookExportApiController extends ApiController
{
- protected $bookRepo;
- protected $exportService;
+ protected $exportFormatter;
- /**
- * BookExportController constructor.
- */
- public function __construct(BookRepo $bookRepo, ExportService $exportService)
+ public function __construct(ExportFormatter $exportFormatter)
{
- $this->bookRepo = $bookRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function exportPdf(int $id)
{
$book = Book::visible()->findOrFail($id);
- $pdfContent = $this->exportService->bookToPdf($book);
+ $pdfContent = $this->exportFormatter->bookToPdf($book);
return $this->downloadResponse($pdfContent, $book->slug . '.pdf');
}
public function exportHtml(int $id)
{
$book = Book::visible()->findOrFail($id);
- $htmlContent = $this->exportService->bookToContainedHtml($book);
+ $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
return $this->downloadResponse($htmlContent, $book->slug . '.html');
}
public function exportPlainText(int $id)
{
$book = Book::visible()->findOrFail($id);
- $textContent = $this->exportService->bookToPlainText($book);
+ $textContent = $this->exportFormatter->bookToPlainText($book);
return $this->downloadResponse($textContent, $book->slug . '.txt');
}
}
<?php namespace BookStack\Http\Controllers\Api;
-use BookStack\Facades\Activity;
use BookStack\Entities\Repos\BookshelfRepo;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Bookshelf;
use Exception;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
/**
* BookshelfApiController constructor.
- * @param BookshelfRepo $bookshelfRepo
*/
public function __construct(BookshelfRepo $bookshelfRepo)
{
{
$shelves = Bookshelf::visible();
return $this->apiListingResponse($shelves, [
- 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'image_id',
+ 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', 'image_id',
]);
}
$bookIds = $request->get('books', []);
$shelf = $this->bookshelfRepo->create($requestData, $bookIds);
- Activity::add($shelf, 'bookshelf_create', $shelf->id);
return response()->json($shelf);
}
public function read(string $id)
{
$shelf = Bookshelf::visible()->with([
- 'tags', 'cover', 'createdBy', 'updatedBy',
+ 'tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy',
'books' => function (BelongsToMany $query) {
$query->visible()->get(['id', 'name', 'slug']);
}
$this->checkOwnablePermission('bookshelf-update', $shelf);
$requestData = $this->validate($request, $this->rules['update']);
-
$bookIds = $request->get('books', null);
$shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
- Activity::add($shelf, 'bookshelf_update', $shelf->id);
-
return response()->json($shelf);
}
/**
- * Delete a single shelf from the system.
+ * Delete a single shelf.
+ * This will typically send the shelf to the recycle bin.
* @throws Exception
*/
public function delete(string $id)
$this->checkOwnablePermission('bookshelf-delete', $shelf);
$this->bookshelfRepo->destroy($shelf);
- Activity::addMessage('bookshelf_delete', $shelf->name);
-
return response('', 204);
}
}
\ No newline at end of file
<?php namespace BookStack\Http\Controllers\Api;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Facades\Activity;
use Illuminate\Database\Eloquent\Relations\HasMany;
$chapters = Chapter::visible();
return $this->apiListingResponse($chapters, [
'id', 'book_id', 'name', 'slug', 'description', 'priority',
- 'created_at', 'updated_at', 'created_by', 'updated_by',
+ 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by',
]);
}
$this->checkOwnablePermission('chapter-create', $book);
$chapter = $this->chapterRepo->create($request->all(), $book);
- Activity::add($chapter, 'chapter_create', $book->id);
-
return response()->json($chapter->load(['tags']));
}
*/
public function read(string $id)
{
- $chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'pages' => function (HasMany $query) {
+ $chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'ownedBy', 'pages' => function (HasMany $query) {
$query->visible()->get(['id', 'name', 'slug']);
}])->findOrFail($id);
return response()->json($chapter);
$this->checkOwnablePermission('chapter-update', $chapter);
$updatedChapter = $this->chapterRepo->update($chapter, $request->all());
- Activity::add($chapter, 'chapter_update', $chapter->book->id);
-
return response()->json($updatedChapter->load(['tags']));
}
/**
- * Delete a chapter from the system.
+ * Delete a chapter.
+ * This will typically send the chapter to the recycle bin.
*/
public function delete(string $id)
{
$this->checkOwnablePermission('chapter-delete', $chapter);
$this->chapterRepo->destroy($chapter);
- Activity::addMessage('chapter_delete', $chapter->name, $chapter->book->id);
-
return response('', 204);
}
}
<?php namespace BookStack\Http\Controllers\Api;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Entities\Repos\BookRepo;
use Throwable;
class ChapterExportApiController extends ApiController
{
- protected $chapterRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* ChapterExportController constructor.
*/
- public function __construct(BookRepo $chapterRepo, ExportService $exportService)
+ public function __construct(ExportFormatter $exportFormatter)
{
- $this->chapterRepo = $chapterRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function exportPdf(int $id)
{
$chapter = Chapter::visible()->findOrFail($id);
- $pdfContent = $this->exportService->chapterToPdf($chapter);
+ $pdfContent = $this->exportFormatter->chapterToPdf($chapter);
return $this->downloadResponse($pdfContent, $chapter->slug . '.pdf');
}
public function exportHtml(int $id)
{
$chapter = Chapter::visible()->findOrFail($id);
- $htmlContent = $this->exportService->chapterToContainedHtml($chapter);
+ $htmlContent = $this->exportFormatter->chapterToContainedHtml($chapter);
return $this->downloadResponse($htmlContent, $chapter->slug . '.html');
}
public function exportPlainText(int $id)
{
$chapter = Chapter::visible()->findOrFail($id);
- $textContent = $this->exportService->chapterToPlainText($chapter);
+ $textContent = $this->exportFormatter->chapterToPlainText($chapter);
return $this->downloadResponse($textContent, $chapter->slug . '.txt');
}
}
--- /dev/null
+<?php
+
+namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\PageRepo;
+use BookStack\Exceptions\PermissionsException;
+use Exception;
+use Illuminate\Http\Request;
+
+class PageApiController extends ApiController
+{
+ protected $pageRepo;
+
+ protected $rules = [
+ 'create' => [
+ 'book_id' => 'required_without:chapter_id|integer',
+ 'chapter_id' => 'required_without:book_id|integer',
+ 'name' => 'required|string|max:255',
+ 'html' => 'required_without:markdown|string',
+ 'markdown' => 'required_without:html|string',
+ 'tags' => 'array',
+ ],
+ 'update' => [
+ 'book_id' => 'required|integer',
+ 'chapter_id' => 'required|integer',
+ 'name' => 'string|min:1|max:255',
+ 'html' => 'string',
+ 'markdown' => 'string',
+ 'tags' => 'array',
+ ],
+ ];
+
+ public function __construct(PageRepo $pageRepo)
+ {
+ $this->pageRepo = $pageRepo;
+ }
+
+ /**
+ * Get a listing of pages visible to the user.
+ */
+ public function list()
+ {
+ $pages = Page::visible();
+ return $this->apiListingResponse($pages, [
+ 'id', 'book_id', 'chapter_id', 'name', 'slug', 'priority',
+ 'draft', 'template',
+ 'created_at', 'updated_at',
+ 'created_by', 'updated_by', 'owned_by',
+ ]);
+ }
+
+ /**
+ * Create a new page in the system.
+ *
+ * The ID of a parent book or chapter is required to indicate
+ * where this page should be located.
+ *
+ * Any HTML content provided should be kept to a single-block depth of plain HTML
+ * elements to remain compatible with the BookStack front-end and editors.
+ */
+ public function create(Request $request)
+ {
+ $this->validate($request, $this->rules['create']);
+
+ if ($request->has('chapter_id')) {
+ $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+ } else {
+ $parent = Book::visible()->findOrFail($request->get('book_id'));
+ }
+ $this->checkOwnablePermission('page-create', $parent);
+
+ $draft = $this->pageRepo->getNewDraftPage($parent);
+ $this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create'])));
+
+ return response()->json($draft->forJsonDisplay());
+ }
+
+ /**
+ * View the details of a single page.
+ *
+ * Pages will always have HTML content. They may have markdown content
+ * if the markdown editor was used to last update the page.
+ */
+ public function read(string $id)
+ {
+ $page = $this->pageRepo->getById($id, []);
+ return response()->json($page->forJsonDisplay());
+ }
+
+ /**
+ * Update the details of a single page.
+ *
+ * See the 'create' action for details on the provided HTML/Markdown.
+ * Providing a 'book_id' or 'chapter_id' property will essentially move
+ * the page into that parent element if you have permissions to do so.
+ */
+ public function update(Request $request, string $id)
+ {
+ $page = $this->pageRepo->getById($id, []);
+ $this->checkOwnablePermission('page-update', $page);
+
+ $parent = null;
+ if ($request->has('chapter_id')) {
+ $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+ } else if ($request->has('book_id')) {
+ $parent = Book::visible()->findOrFail($request->get('book_id'));
+ }
+
+ if ($parent && !$parent->matches($page->getParent())) {
+ $this->checkOwnablePermission('page-delete', $page);
+ try {
+ $this->pageRepo->move($page, $parent->getType() . ':' . $parent->id);
+ } catch (Exception $exception) {
+ if ($exception instanceof PermissionsException) {
+ $this->showPermissionError();
+ }
+
+ return $this->jsonError(trans('errors.selected_book_chapter_not_found'));
+ }
+ }
+
+ $updatedPage = $this->pageRepo->update($page, $request->all());
+ return response()->json($updatedPage->forJsonDisplay());
+ }
+
+ /**
+ * Delete a page.
+ * This will typically send the page to the recycle bin.
+ */
+ public function delete(string $id)
+ {
+ $page = $this->pageRepo->getById($id, []);
+ $this->checkOwnablePermission('page-delete', $page);
+
+ $this->pageRepo->destroy($page);
+ return response('', 204);
+ }
+}
--- /dev/null
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\ExportFormatter;
+use Throwable;
+
+class PageExportApiController extends ApiController
+{
+ protected $exportFormatter;
+
+ public function __construct(ExportFormatter $exportFormatter)
+ {
+ $this->exportFormatter = $exportFormatter;
+ }
+
+ /**
+ * Export a page as a PDF file.
+ * @throws Throwable
+ */
+ public function exportPdf(int $id)
+ {
+ $page = Page::visible()->findOrFail($id);
+ $pdfContent = $this->exportFormatter->pageToPdf($page);
+ return $this->downloadResponse($pdfContent, $page->slug . '.pdf');
+ }
+
+ /**
+ * Export a page as a contained HTML file.
+ * @throws Throwable
+ */
+ public function exportHtml(int $id)
+ {
+ $page = Page::visible()->findOrFail($id);
+ $htmlContent = $this->exportFormatter->pageToContainedHtml($page);
+ return $this->downloadResponse($htmlContent, $page->slug . '.html');
+ }
+
+ /**
+ * Export a page as a plain text file.
+ */
+ public function exportPlainText(int $id)
+ {
+ $page = Page::visible()->findOrFail($id);
+ $textContent = $this->exportFormatter->pageToPlainText($page);
+ return $this->downloadResponse($textContent, $page->slug . '.txt');
+ }
+}
$this->attachmentService = $attachmentService;
$this->attachment = $attachment;
$this->pageRepo = $pageRepo;
- parent::__construct();
}
->orderBy($listDetails['sort'], $listDetails['order']);
if ($listDetails['event']) {
- $query->where('key', '=', $listDetails['event']);
+ $query->where('type', '=', $listDetails['event']);
}
if ($listDetails['date_from']) {
$activities = $query->paginate(100);
$activities->appends($listDetails);
- $keys = DB::table('activities')->select('key')->distinct()->pluck('key');
+ $types = DB::table('activities')->select('type')->distinct()->pluck('type');
$this->setPageTitle(trans('settings.audit'));
return view('settings.audit', [
'activities' => $activities,
'listDetails' => $listDetails,
- 'activityKeys' => $keys,
+ 'activityTypes' => $types,
]);
}
}
/**
* Create a new controller instance.
- *
- * @param EmailConfirmationService $emailConfirmationService
- * @param UserRepo $userRepo
*/
public function __construct(EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
{
$this->emailConfirmationService = $emailConfirmationService;
$this->userRepo = $userRepo;
- parent::__construct();
}
namespace BookStack\Http\Controllers\Auth;
+use BookStack\Actions\ActivityType;
use BookStack\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
{
$this->middleware('guest');
$this->middleware('guard:standard');
- parent::__construct();
}
$request->only('email')
);
+ if ($response === Password::RESET_LINK_SENT) {
+ $this->logActivity(ActivityType::AUTH_PASSWORD_RESET, $request->get('email'));
+ }
+
if ($response === Password::RESET_LINK_SENT || $response === Password::INVALID_USER) {
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
$this->showSuccessNotification($message);
namespace BookStack\Http\Controllers\Auth;
use Activity;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException;
-use BookStack\Exceptions\UserRegistrationException;
use BookStack\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
$this->socialAuthService = $socialAuthService;
$this->redirectPath = url('/');
$this->redirectAfterLogout = url('/login');
- parent::__construct();
}
public function username()
}
}
+ $this->logActivity(ActivityType::AUTH_LOGIN, $user);
return redirect()->intended($this->redirectPath());
}
$this->redirectTo = url('/');
$this->redirectPath = url('/');
- parent::__construct();
}
/**
namespace BookStack\Http\Controllers\Auth;
+use BookStack\Actions\ActivityType;
use BookStack\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
{
$this->middleware('guest');
$this->middleware('guard:standard');
- parent::__construct();
}
/**
{
$message = trans('auth.reset_password_success');
$this->showSuccessNotification($message);
+ $this->logActivity(ActivityType::AUTH_PASSWORD_RESET_UPDATE, user());
return redirect($this->redirectPath())
->with('status', trans($response));
}
*/
public function __construct(Saml2Service $samlService)
{
- parent::__construct();
$this->samlService = $samlService;
$this->middleware('guard:saml2');
}
$this->inviteService = $inviteService;
$this->userRepo = $userRepo;
-
- parent::__construct();
}
/**
<?php namespace BookStack\Http\Controllers;
use Activity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Tools\PermissionsUpdater;
+use BookStack\Entities\Tools\ShelfContext;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Exceptions\ImageUploadException;
-use BookStack\Exceptions\NotifyException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Throwable;
protected $bookRepo;
protected $entityContextManager;
- /**
- * BookController constructor.
- */
- public function __construct(EntityContext $entityContextManager, BookRepo $bookRepo)
+ public function __construct(ShelfContext $entityContextManager, BookRepo $bookRepo)
{
$this->bookRepo = $bookRepo;
$this->entityContextManager = $entityContextManager;
- parent::__construct();
}
/**
$book = $this->bookRepo->create($request->all());
$this->bookRepo->updateCoverImage($book, $request->file('image', null));
- Activity::add($book, 'book_create', $book->id);
if ($bookshelf) {
$bookshelf->appendBook($book);
- Activity::add($bookshelf, 'bookshelf_update');
+ Activity::addForEntity($bookshelf, ActivityType::BOOKSHELF_UPDATE);
}
return redirect($book->getUrl());
$resetCover = $request->has('image_reset');
$this->bookRepo->updateCoverImage($book, $request->file('image', null), $resetCover);
- Activity::add($book, 'book_update', $book->id);
-
return redirect($book->getUrl());
}
$book = $this->bookRepo->getBySlug($bookSlug);
$this->checkOwnablePermission('book-delete', $book);
- Activity::add($book, 'book_delete', $book->id);
$this->bookRepo->destroy($book);
return redirect('/books');
* Set the restrictions for this book.
* @throws Throwable
*/
- public function permissions(Request $request, string $bookSlug)
+ public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
$this->checkOwnablePermission('restrictions-manage', $book);
- $restricted = $request->get('restricted') === 'true';
- $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
- $this->bookRepo->updatePermissions($book, $restricted, $permissions);
+ $permissionsUpdater->updateFromPermissionsForm($book, $request);
$this->showSuccessNotification(trans('entities.books_permissions_updated'));
return redirect($book->getUrl());
namespace BookStack\Http\Controllers;
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Entities\Repos\BookRepo;
use Throwable;
{
protected $bookRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* BookExportController constructor.
*/
- public function __construct(BookRepo $bookRepo, ExportService $exportService)
+ public function __construct(BookRepo $bookRepo, ExportFormatter $exportFormatter)
{
$this->bookRepo = $bookRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function pdf(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $pdfContent = $this->exportService->bookToPdf($book);
+ $pdfContent = $this->exportFormatter->bookToPdf($book);
return $this->downloadResponse($pdfContent, $bookSlug . '.pdf');
}
public function html(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $htmlContent = $this->exportService->bookToContainedHtml($book);
+ $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
return $this->downloadResponse($htmlContent, $bookSlug . '.html');
}
public function plainText(string $bookSlug)
{
$book = $this->bookRepo->getBySlug($bookSlug);
- $textContent = $this->exportService->bookToPlainText($book);
+ $textContent = $this->exportFormatter->bookToPlainText($book);
return $this->downloadResponse($textContent, $bookSlug . '.txt');
}
}
namespace BookStack\Http\Controllers;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\BookContents;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Exceptions\SortOperationException;
use BookStack\Facades\Activity;
protected $bookRepo;
- /**
- * BookSortController constructor.
- * @param $bookRepo
- */
public function __construct(BookRepo $bookRepo)
{
$this->bookRepo = $bookRepo;
- parent::__construct();
}
/**
// Rebuild permissions and add activity for involved books.
$booksInvolved->each(function (Book $book) {
- Activity::add($book, 'book_sort', $book->id);
+ Activity::addForEntity($book, ActivityType::BOOK_SORT);
});
return redirect($book->getUrl());
<?php namespace BookStack\Http\Controllers;
use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\EntityContext;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\PermissionsUpdater;
+use BookStack\Entities\Tools\ShelfContext;
use BookStack\Entities\Repos\BookshelfRepo;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\NotFoundException;
protected $entityContextManager;
protected $imageRepo;
- /**
- * BookController constructor.
- */
- public function __construct(BookshelfRepo $bookshelfRepo, EntityContext $entityContextManager, ImageRepo $imageRepo)
+ public function __construct(BookshelfRepo $bookshelfRepo, ShelfContext $entityContextManager, ImageRepo $imageRepo)
{
$this->bookshelfRepo = $bookshelfRepo;
$this->entityContextManager = $entityContextManager;
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
$shelf = $this->bookshelfRepo->create($request->all(), $bookIds);
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null));
- Activity::add($shelf, 'bookshelf_create');
return redirect($shelf->getUrl());
}
$shelf = $this->bookshelfRepo->update($shelf, $request->all(), $bookIds);
$resetCover = $request->has('image_reset');
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null), $resetCover);
- Activity::add($shelf, 'bookshelf_update');
return redirect($shelf->getUrl());
}
$shelf = $this->bookshelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-delete', $shelf);
- Activity::add($shelf, 'bookshelf_delete');
$this->bookshelfRepo->destroy($shelf);
return redirect('/shelves');
/**
* Set the permissions for this bookshelf.
*/
- public function permissions(Request $request, string $slug)
+ public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);
- $restricted = $request->get('restricted') === 'true';
- $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
- $this->bookshelfRepo->updatePermissions($shelf, $restricted, $permissions);
+ $permissionsUpdater->updateFromPermissionsForm($shelf, $request);
$this->showSuccessNotification(trans('entities.shelves_permissions_updated'));
return redirect($shelf->getUrl());
<?php namespace BookStack\Http\Controllers;
-use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\BookContents;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\NotFoundException;
use Illuminate\Http\Request;
public function __construct(ChapterRepo $chapterRepo)
{
$this->chapterRepo = $chapterRepo;
- parent::__construct();
}
/**
$this->checkOwnablePermission('chapter-create', $book);
$chapter = $this->chapterRepo->create($request->all(), $book);
- Activity::add($chapter, 'chapter_create', $book->id);
return redirect($chapter->getUrl());
}
$this->checkOwnablePermission('chapter-update', $chapter);
$this->chapterRepo->update($chapter, $request->all());
- Activity::add($chapter, 'chapter_update', $chapter->book->id);
return redirect($chapter->getUrl());
}
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
$this->checkOwnablePermission('chapter-delete', $chapter);
- Activity::add($chapter, 'chapter_delete', $chapter->book->id);
$this->chapterRepo->destroy($chapter);
return redirect($chapter->book->getUrl());
return redirect()->back();
}
- Activity::add($chapter, 'chapter_move', $newBook->id);
-
$this->showSuccessNotification(trans('entities.chapter_move_success', ['bookName' => $newBook->name]));
return redirect($chapter->getUrl());
}
* Set the restrictions for this chapter.
* @throws NotFoundException
*/
- public function permissions(Request $request, string $bookSlug, string $chapterSlug)
+ public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
$this->checkOwnablePermission('restrictions-manage', $chapter);
- $restricted = $request->get('restricted') === 'true';
- $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
- $this->chapterRepo->updatePermissions($chapter, $restricted, $permissions);
+ $permissionsUpdater->updateFromPermissionsForm($chapter, $request);
$this->showSuccessNotification(trans('entities.chapters_permissions_success'));
return redirect($chapter->getUrl());
<?php namespace BookStack\Http\Controllers;
-use BookStack\Entities\ExportService;
+use BookStack\Entities\Tools\ExportFormatter;
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Exceptions\NotFoundException;
use Throwable;
{
protected $chapterRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* ChapterExportController constructor.
*/
- public function __construct(ChapterRepo $chapterRepo, ExportService $exportService)
+ public function __construct(ChapterRepo $chapterRepo, ExportFormatter $exportFormatter)
{
$this->chapterRepo = $chapterRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
public function pdf(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $pdfContent = $this->exportService->chapterToPdf($chapter);
+ $pdfContent = $this->exportFormatter->chapterToPdf($chapter);
return $this->downloadResponse($pdfContent, $chapterSlug . '.pdf');
}
public function html(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $containedHtml = $this->exportService->chapterToContainedHtml($chapter);
+ $containedHtml = $this->exportFormatter->chapterToContainedHtml($chapter);
return $this->downloadResponse($containedHtml, $chapterSlug . '.html');
}
public function plainText(string $bookSlug, string $chapterSlug)
{
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
- $chapterText = $this->exportService->chapterToPlainText($chapter);
+ $chapterText = $this->exportFormatter->chapterToPlainText($chapter);
return $this->downloadResponse($chapterText, $chapterSlug . '.txt');
}
}
<?php namespace BookStack\Http\Controllers;
use Activity;
+use BookStack\Actions\ActivityType;
use BookStack\Actions\CommentRepo;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
public function __construct(CommentRepo $commentRepo)
{
$this->commentRepo = $commentRepo;
- parent::__construct();
}
/**
// Create a new comment.
$this->checkPermission('comment-create-all');
$comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id'));
- Activity::add($page, 'commented_on', $page->book->id);
return view('comments.comment', ['comment' => $comment]);
}
namespace BookStack\Http\Controllers;
-use BookStack\Ownable;
+use BookStack\Facades\Activity;
+use BookStack\Interfaces\Loggable;
+use BookStack\HasCreatorAndUpdater;
+use BookStack\Model;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Exceptions\HttpResponseException;
-use Illuminate\Http\Request;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Response;
use Illuminate\Routing\Controller as BaseController;
-use Illuminate\Validation\ValidationException;
abstract class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
- /**
- * Controller constructor.
- */
- public function __construct()
- {
- //
- }
-
/**
* Check if the current user is signed in.
*/
/**
* Adds the page title into the view.
- * @param $title
*/
- public function setPageTitle($title)
+ public function setPageTitle(string $title)
{
view()->share('pageTitle', $title);
}
}
/**
- * Checks for a permission.
- * @param string $permissionName
- * @return bool|\Illuminate\Http\RedirectResponse
+ * Checks that the current user has the given permission otherwise throw an exception.
*/
- protected function checkPermission($permissionName)
+ protected function checkPermission(string $permission): void
{
- if (!user() || !user()->can($permissionName)) {
+ if (!user() || !user()->can($permission)) {
$this->showPermissionError();
}
- return true;
}
/**
- * Check the current user's permissions against an ownable item.
- * @param $permission
- * @param Ownable $ownable
- * @return bool
+ * Check the current user's permissions against an ownable item otherwise throw an exception.
*/
- protected function checkOwnablePermission($permission, Ownable $ownable)
+ protected function checkOwnablePermission(string $permission, Model $ownable): void
{
- if (userCan($permission, $ownable)) {
- return true;
+ if (!userCan($permission, $ownable)) {
+ $this->showPermissionError();
}
- return $this->showPermissionError();
}
/**
- * Check if a user has a permission or bypass if the callback is true.
- * @param $permissionName
- * @param $callback
- * @return bool
+ * Check if a user has a permission or bypass the permission
+ * check if the given callback resolves true.
*/
- protected function checkPermissionOr($permissionName, $callback)
+ protected function checkPermissionOr(string $permission, callable $callback): void
{
- $callbackResult = $callback();
- if ($callbackResult === false) {
- $this->checkPermission($permissionName);
+ if ($callback() !== true) {
+ $this->checkPermission($permission);
}
- return true;
}
/**
* Check if the current user has a permission or bypass if the provided user
* id matches the current user.
- * @param string $permissionName
- * @param int $userId
- * @return bool
*/
- protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
+ protected function checkPermissionOrCurrentUser(string $permission, int $userId): void
{
- return $this->checkPermissionOr($permissionName, function () use ($userId) {
+ $this->checkPermissionOr($permission, function () use ($userId) {
return $userId === user()->id;
});
}
/**
* Send back a json error message.
- * @param string $messageText
- * @param int $statusCode
- * @return mixed
*/
- protected function jsonError($messageText = "", $statusCode = 500)
+ protected function jsonError(string $messageText = "", int $statusCode = 500): JsonResponse
{
return response()->json(['message' => $messageText, 'status' => 'error'], $statusCode);
}
/**
* Create a response that forces a download in the browser.
- * @param string $content
- * @param string $fileName
- * @return \Illuminate\Http\Response
*/
- protected function downloadResponse(string $content, string $fileName)
+ protected function downloadResponse(string $content, string $fileName): Response
{
return response()->make($content, 200, [
'Content-Type' => 'application/octet-stream',
/**
* Show a positive, successful notification to the user on next view load.
- * @param string $message
*/
- protected function showSuccessNotification(string $message)
+ protected function showSuccessNotification(string $message): void
{
session()->flash('success', $message);
}
/**
* Show a warning notification to the user on next view load.
- * @param string $message
*/
- protected function showWarningNotification(string $message)
+ protected function showWarningNotification(string $message): void
{
session()->flash('warning', $message);
}
/**
* Show an error notification to the user on next view load.
- * @param string $message
*/
- protected function showErrorNotification(string $message)
+ protected function showErrorNotification(string $message): void
{
session()->flash('error', $message);
}
+ /**
+ * Log an activity in the system.
+ * @param string|Loggable
+ */
+ protected function logActivity(string $type, $detail = ''): void
+ {
+ Activity::add($type, $detail);
+ }
+
/**
* Get the validation rules for image files.
*/
<?php namespace BookStack\Http\Controllers;
use Activity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Repos\BookshelfRepo;
use Illuminate\Http\Response;
->where('draft', '=', true)
->where('created_by', '=', user()->id)
->orderBy('updated_at', 'desc')
+ ->with('book')
->take(6)
->get();
}
$recents = $this->isSignedIn() ?
Views::getUserRecentlyViewed(12*$recentFactor, 1)
: Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
- $recentlyUpdatedPages = Page::visible()->where('draft', false)
- ->orderBy('updated_at', 'desc')->take(12)->get();
+ $recentlyUpdatedPages = Page::visible()->with('book')
+ ->where('draft', false)
+ ->orderBy('updated_at', 'desc')
+ ->take(12)
+ ->get();
$homepageOptions = ['default', 'books', 'bookshelves', 'page'];
$homepageOption = setting('app-homepage-type', 'default');
/**
* Show the view for /robots.txt
- * @return $this
*/
public function getRobots()
{
$sitePublic = setting('app-public', false);
$allowRobots = config('app.allow_robots');
+
if ($allowRobots === null) {
$allowRobots = $sitePublic;
}
+
return response()
->view('common.robots', ['allowRobots' => $allowRobots])
->header('Content-Type', 'text/plain');
public function __construct(ImageRepo $imageRepo)
{
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
public function __construct(ImageRepo $imageRepo)
{
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
<?php namespace BookStack\Http\Controllers\Images;
-use BookStack\Entities\Page;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Http\Controllers\Controller;
-use BookStack\Entities\Repos\PageRepo;
use BookStack\Uploads\Image;
use BookStack\Uploads\ImageRepo;
use Exception;
use Illuminate\Filesystem\Filesystem as File;
-use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
$this->image = $image;
$this->file = $file;
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
namespace BookStack\Http\Controllers;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Notifications\TestEmail;
use BookStack\Uploads\ImageService;
use Illuminate\Http\Request;
public function cleanupImages(Request $request, ImageService $imageService)
{
$this->checkPermission('settings-manage');
+ $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'cleanup-images');
$checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
$dryRun = !($request->has('confirm'));
public function sendTestEmail()
{
$this->checkPermission('settings-manage');
+ $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email');
try {
user()->notify(new TestEmail());
<?php namespace BookStack\Http\Controllers;
-use Activity;
-use BookStack\Entities\Managers\BookContents;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Managers\PageEditActivity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Tools\PageEditActivity;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
+use BookStack\Entities\Tools\PermissionsUpdater;
use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\NotifyException;
use BookStack\Exceptions\PermissionsException;
public function __construct(PageRepo $pageRepo)
{
$this->pageRepo = $pageRepo;
- parent::__construct();
}
/**
$this->checkOwnablePermission('page-create', $draftPage->getParent());
$page = $this->pageRepo->publishDraft($draftPage, $request->all());
- Activity::add($page, 'page_create', $draftPage->book->id);
return redirect($page->getUrl());
}
$this->checkOwnablePermission('page-update', $page);
$this->pageRepo->update($page, $request->all());
- Activity::add($page, 'page_update', $page->book->id);
return redirect($page->getUrl());
}
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$this->checkOwnablePermission('page-delete', $page);
+ $parent = $page->getParent();
- $book = $page->book;
- $parent = $page->chapter ?? $book;
$this->pageRepo->destroy($page);
- Activity::add($page, 'page_delete', $page->book_id);
return redirect($parent->getUrl());
}
return redirect()->back();
}
- Activity::add($page, 'page_move', $page->book->id);
$this->showSuccessNotification(trans('entities.pages_move_success', ['parentName' => $parent->name]));
return redirect($page->getUrl());
}
return redirect()->back();
}
- Activity::add($pageCopy, 'page_create', $pageCopy->book->id);
-
$this->showSuccessNotification(trans('entities.pages_copy_success'));
return redirect($pageCopy->getUrl());
}
* @throws NotFoundException
* @throws Throwable
*/
- public function permissions(Request $request, string $bookSlug, string $pageSlug)
+ public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug, string $pageSlug)
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$this->checkOwnablePermission('restrictions-manage', $page);
- $restricted = $request->get('restricted') === 'true';
- $permissions = $request->filled('restrictions') ? collect($request->get('restrictions')) : null;
- $this->pageRepo->updatePermissions($page, $restricted, $permissions);
+ $permissionsUpdater->updateFromPermissionsForm($page, $request);
$this->showSuccessNotification(trans('entities.pages_permissions_success'));
return redirect($page->getUrl());
namespace BookStack\Http\Controllers;
-use BookStack\Entities\ExportService;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Tools\ExportFormatter;
+use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Exceptions\NotFoundException;
use Throwable;
{
protected $pageRepo;
- protected $exportService;
+ protected $exportFormatter;
/**
* PageExportController constructor.
- * @param PageRepo $pageRepo
- * @param ExportService $exportService
*/
- public function __construct(PageRepo $pageRepo, ExportService $exportService)
+ public function __construct(PageRepo $pageRepo, ExportFormatter $exportFormatter)
{
$this->pageRepo = $pageRepo;
- $this->exportService = $exportService;
- parent::__construct();
+ $this->exportFormatter = $exportFormatter;
}
/**
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$page->html = (new PageContent($page))->render();
- $pdfContent = $this->exportService->pageToPdf($page);
+ $pdfContent = $this->exportFormatter->pageToPdf($page);
return $this->downloadResponse($pdfContent, $pageSlug . '.pdf');
}
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
$page->html = (new PageContent($page))->render();
- $containedHtml = $this->exportService->pageToContainedHtml($page);
+ $containedHtml = $this->exportFormatter->pageToContainedHtml($page);
return $this->downloadResponse($containedHtml, $pageSlug . '.html');
}
public function plainText(string $bookSlug, string $pageSlug)
{
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
- $pageText = $this->exportService->pageToPlainText($page);
+ $pageText = $this->exportFormatter->pageToPlainText($page);
return $this->downloadResponse($pageText, $pageSlug . '.txt');
}
}
<?php namespace BookStack\Http\Controllers;
-use BookStack\Entities\Managers\PageContent;
+use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Exceptions\NotFoundException;
-use BookStack\Facades\Activity;
-use GatherContent\Htmldiff\Htmldiff;
+use Ssddanbrown\HtmlDiff\Diff;
class PageRevisionController extends Controller
{
public function __construct(PageRepo $pageRepo)
{
$this->pageRepo = $pageRepo;
- parent::__construct();
}
/**
$prev = $revision->getPrevious();
$prevContent = $prev->html ?? '';
- $diff = (new Htmldiff)->diff($prevContent, $revision->html);
+ $diff = Diff::excecute($prevContent, $revision->html);
$page->fill($revision->toArray());
// TODO - Refactor PageContent so we don't need to juggle this
$page = $this->pageRepo->restoreRevision($page, $revisionId);
- Activity::add($page, 'page_restore', $page->book->id);
return redirect($page->getUrl());
}
public function __construct(PageRepo $pageRepo)
{
$this->pageRepo = $pageRepo;
- parent::__construct();
}
/**
<?php namespace BookStack\Http\Controllers;
-use BookStack\Entities\Deletion;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Actions\ActivityType;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Tools\TrashCan;
class RecycleBinController extends Controller
{
$this->checkPermission('restrictions-manage-all');
return $next($request);
});
- parent::__construct();
}
{
$deletions = Deletion::query()->with(['deletable', 'deleter'])->paginate(10);
+ $this->setPageTitle(trans('settings.recycle_bin'));
return view('settings.recycle-bin.index', [
'deletions' => $deletions,
]);
{
/** @var Deletion $deletion */
$deletion = Deletion::query()->findOrFail($id);
+ $this->logActivity(ActivityType::RECYCLE_BIN_RESTORE, $deletion);
$restoreCount = (new TrashCan())->restoreFromDeletion($deletion);
$this->showSuccessNotification(trans('settings.recycle_bin_restore_notification', ['count' => $restoreCount]));
{
/** @var Deletion $deletion */
$deletion = Deletion::query()->findOrFail($id);
+ $this->logActivity(ActivityType::RECYCLE_BIN_DESTROY, $deletion);
$deleteCount = (new TrashCan())->destroyFromDeletion($deletion);
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
{
$deleteCount = (new TrashCan())->empty();
+ $this->logActivity(ActivityType::RECYCLE_BIN_EMPTY);
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
return redirect($this->recycleBinBaseUrl);
}
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
-class PermissionController extends Controller
+class RoleController extends Controller
{
protected $permissionsRepo;
public function __construct(PermissionsRepo $permissionsRepo)
{
$this->permissionsRepo = $permissionsRepo;
- parent::__construct();
}
/**
* Show a listing of the roles in the system.
*/
- public function listRoles()
+ public function list()
{
$this->checkPermission('user-roles-manage');
$roles = $this->permissionsRepo->getAllRoles();
/**
* Show the form to create a new role
*/
- public function createRole()
+ public function create()
{
$this->checkPermission('user-roles-manage');
return view('settings.roles.create');
/**
* Store a new role in the system.
*/
- public function storeRole(Request $request)
+ public function store(Request $request)
{
$this->checkPermission('user-roles-manage');
$this->validate($request, [
* Show the form for editing a user role.
* @throws PermissionsException
*/
- public function editRole(string $id)
+ public function edit(string $id)
{
$this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id);
* Updates a user role.
* @throws ValidationException
*/
- public function updateRole(Request $request, string $id)
+ public function update(Request $request, string $id)
{
$this->checkPermission('user-roles-manage');
$this->validate($request, [
* Show the view to delete a role.
* Offers the chance to migrate users.
*/
- public function showDeleteRole(string $id)
+ public function showDelete(string $id)
{
$this->checkPermission('user-roles-manage');
$role = $this->permissionsRepo->getRoleById($id);
* Migrate from a previous role if set.
* @throws Exception
*/
- public function deleteRole(Request $request, string $id)
+ public function delete(Request $request, string $id)
{
$this->checkPermission('user-roles-manage');
<?php namespace BookStack\Http\Controllers;
use BookStack\Actions\ViewService;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Managers\EntityContext;
-use BookStack\Entities\SearchService;
-use BookStack\Entities\SearchOptions;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\SearchRunner;
+use BookStack\Entities\Tools\ShelfContext;
+use BookStack\Entities\Tools\SearchOptions;
+use BookStack\Entities\Tools\SiblingFetcher;
use Illuminate\Http\Request;
class SearchController extends Controller
{
protected $viewService;
- protected $searchService;
+ protected $searchRunner;
protected $entityContextManager;
- /**
- * SearchController constructor.
- */
public function __construct(
ViewService $viewService,
- SearchService $searchService,
- EntityContext $entityContextManager
+ SearchRunner $searchRunner,
+ ShelfContext $entityContextManager
) {
$this->viewService = $viewService;
- $this->searchService = $searchService;
+ $this->searchRunner = $searchRunner;
$this->entityContextManager = $entityContextManager;
- parent::__construct();
}
/**
$page = intval($request->get('page', '0')) ?: 1;
$nextPageLink = url('/search?term=' . urlencode($fullSearchString) . '&page=' . ($page+1));
- $results = $this->searchService->searchEntities($searchOpts, 'all', $page, 20);
+ $results = $this->searchRunner->searchEntities($searchOpts, 'all', $page, 20);
return view('search.all', [
'entities' => $results['results'],
]);
}
-
/**
* Searches all entities within a book.
*/
public function searchBook(Request $request, int $bookId)
{
$term = $request->get('term', '');
- $results = $this->searchService->searchBook($bookId, $term);
+ $results = $this->searchRunner->searchBook($bookId, $term);
return view('partials.entity-list', ['entities' => $results]);
}
public function searchChapter(Request $request, int $chapterId)
{
$term = $request->get('term', '');
- $results = $this->searchService->searchChapter($chapterId, $term);
+ $results = $this->searchRunner->searchChapter($chapterId, $term);
return view('partials.entity-list', ['entities' => $results]);
}
// Search for entities otherwise show most popular
if ($searchTerm !== false) {
$searchTerm .= ' {type:'. implode('|', $entityTypes) .'}';
- $entities = $this->searchService->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
+ $entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
} else {
$entities = $this->viewService->getPopular(20, 0, $entityTypes, $permission);
}
$type = $request->get('entity_type', null);
$id = $request->get('entity_id', null);
- $entity = Entity::getEntityInstance($type)->newQuery()->visible()->find($id);
- if (!$entity) {
- return $this->jsonError(trans('errors.entity_not_found'), 404);
- }
-
- $entities = [];
-
- // Page in chapter
- if ($entity->isA('page') && $entity->chapter) {
- $entities = $entity->chapter->getVisiblePages();
- }
-
- // Page in book or chapter
- if (($entity->isA('page') && !$entity->chapter) || $entity->isA('chapter')) {
- $entities = $entity->book->getDirectChildren();
- }
-
- // Book
- // Gets just the books in a shelf if shelf is in context
- if ($entity->isA('book')) {
- $contextShelf = $this->entityContextManager->getContextualShelfForBook($entity);
- if ($contextShelf) {
- $entities = $contextShelf->visibleBooks()->get();
- } else {
- $entities = Book::visible()->get();
- }
- }
-
- // Shelve
- if ($entity->isA('bookshelf')) {
- $entities = Bookshelf::visible()->get();
- }
-
+ $entities = (new SiblingFetcher)->fetch($type, $id);
return view('partials.entity-list-basic', ['entities' => $entities, 'style' => 'compact']);
}
}
<?php namespace BookStack\Http\Controllers;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\User;
use BookStack\Uploads\ImageRepo;
use Illuminate\Http\Request;
public function __construct(ImageRepo $imageRepo)
{
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
// Cycles through posted settings and update them
foreach ($request->all() as $name => $value) {
+ $key = str_replace('setting-', '', trim($name));
if (strpos($name, 'setting-') !== 0) {
continue;
}
- $key = str_replace('setting-', '', trim($name));
setting()->put($key, $value);
}
setting()->remove('app-logo');
}
+ $section = $request->get('section', '');
+ $this->logActivity(ActivityType::SETTINGS_UPDATE, $section);
$this->showSuccessNotification(trans('settings.settings_save_success'));
- $redirectLocation = '/settings#' . $request->get('section', '');
+ $redirectLocation = '/settings#' . $section;
return redirect(rtrim($redirectLocation, '#'));
}
}
--- /dev/null
+<?php namespace BookStack\Http\Controllers;
+
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Str;
+
+class StatusController extends Controller
+{
+
+ /**
+ * Show the system status as a simple json page.
+ */
+ public function show()
+ {
+ $statuses = [
+ 'database' => $this->trueWithoutError(function () {
+ return DB::table('migrations')->count() > 0;
+ }),
+ 'cache' => $this->trueWithoutError(function () {
+ $rand = Str::random();
+ Cache::set('status_test', $rand);
+ return Cache::get('status_test') === $rand;
+ }),
+ 'session' => $this->trueWithoutError(function () {
+ $rand = Str::random();
+ Session::put('status_test', $rand);
+ return Session::get('status_test') === $rand;
+ }),
+ ];
+
+ $hasError = in_array(false, $statuses);
+ return response()->json($statuses, $hasError ? 500 : 200);
+ }
+
+ /**
+ * Check the callable passed returns true and does not throw an exception.
+ */
+ protected function trueWithoutError(callable $test): bool
+ {
+ try {
+ return $test() === true;
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+}
public function __construct(TagRepo $tagRepo)
{
$this->tagRepo = $tagRepo;
- parent::__construct();
}
/**
<?php namespace BookStack\Http\Controllers;
+use BookStack\Actions\ActivityType;
use BookStack\Api\ApiToken;
use BookStack\Auth\User;
use Illuminate\Http\Request;
-use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
session()->flash('api-token-secret:' . $token->id, $secret);
$this->showSuccessNotification(trans('settings.user_api_token_create_success'));
+ $this->logActivity(ActivityType::API_TOKEN_CREATE, $token);
+
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
}
])->save();
$this->showSuccessNotification(trans('settings.user_api_token_update_success'));
+ $this->logActivity(ActivityType::API_TOKEN_UPDATE, $token);
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
}
$token->delete();
$this->showSuccessNotification(trans('settings.user_api_token_delete_success'));
+ $this->logActivity(ActivityType::API_TOKEN_DELETE, $token);
+
return redirect($user->getEditUrl('#api_tokens'));
}
<?php namespace BookStack\Http\Controllers;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Auth\Access\UserInviteService;
use BookStack\Auth\User;
$this->userRepo = $userRepo;
$this->inviteService = $inviteService;
$this->imageRepo = $imageRepo;
- parent::__construct();
}
/**
'sort' => $request->get('sort', 'name'),
];
$users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
+
$this->setPageTitle(trans('settings.users'));
$users->appends($listDetails);
return view('users.index', ['users' => $users, 'listDetails' => $listDetails]);
$this->userRepo->downloadAndAssignUserAvatar($user);
+ $this->logActivity(ActivityType::USER_CREATE, $user);
return redirect('/settings/users');
}
$user->image_id = $image->id;
}
- // Delete the profile image if set to
+ // Delete the profile image if reset option is in request
if ($request->has('profile_image_reset')) {
$this->imageRepo->destroyImage($user->avatar);
}
$user->save();
$this->showSuccessNotification(trans('settings.users_edit_success'));
+ $this->logActivity(ActivityType::USER_UPDATE, $user);
$redirectUrl = userCan('users-manage') ? '/settings/users' : ('/settings/users/' . $user->id);
return redirect($redirectUrl);
* Remove the specified user from storage.
* @throws \Exception
*/
- public function destroy(int $id)
+ public function destroy(Request $request, int $id)
{
$this->preventAccessInDemoMode();
$this->checkPermissionOrCurrentUser('users-manage', $id);
$user = $this->userRepo->getById($id);
+ $newOwnerId = $request->get('new_owner_id', null);
if ($this->userRepo->isOnlyAdmin($user)) {
$this->showErrorNotification(trans('errors.users_cannot_delete_only_admin'));
return redirect($user->getEditUrl());
}
- $this->userRepo->destroy($user);
+ $this->userRepo->destroy($user, $newOwnerId);
$this->showSuccessNotification(trans('settings.users_delete_success'));
+ $this->logActivity(ActivityType::USER_DELETE, $user);
return redirect('/settings/users');
}
--- /dev/null
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+
+class UserSearchController extends Controller
+{
+ /**
+ * Search users in the system, with the response formatted
+ * for use in a select-style list.
+ */
+ public function forSelect(Request $request)
+ {
+ $search = $request->get('search', '');
+ $query = User::query()->orderBy('name', 'desc')
+ ->take(20);
+
+ if (!empty($search)) {
+ $query->where(function (Builder $query) use ($search) {
+ $query->where('email', 'like', '%' . $search . '%')
+ ->orWhere('name', 'like', '%' . $search . '%');
+ });
+ }
+
+ $users = $query->get();
+ return view('components.user-select-list', compact('users'));
+ }
+}
*/
protected $middlewareGroups = [
'web' => [
+ \BookStack\Http\Middleware\ControlIframeSecurity::class,
\BookStack\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\BookStack\Http\Middleware\VerifyCsrfToken::class,
\BookStack\Http\Middleware\Localization::class,
- \BookStack\Http\Middleware\GlobalViewData::class,
],
'api' => [
\BookStack\Http\Middleware\ThrottleApiRequests::class,
--- /dev/null
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Closure;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Sets CSP headers to restrict the hosts that BookStack can be
+ * iframed within. Also adjusts the cookie samesite options
+ * so that cookies will operate in the third-party context.
+ */
+class ControlIframeSecurity
+{
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @return mixed
+ */
+ public function handle($request, Closure $next)
+ {
+ $iframeHosts = collect(explode(' ', config('app.iframe_hosts', '')))->filter();
+ if ($iframeHosts->count() > 0) {
+ config()->set('session.same_site', 'none');
+ }
+
+ $iframeHosts->prepend("'self'");
+
+ $response = $next($request);
+ $cspValue = 'frame-ancestors ' . $iframeHosts->join(' ');
+ $response->headers->set('Content-Security-Policy', $cspValue);
+ return $response;
+ }
+}
+++ /dev/null
-<?php namespace BookStack\Http\Middleware;
-
-use Closure;
-use Illuminate\Http\Request;
-
-/**
- * Class GlobalViewData
- * Sets up data that is accessible to any view rendered by the web routes.
- */
-class GlobalViewData
-{
-
- /**
- * Handle an incoming request.
- *
- * @param Request $request
- * @param Closure $next
- * @return mixed
- */
- public function handle(Request $request, Closure $next)
- {
- view()->share('signedIn', auth()->check());
- view()->share('currentUser', user());
-
- return $next($request);
- }
-}
/**
* Array of right-to-left locales
- * @var array
*/
protected $rtlLocales = ['ar', 'he'];
/**
* Map of BookStack locale names to best-estimate system locale names.
- * @var array
*/
protected $localeMap = [
'ar' => 'ar',
'ja' => 'ja',
'ko' => 'ko_KR',
'nl' => 'nl_NL',
+ 'nb' => 'nb_NO',
'pl' => 'pl_PL',
'pt' => 'pl_PT',
'pt_BR' => 'pt_BR',
$defaultLang = config('app.locale');
config()->set('app.default_locale', $defaultLang);
- if (user()->isDefault() && config('app.auto_detect_locale')) {
- $locale = $this->autoDetectLocale($request, $defaultLang);
- } else {
- $locale = setting()->getUser(user(), 'language', $defaultLang);
- }
-
+ $locale = $this->getUserLocale($request, $defaultLang);
config()->set('app.lang', str_replace('_', '-', $this->getLocaleIso($locale)));
// Set text direction
return $next($request);
}
+ /**
+ * Get the locale specifically for the currently logged in user if available.
+ */
+ protected function getUserLocale(Request $request, string $default): string
+ {
+ try {
+ $user = user();
+ } catch (\Exception $exception) {
+ return $default;
+ }
+
+ if ($user->isDefault() && config('app.auto_detect_locale')) {
+ return $this->autoDetectLocale($request, $default);
+ }
+
+ return setting()->getUser($user, 'language', $default);
+ }
+
/**
* Autodetect the visitors locale by matching locales in their headers
* against the locales supported by BookStack.
- * @param Request $request
- * @param string $default
- * @return string
*/
- protected function autoDetectLocale(Request $request, string $default)
+ protected function autoDetectLocale(Request $request, string $default): string
{
$availableLocales = config('app.locales');
foreach ($request->getLanguages() as $lang) {
/**
* Get the ISO version of a BookStack language name
- * @param string $locale
- * @return string
*/
- public function getLocaleIso(string $locale)
+ public function getLocaleIso(string $locale): string
{
return $this->localeMap[$locale] ?? $locale;
}
/**
* Set the system date locale for localized date formatting.
* Will try both the standard locale name and the UTF8 variant.
- * @param string $locale
*/
protected function setSystemDateLocale(string $locale)
{
--- /dev/null
+<?php
+
+namespace BookStack\Interfaces;
+
+interface Loggable
+{
+ /**
+ * Get the string descriptor for this item.
+ */
+ public function logDescriptor(): string;
+}
\ No newline at end of file
+++ /dev/null
-<?php namespace BookStack;
-
-use BookStack\Auth\User;
-
-/**
- * @property int created_by
- * @property int updated_by
- */
-abstract class Ownable extends Model
-{
- /**
- * Relation for the user that created this entity.
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
- */
- public function createdBy()
- {
- return $this->belongsTo(User::class, 'created_by');
- }
-
- /**
- * Relation for the user that updated this entity.
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
- */
- public function updatedBy()
- {
- return $this->belongsTo(User::class, 'updated_by');
- }
-
- /**
- * Gets the class name.
- * @return string
- */
- public static function getClassName()
- {
- return strtolower(array_slice(explode('\\', static::class), -1, 1)[0]);
- }
-}
<?php namespace BookStack\Providers;
use Blade;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\BreadcrumbsViewComposer;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Settings\Setting;
use BookStack\Settings\SettingService;
+use Illuminate\Contracts\Cache\Repository;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Schema;
use URL;
-use Validator;
class AppServiceProvider extends ServiceProvider
{
URL::forceScheme($isHttps ? 'https' : 'http');
}
- // Custom validation methods
- Validator::extend('image_extension', function ($attribute, $value, $parameters, $validator) {
- $validImageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'webp'];
- return in_array(strtolower($value->getClientOriginalExtension()), $validImageExtensions);
- });
-
- Validator::extend('no_double_extension', function ($attribute, $value, $parameters, $validator) {
- $uploadName = $value->getClientOriginalName();
- return substr_count($uploadName, '.') < 2;
- });
-
- Validator::extend('safe_url', function ($attribute, $value, $parameters, $validator) {
- $cleanLinkName = strtolower(trim($value));
- $isJs = strpos($cleanLinkName, 'javascript:') === 0;
- $isData = strpos($cleanLinkName, 'data:') === 0;
- return !$isJs && !$isData;
- });
-
// Custom blade view directives
Blade::directive('icon', function ($expression) {
return "<?php echo icon($expression); ?>";
});
- Blade::directive('exposeTranslations', function ($expression) {
- return "<?php \$__env->startPush('translations'); ?>" .
- "<?php foreach({$expression} as \$key): ?>" .
- '<meta name="translation" key="<?php echo e($key); ?>" value="<?php echo e(trans($key)); ?>">' . "\n" .
- "<?php endforeach; ?>" .
- '<?php $__env->stopPush(); ?>';
- });
-
// Allow longer string lengths after upgrade to utf8mb4
Schema::defaultStringLength(191);
public function register()
{
$this->app->singleton(SettingService::class, function ($app) {
- return new SettingService($app->make(Setting::class), $app->make('Illuminate\Contracts\Cache\Repository'));
+ return new SettingService($app->make(Setting::class), $app->make(Repository::class));
});
}
}
--- /dev/null
+<?php
+
+namespace BookStack\Providers;
+
+use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\ServiceProvider;
+
+class CustomValidationServiceProvider extends ServiceProvider
+{
+
+ /**
+ * Register our custom validation rules when the application boots.
+ */
+ public function boot(): void
+ {
+ Validator::extend('image_extension', function ($attribute, $value, $parameters, $validator) {
+ $validImageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'webp'];
+ return in_array(strtolower($value->getClientOriginalExtension()), $validImageExtensions);
+ });
+
+ Validator::extend('no_double_extension', function ($attribute, $value, $parameters, $validator) {
+ $uploadName = $value->getClientOriginalName();
+ return substr_count($uploadName, '.') < 2;
+ });
+
+ Validator::extend('safe_url', function ($attribute, $value, $parameters, $validator) {
+ $cleanLinkName = strtolower(trim($value));
+ $isJs = strpos($cleanLinkName, 'javascript:') === 0;
+ $isData = strpos($cleanLinkName, 'data:') === 0;
+ return !$isJs && !$isData;
+ });
+ }
+}
<?php namespace BookStack\Settings;
+use BookStack\Auth\User;
use Illuminate\Contracts\Cache\Repository as Cache;
/**
*/
class SettingService
{
-
protected $setting;
protected $cache;
protected $localCache = [];
/**
* SettingService constructor.
- * @param Setting $setting
- * @param Cache $cache
*/
public function __construct(Setting $setting, Cache $cache)
{
/**
* Gets a setting from the database,
* If not found, Returns default, Which is false by default.
- * @param $key
- * @param string|bool $default
- * @return bool|string
*/
- public function get($key, $default = false)
+ public function get(string $key, $default = false)
{
if ($default === false) {
$default = config('setting-defaults.' . $key, false);
return $this->localCache[$key];
}
- $value = $this->getValueFromStore($key, $default);
+ $value = $this->getValueFromStore($key) ?? $default;
$formatted = $this->formatValue($value, $default);
$this->localCache[$key] = $formatted;
return $formatted;
/**
* Get a value from the session instead of the main store option.
- * @param $key
- * @param bool $default
- * @return mixed
*/
- protected function getFromSession($key, $default = false)
+ protected function getFromSession(string $key, $default = false)
{
$value = session()->get($key, $default);
- $formatted = $this->formatValue($value, $default);
- return $formatted;
+ return $this->formatValue($value, $default);
}
/**
* Get a user-specific setting from the database or cache.
- * @param \BookStack\Auth\User $user
- * @param $key
- * @param bool $default
- * @return bool|string
*/
- public function getUser($user, $key, $default = false)
+ public function getUser(User $user, string $key, $default = false)
{
if ($user->isDefault()) {
return $this->getFromSession($key, $default);
/**
* Get a value for the current logged-in user.
- * @param $key
- * @param bool $default
- * @return bool|string
*/
- public function getForCurrentUser($key, $default = false)
+ public function getForCurrentUser(string $key, $default = false)
{
return $this->getUser(user(), $key, $default);
}
/**
* Gets a setting value from the cache or database.
* Looks at the system defaults if not cached or in database.
- * @param $key
- * @param $default
- * @return mixed
+ * Returns null if nothing is found.
*/
- protected function getValueFromStore($key, $default)
+ protected function getValueFromStore(string $key)
{
// Check the cache
$cacheKey = $this->cachePrefix . $key;
$settingObject = $this->getSettingObjectByKey($key);
if ($settingObject !== null) {
$value = $settingObject->value;
+
+ if ($settingObject->type === 'array') {
+ $value = json_decode($value, true) ?? [];
+ }
+
$this->cache->forever($cacheKey, $value);
return $value;
}
- return $default;
+ return null;
}
/**
* Clear an item from the cache completely.
- * @param $key
*/
- protected function clearFromCache($key)
+ protected function clearFromCache(string $key)
{
$cacheKey = $this->cachePrefix . $key;
$this->cache->forget($cacheKey);
/**
* Format a settings value
- * @param $value
- * @param $default
- * @return mixed
*/
protected function formatValue($value, $default)
{
// Change string booleans to actual booleans
if ($value === 'true') {
$value = true;
- }
- if ($value === 'false') {
+ } else if ($value === 'false') {
$value = false;
}
/**
* Checks if a setting exists.
- * @param $key
- * @return bool
*/
- public function has($key)
+ public function has(string $key): bool
{
$setting = $this->getSettingObjectByKey($key);
return $setting !== null;
}
- /**
- * Check if a user setting is in the database.
- * @param $key
- * @return bool
- */
- public function hasUser($key)
- {
- return $this->has($this->userKey($key));
- }
-
/**
* Add a setting to the database.
- * @param $key
- * @param $value
- * @return bool
+ * Values can be an array or a string.
*/
- public function put($key, $value)
+ public function put(string $key, $value): bool
{
- $setting = $this->setting->firstOrNew([
+ $setting = $this->setting->newQuery()->firstOrNew([
'setting_key' => $key
]);
+ $setting->type = 'string';
+
+ if (is_array($value)) {
+ $setting->type = 'array';
+ $value = $this->formatArrayValue($value);
+ }
+
$setting->value = $value;
$setting->save();
$this->clearFromCache($key);
return true;
}
+ /**
+ * Format an array to be stored as a setting.
+ * Array setting types are expected to be a flat array of child key=>value array items.
+ * This filters out any child items that are empty.
+ */
+ protected function formatArrayValue(array $value): string
+ {
+ $values = collect($value)->values()->filter(function(array $item) {
+ return count(array_filter($item)) > 0;
+ });
+ return json_encode($values);
+ }
+
/**
* Put a user-specific setting into the database.
- * @param \BookStack\Auth\User $user
- * @param $key
- * @param $value
- * @return bool
*/
- public function putUser($user, $key, $value)
+ public function putUser(User $user, string $key, string $value): bool
{
if ($user->isDefault()) {
- return session()->put($key, $value);
+ session()->put($key, $value);
+ return true;
}
+
return $this->put($this->userKey($user->id, $key), $value);
}
/**
* Convert a setting key into a user-specific key.
- * @param $key
- * @return string
*/
- protected function userKey($userId, $key = '')
+ protected function userKey(string $userId, string $key = ''): string
{
return 'user:' . $userId . ':' . $key;
}
/**
* Removes a setting from the database.
- * @param $key
- * @return bool
*/
- public function remove($key)
+ public function remove(string $key): void
{
$setting = $this->getSettingObjectByKey($key);
if ($setting) {
$setting->delete();
}
$this->clearFromCache($key);
- return true;
}
/**
* Delete settings for a given user id.
- * @param $userId
- * @return mixed
*/
- public function deleteUserSettings($userId)
+ public function deleteUserSettings(string $userId)
{
- return $this->setting->where('setting_key', 'like', $this->userKey($userId) . '%')->delete();
+ return $this->setting->newQuery()
+ ->where('setting_key', 'like', $this->userKey($userId) . '%')
+ ->delete();
}
/**
* Gets a setting model from the database for the given key.
- * @param $key
- * @return mixed
*/
- protected function getSettingObjectByKey($key)
+ protected function getSettingObjectByKey(string $key): ?Setting
{
- return $this->setting->where('setting_key', '=', $key)->first();
+ return $this->setting->newQuery()
+ ->where('setting_key', '=', $key)->first();
}
}
--- /dev/null
+<?php namespace BookStack\Traits;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int created_by
+ * @property int updated_by
+ */
+trait HasCreatorAndUpdater
+{
+ /**
+ * Relation for the user that created this entity.
+ */
+ public function createdBy(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'created_by');
+ }
+
+ /**
+ * Relation for the user that updated this entity.
+ */
+ public function updatedBy(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'updated_by');
+ }
+
+}
--- /dev/null
+<?php namespace BookStack\Traits;
+
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int owned_by
+ */
+trait HasOwner
+{
+ /**
+ * Relation for the user that owns this entity.
+ */
+ public function ownedBy(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'owned_by');
+ }
+
+}
<?php namespace BookStack\Uploads;
-use BookStack\Entities\Page;
-use BookStack\Ownable;
+use BookStack\Entities\Models\Page;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
/**
* @property int id
* @property string extension
* @property bool external
*/
-class Attachment extends Ownable
+class Attachment extends Model
{
+ use HasCreatorAndUpdater;
+
protected $fillable = ['name', 'order'];
/**
use BookStack\Exceptions\FileUploadException;
use Exception;
+use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\File\UploadedFile;
-class AttachmentService extends UploadService
+class AttachmentService
{
+ protected $fileSystem;
+
+ /**
+ * AttachmentService constructor.
+ */
+ public function __construct(FileSystem $fileSystem)
+ {
+ $this->fileSystem = $fileSystem;
+ }
+
+
/**
* Get the storage that will be used for storing files.
- * @return \Illuminate\Contracts\Filesystem\Filesystem
*/
- protected function getStorage()
+ protected function getStorage(): FileSystemInstance
{
$storageType = config('filesystems.attachments');
<?php namespace BookStack\Uploads;
-use BookStack\Entities\Page;
-use BookStack\Ownable;
+use BookStack\Entities\Models\Page;
+use BookStack\Model;
+use BookStack\Traits\HasCreatorAndUpdater;
use Images;
-class Image extends Ownable
+class Image extends Model
{
+ use HasCreatorAndUpdater;
protected $fillable = ['name'];
protected $hidden = [];
<?php namespace BookStack\Uploads;
use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Exceptions\ImageUploadException;
use Exception;
use Illuminate\Database\Eloquent\Builder;
if ($filterType === 'page') {
$query->where('uploaded_to', '=', $contextPage->id);
} elseif ($filterType === 'book') {
- $validPageIds = $contextPage->book->pages()->get(['id'])->pluck('id')->toArray();
+ $validPageIds = $contextPage->book->pages()->visible()->get(['id'])->pluck('id')->toArray();
$query->whereIn('uploaded_to', $validPageIds);
}
};
<?php namespace BookStack\Uploads;
-use BookStack\Auth\User;
-use BookStack\Exceptions\HttpFetchException;
use BookStack\Exceptions\ImageUploadException;
use DB;
+use ErrorException;
use Exception;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
+use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Str;
use Intervention\Image\Exception\NotSupportedException;
use Intervention\Image\ImageManager;
-use phpDocumentor\Reflection\Types\Integer;
use Symfony\Component\HttpFoundation\File\UploadedFile;
-class ImageService extends UploadService
+class ImageService
{
-
protected $imageTool;
protected $cache;
protected $storageUrl;
protected $image;
- protected $http;
+ protected $fileSystem;
/**
* ImageService constructor.
- * @param Image $image
- * @param ImageManager $imageTool
- * @param FileSystem $fileSystem
- * @param Cache $cache
- * @param HttpFetcher $http
*/
- public function __construct(Image $image, ImageManager $imageTool, FileSystem $fileSystem, Cache $cache, HttpFetcher $http)
+ public function __construct(Image $image, ImageManager $imageTool, FileSystem $fileSystem, Cache $cache)
{
$this->image = $image;
$this->imageTool = $imageTool;
+ $this->fileSystem = $fileSystem;
$this->cache = $cache;
- $this->http = $http;
- parent::__construct($fileSystem);
}
/**
* Get the storage that will be used for storing images.
- * @param string $type
- * @return \Illuminate\Contracts\Filesystem\Filesystem
*/
- protected function getStorage($type = '')
+ protected function getStorage(string $type = ''): FileSystemInstance
{
$storageType = config('filesystems.images');
/**
* Saves a new image from an upload.
- * @param UploadedFile $uploadedFile
- * @param string $type
- * @param int $uploadedTo
- * @param int|null $resizeWidth
- * @param int|null $resizeHeight
- * @param bool $keepRatio
* @return mixed
* @throws ImageUploadException
*/
/**
* Save a new image from a uri-encoded base64 string of data.
- * @param string $base64Uri
- * @param string $name
- * @param string $type
- * @param int $uploadedTo
- * @return Image
* @throws ImageUploadException
*/
- public function saveNewFromBase64Uri(string $base64Uri, string $name, string $type, $uploadedTo = 0)
+ public function saveNewFromBase64Uri(string $base64Uri, string $name, string $type, int $uploadedTo = 0): Image
{
$splitData = explode(';base64,', $base64Uri);
if (count($splitData) < 2) {
return $this->saveNew($name, $data, $type, $uploadedTo);
}
- /**
- * Gets an image from url and saves it to the database.
- * @param $url
- * @param string $type
- * @param bool|string $imageName
- * @return mixed
- * @throws \Exception
- */
- private function saveNewFromUrl($url, $type, $imageName = false)
- {
- $imageName = $imageName ? $imageName : basename($url);
- try {
- $imageData = $this->http->fetch($url);
- } catch (HttpFetchException $exception) {
- throw new \Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
- }
- return $this->saveNew($imageName, $imageData, $type);
- }
-
/**
* Save a new image into storage.
* @throws ImageUploadException
*/
- private function saveNew(string $imageName, string $imageData, string $type, int $uploadedTo = 0): Image
+ public function saveNew(string $imageName, string $imageData, string $type, int $uploadedTo = 0): Image
{
$storage = $this->getStorage($type);
$secureUploads = setting('app-secure-images');
}
$imageDetails = [
- 'name' => $imageName,
- 'path' => $fullPath,
- 'url' => $this->getPublicUrl($fullPath),
- 'type' => $type,
+ 'name' => $imageName,
+ 'path' => $fullPath,
+ 'url' => $this->getPublicUrl($fullPath),
+ 'type' => $type,
'uploaded_to' => $uploadedTo
];
$name = Str::random(10);
}
- return $name . '.' . $extension;
+ return $name . '.' . $extension;
}
/**
* Checks if the image is a gif. Returns true if it is, else false.
- * @param Image $image
- * @return boolean
*/
- protected function isGif(Image $image)
+ protected function isGif(Image $image): bool
{
return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
}
try {
$thumb = $this->imageTool->make($imageData);
} catch (Exception $e) {
- if ($e instanceof \ErrorException || $e instanceof NotSupportedException) {
+ if ($e instanceof ErrorException || $e instanceof NotSupportedException) {
throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
}
throw $e;
/**
* Get the raw data content from an image.
- * @param Image $image
- * @return string
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+ * @throws FileNotFoundException
*/
- public function getImageData(Image $image)
+ public function getImageData(Image $image): string
{
$imagePath = $image->path;
$storage = $this->getStorage();
/**
* Destroy an image along with its revisions, thumbnails and remaining folders.
- * @param Image $image
* @throws Exception
*/
public function destroy(Image $image)
// Cleanup of empty folders
$foldersInvolved = array_merge([$imageFolder], $storage->directories($imageFolder));
foreach ($foldersInvolved as $directory) {
- if ($this->isFolderEmpty($directory)) {
+ if ($this->isFolderEmpty($storage, $directory)) {
$storage->deleteDirectory($directory);
}
}
}
/**
- * Save an avatar image from an external service.
- * @param \BookStack\Auth\User $user
- * @param int $size
- * @return Image
- * @throws Exception
- */
- public function saveUserAvatar(User $user, $size = 500)
- {
- $avatarUrl = $this->getAvatarUrl();
- $email = strtolower(trim($user->email));
-
- $replacements = [
- '${hash}' => md5($email),
- '${size}' => $size,
- '${email}' => urlencode($email),
- ];
-
- $userAvatarUrl = strtr($avatarUrl, $replacements);
- $imageName = str_replace(' ', '-', $user->name . '-avatar.png');
- $image = $this->saveNewFromUrl($userAvatarUrl, 'user', $imageName);
- $image->created_by = $user->id;
- $image->updated_by = $user->id;
- $image->uploaded_to = $user->id;
- $image->save();
-
- return $image;
- }
-
- /**
- * Check if fetching external avatars is enabled.
- * @return bool
- */
- public function avatarFetchEnabled()
- {
- $fetchUrl = $this->getAvatarUrl();
- return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
- }
-
- /**
- * Get the URL to fetch avatars from.
- * @return string|mixed
+ * Check whether or not a folder is empty.
*/
- protected function getAvatarUrl()
+ protected function isFolderEmpty(FileSystemInstance $storage, string $path): bool
{
- $url = trim(config('services.avatar_url'));
-
- if (empty($url) && !config('services.disable_services')) {
- $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
- }
-
- return $url;
+ $files = $storage->files($path);
+ $folders = $storage->directories($path);
+ return (count($files) === 0 && count($folders) === 0);
}
/**
* Could be much improved to be more specific but kept it generic for now to be safe.
*
* Returns the path of the images that would be/have been deleted.
- * @param bool $checkRevisions
- * @param bool $dryRun
- * @param array $types
- * @return array
*/
- public function deleteUnusedImages($checkRevisions = true, $dryRun = true, $types = ['gallery', 'drawio'])
+ public function deleteUnusedImages(bool $checkRevisions = true, bool $dryRun = true)
{
- $types = array_intersect($types, ['gallery', 'drawio']);
+ $types = ['gallery', 'drawio'];
$deletedPaths = [];
$this->image->newQuery()->whereIn('type', $types)
- ->chunk(1000, function ($images) use ($types, $checkRevisions, &$deletedPaths, $dryRun) {
+ ->chunk(1000, function ($images) use ($checkRevisions, &$deletedPaths, $dryRun) {
foreach ($images as $image) {
$searchQuery = '%' . basename($image->path) . '%';
$inPage = DB::table('pages')
- ->where('html', 'like', $searchQuery)->count() > 0;
+ ->where('html', 'like', $searchQuery)->count() > 0;
+
$inRevision = false;
if ($checkRevisions) {
- $inRevision = DB::table('page_revisions')
- ->where('html', 'like', $searchQuery)->count() > 0;
+ $inRevision = DB::table('page_revisions')
+ ->where('html', 'like', $searchQuery)->count() > 0;
}
if (!$inPage && !$inRevision) {
/**
* Convert a image URI to a Base64 encoded string.
- * Attempts to find locally via set storage method first.
- * @param string $uri
- * @return null|string
- * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+ * Attempts to convert the URL to a system storage url then
+ * fetch the data from the disk or storage location.
+ * Returns null if the image data cannot be fetched from storage.
+ * @throws FileNotFoundException
*/
- public function imageUriToBase64(string $uri)
+ public function imageUriToBase64(string $uri): ?string
{
- $isLocal = strpos(trim($uri), 'http') !== 0;
-
- // Attempt to find local files even if url not absolute
- $base = url('/');
- if (!$isLocal && strpos($uri, $base) === 0) {
- $isLocal = true;
- $uri = str_replace($base, '', $uri);
+ $storagePath = $this->imageUrlToStoragePath($uri);
+ if (empty($uri) || is_null($storagePath)) {
+ return null;
}
+ $storage = $this->getStorage();
$imageData = null;
-
- if ($isLocal) {
- $uri = trim($uri, '/');
- $storage = $this->getStorage();
- if ($storage->exists($uri)) {
- $imageData = $storage->get($uri);
- }
- } else {
- try {
- $imageData = $this->http->fetch($uri);
- } catch (\Exception $e) {
- }
+ if ($storage->exists($storagePath)) {
+ $imageData = $storage->get($storagePath);
}
- if ($imageData === null) {
+ if (is_null($imageData)) {
return null;
}
return 'data:image/' . $extension . ';base64,' . base64_encode($imageData);
}
+ /**
+ * Get a storage path for the given image URL.
+ * Ensures the path will start with "uploads/images".
+ * Returns null if the url cannot be resolved to a local URL.
+ */
+ private function imageUrlToStoragePath(string $url): ?string
+ {
+ $url = ltrim(trim($url), '/');
+
+ // Handle potential relative paths
+ $isRelative = strpos($url, 'http') !== 0;
+ if ($isRelative) {
+ if (strpos(strtolower($url), 'uploads/images') === 0) {
+ return trim($url, '/');
+ }
+ return null;
+ }
+
+ // Handle local images based on paths on the same domain
+ $potentialHostPaths = [
+ url('uploads/images/'),
+ $this->getPublicUrl('/uploads/images/'),
+ ];
+
+ foreach ($potentialHostPaths as $potentialBasePath) {
+ $potentialBasePath = strtolower($potentialBasePath);
+ if (strpos(strtolower($url), $potentialBasePath) === 0) {
+ return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
+ }
+ }
+
+ return null;
+ }
+
/**
* Gets a public facing url for an image by checking relevant environment variables.
- * @param string $filePath
- * @return string
+ * If s3-style store is in use it will default to guessing a public bucket URL.
*/
- private function getPublicUrl($filePath)
+ private function getPublicUrl(string $filePath): string
{
if ($this->storageUrl === null) {
$storageUrl = config('filesystems.url');
+++ /dev/null
-<?php namespace BookStack\Uploads;
-
-use Illuminate\Contracts\Filesystem\Factory as FileSystem;
-use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
-
-abstract class UploadService
-{
-
- /**
- * @var FileSystem
- */
- protected $fileSystem;
-
-
- /**
- * FileService constructor.
- * @param $fileSystem
- */
- public function __construct(FileSystem $fileSystem)
- {
- $this->fileSystem = $fileSystem;
- }
-
- /**
- * Get the storage that will be used for storing images.
- * @return FileSystemInstance
- */
- protected function getStorage()
- {
- $storageType = config('filesystems.default');
- return $this->fileSystem->disk($storageType);
- }
-
- /**
- * Check whether or not a folder is empty.
- * @param $path
- * @return bool
- */
- protected function isFolderEmpty($path)
- {
- $files = $this->getStorage()->files($path);
- $folders = $this->getStorage()->directories($path);
- return (count($files) === 0 && count($folders) === 0);
- }
-}
--- /dev/null
+<?php namespace BookStack\Uploads;
+
+use BookStack\Auth\User;
+use BookStack\Exceptions\HttpFetchException;
+use Exception;
+use Illuminate\Support\Facades\Log;
+
+class UserAvatars
+{
+ protected $imageService;
+ protected $http;
+
+ public function __construct(ImageService $imageService, HttpFetcher $http)
+ {
+ $this->imageService = $imageService;
+ $this->http = $http;
+ }
+
+ /**
+ * Fetch and assign an avatar image to the given user.
+ */
+ public function fetchAndAssignToUser(User $user): void
+ {
+ if (!$this->avatarFetchEnabled()) {
+ return;
+ }
+
+ try {
+ $avatar = $this->saveAvatarImage($user);
+ $user->avatar()->associate($avatar);
+ $user->save();
+ } catch (Exception $e) {
+ Log::error('Failed to save user avatar image');
+ }
+ }
+
+ /**
+ * Save an avatar image from an external service.
+ * @throws Exception
+ */
+ protected function saveAvatarImage(User $user, int $size = 500): Image
+ {
+ $avatarUrl = $this->getAvatarUrl();
+ $email = strtolower(trim($user->email));
+
+ $replacements = [
+ '${hash}' => md5($email),
+ '${size}' => $size,
+ '${email}' => urlencode($email),
+ ];
+
+ $userAvatarUrl = strtr($avatarUrl, $replacements);
+ $imageName = str_replace(' ', '-', $user->id . '-avatar.png');
+ $imageData = $this->getAvatarImageData($userAvatarUrl);
+
+ $image = $this->imageService->saveNew($imageName, $imageData, 'user', $user->id);
+ $image->created_by = $user->id;
+ $image->updated_by = $user->id;
+ $image->save();
+
+ return $image;
+ }
+
+ /**
+ * Gets an image from url and returns it as a string of image data.
+ * @throws Exception
+ */
+ protected function getAvatarImageData(string $url): string
+ {
+ try {
+ $imageData = $this->http->fetch($url);
+ } catch (HttpFetchException $exception) {
+ throw new Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
+ }
+ return $imageData;
+ }
+
+ /**
+ * Check if fetching external avatars is enabled.
+ */
+ protected function avatarFetchEnabled(): bool
+ {
+ $fetchUrl = $this->getAvatarUrl();
+ return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
+ }
+
+ /**
+ * Get the URL to fetch avatars from.
+ */
+ protected function getAvatarUrl(): string
+ {
+ $url = trim(config('services.avatar_url'));
+
+ if (empty($url) && !config('services.disable_services')) {
+ $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
+ }
+
+ return $url;
+ }
+
+}
\ No newline at end of file
use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User;
-use BookStack\Ownable;
+use BookStack\Model;
use BookStack\Settings\SettingService;
/**
* Check if the current user has a permission. If an ownable element
* is passed in the jointPermissions are checked against that particular item.
*/
-function userCan(string $permission, Ownable $ownable = null): bool
+function userCan(string $permission, Model $ownable = null): bool
{
if ($ownable === null) {
return user() && user()->can($permission);
"license": "MIT",
"type": "project",
"require": {
- "php": "^7.2",
+ "php": "^7.2.5",
"ext-curl": "*",
"ext-dom": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
- "ext-tidy": "*",
"ext-xml": "*",
- "barryvdh/laravel-dompdf": "^0.8.6",
- "barryvdh/laravel-snappy": "^0.4.7",
+ "barryvdh/laravel-dompdf": "^0.8.7",
+ "barryvdh/laravel-snappy": "^0.4.8",
"doctrine/dbal": "^2.9",
- "facade/ignition": "^1.4",
- "fideloper/proxy": "^4.0",
- "gathercontent/htmldiff": "^0.2.1",
- "intervention/image": "^2.5",
- "laravel/framework": "^6.18",
- "laravel/socialite": "^4.3.2",
- "league/commonmark": "^1.4",
- "league/flysystem-aws-s3-v3": "^1.0",
- "nunomaduro/collision": "^3.0",
+ "facade/ignition": "^1.16.4",
+ "fideloper/proxy": "^4.4.1",
+ "intervention/image": "^2.5.1",
+ "laravel/framework": "^6.20.16",
+ "laravel/socialite": "^5.1",
+ "league/commonmark": "^1.5",
+ "league/flysystem-aws-s3-v3": "^1.0.29",
+ "nunomaduro/collision": "^3.1",
"onelogin/php-saml": "^3.3",
- "predis/predis": "^1.1",
- "socialiteproviders/discord": "^2.0",
- "socialiteproviders/gitlab": "^3.0",
- "socialiteproviders/microsoft-azure": "^3.0",
- "socialiteproviders/okta": "^1.0",
- "socialiteproviders/slack": "^3.0",
- "socialiteproviders/twitch": "^5.0"
+ "predis/predis": "^1.1.6",
+ "socialiteproviders/discord": "^4.1",
+ "socialiteproviders/gitlab": "^4.1",
+ "socialiteproviders/microsoft-azure": "^4.1",
+ "socialiteproviders/okta": "^4.1",
+ "socialiteproviders/slack": "^4.1",
+ "socialiteproviders/twitch": "^5.3",
+ "ssddanbrown/htmldiff": "^v1.0.1"
},
"require-dev": {
- "barryvdh/laravel-debugbar": "^3.2.8",
- "barryvdh/laravel-ide-helper": "^2.6.4",
- "fzaninotto/faker": "^1.4",
- "laravel/browser-kit-testing": "^5.1",
- "mockery/mockery": "^1.0",
+ "barryvdh/laravel-debugbar": "^3.5.1",
+ "barryvdh/laravel-ide-helper": "^2.8.2",
+ "fakerphp/faker": "^1.9.1",
+ "laravel/browser-kit-testing": "^5.2",
+ "mockery/mockery": "^1.3.3",
"phpunit/phpunit": "^8.0",
- "squizlabs/php_codesniffer": "^3.4",
- "wnx/laravel-stats": "^2.0"
+ "squizlabs/php_codesniffer": "^3.5.8"
},
"autoload": {
"classmap": [
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
],
- "pre-update-cmd": [
- "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
- "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
- ],
"pre-install-cmd": [
- "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\"",
- "@php -r \"!file_exists('bootstrap/cache/compiled.php') || @unlink('bootstrap/cache/compiled.php');\""
+ "@php -r \"!file_exists('bootstrap/cache/services.php') || @unlink('bootstrap/cache/services.php');\""
],
"post-install-cmd": [
"@php artisan cache:clear",
"preferred-install": "dist",
"sort-packages": true,
"platform": {
- "php": "7.2.0"
+ "php": "7.2.5"
}
},
"extra": {
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "34390536dd685e0bc49b179babaa06ec",
+ "content-hash": "fc011a4d14d89014565f53626a0e331c",
"packages": [
{
"name": "aws/aws-sdk-php",
- "version": "3.154.6",
+ "version": "3.173.4",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "83a1382930359e4d4f4c9187239f059d5b282520"
+ "reference": "1400b2d08ded25bd9c6876edb0490b8cb9aeaa60"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/83a1382930359e4d4f4c9187239f059d5b282520",
- "reference": "83a1382930359e4d4f4c9187239f059d5b282520",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1400b2d08ded25bd9c6876edb0490b8cb9aeaa60",
+ "reference": "1400b2d08ded25bd9c6876edb0490b8cb9aeaa60",
"shasum": ""
},
"require": {
"s3",
"sdk"
],
- "time": "2020-09-18T18:16:42+00:00"
+ "support": {
+ "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
+ "issues": "https://github.com/aws/aws-sdk-php/issues",
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.173.4"
+ },
+ "time": "2021-02-05T19:19:35+00:00"
},
{
"name": "barryvdh/laravel-dompdf",
"laravel",
"pdf"
],
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-dompdf/issues",
+ "source": "https://github.com/barryvdh/laravel-dompdf/tree/master"
+ },
"funding": [
{
"url": "https://github.com/barryvdh",
"wkhtmltoimage",
"wkhtmltopdf"
],
- "time": "2020-09-07T12:33:10+00:00"
- },
- {
- "name": "cogpowered/finediff",
- "version": "0.3.1",
- "source": {
- "type": "git",
- "url": "https://github.com/cogpowered/FineDiff.git",
- "reference": "339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/cogpowered/FineDiff/zipball/339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8",
- "reference": "339ddc8c3afb656efed4f2f0a80e5c3d026f8ea8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0"
- },
- "require-dev": {
- "mockery/mockery": "*",
- "phpunit/phpunit": "*"
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-snappy/issues",
+ "source": "https://github.com/barryvdh/laravel-snappy/tree/master"
},
- "type": "library",
- "autoload": {
- "psr-0": {
- "cogpowered\\FineDiff": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Rob Crowe",
- },
- {
- "name": "Raymond Hill"
- }
- ],
- "description": "PHP implementation of a Fine granularity Diff engine",
- "homepage": "https://github.com/cogpowered/FineDiff",
- "keywords": [
- "diff",
- "finediff",
- "opcode",
- "string",
- "text"
- ],
- "time": "2014-05-19T10:25:02+00:00"
+ "time": "2020-09-07T12:33:10+00:00"
},
{
"name": "doctrine/cache",
"redis",
"xcache"
],
+ "support": {
+ "issues": "https://github.com/doctrine/cache/issues",
+ "source": "https://github.com/doctrine/cache/tree/1.10.x"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"sqlserver",
"sqlsrv"
],
+ "support": {
+ "issues": "https://github.com/doctrine/dbal/issues",
+ "source": "https://github.com/doctrine/dbal/tree/2.10.4"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"event system",
"events"
],
+ "support": {
+ "issues": "https://github.com/doctrine/event-manager/issues",
+ "source": "https://github.com/doctrine/event-manager/tree/1.1.x"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"uppercase",
"words"
],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.x"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"parser",
"php"
],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/1.2.1"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
+ "support": {
+ "issues": "https://github.com/dompdf/dompdf/issues",
+ "source": "https://github.com/dompdf/dompdf/tree/master"
+ },
"time": "2020-08-30T22:54:22+00:00"
},
{
"name": "dragonmantank/cron-expression",
- "version": "v2.3.0",
+ "version": "v2.3.1",
"source": {
"type": "git",
"url": "https://github.com/dragonmantank/cron-expression.git",
- "reference": "72b6fbf76adb3cf5bc0db68559b33d41219aba27"
+ "reference": "65b2d8ee1f10915efb3b55597da3404f096acba2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/72b6fbf76adb3cf5bc0db68559b33d41219aba27",
- "reference": "72b6fbf76adb3cf5bc0db68559b33d41219aba27",
+ "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/65b2d8ee1f10915efb3b55597da3404f096acba2",
+ "reference": "65b2d8ee1f10915efb3b55597da3404f096acba2",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": "^7.0|^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^6.4|^7.0"
+ "phpunit/phpunit": "^6.4|^7.0|^8.0|^9.0"
},
"type": "library",
"extra": {
"cron",
"schedule"
],
- "time": "2019-03-31T00:38:28+00:00"
+ "support": {
+ "issues": "https://github.com/dragonmantank/cron-expression/issues",
+ "source": "https://github.com/dragonmantank/cron-expression/tree/v2.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/dragonmantank",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-13T00:52:37+00:00"
},
{
"name": "egulias/email-validator",
- "version": "2.1.20",
+ "version": "2.1.25",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
- "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff"
+ "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/f46887bc48db66c7f38f668eb7d6ae54583617ff",
- "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4",
+ "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4",
"shasum": ""
},
"require": {
"validation",
"validator"
],
- "time": "2020-09-06T13:44:32+00:00"
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/2.1.25"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2020-12-29T14:50:06+00:00"
},
{
"name": "facade/flare-client-php",
- "version": "1.3.6",
+ "version": "1.3.7",
"source": {
"type": "git",
"url": "https://github.com/facade/flare-client-php.git",
- "reference": "451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799"
+ "reference": "fd688d3c06658f2b3b5f7bb19f051ee4ddf02492"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/facade/flare-client-php/zipball/451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799",
- "reference": "451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799",
+ "url": "https://api.github.com/repos/facade/flare-client-php/zipball/fd688d3c06658f2b3b5f7bb19f051ee4ddf02492",
+ "reference": "fd688d3c06658f2b3b5f7bb19f051ee4ddf02492",
"shasum": ""
},
"require": {
"facade/ignition-contracts": "~1.0",
"illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0",
- "php": "^7.1",
+ "php": "^7.1|^8.0",
"symfony/http-foundation": "^3.3|^4.1|^5.0",
"symfony/mime": "^3.4|^4.0|^5.1",
"symfony/var-dumper": "^3.4|^4.0|^5.0"
"flare",
"reporting"
],
+ "support": {
+ "issues": "https://github.com/facade/flare-client-php/issues",
+ "source": "https://github.com/facade/flare-client-php/tree/1.3.7"
+ },
"funding": [
{
"url": "https://github.com/spatie",
"type": "github"
}
],
- "time": "2020-09-18T06:35:11+00:00"
+ "time": "2020-10-21T16:02:39+00:00"
},
{
"name": "facade/ignition",
- "version": "1.16.3",
+ "version": "1.16.4",
"source": {
"type": "git",
"url": "https://github.com/facade/ignition.git",
- "reference": "19674150bb46a4de0ba138c747f538fe7be11dbc"
+ "reference": "1da1705e7f6b24ed45af05461463228da424e14f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/facade/ignition/zipball/19674150bb46a4de0ba138c747f538fe7be11dbc",
- "reference": "19674150bb46a4de0ba138c747f538fe7be11dbc",
+ "url": "https://api.github.com/repos/facade/ignition/zipball/1da1705e7f6b24ed45af05461463228da424e14f",
+ "reference": "1da1705e7f6b24ed45af05461463228da424e14f",
"shasum": ""
},
"require": {
"filp/whoops": "^2.4",
"illuminate/support": "~5.5.0 || ~5.6.0 || ~5.7.0 || ~5.8.0 || ^6.0",
"monolog/monolog": "^1.12 || ^2.0",
- "php": "^7.1",
+ "php": "^7.1|^8.0",
"scrivo/highlight.php": "^9.15",
"symfony/console": "^3.4 || ^4.0",
"symfony/var-dumper": "^3.4 || ^4.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^2.14",
- "mockery/mockery": "^1.2",
+ "mockery/mockery": "~1.3.3|^1.4.2",
"orchestra/testbench": "^3.5 || ^3.6 || ^3.7 || ^3.8 || ^4.0"
},
"suggest": {
"laravel",
"page"
],
- "time": "2020-07-13T15:54:05+00:00"
+ "support": {
+ "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction",
+ "forum": "https://twitter.com/flareappio",
+ "issues": "https://github.com/facade/ignition/issues",
+ "source": "https://github.com/facade/ignition"
+ },
+ "time": "2020-10-30T13:40:01+00:00"
},
{
"name": "facade/ignition-contracts",
"flare",
"ignition"
],
+ "support": {
+ "issues": "https://github.com/facade/ignition-contracts/issues",
+ "source": "https://github.com/facade/ignition-contracts/tree/1.0.1"
+ },
"time": "2020-07-14T10:10:28+00:00"
},
{
"name": "fideloper/proxy",
- "version": "4.4.0",
+ "version": "4.4.1",
"source": {
"type": "git",
"url": "https://github.com/fideloper/TrustedProxy.git",
- "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8"
+ "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8",
- "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8",
+ "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/c073b2bd04d1c90e04dc1b787662b558dd65ade0",
+ "reference": "c073b2bd04d1c90e04dc1b787662b558dd65ade0",
"shasum": ""
},
"require": {
- "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0",
+ "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
"php": ">=5.4.0"
},
"require-dev": {
- "illuminate/http": "^5.0|^6.0|^7.0|^8.0",
+ "illuminate/http": "^5.0|^6.0|^7.0|^8.0|^9.0",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^6.0"
},
"proxy",
"trusted proxy"
],
- "time": "2020-06-23T01:36:47+00:00"
+ "support": {
+ "issues": "https://github.com/fideloper/TrustedProxy/issues",
+ "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.1"
+ },
+ "time": "2020-10-22T13:48:01+00:00"
},
{
"name": "filp/whoops",
- "version": "2.7.3",
+ "version": "2.9.2",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
- "reference": "5d5fe9bb3d656b514d455645b3addc5f7ba7714d"
+ "reference": "df7933820090489623ce0be5e85c7e693638e536"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/5d5fe9bb3d656b514d455645b3addc5f7ba7714d",
- "reference": "5d5fe9bb3d656b514d455645b3addc5f7ba7714d",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/df7933820090489623ce0be5e85c7e693638e536",
+ "reference": "df7933820090489623ce0be5e85c7e693638e536",
"shasum": ""
},
"require": {
- "php": "^5.5.9 || ^7.0",
+ "php": "^5.5.9 || ^7.0 || ^8.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
},
"suggest": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
"throwable",
"whoops"
],
- "time": "2020-06-14T09:00:00+00:00"
- },
- {
- "name": "gathercontent/htmldiff",
- "version": "0.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/gathercontent/htmldiff.git",
- "reference": "24674a62315f64330134b4a4c5b01a7b59193c93"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/gathercontent/htmldiff/zipball/24674a62315f64330134b4a4c5b01a7b59193c93",
- "reference": "24674a62315f64330134b4a4c5b01a7b59193c93",
- "shasum": ""
- },
- "require": {
- "cogpowered/finediff": "0.3.1",
- "ext-tidy": "*"
- },
- "require-dev": {
- "phpunit/phpunit": "4.*",
- "squizlabs/php_codesniffer": "1.*"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "GatherContent\\Htmldiff": "src/"
- }
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.9.2"
},
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Andrew Cairns",
- },
- {
- "name": "Mathew Chapman",
- },
+ "funding": [
{
- "name": "Peter Legierski",
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
}
],
- "description": "Compare two HTML strings",
- "time": "2015-04-15T15:39:46+00:00"
+ "time": "2021-01-24T12:00:00+00:00"
},
{
"name": "guzzlehttp/guzzle",
- "version": "6.5.5",
+ "version": "7.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
+ "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
- "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
+ "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
"shasum": ""
},
"require": {
"ext-json": "*",
- "guzzlehttp/promises": "^1.0",
- "guzzlehttp/psr7": "^1.6.1",
- "php": ">=5.5",
- "symfony/polyfill-intl-idn": "^1.17.0"
+ "guzzlehttp/promises": "^1.4",
+ "guzzlehttp/psr7": "^1.7",
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-client": "^1.0"
+ },
+ "provide": {
+ "psr/http-client-implementation": "1.0"
},
"require-dev": {
"ext-curl": "*",
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+ "php-http/client-integration-tests": "^3.0",
+ "phpunit/phpunit": "^8.5.5 || ^9.3.5",
"psr/log": "^1.1"
},
"suggest": {
+ "ext-curl": "Required for CURL handler support",
+ "ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "6.5-dev"
+ "dev-master": "7.1-dev"
}
},
"autoload": {
"name": "Michael Dowling",
"homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "homepage": "https://sagikazarmark.hu"
}
],
"description": "Guzzle is a PHP HTTP client library",
"framework",
"http",
"http client",
+ "psr-18",
+ "psr-7",
"rest",
"web service"
],
- "time": "2020-06-16T21:01:06+00:00"
+ "support": {
+ "issues": "https://github.com/guzzle/guzzle/issues",
+ "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/alexeyshockov",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/gmponos",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-10T11:47:56+00:00"
},
{
"name": "guzzlehttp/promises",
- "version": "v1.3.1",
+ "version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
- "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+ "reference": "60d379c243457e073cff02bc323a2a86cb355631"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
- "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
+ "reference": "60d379c243457e073cff02bc323a2a86cb355631",
"shasum": ""
},
"require": {
- "php": ">=5.5.0"
+ "php": ">=5.5"
},
"require-dev": {
- "phpunit/phpunit": "^4.0"
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"type": "library",
"extra": {
"keywords": [
"promise"
],
- "time": "2016-12-20T10:07:11+00:00"
+ "support": {
+ "issues": "https://github.com/guzzle/promises/issues",
+ "source": "https://github.com/guzzle/promises/tree/1.4.0"
+ },
+ "time": "2020-09-30T07:37:28+00:00"
},
{
"name": "guzzlehttp/psr7",
- "version": "1.6.1",
+ "version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
+ "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
- "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
+ "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
"shasum": ""
},
"require": {
},
"require-dev": {
"ext-zlib": "*",
- "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
},
"suggest": {
- "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.6-dev"
+ "dev-master": "1.7-dev"
}
},
"autoload": {
"uri",
"url"
],
- "time": "2019-07-01T23:21:34+00:00"
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+ },
+ "time": "2020-09-30T07:37:11+00:00"
},
{
"name": "intervention/image",
"thumbnail",
"watermark"
],
+ "support": {
+ "issues": "https://github.com/Intervention/image/issues",
+ "source": "https://github.com/Intervention/image/tree/master"
+ },
"time": "2019-11-02T09:15:47+00:00"
},
{
}
],
+ "support": {
+ "issues": "https://github.com/JakubOnderka/PHP-Console-Color/issues",
+ "source": "https://github.com/JakubOnderka/PHP-Console-Color/tree/master"
+ },
"abandoned": "php-parallel-lint/php-console-color",
"time": "2018-09-29T17:23:10+00:00"
},
}
],
"description": "Highlight PHP code in terminal",
+ "support": {
+ "issues": "https://github.com/JakubOnderka/PHP-Console-Highlighter/issues",
+ "source": "https://github.com/JakubOnderka/PHP-Console-Highlighter/tree/master"
+ },
"abandoned": "php-parallel-lint/php-console-highlighter",
"time": "2018-09-29T18:48:56+00:00"
},
"thumbnail",
"wkhtmltopdf"
],
+ "support": {
+ "issues": "https://github.com/KnpLabs/snappy/issues",
+ "source": "https://github.com/KnpLabs/snappy/tree/master"
+ },
"time": "2020-01-20T08:30:30+00:00"
},
{
"name": "laravel/framework",
- "version": "v6.18.40",
+ "version": "v6.20.16",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "e42450df0896b7130ccdb5290a114424e18887c9"
+ "reference": "806082fb559fe595cb17cd6aa8571f03ed287814"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/e42450df0896b7130ccdb5290a114424e18887c9",
- "reference": "e42450df0896b7130ccdb5290a114424e18887c9",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/806082fb559fe595cb17cd6aa8571f03ed287814",
+ "reference": "806082fb559fe595cb17cd6aa8571f03ed287814",
"shasum": ""
},
"require": {
"doctrine/inflector": "^1.4|^2.0",
- "dragonmantank/cron-expression": "^2.0",
+ "dragonmantank/cron-expression": "^2.3.1",
"egulias/email-validator": "^2.1.10",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"league/commonmark": "^1.3",
- "league/flysystem": "^1.0.34",
+ "league/flysystem": "^1.1",
"monolog/monolog": "^1.12|^2.0",
- "nesbot/carbon": "^2.0",
- "opis/closure": "^3.1",
- "php": "^7.2",
+ "nesbot/carbon": "^2.31",
+ "opis/closure": "^3.6",
+ "php": "^7.2.5|^8.0",
"psr/container": "^1.0",
"psr/simple-cache": "^1.0",
"ramsey/uuid": "^3.7",
"illuminate/view": "self.version"
},
"require-dev": {
- "aws/aws-sdk-php": "^3.0",
+ "aws/aws-sdk-php": "^3.155",
"doctrine/dbal": "^2.6",
- "filp/whoops": "^2.4",
- "guzzlehttp/guzzle": "^6.3|^7.0",
+ "filp/whoops": "^2.8",
+ "guzzlehttp/guzzle": "^6.3.1|^7.0.1",
"league/flysystem-cached-adapter": "^1.0",
- "mockery/mockery": "^1.3.1",
+ "mockery/mockery": "~1.3.3|^1.4.2",
"moontoast/math": "^1.1",
- "orchestra/testbench-core": "^4.0",
+ "orchestra/testbench-core": "^4.8",
"pda/pheanstalk": "^4.0",
- "phpunit/phpunit": "^7.5.15|^8.4|^9.0",
+ "phpunit/phpunit": "^7.5.15|^8.4|^9.3.3",
"predis/predis": "^1.1.1",
"symfony/cache": "^4.3.4"
},
"suggest": {
- "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.0).",
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
"ext-ftp": "Required to use the Flysystem FTP driver.",
"ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
"ext-pcntl": "Required to use all features of the queue worker.",
"ext-posix": "Required to use all features of the queue worker.",
"ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
- "filp/whoops": "Required for friendly error pages in development (^2.4).",
- "fzaninotto/faker": "Required to use the eloquent factory builder (^1.9.1).",
- "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.0|^7.0).",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "filp/whoops": "Required for friendly error pages in development (^2.8).",
+ "guzzlehttp/guzzle": "Required to use the Mailgun mail driver and the ping methods on schedules (^6.3.1|^7.0.1).",
"laravel/tinker": "Required to use the tinker console command (^2.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
"league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
"framework",
"laravel"
],
- "time": "2020-09-09T15:02:20+00:00"
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-02-02T13:50:12+00:00"
},
{
"name": "laravel/socialite",
- "version": "v4.4.1",
+ "version": "v5.1.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
- "reference": "80951df0d93435b773aa00efe1fad6d5015fac75"
+ "reference": "2e6beafe911a09f2300353c102d882e9d63f1f72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/socialite/zipball/80951df0d93435b773aa00efe1fad6d5015fac75",
- "reference": "80951df0d93435b773aa00efe1fad6d5015fac75",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/2e6beafe911a09f2300353c102d882e9d63f1f72",
+ "reference": "2e6beafe911a09f2300353c102d882e9d63f1f72",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
- "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0",
- "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0",
+ "illuminate/http": "^6.0|^7.0|^8.0",
+ "illuminate/support": "^6.0|^7.0|^8.0",
"league/oauth1-client": "^1.0",
- "php": "^7.1.3"
+ "php": "^7.2|^8.0"
},
"require-dev": {
- "illuminate/contracts": "~5.7.0|~5.8.0|^6.0|^7.0",
+ "illuminate/contracts": "^6.0|^7.0",
"mockery/mockery": "^1.0",
- "orchestra/testbench": "^3.7|^3.8|^4.0|^5.0",
- "phpunit/phpunit": "^7.0|^8.0"
+ "orchestra/testbench": "^4.0|^5.0|^6.0",
+ "phpunit/phpunit": "^8.0|^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.x-dev"
+ "dev-master": "5.x-dev"
},
"laravel": {
"providers": [
"laravel",
"oauth"
],
- "time": "2020-06-03T13:30:03+00:00"
+ "support": {
+ "issues": "https://github.com/laravel/socialite/issues",
+ "source": "https://github.com/laravel/socialite"
+ },
+ "time": "2021-01-05T17:02:09+00:00"
},
{
"name": "league/commonmark",
- "version": "1.5.5",
+ "version": "1.5.7",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "45832dfed6007b984c0d40addfac48d403dc6432"
+ "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/45832dfed6007b984c0d40addfac48d403dc6432",
- "reference": "45832dfed6007b984c0d40addfac48d403dc6432",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+ "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54",
"shasum": ""
},
"require": {
"md",
"parser"
],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
"funding": [
{
"url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark",
"type": "tidelift"
}
],
- "time": "2020-09-13T14:44:46+00:00"
+ "time": "2020-10-31T13:49:32+00:00"
},
{
"name": "league/flysystem",
- "version": "1.0.70",
+ "version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
- "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493"
+ "reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/585824702f534f8d3cf7fab7225e8466cc4b7493",
- "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
+ "reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
- "php": ">=5.5.9"
+ "league/mime-type-detection": "^1.3",
+ "php": "^7.2.5 || ^8.0"
},
"conflict": {
"league/flysystem-sftp": "<1.0.6"
},
"require-dev": {
- "phpspec/phpspec": "^3.4 || ^4.0 || ^5.0 || ^6.0",
- "phpunit/phpunit": "^5.7.26"
+ "phpspec/prophecy": "^1.11.1",
+ "phpunit/phpunit": "^8.5.8"
},
"suggest": {
"ext-fileinfo": "Required for MimeType",
"sftp",
"storage"
],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/1.x"
+ },
"funding": [
{
"url": "https://offset.earth/frankdejonge",
"type": "other"
}
],
- "time": "2020-07-26T07:20:36+00:00"
+ "time": "2020-08-23T07:39:11+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
- "version": "1.0.28",
+ "version": "1.0.29",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
- "reference": "af7384a12f7cd7d08183390d930c9d0ec629c990"
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/af7384a12f7cd7d08183390d930c9d0ec629c990",
- "reference": "af7384a12f7cd7d08183390d930c9d0ec629c990",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972",
+ "reference": "4e25cc0582a36a786c31115e419c6e40498f6972",
"shasum": ""
},
"require": {
}
],
"description": "Flysystem adapter for the AWS S3 SDK v3.x",
- "time": "2020-08-22T08:43:01+00:00"
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
+ "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29"
+ },
+ "time": "2020-10-08T18:58:37+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+ "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.18",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.7.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-18T20:58:21+00:00"
},
{
"name": "league/oauth1-client",
- "version": "v1.8.1",
+ "version": "v1.9.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth1-client.git",
- "reference": "3a68155c3f27a91f4b66a2dc03996cd6f3281c9f"
+ "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/3a68155c3f27a91f4b66a2dc03996cd6f3281c9f",
- "reference": "3a68155c3f27a91f4b66a2dc03996cd6f3281c9f",
+ "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
+ "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-openssl": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
- "php": ">=7.1"
+ "php": ">=7.1||>=8.0"
},
"require-dev": {
"ext-simplexml": "*",
- "friendsofphp/php-cs-fixer": "^2.16.1",
- "mockery/mockery": "^1.3",
+ "friendsofphp/php-cs-fixer": "^2.17",
+ "mockery/mockery": "^1.3.3",
"phpstan/phpstan": "^0.12.42",
- "phpunit/phpunit": "^7.5"
+ "phpunit/phpunit": "^7.5||9.5"
},
"suggest": {
"ext-simplexml": "For decoding XML-based responses."
"tumblr",
"twitter"
],
- "time": "2020-09-04T11:07:03+00:00"
+ "support": {
+ "issues": "https://github.com/thephpleague/oauth1-client/issues",
+ "source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0"
+ },
+ "time": "2021-01-20T01:40:53+00:00"
},
{
"name": "monolog/monolog",
- "version": "2.1.1",
+ "version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5"
+ "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f9eee5cec93dfb313a38b6b288741e84e53f02d5",
- "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
+ "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084",
"shasum": ""
},
"require": {
"require-dev": {
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev",
- "elasticsearch/elasticsearch": "^6.0",
+ "elasticsearch/elasticsearch": "^7",
"graylog2/gelf-php": "^1.4.2",
+ "mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4",
"php-console/php-console": "^3.1.3",
- "php-parallel-lint/php-parallel-lint": "^1.0",
"phpspec/prophecy": "^1.6.1",
+ "phpstan/phpstan": "^0.12.59",
"phpunit/phpunit": "^8.5",
"predis/predis": "^1.1",
"rollbar/rollbar": "^1.3",
- "ruflin/elastica": ">=0.90 <3.0",
+ "ruflin/elastica": ">=0.90 <7.0.1",
"swiftmailer/swiftmailer": "^5.3|^6.0"
},
"suggest": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.x-dev"
+ "dev-main": "2.x-dev"
}
},
"autoload": {
{
"name": "Jordi Boggiano",
- "homepage": "http://seld.be"
+ "homepage": "https://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
- "homepage": "http://github.com/Seldaek/monolog",
+ "homepage": "https://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
],
+ "support": {
+ "issues": "https://github.com/Seldaek/monolog/issues",
+ "source": "https://github.com/Seldaek/monolog/tree/2.2.0"
+ },
"funding": [
{
"url": "https://github.com/Seldaek",
"type": "tidelift"
}
],
- "time": "2020-07-23T08:41:23+00:00"
+ "time": "2020-12-14T13:15:25+00:00"
},
{
"name": "mtdowling/jmespath.php",
"json",
"jsonpath"
],
+ "support": {
+ "issues": "https://github.com/jmespath/jmespath.php/issues",
+ "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0"
+ },
"time": "2020-07-31T21:01:56+00:00"
},
{
"name": "nesbot/carbon",
- "version": "2.40.0",
+ "version": "2.44.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
- "reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d"
+ "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/6c7646154181013ecd55e80c201b9fd873c6ee5d",
- "reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd",
+ "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd",
"shasum": ""
},
"require": {
"kylekatarnls/multi-tester": "^2.0",
"phpmd/phpmd": "^2.9",
"phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "^0.12.35",
- "phpunit/phpunit": "^7.5 || ^8.0",
+ "phpstan/phpstan": "^0.12.54",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.14",
"squizlabs/php_codesniffer": "^3.4"
},
"bin": [
"datetime",
"time"
],
+ "support": {
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
"funding": [
{
"url": "https://opencollective.com/Carbon",
"type": "tidelift"
}
],
- "time": "2020-09-11T19:00:58+00:00"
+ "time": "2021-01-26T20:46:41+00:00"
},
{
"name": "nunomaduro/collision",
- "version": "v3.0.1",
+ "version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
- "reference": "af42d339fe2742295a54f6fdd42aaa6f8c4aca68"
+ "reference": "88b58b5bd9bdcc54756480fb3ce87234696544ee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/collision/zipball/af42d339fe2742295a54f6fdd42aaa6f8c4aca68",
- "reference": "af42d339fe2742295a54f6fdd42aaa6f8c4aca68",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/88b58b5bd9bdcc54756480fb3ce87234696544ee",
+ "reference": "88b58b5bd9bdcc54756480fb3ce87234696544ee",
"shasum": ""
},
"require": {
"filp/whoops": "^2.1.4",
"jakub-onderka/php-console-highlighter": "0.3.*|0.4.*",
- "php": "^7.1",
+ "php": "^7.1 || ^8.0",
"symfony/console": "~2.8|~3.3|~4.0"
},
"require-dev": {
- "laravel/framework": "5.8.*",
- "nunomaduro/larastan": "^0.3.0",
- "phpstan/phpstan": "^0.11",
- "phpunit/phpunit": "~8.0"
+ "laravel/framework": "^6.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
},
"type": "library",
"extra": {
"php",
"symfony"
],
- "time": "2019-03-07T21:35:13+00:00"
+ "support": {
+ "issues": "https://github.com/nunomaduro/collision/issues",
+ "source": "https://github.com/nunomaduro/collision"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2020-10-29T16:05:21+00:00"
},
{
"name": "onelogin/php-saml",
- "version": "3.4.1",
+ "version": "3.5.1",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
- "reference": "5fbf3486704ac9835b68184023ab54862c95f213"
+ "reference": "593aca859b67d607923fe50d8ad7315373f5b6dd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/onelogin/php-saml/zipball/5fbf3486704ac9835b68184023ab54862c95f213",
- "reference": "5fbf3486704ac9835b68184023ab54862c95f213",
+ "url": "https://api.github.com/repos/onelogin/php-saml/zipball/593aca859b67d607923fe50d8ad7315373f5b6dd",
+ "reference": "593aca859b67d607923fe50d8ad7315373f5b6dd",
"shasum": ""
},
"require": {
"php": ">=5.4",
- "robrichards/xmlseclibs": ">=3.0.4"
+ "robrichards/xmlseclibs": ">=3.1.1"
},
"require-dev": {
"pdepend/pdepend": "^2.5.0",
"onelogin",
"saml"
],
- "time": "2019-11-25T17:30:07+00:00"
+ "support": {
+ "issues": "https://github.com/onelogin/php-saml/issues",
+ "source": "https://github.com/onelogin/php-saml/"
+ },
+ "time": "2020-12-03T20:08:41+00:00"
},
{
"name": "opis/closure",
- "version": "3.5.7",
+ "version": "3.6.1",
"source": {
"type": "git",
"url": "https://github.com/opis/closure.git",
- "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf"
+ "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/opis/closure/zipball/4531e53afe2fc660403e76fb7644e95998bff7bf",
- "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf",
+ "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+ "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
"shasum": ""
},
"require": {
- "php": "^5.4 || ^7.0"
+ "php": "^5.4 || ^7.0 || ^8.0"
},
"require-dev": {
"jeremeamia/superclosure": "^2.0",
- "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.5.x-dev"
+ "dev-master": "3.6.x-dev"
}
},
"autoload": {
"serialization",
"serialize"
],
- "time": "2020-09-06T17:02:15+00:00"
+ "support": {
+ "issues": "https://github.com/opis/closure/issues",
+ "source": "https://github.com/opis/closure/tree/3.6.1"
+ },
+ "time": "2020-11-07T02:01:34+00:00"
},
{
"name": "paragonie/random_compat",
"pseudorandom",
"random"
],
+ "support": {
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
"time": "2018-07-02T15:55:56+00:00"
},
{
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/PhenX/php-font-lib",
+ "support": {
+ "issues": "https://github.com/PhenX/php-font-lib/issues",
+ "source": "https://github.com/PhenX/php-font-lib/tree/0.5.2"
+ },
"time": "2020-03-08T15:31:32+00:00"
},
{
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/PhenX/php-svg-lib",
+ "support": {
+ "issues": "https://github.com/PhenX/php-svg-lib/issues",
+ "source": "https://github.com/PhenX/php-svg-lib/tree/master"
+ },
"time": "2019-09-11T20:02:13+00:00"
},
{
"php",
"type"
],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.7.5"
+ },
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"predis",
"redis"
],
+ "support": {
+ "issues": "https://github.com/predis/predis/issues",
+ "source": "https://github.com/predis/predis/tree/v1.1.6"
+ },
"funding": [
{
"url": "https://github.com/sponsors/tillkruss",
"container-interop",
"psr"
],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/master"
+ },
"time": "2017-02-14T16:28:37+00:00"
},
+ {
+ "name": "psr/http-client",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
+ },
{
"name": "psr/http-message",
"version": "1.0.1",
"request",
"response"
],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/master"
+ },
"time": "2016-08-06T14:39:51+00:00"
},
{
"psr",
"psr-3"
],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.3"
+ },
"time": "2020-03-23T09:12:05+00:00"
},
{
"psr-16",
"simple-cache"
],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/master"
+ },
"time": "2017-10-23T01:57:42+00:00"
},
{
}
],
"description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
"time": "2019-03-08T08:55:37+00:00"
},
{
"identifier",
"uuid"
],
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "rss": "https://github.com/ramsey/uuid/releases.atom",
+ "source": "https://github.com/ramsey/uuid",
+ "wiki": "https://github.com/ramsey/uuid/wiki"
+ },
"time": "2020-02-21T04:36:14+00:00"
},
{
"xml",
"xmldsig"
],
+ "support": {
+ "issues": "https://github.com/robrichards/xmlseclibs/issues",
+ "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.1"
+ },
"time": "2020-09-05T13:00:25+00:00"
},
{
"parser",
"stylesheet"
],
+ "support": {
+ "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
+ "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.3.1"
+ },
"time": "2020-06-01T09:10:00+00:00"
},
{
"name": "scrivo/highlight.php",
- "version": "v9.18.1.2",
+ "version": "v9.18.1.6",
"source": {
"type": "git",
"url": "https://github.com/scrivo/highlight.php.git",
- "reference": "efb6e445494a9458aa59b0af5edfa4bdcc6809d9"
+ "reference": "44a3d4136edb5ad8551590bf90f437db80b2d466"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/efb6e445494a9458aa59b0af5edfa4bdcc6809d9",
- "reference": "efb6e445494a9458aa59b0af5edfa4bdcc6809d9",
+ "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/44a3d4136edb5ad8551590bf90f437db80b2d466",
+ "reference": "44a3d4136edb5ad8551590bf90f437db80b2d466",
"shasum": ""
},
"require": {
"symfony/finder": "^2.8|^3.4",
"symfony/var-dumper": "^2.8|^3.4"
},
- "suggest": {
- "ext-dom": "Needed to make use of the features in the utilities namespace"
- },
"type": "library",
"autoload": {
"psr-0": {
"highlight.php",
"syntax"
],
+ "support": {
+ "issues": "https://github.com/scrivo/highlight.php/issues",
+ "source": "https://github.com/scrivo/highlight.php"
+ },
"funding": [
{
"url": "https://github.com/allejo",
"type": "github"
}
],
- "time": "2020-08-27T03:24:44+00:00"
+ "time": "2020-12-22T19:20:29+00:00"
},
{
"name": "socialiteproviders/discord",
- "version": "v2.0.2",
+ "version": "4.1.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Discord.git",
- "reference": "e0cd8895f321943b36f533e7bf21ad29bcdece9a"
+ "reference": "c6eddeb07ace7473e82d02d4db852dfacf5ef574"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/e0cd8895f321943b36f533e7bf21ad29bcdece9a",
- "reference": "e0cd8895f321943b36f533e7bf21ad29bcdece9a",
+ "url": "https://api.github.com/repos/SocialiteProviders/Discord/zipball/c6eddeb07ace7473e82d02d4db852dfacf5ef574",
+ "reference": "c6eddeb07ace7473e82d02d4db852dfacf5ef574",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "ext-json": "*",
+ "php": "^7.2 || ^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "Discord OAuth2 Provider for Laravel Socialite",
- "time": "2018-05-26T03:40:07+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/Discord/tree/4.1.1"
+ },
+ "time": "2021-01-05T22:03:58+00:00"
},
{
"name": "socialiteproviders/gitlab",
- "version": "v3.1",
+ "version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/GitLab.git",
- "reference": "69e537f6192ca15483e98b8662495384f44299ca"
+ "reference": "a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/69e537f6192ca15483e98b8662495384f44299ca",
- "reference": "69e537f6192ca15483e98b8662495384f44299ca",
+ "url": "https://api.github.com/repos/SocialiteProviders/GitLab/zipball/a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d",
+ "reference": "a8f67d3b02c9ee8c70c25c6728417c0eddcbbb9d",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "ext-json": "*",
+ "php": "^7.2 || ^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "GitLab OAuth2 Provider for Laravel Socialite",
- "time": "2018-06-27T05:10:32+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/GitLab/tree/4.1.0"
+ },
+ "time": "2020-12-01T23:10:59+00:00"
},
{
"name": "socialiteproviders/manager",
- "version": "v3.6",
+ "version": "4.0.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
- "reference": "fc8dbcf0061f12bfe0cc347e9655af932860ad36"
+ "reference": "0f5e82af0404df0080bdc5c105cef936c1711524"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/fc8dbcf0061f12bfe0cc347e9655af932860ad36",
- "reference": "fc8dbcf0061f12bfe0cc347e9655af932860ad36",
+ "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/0f5e82af0404df0080bdc5c105cef936c1711524",
+ "reference": "0f5e82af0404df0080bdc5c105cef936c1711524",
"shasum": ""
},
"require": {
"illuminate/support": "^6.0|^7.0|^8.0",
"laravel/socialite": "~4.0|~5.0",
- "php": "^7.2"
+ "php": "^7.2 || ^8.0"
},
"require-dev": {
"mockery/mockery": "^1.2",
- "phpunit/phpunit": "^8.0"
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
],
"description": "Easily add new or override built-in providers in Laravel Socialite.",
"homepage": "https://socialiteproviders.com/",
- "time": "2020-09-08T10:41:06+00:00"
+ "support": {
+ "issues": "https://github.com/SocialiteProviders/Manager/issues",
+ "source": "https://github.com/SocialiteProviders/Manager/tree/4.0.1"
+ },
+ "time": "2020-12-01T23:09:06+00:00"
},
{
"name": "socialiteproviders/microsoft-azure",
- "version": "v3.1.0",
+ "version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
- "reference": "b22f4696cccecd6de902cf0bc923de7fc2e4608e"
+ "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/b22f4696cccecd6de902cf0bc923de7fc2e4608e",
- "reference": "b22f4696cccecd6de902cf0bc923de7fc2e4608e",
+ "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/7808764f777a01df88be9ca6b14d683e50aaf88a",
+ "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a",
"shasum": ""
},
"require": {
"ext-json": "*",
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "php": "^7.2 || ^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
- "time": "2020-04-30T23:01:40+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/Microsoft-Azure/tree/4.2.0"
+ },
+ "time": "2020-12-01T23:10:59+00:00"
},
{
"name": "socialiteproviders/okta",
- "version": "v1.1.0",
+ "version": "4.1.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Okta.git",
- "reference": "7c2512f0872316b139e3eea1c50c9351747a57ea"
+ "reference": "e3ef9f23c7d2f86b3b16a174b82333cf4e2459e8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/7c2512f0872316b139e3eea1c50c9351747a57ea",
- "reference": "7c2512f0872316b139e3eea1c50c9351747a57ea",
+ "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/e3ef9f23c7d2f86b3b16a174b82333cf4e2459e8",
+ "reference": "e3ef9f23c7d2f86b3b16a174b82333cf4e2459e8",
"shasum": ""
},
"require": {
"ext-json": "*",
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "php": "^7.2 || ^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "Okta OAuth2 Provider for Laravel Socialite",
- "time": "2019-09-06T15:27:03+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/Okta/tree/4.1.1"
+ },
+ "time": "2021-01-12T23:51:01+00:00"
},
{
"name": "socialiteproviders/slack",
- "version": "v3.1",
+ "version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Slack.git",
- "reference": "d46826640fbeae8f34328d99c358404a1e1050a3"
+ "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/d46826640fbeae8f34328d99c358404a1e1050a3",
- "reference": "d46826640fbeae8f34328d99c358404a1e1050a3",
+ "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
+ "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "ext-json": "*",
+ "php": "^7.2|^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "Slack OAuth2 Provider for Laravel Socialite",
- "time": "2019-01-11T19:48:14+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/Slack/tree/4.1.0"
+ },
+ "time": "2020-11-26T17:57:15+00:00"
},
{
"name": "socialiteproviders/twitch",
- "version": "v5.2.0",
+ "version": "5.3.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Twitch.git",
- "reference": "9ee6fe196d7c28777139b3cde04cbd537cf7e652"
+ "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/9ee6fe196d7c28777139b3cde04cbd537cf7e652",
- "reference": "9ee6fe196d7c28777139b3cde04cbd537cf7e652",
+ "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/7accf30ae7a3139b757b4ca8f34989c09a3dbee7",
+ "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7",
"shasum": ""
},
"require": {
"ext-json": "*",
- "php": "^5.6 || ^7.0",
- "socialiteproviders/manager": "~2.0 || ~3.0"
+ "php": "^7.2 || ^8.0",
+ "socialiteproviders/manager": "~4.0"
},
"type": "library",
"autoload": {
}
],
"description": "Twitch OAuth2 Provider for Laravel Socialite",
- "time": "2020-05-06T22:51:30+00:00"
+ "support": {
+ "source": "https://github.com/SocialiteProviders/Twitch/tree/5.3.1"
+ },
+ "time": "2020-12-01T23:10:59+00:00"
+ },
+ {
+ "name": "ssddanbrown/htmldiff",
+ "version": "v1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ssddanbrown/HtmlDiff.git",
+ "reference": "f60d5cc278b60305ab980a6665f46117c5b589c0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ssddanbrown/HtmlDiff/zipball/f60d5cc278b60305ab980a6665f46117c5b589c0",
+ "reference": "f60d5cc278b60305ab980a6665f46117c5b589c0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=7.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5|^9.4.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Ssddanbrown\\HtmlDiff\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dan Brown",
+ "role": "Developer"
+ }
+ ],
+ "description": "HTML Content Diff Generator",
+ "homepage": "https://github.com/ssddanbrown/htmldiff",
+ "support": {
+ "issues": "https://github.com/ssddanbrown/HtmlDiff/issues",
+ "source": "https://github.com/ssddanbrown/HtmlDiff/tree/v1.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ssddanbrown",
+ "type": "github"
+ }
+ ],
+ "time": "2021-01-24T18:51:30+00:00"
},
{
"name": "swiftmailer/swiftmailer",
- "version": "v6.2.3",
+ "version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
- "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9"
+ "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
- "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7",
+ "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7",
"shasum": ""
},
"require": {
- "egulias/email-validator": "~2.0",
+ "egulias/email-validator": "^2.0",
"php": ">=7.0.0",
"symfony/polyfill-iconv": "^1.0",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"require-dev": {
- "mockery/mockery": "~0.9.1",
- "symfony/phpunit-bridge": "^3.4.19|^4.1.8"
+ "mockery/mockery": "^1.0",
+ "symfony/phpunit-bridge": "^4.4|^5.0"
},
"suggest": {
- "ext-intl": "Needed to support internationalized email addresses",
- "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed"
+ "ext-intl": "Needed to support internationalized email addresses"
},
"type": "library",
"extra": {
"mail",
"mailer"
],
- "time": "2019-11-12T09:31:26+00:00"
+ "support": {
+ "issues": "https://github.com/swiftmailer/swiftmailer/issues",
+ "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-12T09:35:59+00:00"
},
{
"name": "symfony/console",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "b39fd99b9297b67fb7633b7d8083957a97e1e727"
+ "reference": "24026c44fc37099fa145707fecd43672831b837a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/b39fd99b9297b67fb7633b7d8083957a97e1e727",
- "reference": "b39fd99b9297b67fb7633b7d8083957a97e1e727",
+ "url": "https://api.github.com/repos/symfony/console/zipball/24026c44fc37099fa145707fecd43672831b837a",
+ "reference": "24026c44fc37099fa145707fecd43672831b837a",
"shasum": ""
},
"require": {
"symfony/process": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Console Component",
+ "description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-09-02T07:07:21+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc"
+ "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
- "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/f907d3e53ecb2a5fad8609eb2f30525287a734c8",
+ "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8",
"shasum": ""
},
"require": {
"php": ">=7.1.3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony CssSelector Component",
+ "description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-05T09:39:30+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/debug",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
- "reference": "aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e"
+ "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e",
- "reference": "aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e",
+ "url": "https://api.github.com/repos/symfony/debug/zipball/af4987aa4a5630e9615be9d9c3ed1b0f24ca449c",
+ "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c",
"shasum": ""
},
"require": {
"symfony/http-kernel": "^3.4|^4.0|^5.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Debug\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Debug Component",
+ "description": "Provides tools to ease debugging PHP code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/debug/tree/v4.4.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-27T09:09:26+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/master"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-10T07:47:39+00:00"
+ "time": "2020-09-07T11:33:47+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "2434fb32851f252e4f27691eee0b77c16198db62"
+ "reference": "d603654eaeb713503bba3e308b9e748e5a6d3f2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/2434fb32851f252e4f27691eee0b77c16198db62",
- "reference": "2434fb32851f252e4f27691eee0b77c16198db62",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/d603654eaeb713503bba3e308b9e748e5a6d3f2e",
+ "reference": "d603654eaeb713503bba3e308b9e748e5a6d3f2e",
"shasum": ""
},
"require": {
"symfony/serializer": "^4.4|^5.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\ErrorHandler\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony ErrorHandler Component",
+ "description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T09:56:45+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "3e8ea5ccddd00556b86d69d42f99f1061a704030"
+ "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3e8ea5ccddd00556b86d69d42f99f1061a704030",
- "reference": "3e8ea5ccddd00556b86d69d42f99f1061a704030",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
+ "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
"shasum": ""
},
"require": {
"psr/log": "~1.0",
"symfony/config": "^3.4|^4.0|^5.0",
"symfony/dependency-injection": "^3.4|^4.0|^5.0",
+ "symfony/error-handler": "~3.4|~4.4",
"symfony/expression-language": "^3.4|^4.0|^5.0",
"symfony/http-foundation": "^3.4|^4.0|^5.0",
"symfony/service-contracts": "^1.1|^2",
"symfony/http-kernel": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony EventDispatcher Component",
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-13T14:18:44+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.9"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
},
{
"name": "symfony/finder",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "2a78590b2c7e3de5c429628457c47541c58db9c7"
+ "reference": "25d79cfccfc12e84e7a63a248c3f0720fdd92db6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/2a78590b2c7e3de5c429628457c47541c58db9c7",
- "reference": "2a78590b2c7e3de5c429628457c47541c58db9c7",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/25d79cfccfc12e84e7a63a248c3f0720fdd92db6",
+ "reference": "25d79cfccfc12e84e7a63a248c3f0720fdd92db6",
"shasum": ""
},
"require": {
"php": ">=7.1.3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Finder Component",
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v4.4.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-01-27T09:09:26+00:00"
+ },
+ {
+ "name": "symfony/http-client-contracts",
+ "version": "v2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client-contracts.git",
+ "reference": "41db680a15018f9c1d4b23516059633ce280ca33"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41db680a15018f9c1d4b23516059633ce280ca33",
+ "reference": "41db680a15018f9c1d4b23516059633ce280ca33",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5"
+ },
+ "suggest": {
+ "symfony/http-client-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-version": "2.3",
+ "branch-alias": {
+ "dev-main": "2.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\HttpClient\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to HTTP clients",
"homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T09:56:45+00:00"
+ "time": "2020-10-14T17:08:19+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "e3e5a62a6631a461954d471e7206e3750dbe8ee1"
+ "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e3e5a62a6631a461954d471e7206e3750dbe8ee1",
- "reference": "e3e5a62a6631a461954d471e7206e3750dbe8ee1",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8888741b633f6c3d1e572b7735ad2cae3e03f9c5",
+ "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"symfony/mime": "^4.3|^5.0",
- "symfony/polyfill-mbstring": "~1.1"
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php80": "^1.15"
},
"require-dev": {
"predis/predis": "~1.0",
"symfony/expression-language": "^3.4|^4.0|^5.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpFoundation\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony HttpFoundation Component",
+ "description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T07:39:58+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "2bb7b90ecdc79813c0bf237b7ff20e79062b5188"
+ "reference": "07ea794a327d7c8c5d76e3058fde9fec6a711cb4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2bb7b90ecdc79813c0bf237b7ff20e79062b5188",
- "reference": "2bb7b90ecdc79813c0bf237b7ff20e79062b5188",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/07ea794a327d7c8c5d76e3058fde9fec6a711cb4",
+ "reference": "07ea794a327d7c8c5d76e3058fde9fec6a711cb4",
"shasum": ""
},
"require": {
"psr/log": "~1.0",
"symfony/error-handler": "^4.4",
"symfony/event-dispatcher": "^4.4",
+ "symfony/http-client-contracts": "^1.1|^2",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-php73": "^1.9",
"symfony/console": ">=5",
"symfony/dependency-injection": "<4.3",
"symfony/translation": "<4.2",
- "twig/twig": "<1.34|<2.4,>=2"
+ "twig/twig": "<1.43|<2.13,>=2"
},
"provide": {
"psr/log-implementation": "1.0"
"symfony/templating": "^3.4|^4.0|^5.0",
"symfony/translation": "^4.2|^5.0",
"symfony/translation-contracts": "^1.1|^2",
- "twig/twig": "^1.34|^2.4|^3.0"
+ "twig/twig": "^1.43|^2.13|^3.0.4"
},
"suggest": {
"symfony/browser-kit": "",
"symfony/dependency-injection": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpKernel\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony HttpKernel Component",
+ "description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-09-02T08:09:29+00:00"
+ "time": "2021-01-27T13:50:53+00:00"
},
{
"name": "symfony/mime",
- "version": "v4.4.13",
+ "version": "v5.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "50ad671306d3d3ffb888d95b4fb1859496831e3a"
+ "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/50ad671306d3d3ffb888d95b4fb1859496831e3a",
- "reference": "50ad671306d3d3ffb888d95b4fb1859496831e3a",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/7dee6a43493f39b51ff6c5bb2bd576fe40a76c86",
+ "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1",
"symfony/polyfill-intl-idn": "^1.10",
- "symfony/polyfill-mbstring": "^1.0"
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/polyfill-php80": "^1.15"
},
"conflict": {
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
"symfony/mailer": "<4.4"
},
"require-dev": {
"egulias/email-validator": "^2.1.10",
- "symfony/dependency-injection": "^3.4|^4.1|^5.0"
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/property-access": "^4.4|^5.1",
+ "symfony/property-info": "^4.4|^5.1",
+ "symfony/serializer": "^5.2"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Mime\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "A library to manipulate MIME messages",
+ "description": "Allows manipulating MIME messages",
"homepage": "https://symfony.com",
"keywords": [
"mime",
"mime-type"
],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v5.2.3"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T09:56:45+00:00"
+ "time": "2021-02-02T06:10:15+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
+ "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
- "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
+ "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
"ext-ctype": "For best performance"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"polyfill",
"portable"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/polyfill-iconv",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
- "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36"
+ "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36",
- "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36",
+ "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6",
+ "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
"ext-iconv": "For best performance"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251"
+ "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/5dcab1bc7146cf8c1beaa4502a3d9be344334251",
- "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44",
+ "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
+ "php": ">=7.1",
"symfony/polyfill-intl-normalizer": "^1.10",
- "symfony/polyfill-php70": "^1.10",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-04T06:02:08+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
+ "reference": "6e971c891537eb617a00bb07a43d182a6915faba"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
- "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba",
+ "reference": "6e971c891537eb617a00bb07a43d182a6915faba",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
"ext-intl": "For best performance"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T17:09:11+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
+ "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
+ "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
"ext-mbstring": "For best performance"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
- "name": "symfony/polyfill-php70",
- "version": "v1.18.1",
+ "name": "symfony/polyfill-php72",
+ "version": "v1.22.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php70.git",
- "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3"
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
- "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+ "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
"shasum": ""
},
"require": {
- "paragonie/random_compat": "~1.0|~2.0|~9.99",
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
},
"autoload": {
"psr-4": {
- "Symfony\\Polyfill\\Php70\\": ""
+ "Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
- ],
- "classmap": [
- "Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
- },
- {
- "name": "symfony/polyfill-php72",
- "version": "v1.18.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php72.git",
- "reference": "639447d008615574653fb3bc60d1986d7172eaae"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
- "reference": "639447d008615574653fb3bc60d1986d7172eaae",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.18-dev"
- },
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Polyfill\\Php72\\": ""
- },
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/polyfill-php73",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca"
+ "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
- "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.18.1",
+ "version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
+ "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
"shasum": ""
},
"require": {
- "php": ">=7.0.8"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"portable",
"shim"
],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/process",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479"
+ "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/65e70bab62f3da7089a8d4591fb23fbacacb3479",
- "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479",
+ "url": "https://api.github.com/repos/symfony/process/zipball/7e950b6366d4da90292c2e7fa820b3c1842b965a",
+ "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a",
"shasum": ""
},
"require": {
"php": ">=7.1.3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Process Component",
+ "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-23T08:31:43+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/routing",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "e3387963565da9bae51d1d3ab8041646cc93bd04"
+ "reference": "87529f6e305c7acb162840d1ea57922038072425"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/e3387963565da9bae51d1d3ab8041646cc93bd04",
- "reference": "e3387963565da9bae51d1d3ab8041646cc93bd04",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/87529f6e305c7acb162840d1ea57922038072425",
+ "reference": "87529f6e305c7acb162840d1ea57922038072425",
"shasum": ""
},
"require": {
"symfony/yaml": "<3.4"
},
"require-dev": {
- "doctrine/annotations": "~1.2",
+ "doctrine/annotations": "^1.10.4",
"psr/log": "~1.0",
"symfony/config": "^4.2|^5.0",
"symfony/dependency-injection": "^3.4|^4.0|^5.0",
"symfony/yaml": "For using the YAML loader"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Routing\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Routing Component",
+ "description": "Maps an HTTP request to a set of configuration variables",
"homepage": "https://symfony.com",
"keywords": [
"router",
"uri",
"url"
],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-10T07:27:51+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v1.1.9",
+ "version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26"
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26",
- "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
+ "php": ">=7.2.5",
"psr/container": "^1.0"
},
"suggest": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1-dev"
+ "dev-master": "2.2-dev"
},
"thanks": {
"name": "symfony/contracts",
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/master"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-07-06T13:19:58+00:00"
+ "time": "2020-09-07T11:33:47+00:00"
},
{
"name": "symfony/translation",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "700e6e50174b0cdcf0fa232773bec5c314680575"
+ "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/700e6e50174b0cdcf0fa232773bec5c314680575",
- "reference": "700e6e50174b0cdcf0fa232773bec5c314680575",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/e1d0c67167a553556d9f974b5fa79c2448df317a",
+ "reference": "e1d0c67167a553556d9f974b5fa79c2448df317a",
"shasum": ""
},
"require": {
"symfony/yaml": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Translation\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Translation Component",
+ "description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T09:56:45+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/translation-contracts",
- "version": "v1.1.10",
+ "version": "v2.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "84180a25fad31e23bebd26ca09d89464f082cacc"
+ "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/84180a25fad31e23bebd26ca09d89464f082cacc",
- "reference": "84180a25fad31e23bebd26ca09d89464f082cacc",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105",
+ "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105",
"shasum": ""
},
"require": {
- "php": ">=7.1.3"
+ "php": ">=7.2.5"
},
"suggest": {
"symfony/translation-implementation": ""
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1-dev"
+ "dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",
"interoperability",
"standards"
],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-09-02T16:08:58+00:00"
+ "time": "2020-09-28T13:05:58+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "1bef32329f3166486ab7cb88599cae4875632b99"
+ "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1bef32329f3166486ab7cb88599cae4875632b99",
- "reference": "1bef32329f3166486ab7cb88599cae4875632b99",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
+ "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"symfony/console": "^3.4|^4.0|^5.0",
"symfony/process": "^4.4|^5.0",
- "twig/twig": "^1.34|^2.4|^3.0"
+ "twig/twig": "^1.43|^2.13|^3.0.4"
},
"suggest": {
"ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
"Resources/bin/var-dump-server"
],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"files": [
"Resources/functions/dump.php"
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony mechanism for exploring and dumping PHP variables",
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
"homepage": "https://symfony.com",
"keywords": [
"debug",
"dump"
],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-17T07:31:35+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
],
"description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+ "support": {
+ "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+ "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.3"
+ },
"time": "2020-07-13T06:12:54+00:00"
},
{
"name": "vlucas/phpdotenv",
- "version": "v3.6.7",
+ "version": "v3.6.8",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
- "reference": "2065beda6cbe75e2603686907b2e45f6f3a5ad82"
+ "reference": "5e679f7616db829358341e2d5cccbd18773bdab8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2065beda6cbe75e2603686907b2e45f6f3a5ad82",
- "reference": "2065beda6cbe75e2603686907b2e45f6f3a5ad82",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/5e679f7616db829358341e2d5cccbd18773bdab8",
+ "reference": "5e679f7616db829358341e2d5cccbd18773bdab8",
"shasum": ""
},
"require": {
"require-dev": {
"ext-filter": "*",
"ext-pcre": "*",
- "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0"
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20"
},
"suggest": {
"ext-filter": "Required to use the boolean validator.",
"env",
"environment"
],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v3.6.8"
+ },
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "tidelift"
}
],
- "time": "2020-07-14T19:04:52+00:00"
+ "time": "2021-01-20T14:39:46+00:00"
}
],
"packages-dev": [
{
"name": "barryvdh/laravel-debugbar",
- "version": "v3.5.1",
+ "version": "v3.5.2",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
- "reference": "233c10688f4c1a6e66ed2ef123038b1363d1bedc"
+ "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/233c10688f4c1a6e66ed2ef123038b1363d1bedc",
- "reference": "233c10688f4c1a6e66ed2ef123038b1363d1bedc",
+ "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/cae0a8d1cb89b0f0522f65e60465e16d738e069b",
+ "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b",
"shasum": ""
},
"require": {
"symfony/finder": "^4.3|^5"
},
"require-dev": {
+ "mockery/mockery": "^1.3.3",
"orchestra/testbench-dusk": "^4|^5|^6",
"phpunit/phpunit": "^8.5|^9.0",
"squizlabs/php_codesniffer": "^3.5"
"profiler",
"webprofiler"
],
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-debugbar/issues",
+ "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.5.2"
+ },
"funding": [
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
- "time": "2020-09-07T19:32:39+00:00"
+ "time": "2021-01-06T14:21:44+00:00"
},
{
"name": "barryvdh/laravel-ide-helper",
- "version": "v2.8.1",
+ "version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
- "reference": "affa55122f83575888d4ebf1728992686e8223de"
+ "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/affa55122f83575888d4ebf1728992686e8223de",
- "reference": "affa55122f83575888d4ebf1728992686e8223de",
+ "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/5515cabea39b9cf55f98980d0f269dc9d85cfcca",
+ "reference": "5515cabea39b9cf55f98980d0f269dc9d85cfcca",
"shasum": ""
},
"require": {
"barryvdh/reflection-docblock": "^2.0.6",
- "composer/composer": "^1.6 || ^2.0@dev",
+ "composer/composer": "^1.6 || ^2",
"doctrine/dbal": "~2.3",
"ext-json": "*",
"illuminate/console": "^6 || ^7 || ^8",
"phpdocumentor/type-resolver": "^1.1.0"
},
"require-dev": {
+ "ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^2",
"illuminate/config": "^6 || ^7 || ^8",
"illuminate/view": "^6 || ^7 || ^8",
- "mockery/mockery": "^1.3",
+ "mockery/mockery": "^1.3.3",
"orchestra/testbench": "^4 || ^5 || ^6",
"phpunit/phpunit": "^8.5 || ^9",
- "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3",
+ "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3 || ^4",
"vimeo/psalm": "^3.12"
},
"type": "library",
"phpstorm",
"sublime"
],
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
+ "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.8.2"
+ },
"funding": [
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
- "time": "2020-09-07T07:36:37+00:00"
+ "time": "2020-12-06T08:55:05+00:00"
},
{
"name": "barryvdh/reflection-docblock",
}
],
+ "support": {
+ "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.0.6"
+ },
"time": "2018-12-13T10:34:14+00:00"
},
{
"name": "composer/ca-bundle",
- "version": "1.2.8",
+ "version": "1.2.9",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
- "reference": "8a7ecad675253e4654ea05505233285377405215"
+ "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215",
- "reference": "8a7ecad675253e4654ea05505233285377405215",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
+ "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
+ "phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
+ "symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.x-dev"
+ "dev-main": "1.x-dev"
}
},
"autoload": {
"ssl",
"tls"
],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/ca-bundle/issues",
+ "source": "https://github.com/composer/ca-bundle/tree/1.2.9"
+ },
"funding": [
{
"url": "https://packagist.com",
"type": "tidelift"
}
],
- "time": "2020-08-23T12:54:47+00:00"
+ "time": "2021-01-12T12:10:35+00:00"
},
{
"name": "composer/composer",
- "version": "1.10.13",
+ "version": "2.0.9",
"source": {
"type": "git",
"url": "https://github.com/composer/composer.git",
- "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b"
+ "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/composer/zipball/47c841ba3b2d3fc0b4b13282cf029ea18b66d78b",
- "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b",
+ "url": "https://api.github.com/repos/composer/composer/zipball/591c2c155cac0d2d7f34af41d3b1e29bcbfc685e",
+ "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.0",
- "composer/semver": "^1.0",
+ "composer/semver": "^3.0",
"composer/spdx-licenses": "^1.2",
"composer/xdebug-handler": "^1.1",
"justinrainbow/json-schema": "^5.2.10",
- "php": "^5.3.2 || ^7.0",
+ "php": "^5.3.2 || ^7.0 || ^8.0",
"psr/log": "^1.0",
+ "react/promise": "^1.2 || ^2.7",
"seld/jsonlint": "^1.4",
"seld/phar-utils": "^1.0",
- "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0",
- "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0",
- "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0",
- "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0"
- },
- "conflict": {
- "symfony/console": "2.8.38"
+ "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+ "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+ "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
+ "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0"
},
"require-dev": {
"phpspec/prophecy": "^1.10",
- "symfony/phpunit-bridge": "^4.2"
+ "symfony/phpunit-bridge": "^4.2 || ^5.0"
},
"suggest": {
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.10-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
{
"name": "Nils Adermann",
- "homepage": "http://www.naderman.de"
+ "homepage": "https://www.naderman.de"
},
{
"name": "Jordi Boggiano",
- "homepage": "http://seld.be"
+ "homepage": "https://seld.be"
}
],
"description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
"dependency",
"package"
],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/composer/issues",
+ "source": "https://github.com/composer/composer/tree/2.0.9"
+ },
"funding": [
{
"url": "https://packagist.com",
"type": "tidelift"
}
],
- "time": "2020-09-09T09:46:34+00:00"
+ "time": "2021-01-27T15:09:27+00:00"
},
{
"name": "composer/semver",
- "version": "1.7.0",
+ "version": "3.2.4",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
- "reference": "114f819054a2ea7db03287f5efb757e2af6e4079"
+ "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/114f819054a2ea7db03287f5efb757e2af6e4079",
- "reference": "114f819054a2ea7db03287f5efb757e2af6e4079",
+ "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+ "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
"shasum": ""
},
"require": {
- "php": "^5.3.2 || ^7.0"
+ "php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.5 || ^5.0.5"
+ "phpstan/phpstan": "^0.12.54",
+ "symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.x-dev"
+ "dev-main": "3.x-dev"
}
},
"autoload": {
"validation",
"versioning"
],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.2.4"
+ },
"funding": [
{
"url": "https://packagist.com",
"type": "tidelift"
}
],
- "time": "2020-09-09T09:34:06+00:00"
+ "time": "2020-11-13T08:59:24+00:00"
},
{
"name": "composer/spdx-licenses",
- "version": "1.5.4",
+ "version": "1.5.5",
"source": {
"type": "git",
"url": "https://github.com/composer/spdx-licenses.git",
- "reference": "6946f785871e2314c60b4524851f3702ea4f2223"
+ "reference": "de30328a7af8680efdc03e396aad24befd513200"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/6946f785871e2314c60b4524851f3702ea4f2223",
- "reference": "6946f785871e2314c60b4524851f3702ea4f2223",
+ "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/de30328a7af8680efdc03e396aad24befd513200",
+ "reference": "de30328a7af8680efdc03e396aad24befd513200",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.x-dev"
+ "dev-main": "1.x-dev"
}
},
"autoload": {
"spdx",
"validator"
],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/spdx-licenses/issues",
+ "source": "https://github.com/composer/spdx-licenses/tree/1.5.5"
+ },
"funding": [
{
"url": "https://packagist.com",
"type": "tidelift"
}
],
- "time": "2020-07-15T15:35:07+00:00"
+ "time": "2020-12-03T16:04:16+00:00"
},
{
"name": "composer/xdebug-handler",
- "version": "1.4.3",
+ "version": "1.4.5",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
- "reference": "ebd27a9866ae8254e873866f795491f02418c5a5"
+ "reference": "f28d44c286812c714741478d968104c5e604a1d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ebd27a9866ae8254e873866f795491f02418c5a5",
- "reference": "ebd27a9866ae8254e873866f795491f02418c5a5",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4",
+ "reference": "f28d44c286812c714741478d968104c5e604a1d4",
"shasum": ""
},
"require": {
"Xdebug",
"performance"
],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/1.4.5"
+ },
"funding": [
{
"url": "https://packagist.com",
"type": "tidelift"
}
],
- "time": "2020-08-19T10:27:58+00:00"
+ "time": "2020-11-13T08:04:11+00:00"
},
{
"name": "doctrine/instantiator",
- "version": "1.3.1",
+ "version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
- "reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
- "reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
- "doctrine/coding-standard": "^6.0",
+ "doctrine/coding-standard": "^8.0",
"ext-pdo": "*",
"ext-phar": "*",
- "phpbench/phpbench": "^0.13",
- "phpstan/phpstan-phpunit": "^0.11",
- "phpstan/phpstan-shim": "^0.11",
- "phpunit/phpunit": "^7.0"
+ "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.2.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
{
"name": "Marco Pivetta",
- "homepage": "http://ocramius.github.com/"
+ "homepage": "https://ocramius.github.io/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"constructor",
"instantiate"
],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+ },
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "tidelift"
}
],
- "time": "2020-05-29T17:27:14+00:00"
+ "time": "2020-11-10T18:47:58+00:00"
},
{
- "name": "fzaninotto/faker",
- "version": "v1.9.1",
+ "name": "fakerphp/faker",
+ "version": "v1.13.0",
"source": {
"type": "git",
- "url": "https://github.com/fzaninotto/Faker.git",
- "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f"
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/fc10d778e4b84d5bd315dad194661e091d307c6f",
- "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ab3f5364d01f2c2c16113442fb987d26e4004913",
+ "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "fzaninotto/faker": "*"
},
"require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
"ext-intl": "*",
- "phpunit/phpunit": "^4.8.35 || ^5.7",
- "squizlabs/php_codesniffer": "^2.9.2"
+ "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.4.2"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.9-dev"
- }
- },
"autoload": {
"psr-4": {
"Faker\\": "src/Faker/"
"faker",
"fixtures"
],
- "time": "2019-12-12T13:22:17+00:00"
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0"
+ },
+ "time": "2020-12-18T16:50:48+00:00"
},
{
"name": "hamcrest/hamcrest-php",
"keywords": [
"test"
],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
"time": "2020-07-09T08:09:16+00:00"
},
{
"json",
"schema"
],
+ "support": {
+ "issues": "https://github.com/justinrainbow/json-schema/issues",
+ "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10"
+ },
"time": "2020-05-27T16:41:55+00:00"
},
{
"name": "laravel/browser-kit-testing",
- "version": "v5.1.4",
+ "version": "v5.2.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/browser-kit-testing.git",
- "reference": "7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1"
+ "reference": "fa0efb279c009e2a276f934f8aff946caf66edc7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1",
- "reference": "7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1",
+ "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/fa0efb279c009e2a276f934f8aff946caf66edc7",
+ "reference": "fa0efb279c009e2a276f934f8aff946caf66edc7",
"shasum": ""
},
"require": {
"illuminate/http": "~5.7.0|~5.8.0|^6.0",
"illuminate/support": "~5.7.0|~5.8.0|^6.0",
"mockery/mockery": "^1.0",
- "php": ">=7.1.3",
- "phpunit/phpunit": "^7.5|^8.0",
+ "php": "^7.1.3|^8.0",
+ "phpunit/phpunit": "^7.5|^8.0|^9.3",
"symfony/console": "^4.2",
"symfony/css-selector": "^4.2",
"symfony/dom-crawler": "^4.2",
"laravel",
"testing"
],
- "time": "2020-08-25T16:54:44+00:00"
+ "support": {
+ "issues": "https://github.com/laravel/browser-kit-testing/issues",
+ "source": "https://github.com/laravel/browser-kit-testing/tree/v5.2.0"
+ },
+ "time": "2020-10-30T08:49:09+00:00"
},
{
"name": "maximebf/debugbar",
- "version": "v1.16.3",
+ "version": "v1.16.5",
"source": {
"type": "git",
"url": "https://github.com/maximebf/php-debugbar.git",
- "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372"
+ "reference": "6d51ee9e94cff14412783785e79a4e7ef97b9d62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1a1605b8e9bacb34cc0c6278206d699772e1d372",
- "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372",
+ "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/6d51ee9e94cff14412783785e79a4e7ef97b9d62",
+ "reference": "6d51ee9e94cff14412783785e79a4e7ef97b9d62",
"shasum": ""
},
"require": {
- "php": "^7.1",
+ "php": "^7.1|^8",
"psr/log": "^1.0",
"symfony/var-dumper": "^2.6|^3|^4|^5"
},
"require-dev": {
- "phpunit/phpunit": "^5"
+ "phpunit/phpunit": "^7.5.20 || ^9.4.2"
},
"suggest": {
"kriswallsmith/assetic": "The best way to manage assets",
"debug",
"debugbar"
],
- "time": "2020-05-06T07:06:27+00:00"
+ "support": {
+ "issues": "https://github.com/maximebf/php-debugbar/issues",
+ "source": "https://github.com/maximebf/php-debugbar/tree/v1.16.5"
+ },
+ "time": "2020-12-07T11:07:24+00:00"
},
{
"name": "mockery/mockery",
"test double",
"testing"
],
+ "support": {
+ "issues": "https://github.com/mockery/mockery/issues",
+ "source": "https://github.com/mockery/mockery/tree/1.3.3"
+ },
"time": "2020-08-11T18:10:21+00:00"
},
{
"name": "myclabs/deep-copy",
- "version": "1.10.1",
+ "version": "1.10.2",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
- "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
"shasum": ""
},
"require": {
"object",
"object graph"
],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+ },
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
- "time": "2020-06-29T13:22:24+00:00"
+ "time": "2020-11-13T09:40:50+00:00"
},
{
"name": "phar-io/manifest",
- "version": "1.0.3",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
+ "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
- "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
+ "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-phar": "*",
- "phar-io/version": "^2.0",
- "php": "^5.6 || ^7.0"
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "2.0.x-dev"
}
},
"autoload": {
"authors": [
{
"name": "Arne Blankerts",
- "role": "Developer",
+ "role": "Developer"
},
{
"name": "Sebastian Heuer",
- "role": "Developer",
+ "role": "Developer"
},
{
"name": "Sebastian Bergmann",
- "role": "Developer",
+ "role": "Developer"
}
],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
- "time": "2018-07-08T19:23:20+00:00"
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/master"
+ },
+ "time": "2020-06-27T14:33:11+00:00"
},
{
"name": "phar-io/version",
- "version": "2.0.1",
+ "version": "3.0.4",
"source": {
"type": "git",
"url": "https://github.com/phar-io/version.git",
- "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
+ "reference": "e4782611070e50613683d2b9a57730e9a3ba5451"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
- "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451",
+ "reference": "e4782611070e50613683d2b9a57730e9a3ba5451",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": "^7.2 || ^8.0"
},
"type": "library",
"autoload": {
}
],
"description": "Library for handling version information and constraints",
- "time": "2018-07-08T19:19:57+00:00"
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.0.4"
+ },
+ "time": "2020-12-13T23:18:30+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"reflection",
"static analysis"
],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
"time": "2020-06-27T09:03:43+00:00"
},
{
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master"
+ },
"time": "2020-09-03T19:13:55+00:00"
},
{
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
- "time": "2020-09-17T18:55:26+00:00"
- },
- {
- "name": "phploc/phploc",
- "version": "5.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/phploc.git",
- "reference": "5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884",
- "reference": "5b714ccb7cb8ca29ccf9caf6eb1aed0131d3a884",
- "shasum": ""
- },
- "require": {
- "php": "^7.2",
- "sebastian/finder-facade": "^1.1",
- "sebastian/version": "^2.0",
- "symfony/console": "^4.0"
- },
- "bin": [
- "phploc"
- ],
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.0-dev"
- }
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
},
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "role": "lead"
- }
- ],
- "description": "A tool for quickly measuring the size of a PHP project.",
- "homepage": "https://github.com/sebastianbergmann/phploc",
- "time": "2019-03-16T10:41:19+00:00"
+ "time": "2020-09-17T18:55:26+00:00"
},
{
"name": "phpspec/prophecy",
- "version": "1.11.1",
+ "version": "1.12.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160"
+ "reference": "245710e971a030f42e08f4912863805570f23d39"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160",
- "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39",
+ "reference": "245710e971a030f42e08f4912863805570f23d39",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.2",
- "php": "^7.2",
- "phpdocumentor/reflection-docblock": "^5.0",
+ "php": "^7.2 || ~8.0, <8.1",
+ "phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0",
"sebastian/recursion-context": "^3.0 || ^4.0"
},
"require-dev": {
"phpspec/phpspec": "^6.0",
- "phpunit/phpunit": "^8.0"
+ "phpunit/phpunit": "^8.0 || ^9.0"
},
"type": "library",
"extra": {
"spy",
"stub"
],
- "time": "2020-07-08T12:44:21+00:00"
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/1.12.2"
+ },
+ "time": "2020-12-19T10:15:11+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "7.0.10",
+ "version": "7.0.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf"
+ "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf",
- "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c",
+ "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xmlwriter": "*",
- "php": "^7.2",
+ "php": ">=7.2",
"phpunit/php-file-iterator": "^2.0.2",
"phpunit/php-text-template": "^1.2.1",
- "phpunit/php-token-stream": "^3.1.1",
+ "phpunit/php-token-stream": "^3.1.1 || ^4.0",
"sebastian/code-unit-reverse-lookup": "^1.0.1",
"sebastian/environment": "^4.2.2",
"sebastian/version": "^2.0.1",
"testing",
"xunit"
],
- "time": "2019-11-20T13:55:58+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-12-02T13:39:03+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "2.0.2",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "050bedf145a257b1ff02746c31894800e5122946"
+ "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
- "reference": "050bedf145a257b1ff02746c31894800e5122946",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357",
+ "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357",
"shasum": ""
},
"require": {
- "php": "^7.1"
+ "php": ">=7.1"
},
"require-dev": {
- "phpunit/phpunit": "^7.1"
+ "phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
"filesystem",
"iterator"
],
- "time": "2018-09-13T20:33:42+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:25:21+00:00"
},
{
"name": "phpunit/php-text-template",
"keywords": [
"template"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
+ },
"time": "2015-06-21T13:50:34+00:00"
},
{
"name": "phpunit/php-timer",
- "version": "2.1.2",
+ "version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
+ "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
- "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
+ "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
"shasum": ""
},
"require": {
- "php": "^7.1"
+ "php": ">=7.1"
},
"require-dev": {
- "phpunit/phpunit": "^7.0"
+ "phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
"keywords": [
"timer"
],
- "time": "2019-06-07T04:22:29+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:20:02+00:00"
},
{
"name": "phpunit/php-token-stream",
- "version": "3.1.1",
+ "version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff"
+ "reference": "472b687829041c24b25f475e14c2f38a09edf1c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff",
- "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2",
+ "reference": "472b687829041c24b25f475e14c2f38a09edf1c2",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
- "php": "^7.1"
+ "php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.0"
"keywords": [
"tokenizer"
],
- "time": "2019-09-17T06:23:10+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
+ "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "abandoned": true,
+ "time": "2020-11-30T08:38:46+00:00"
},
{
"name": "phpunit/phpunit",
- "version": "8.5.8",
+ "version": "8.5.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997"
+ "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34c18baa6a44f1d1fbf0338907139e9dce95b997",
- "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c25f79895d27b6ecd5abfa63de1606b786a461a3",
+ "reference": "c25f79895d27b6ecd5abfa63de1606b786a461a3",
"shasum": ""
},
"require": {
- "doctrine/instantiator": "^1.2.0",
+ "doctrine/instantiator": "^1.3.1",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.9.1",
- "phar-io/manifest": "^1.0.3",
- "phar-io/version": "^2.0.1",
- "php": "^7.2",
- "phpspec/prophecy": "^1.8.1",
- "phpunit/php-code-coverage": "^7.0.7",
+ "myclabs/deep-copy": "^1.10.0",
+ "phar-io/manifest": "^2.0.1",
+ "phar-io/version": "^3.0.2",
+ "php": ">=7.2",
+ "phpspec/prophecy": "^1.10.3",
+ "phpunit/php-code-coverage": "^7.0.12",
"phpunit/php-file-iterator": "^2.0.2",
"phpunit/php-text-template": "^1.2.1",
"phpunit/php-timer": "^2.1.2",
"sebastian/comparator": "^3.0.2",
"sebastian/diff": "^3.0.2",
- "sebastian/environment": "^4.2.2",
- "sebastian/exporter": "^3.1.1",
+ "sebastian/environment": "^4.2.3",
+ "sebastian/exporter": "^3.1.2",
"sebastian/global-state": "^3.0.0",
"sebastian/object-enumerator": "^3.0.3",
"sebastian/resource-operations": "^2.0.1",
"testing",
"xunit"
],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.14"
+ },
"funding": [
{
"url": "https://phpunit.de/donate.html",
"type": "github"
}
],
- "time": "2020-06-22T07:06:58+00:00"
+ "time": "2021-01-17T07:37:30+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4",
+ "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/promise/issues",
+ "source": "https://github.com/reactphp/promise/tree/v2.8.0"
+ },
+ "time": "2020-05-12T15:16:56+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+ "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+ "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": ">=5.6"
},
"require-dev": {
- "phpunit/phpunit": "^5.7 || ^6.0"
+ "phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
- "time": "2017-03-04T06:30:41+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:15:22+00:00"
},
{
"name": "sebastian/comparator",
- "version": "3.0.2",
+ "version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
+ "reference": "1071dfcef776a57013124ff35e1fc41ccd294758"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
- "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758",
+ "reference": "1071dfcef776a57013124ff35e1fc41ccd294758",
"shasum": ""
},
"require": {
- "php": "^7.1",
+ "php": ">=7.1",
"sebastian/diff": "^3.0",
"sebastian/exporter": "^3.1"
},
"require-dev": {
- "phpunit/phpunit": "^7.1"
+ "phpunit/phpunit": "^8.5"
},
"type": "library",
"extra": {
"BSD-3-Clause"
],
"authors": [
+ {
+ "name": "Sebastian Bergmann",
+ },
{
"name": "Jeff Welch",
{
"name": "Bernhard Schussek",
- },
- {
- "name": "Sebastian Bergmann",
}
],
"description": "Provides the functionality to compare PHP values for equality",
"compare",
"equality"
],
- "time": "2018-07-12T15:12:46+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T08:04:30+00:00"
},
{
"name": "sebastian/diff",
- "version": "3.0.2",
+ "version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
+ "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
- "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
+ "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
"shasum": ""
},
"require": {
- "php": "^7.1"
+ "php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.0",
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Kore Nordmann",
- },
{
"name": "Sebastian Bergmann",
+ },
+ {
+ "name": "Kore Nordmann",
}
],
"description": "Diff implementation",
"unidiff",
"unified diff"
],
- "time": "2019-02-04T06:01:07+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:59:04+00:00"
},
{
"name": "sebastian/environment",
- "version": "4.2.3",
+ "version": "4.2.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368"
+ "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
- "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
+ "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
"shasum": ""
},
"require": {
- "php": "^7.1"
+ "php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5"
"environment",
"hhvm"
],
- "time": "2019-11-20T08:46:58+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:53:42+00:00"
},
{
"name": "sebastian/exporter",
- "version": "3.1.2",
+ "version": "3.1.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
+ "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
- "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e",
+ "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e",
"shasum": ""
},
"require": {
- "php": "^7.0",
+ "php": ">=7.0",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
"export",
"exporter"
],
- "time": "2019-09-14T09:02:43+00:00"
- },
- {
- "name": "sebastian/finder-facade",
- "version": "1.2.3",
- "source": {
- "type": "git",
- "url": "https://github.com/sebastianbergmann/finder-facade.git",
- "reference": "167c45d131f7fc3d159f56f191a0a22228765e16"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/167c45d131f7fc3d159f56f191a0a22228765e16",
- "reference": "167c45d131f7fc3d159f56f191a0a22228765e16",
- "shasum": ""
- },
- "require": {
- "php": "^7.1",
- "symfony/finder": "^2.3|^3.0|^4.0|^5.0",
- "theseer/fdomdocument": "^1.6"
- },
- "type": "library",
- "extra": {
- "branch-alias": []
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
},
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
+ "funding": [
{
- "name": "Sebastian Bergmann",
- "role": "lead"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.",
- "homepage": "https://github.com/sebastianbergmann/finder-facade",
- "time": "2020-01-16T08:08:45+00:00"
+ "time": "2020-11-30T07:47:53+00:00"
},
{
"name": "sebastian/global-state",
- "version": "3.0.0",
+ "version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4"
+ "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
- "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b",
+ "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b",
"shasum": ""
},
"require": {
- "php": "^7.2",
+ "php": ">=7.2",
"sebastian/object-reflector": "^1.1.1",
"sebastian/recursion-context": "^3.0"
},
"keywords": [
"global state"
],
- "time": "2019-02-01T05:30:01+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:43:24+00:00"
},
{
"name": "sebastian/object-enumerator",
- "version": "3.0.3",
+ "version": "3.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+ "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
- "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
+ "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
"shasum": ""
},
"require": {
- "php": "^7.0",
+ "php": ">=7.0",
"sebastian/object-reflector": "^1.1.1",
"sebastian/recursion-context": "^3.0"
},
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
- "time": "2017-08-03T12:35:26+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:40:27+00:00"
},
{
"name": "sebastian/object-reflector",
- "version": "1.1.1",
+ "version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "773f97c67f28de00d397be301821b06708fca0be"
+ "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
- "reference": "773f97c67f28de00d397be301821b06708fca0be",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
+ "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
],
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
- "time": "2017-03-29T09:07:27+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:37:18+00:00"
},
{
"name": "sebastian/recursion-context",
- "version": "3.0.0",
+ "version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+ "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
- "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
+ "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Jeff Welch",
- },
{
"name": "Sebastian Bergmann",
},
+ {
+ "name": "Jeff Welch",
+ },
{
"name": "Adam Harvey",
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2017-03-03T06:23:57+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:34:24+00:00"
},
{
"name": "sebastian/resource-operations",
- "version": "2.0.1",
+ "version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
+ "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
- "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
+ "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
"shasum": ""
},
"require": {
- "php": "^7.1"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
- "time": "2018-10-04T04:07:39+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:30:19+00:00"
},
{
"name": "sebastian/type",
- "version": "1.1.3",
+ "version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
- "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3"
+ "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3",
- "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4",
+ "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4",
"shasum": ""
},
"require": {
- "php": "^7.2"
+ "php": ">=7.2"
},
"require-dev": {
"phpunit/phpunit": "^8.2"
],
"description": "Collection of value objects that represent the types of the PHP type system",
"homepage": "https://github.com/sebastianbergmann/type",
- "time": "2019-07-02T08:10:15+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/1.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-30T07:25:11+00:00"
},
{
"name": "sebastian/version",
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/master"
+ },
"time": "2016-10-03T07:35:21+00:00"
},
{
"name": "seld/jsonlint",
- "version": "1.8.2",
+ "version": "1.8.3",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/jsonlint.git",
- "reference": "590cfec960b77fd55e39b7d9246659e95dd6d337"
+ "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/590cfec960b77fd55e39b7d9246659e95dd6d337",
- "reference": "590cfec960b77fd55e39b7d9246659e95dd6d337",
+ "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9ad6ce79c342fbd44df10ea95511a1b24dee5b57",
+ "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57",
"shasum": ""
},
"require": {
"parser",
"validator"
],
+ "support": {
+ "issues": "https://github.com/Seldaek/jsonlint/issues",
+ "source": "https://github.com/Seldaek/jsonlint/tree/1.8.3"
+ },
"funding": [
{
"url": "https://github.com/Seldaek",
"type": "tidelift"
}
],
- "time": "2020-08-25T06:56:57+00:00"
+ "time": "2020-11-11T09:19:24+00:00"
},
{
"name": "seld/phar-utils",
"keywords": [
"phar"
],
+ "support": {
+ "issues": "https://github.com/Seldaek/phar-utils/issues",
+ "source": "https://github.com/Seldaek/phar-utils/tree/master"
+ },
"time": "2020-07-07T18:42:57+00:00"
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.5.6",
+ "version": "3.5.8",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "e97627871a7eab2f70e59166072a6b767d5834e0"
+ "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e97627871a7eab2f70e59166072a6b767d5834e0",
- "reference": "e97627871a7eab2f70e59166072a6b767d5834e0",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
+ "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
"shasum": ""
},
"require": {
"phpcs",
"standards"
],
- "time": "2020-08-10T04:50:15+00:00"
+ "support": {
+ "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+ "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+ },
+ "time": "2020-10-23T02:01:07+00:00"
},
{
"name": "symfony/dom-crawler",
- "version": "v4.4.13",
+ "version": "v4.4.19",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "6dd1e7adef4b7efeeb9691fd619279027d4dcf85"
+ "reference": "21032c566558255e551d23f4a516434c9e3a9a78"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/6dd1e7adef4b7efeeb9691fd619279027d4dcf85",
- "reference": "6dd1e7adef4b7efeeb9691fd619279027d4dcf85",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/21032c566558255e551d23f4a516434c9e3a9a78",
+ "reference": "21032c566558255e551d23f4a516434c9e3a9a78",
"shasum": ""
},
"require": {
"symfony/css-selector": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\DomCrawler\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony DomCrawler Component",
+ "description": "Eases DOM navigation for HTML and XML documents",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dom-crawler/tree/v4.4.19"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-12T06:20:35+00:00"
+ "time": "2021-01-27T09:09:26+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v4.4.13",
+ "version": "v5.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "27575bcbc68db1f6d06218891296572c9b845704"
+ "reference": "262d033b57c73e8b59cd6e68a45c528318b15038"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/27575bcbc68db1f6d06218891296572c9b845704",
- "reference": "27575bcbc68db1f6d06218891296572c9b845704",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038",
+ "reference": "262d033b57c73e8b59cd6e68a45c528318b15038",
"shasum": ""
},
"require": {
- "php": ">=7.1.3",
+ "php": ">=7.2.5",
"symfony/polyfill-ctype": "~1.8"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.4-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony Filesystem Component",
+ "description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v5.2.3"
+ },
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "tidelift"
}
],
- "time": "2020-08-21T17:19:37+00:00"
- },
- {
- "name": "theseer/fdomdocument",
- "version": "1.6.6",
- "source": {
- "type": "git",
- "url": "https://github.com/theseer/fDOMDocument.git",
- "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/6e8203e40a32a9c770bcb62fe37e68b948da6dca",
- "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "lib-libxml": "*",
- "php": ">=5.3.3"
- },
- "type": "library",
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Arne Blankerts",
- "role": "lead",
- }
- ],
- "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.",
- "homepage": "https://github.com/theseer/fDOMDocument",
- "time": "2017-06-30T11:53:12+00:00"
+ "time": "2021-01-27T10:01:46+00:00"
},
{
"name": "theseer/tokenizer",
}
],
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/master"
+ },
"funding": [
{
"url": "https://github.com/theseer",
"version": "1.9.1",
"source": {
"type": "git",
- "url": "https://github.com/webmozart/assert.git",
+ "url": "https://github.com/webmozarts/assert.git",
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
"shasum": ""
},
"check",
"validate"
],
- "time": "2020-07-08T17:02:28+00:00"
- },
- {
- "name": "wnx/laravel-stats",
- "version": "v2.0.2",
- "source": {
- "type": "git",
- "url": "https://github.com/stefanzweifel/laravel-stats.git",
- "reference": "e86ebfdd149383b18a41fe3efa1601d82d447140"
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.9.1"
},
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/stefanzweifel/laravel-stats/zipball/e86ebfdd149383b18a41fe3efa1601d82d447140",
- "reference": "e86ebfdd149383b18a41fe3efa1601d82d447140",
- "shasum": ""
- },
- "require": {
- "illuminate/console": "~5.8.0|^6.0|^7.0",
- "illuminate/support": "~5.8.0|^6.0|^7.0",
- "php": ">=7.2.0",
- "phploc/phploc": "~5.0|~6.0",
- "symfony/finder": "~4.0"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "^2.15",
- "laravel/browser-kit-testing": "~5.0",
- "laravel/dusk": "~5.0",
- "mockery/mockery": "^1.1",
- "orchestra/testbench": "^3.8|^4.0|^5.0",
- "phpunit/phpunit": "8.*|9.*"
- },
- "type": "library",
- "extra": {
- "laravel": {
- "providers": [
- "Wnx\\LaravelStats\\StatsServiceProvider"
- ]
- }
- },
- "autoload": {
- "psr-4": {
- "Wnx\\LaravelStats\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Stefan Zweifel",
- "homepage": "https://stefanzweifel.io",
- "role": "Developer"
- }
- ],
- "description": "Get insights about your Laravel Project",
- "homepage": "https://github.com/stefanzweifel/laravel-stats",
- "keywords": [
- "laravel",
- "statistics",
- "stats",
- "wnx"
- ],
- "time": "2020-02-22T19:09:14+00:00"
+ "time": "2020-07-08T17:02:28+00:00"
}
],
"aliases": [],
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^7.2",
+ "php": "^7.2.5",
"ext-curl": "*",
"ext-dom": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
- "ext-tidy": "*",
"ext-xml": "*"
},
"platform-dev": [],
"platform-overrides": {
- "php": "7.2.0"
+ "php": "7.2.5"
},
- "plugin-api-version": "1.1.0"
+ "plugin-api-version": "2.0.0"
}
];
});
-$factory->define(\BookStack\Entities\Bookshelf::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Bookshelf::class, function ($faker) {
return [
'name' => $faker->sentence,
'slug' => Str::random(10),
];
});
-$factory->define(\BookStack\Entities\Book::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Book::class, function ($faker) {
return [
'name' => $faker->sentence,
'slug' => Str::random(10),
];
});
-$factory->define(\BookStack\Entities\Chapter::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Chapter::class, function ($faker) {
return [
'name' => $faker->sentence,
'slug' => Str::random(10),
];
});
-$factory->define(\BookStack\Entities\Page::class, function ($faker) {
+$factory->define(\BookStack\Entities\Models\Page::class, function ($faker) {
$html = '<p>' . implode('</p>', $faker->paragraphs(5)) . '</p>';
return [
'name' => $faker->sentence,
Schema::dropIfExists('bookshelves');
// Drop related polymorphic items
- DB::table('activities')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
- DB::table('views')->where('viewable_type', '=', 'BookStack\Entities\Bookshelf')->delete();
- DB::table('entity_permissions')->where('restrictable_type', '=', 'BookStack\Entities\Bookshelf')->delete();
- DB::table('tags')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
- DB::table('search_terms')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
- DB::table('comments')->where('entity_type', '=', 'BookStack\Entities\Bookshelf')->delete();
+ DB::table('activities')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+ DB::table('views')->where('viewable_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+ DB::table('entity_permissions')->where('restrictable_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+ DB::table('tags')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+ DB::table('search_terms')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
+ DB::table('comments')->where('entity_type', '=', 'BookStack\Entities\Models\Bookshelf')->delete();
}
}
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class SimplifyActivitiesTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('activities', function (Blueprint $table) {
+ $table->renameColumn('key', 'type');
+ $table->renameColumn('extra', 'detail');
+ $table->dropColumn('book_id');
+ $table->integer('entity_id')->nullable()->change();
+ $table->string('entity_type', 191)->nullable()->change();
+ });
+
+ DB::table('activities')
+ ->where('entity_id', '=', 0)
+ ->update([
+ 'entity_id' => null,
+ 'entity_type' => null,
+ ]);
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ DB::table('activities')
+ ->whereNull('entity_id')
+ ->update([
+ 'entity_id' => 0,
+ 'entity_type' => '',
+ ]);
+
+ Schema::table('activities', function (Blueprint $table) {
+ $table->renameColumn('type', 'key');
+ $table->renameColumn('detail', 'extra');
+ $table->integer('book_id');
+
+ $table->integer('entity_id')->change();
+ $table->string('entity_type', 191)->change();
+
+ $table->index('book_id');
+ });
+ }
+}
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class AddOwnedByFieldToEntities extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+ foreach ($tables as $table) {
+ Schema::table($table, function (Blueprint $table) {
+ $table->integer('owned_by')->unsigned()->index();
+ });
+
+ DB::table($table)->update(['owned_by' => DB::raw('`created_by`')]);
+ }
+
+ Schema::table('joint_permissions', function (Blueprint $table) {
+ $table->renameColumn('created_by', 'owned_by');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ $tables = ['pages', 'books', 'chapters', 'bookshelves'];
+ foreach ($tables as $table) {
+ Schema::table($table, function (Blueprint $table) {
+ $table->dropColumn('owned_by');
+ });
+ }
+
+ Schema::table('joint_permissions', function (Blueprint $table) {
+ $table->renameColumn('owned_by', 'created_by');
+ });
+ }
+}
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddSettingsTypeColumn extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('settings', function (Blueprint $table) {
+ $table->string('type', 50)->default('string');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('settings', function (Blueprint $table) {
+ $table->dropColumn('type');
+ });
+ }
+}
use BookStack\Auth\Permissions\RolePermission;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\SearchIndex;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
$role = Role::getRole('viewer');
$viewerUser->attachRole($role);
- $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id];
+ $byData = ['created_by' => $editorUser->id, 'updated_by' => $editorUser->id, 'owned_by' => $editorUser->id];
- factory(\BookStack\Entities\Book::class, 5)->create($byData)
+ factory(\BookStack\Entities\Models\Book::class, 5)->create($byData)
->each(function($book) use ($editorUser, $byData) {
$chapters = factory(Chapter::class, 3)->create($byData)
->each(function($chapter) use ($editorUser, $book, $byData){
$book->pages()->saveMany($pages);
});
- $largeBook = factory(\BookStack\Entities\Book::class)->create(array_merge($byData, ['name' => 'Large book' . Str::random(10)]));
+ $largeBook = factory(\BookStack\Entities\Models\Book::class)->create(array_merge($byData, ['name' => 'Large book' . Str::random(10)]));
$pages = factory(Page::class, 200)->make($byData);
$chapters = factory(Chapter::class, 50)->make($byData);
$largeBook->pages()->saveMany($pages);
$token->save();
app(PermissionService::class)->buildJointPermissions();
- app(SearchService::class)->indexAllEntities();
+ app(SearchIndex::class)->indexAllEntities();
}
}
use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Entities\SearchService;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\SearchIndex;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
$editorRole = Role::getRole('editor');
$editorUser->attachRole($editorRole);
- $largeBook = factory(\BookStack\Entities\Book::class)->create(['name' => 'Large book' . Str::random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
+ $largeBook = factory(\BookStack\Entities\Models\Book::class)->create(['name' => 'Large book' . Str::random(10), 'created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
$pages = factory(Page::class, 200)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
$chapters = factory(Chapter::class, 50)->make(['created_by' => $editorUser->id, 'updated_by' => $editorUser->id]);
$largeBook->pages()->saveMany($pages);
$largeBook->chapters()->saveMany($chapters);
app(PermissionService::class)->buildJointPermissions();
- app(SearchService::class)->indexAllEntities();
+ app(SearchIndex::class)->indexAllEntities();
}
}
--- /dev/null
+{
+ "book_id": 1,
+ "name": "My API Page",
+ "html": "<p>my new API page</p>",
+ "tags": [
+ {"name": "Category", "value": "Not Bad Content"},
+ {"name": "Rating", "value": "Average"}
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "chapter_id": 1,
+ "name": "My updated API Page",
+ "html": "<p>my new API page - Updated</p>",
+ "tags": [
+ {"name": "Category", "value": "API Examples"},
+ {"name": "Rating", "value": "Alright"}
+ ]
+}
\ No newline at end of file
"description": "This is a book created via the API",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"slug": "my-new-book",
"updated_at": "2020-01-12 14:05:11",
"created_at": "2020-01-12 14:05:11",
"updated_at": "2019-12-11 20:57:31",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"image_id": 3
},
{
"updated_at": "2019-12-11 20:57:23",
"created_by": 4,
"updated_by": 3,
+ "owned_by": 3,
"image_id": 34
}
],
"id": 1,
"name": "Admin"
},
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
"tags": [
{
"id": 13,
- "entity_id": 16,
- "entity_type": "BookStack\\Book",
"name": "Category",
"value": "Guide",
- "order": 0,
- "created_at": "2020-01-12 14:11:51",
- "updated_at": "2020-01-12 14:11:51"
+ "order": 0
}
],
"cover": {
"updated_at": "2020-01-12 14:16:10",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"image_id": 452
}
\ No newline at end of file
"description": "This is a great new chapter that I've created via the API",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"slug": "my-fantastic-new-chapter",
"updated_at": "2020-05-22 22:59:55",
"created_at": "2020-05-22 22:59:55",
"created_at": "2019-05-05 21:49:56",
"updated_at": "2019-09-28 11:24:23",
"created_by": 1,
- "updated_by": 1
+ "updated_by": 1,
+ "owned_by": 1
},
{
"id": 2,
"created_at": "2019-05-05 21:58:07",
"updated_at": "2019-10-17 15:05:34",
"created_by": 3,
- "updated_by": 3
+ "updated_by": 3,
+ "owned_by": 3
}
],
"total": 40
"id": 1,
"name": "Admin"
},
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
"tags": [
{
"name": "Category",
"value": "Guide",
- "order": 0,
- "created_at": "2020-05-22 22:51:51",
- "updated_at": "2020-05-22 22:51:51"
+ "order": 0
}
],
"pages": [
"updated_at": "2019-08-26 14:32:59",
"created_by": 1,
"updated_by": 1,
- "draft": 0,
+ "draft": false,
"revision_count": 2,
- "template": 0
+ "template": false
},
{
"id": 7,
"updated_at": "2019-06-06 12:03:04",
"created_by": 3,
"updated_by": 3,
- "draft": 0,
+ "draft": false,
"revision_count": 1,
- "template": 0
+ "template": false
}
]
}
\ No newline at end of file
"updated_at": "2020-05-22 23:07:20",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"book": {
"id": 1,
"name": "BookStack User Guide",
--- /dev/null
+{
+ "id": 358,
+ "book_id": 1,
+ "chapter_id": 0,
+ "name": "My API Page",
+ "slug": "my-api-page",
+ "html": "<p id=\"bkmrk-my-new-api-page\">my new API page</p>",
+ "priority": 14,
+ "created_at": "2020-11-28 15:01:39",
+ "updated_at": "2020-11-28 15:01:39",
+ "created_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "updated_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "draft": false,
+ "markdown": "",
+ "revision_count": 1,
+ "template": false,
+ "tags": [
+ {
+ "name": "Category",
+ "value": "Not Bad Content",
+ "order": 0
+ },
+ {
+ "name": "Rating",
+ "value": "Average",
+ "order": 1
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "data": [
+ {
+ "id": 1,
+ "book_id": 1,
+ "chapter_id": 1,
+ "name": "How to create page content",
+ "slug": "how-to-create-page-content",
+ "priority": 0,
+ "draft": false,
+ "template": false,
+ "created_at": "2019-05-05 21:49:58",
+ "updated_at": "2020-07-04 15:50:58",
+ "created_by": 1,
+ "updated_by": 1,
+ "owned_by": 1
+ },
+ {
+ "id": 2,
+ "book_id": 1,
+ "chapter_id": 1,
+ "name": "How to use images",
+ "slug": "how-to-use-images",
+ "priority": 2,
+ "draft": false,
+ "template": false,
+ "created_at": "2019-05-05 21:53:30",
+ "updated_at": "2019-06-06 12:03:04",
+ "created_by": 1,
+ "updated_by": 1,
+ "owned_by": 1
+ },
+ {
+ "id": 3,
+ "book_id": 1,
+ "chapter_id": 1,
+ "name": "Drawings via draw.io",
+ "slug": "drawings-via-drawio",
+ "priority": 3,
+ "draft": false,
+ "template": false,
+ "created_at": "2019-05-05 21:53:49",
+ "updated_at": "2019-12-18 21:56:52",
+ "created_by": 1,
+ "updated_by": 1,
+ "owned_by": 1
+ }
+ ],
+ "total": 322
+}
\ No newline at end of file
--- /dev/null
+{
+ "id": 306,
+ "book_id": 1,
+ "chapter_id": 0,
+ "name": "A page written in markdown",
+ "slug": "a-page-written-in-markdown",
+ "html": "<h1 id=\"bkmrk-how-this-is-built\">How this is built</h1>\r\n<p id=\"bkmrk-this-page-is-written\">This page is written in markdown. BookStack stores the page data in HTML.</p>\r\n<p id=\"bkmrk-here%27s-a-cute-pictur\">Here's a cute picture of my cat:</p>\r\n<p id=\"bkmrk-\"><a href=\"http://example.com/uploads/images/gallery/2020-04/yXSrubes.jpg\"><img src=\"http://example.com/uploads/images/gallery/2020-04/scaled-1680-/yXSrubes.jpg\" alt=\"yXSrubes.jpg\"></a></p>",
+ "priority": 13,
+ "created_at": "2020-02-02 21:40:38",
+ "updated_at": "2020-11-28 14:43:20",
+ "created_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "updated_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "draft": false,
+ "markdown": "# How this is built\r\n\r\nThis page is written in markdown. BookStack stores the page data in HTML.\r\n\r\nHere's a cute picture of my cat:\r\n\r\n[](http://example.com/uploads/images/gallery/2020-04/yXSrubes.jpg)",
+ "revision_count": 5,
+ "template": false,
+ "tags": [
+ {
+ "name": "Category",
+ "value": "Top Content",
+ "order": 0
+ },
+ {
+ "name": "Animal",
+ "value": "Cat",
+ "order": 1
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "id": 361,
+ "book_id": 1,
+ "chapter_id": 1,
+ "name": "My updated API Page",
+ "slug": "my-updated-api-page",
+ "html": "<p id=\"bkmrk-my-new-api-page---up\">my new API page - Updated</p>",
+ "priority": 16,
+ "created_at": "2020-11-28 15:10:54",
+ "updated_at": "2020-11-28 15:13:03",
+ "created_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "updated_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
+ "draft": false,
+ "markdown": "",
+ "revision_count": 5,
+ "template": false,
+ "tags": [
+ {
+ "name": "Category",
+ "value": "API Examples",
+ "order": 0
+ },
+ {
+ "name": "Rating",
+ "value": "Alright",
+ "order": 0
+ }
+ ]
+}
\ No newline at end of file
"description": "This is my shelf with some books",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"slug": "my-shelf",
"updated_at": "2020-04-10 13:24:09",
"created_at": "2020-04-10 13:24:09",
"updated_at": "2020-04-10 13:00:45",
"created_by": 4,
"updated_by": 1,
+ "owned_by": 1,
"image_id": 31
},
{
"updated_at": "2020-04-10 13:00:58",
"created_by": 4,
"updated_by": 1,
+ "owned_by": 1,
"image_id": 28
},
{
"updated_at": "2020-04-10 13:00:53",
"created_by": 4,
"updated_by": 1,
+ "owned_by": 4,
"image_id": 30
}
],
"id": 1,
"name": "Admin"
},
+ "owned_by": {
+ "id": 1,
+ "name": "Admin"
+ },
"created_at": "2020-04-10 13:24:09",
"updated_at": "2020-04-10 13:31:04",
"tags": [
{
"id": 16,
- "entity_id": 14,
- "entity_type": "BookStack\\Bookshelf",
"name": "Category",
"value": "Guide",
- "order": 0,
- "created_at": "2020-04-10 13:31:04",
- "updated_at": "2020-04-10 13:31:04"
+ "order": 0
}
],
"cover": {
"description": "This is my update shelf with some books",
"created_by": 1,
"updated_by": 1,
+ "owned_by": 1,
"image_id": 501,
"created_at": "2020-04-10 13:24:09",
"updated_at": "2020-04-10 13:48:22"
node:
image: node:alpine
working_dir: /app
+ user: node
volumes:
- ./:/app
entrypoint: /app/dev/docker/entrypoint.node.sh
<server name="APP_LANG" value="en"/>
<server name="APP_THEME" value="none"/>
<server name="APP_AUTO_LANG_PUBLIC" value="true"/>
+ <server name="APP_URL" value="http://bookstack.dev"/>
+ <server name="ALLOWED_IFRAME_HOSTS" value=""/>
<server name="CACHE_DRIVER" value="array"/>
<server name="SESSION_DRIVER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="DISABLE_EXTERNAL_SERVICES" value="true"/>
<server name="AVATAR_URL" value=""/>
<server name="LDAP_VERSION" value="3"/>
+ <server name="SESSION_SECURE_COOKIE" value="null"/>
<server name="STORAGE_TYPE" value="local"/>
<server name="STORAGE_ATTACHMENT_TYPE" value="local"/>
<server name="STORAGE_IMAGE_TYPE" value="local"/>
<server name="GOOGLE_AUTO_REGISTER" value=""/>
<server name="GOOGLE_AUTO_CONFIRM_EMAIL" value=""/>
<server name="GOOGLE_SELECT_ACCOUNT" value=""/>
- <server name="APP_URL" value="http://bookstack.dev"/>
<server name="DEBUGBAR_ENABLED" value="false"/>
<server name="SAML2_ENABLED" value="false"/>
<server name="API_REQUESTS_PER_MIN" value="180"/>
<server name="LOG_FAILED_LOGIN_MESSAGE" value=""/>
<server name="LOG_FAILED_LOGIN_CHANNEL" value="testing"/>
+ <server name="WKHTMLTOPDF" value="false"/>
</php>
</phpunit>
[](https://github.com/BookStackApp/BookStack/blob/master/LICENSE)
[](https://crowdin.com/project/bookstack)
[](https://github.com/BookStackApp/BookStack/actions)
-[](https://discord.gg/ztkBqR2)
+[](https://discord.gg/ztkBqR2)
+[](https://gh-stats.bookstackapp.com/)
A platform for storing and organising information and documentation. Details for BookStack can be found on the official website at https://www.bookstackapp.com/.
Each BookStack release will have a [milestone](https://github.com/BookStackApp/BookStack/milestones) created with issues & pull requests assigned to it to define what will be in that release. Milestones are built up then worked through until complete at which point, after some testing and documentation updates, the release will be deployed.
-For feature releases, and some patch releases, the release will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](http://eepurl.com/cmmq5j).
+For feature releases, and some patch releases, the release will be accompanied by a post on the [BookStack blog](https://www.bookstackapp.com/blog/) which will provide additional detail on features, changes & updates otherwise the [GitHub release page](https://github.com/BookStackApp/BookStack/releases) will show a list of changes. You can sign up to be alerted to new BookStack blogs posts (once per week maximum) [at this link](https://updates.bookstackapp.com/signup/bookstack-news-and-updates).
## 🛠️ Development & Testing
All development on BookStack is currently done on the master branch. When it's time for a release the master branch is merged into release with built & minified CSS & JS then tagged at its version. Here are the current development requirements:
-* [Node.js](https://nodejs.org/en/) v10.0+
+* [Node.js](https://nodejs.org/en/) v12.0+
This project uses SASS for CSS development and this is built, along with the JavaScript, using a range of npm scripts. The below npm commands can be used to install the dependencies & run the build tasks:
Pull requests are welcome. Unless a small tweak or language update, It may be best to open the pull request early or create an issue for your intended change to discuss how it will fit in to the project and plan out the merge. Just because a feature request exists, or is tagged, does not mean that feature would be accepted into the core project.
-Pull requests should be created from the `master` branch since they will be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases. If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/assets`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
+Pull requests should be created from the `master` branch since they will be merged back into `master` once done. Please do not build from or request a merge into the `release` branch as this is only for publishing releases. If you are looking to alter CSS or JavaScript content please edit the source files found in `resources/`. Any CSS or JS files within `public` are built from these source files and therefore should not be edited directly.
The project's code of conduct [can be found here](https://github.com/BookStackApp/BookStack/blob/master/.github/CODE_OF_CONDUCT.md).
Security information for administering a BookStack instance can be found on the [documentation site here](https://www.bookstackapp.com/docs/admin/security/).
-If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](http://eepurl.com/glIh8z).
+If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates).
If you would like to report a security concern in a more confidential manner than via a GitHub issue, You can directly email the lead maintainer [ssddanbrown](https://github.com/ssddanbrown). You will need to login to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown). Alternatively you can send a DM via twitter to [@ssddanbrown](https://twitter.com/ssddanbrown).
* [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper)
* [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html)
* [diagrams.net](https://github.com/jgraph/drawio)
-* [Laravel Stats](https://github.com/stefanzweifel/laravel-stats)
* [OneLogin's SAML PHP Toolkit](https://github.com/onelogin/php-saml)
+* [League/CommonMark](https://commonmark.thephpleague.com/)
+* [League/Flysystem](https://flysystem.thephpleague.com)
+++ /dev/null
-
-
-class BreadcrumbListing {
-
- constructor(elem) {
- this.elem = elem;
- this.searchInput = elem.querySelector('input');
- this.loadingElem = elem.querySelector('.loading-container');
- this.entityListElem = elem.querySelector('.breadcrumb-listing-entity-list');
-
- // this.loadingElem.style.display = 'none';
- const entityDescriptor = elem.getAttribute('breadcrumb-listing').split(':');
- this.entityType = entityDescriptor[0];
- this.entityId = Number(entityDescriptor[1]);
-
- this.elem.addEventListener('show', this.onShow.bind(this));
- this.searchInput.addEventListener('input', this.onSearch.bind(this));
- }
-
- onShow() {
- this.loadEntityView();
- }
-
- onSearch() {
- const input = this.searchInput.value.toLowerCase().trim();
- const listItems = this.entityListElem.querySelectorAll('.entity-list-item');
- for (let listItem of listItems) {
- const match = !input || listItem.textContent.toLowerCase().includes(input);
- listItem.style.display = match ? 'flex' : 'none';
- listItem.classList.toggle('hidden', !match);
- }
- }
-
- loadEntityView() {
- this.toggleLoading(true);
-
- const params = {
- 'entity_id': this.entityId,
- 'entity_type': this.entityType,
- };
-
- window.$http.get('/search/entity/siblings', params).then(resp => {
- this.entityListElem.innerHTML = resp.data;
- }).catch(err => {
- console.error(err);
- }).then(() => {
- this.toggleLoading(false);
- this.onSearch();
- });
- }
-
- toggleLoading(show = false) {
- this.loadingElem.style.display = show ? 'block' : 'none';
- }
-
-}
-
-export default BreadcrumbListing;
\ No newline at end of file
--- /dev/null
+import {debounce} from "../services/util";
+
+class DropdownSearch {
+
+ setup() {
+ this.elem = this.$el;
+ this.searchInput = this.$refs.searchInput;
+ this.loadingElem = this.$refs.loading;
+ this.listContainerElem = this.$refs.listContainer;
+
+ this.localSearchSelector = this.$opts.localSearchSelector;
+ this.url = this.$opts.url;
+
+ this.elem.addEventListener('show', this.onShow.bind(this));
+ this.searchInput.addEventListener('input', this.onSearch.bind(this));
+
+ this.runAjaxSearch = debounce(this.runAjaxSearch, 300, false);
+ }
+
+ onShow() {
+ this.loadList();
+ }
+
+ onSearch() {
+ const input = this.searchInput.value.toLowerCase().trim();
+ if (this.localSearchSelector) {
+ this.runLocalSearch(input);
+ } else {
+ this.toggleLoading(true);
+ this.runAjaxSearch(input);
+ }
+ }
+
+ runAjaxSearch(searchTerm) {
+ this.loadList(searchTerm);
+ }
+
+ runLocalSearch(searchTerm) {
+ const listItems = this.listContainerElem.querySelectorAll(this.localSearchSelector);
+ for (let listItem of listItems) {
+ const match = !searchTerm || listItem.textContent.toLowerCase().includes(searchTerm);
+ listItem.style.display = match ? 'flex' : 'none';
+ listItem.classList.toggle('hidden', !match);
+ }
+ }
+
+ async loadList(searchTerm = '') {
+ this.listContainerElem.innerHTML = '';
+ this.toggleLoading(true);
+
+ try {
+ const resp = await window.$http.get(this.getAjaxUrl(searchTerm));
+ this.listContainerElem.innerHTML = resp.data;
+ } catch (err) {
+ console.error(err);
+ }
+
+ this.toggleLoading(false);
+ if (this.localSearchSelector) {
+ this.onSearch();
+ }
+ }
+
+ getAjaxUrl(searchTerm = null) {
+ if (!searchTerm) {
+ return this.url;
+ }
+
+ const joiner = this.url.includes('?') ? '&' : '?';
+ return `${this.url}${joiner}search=${encodeURIComponent(searchTerm)}`;
+ }
+
+ toggleLoading(show = false) {
+ this.loadingElem.style.display = show ? 'block' : 'none';
+ }
+
+}
+
+export default DropdownSearch;
\ No newline at end of file
this.body = document.body;
this.showing = false;
this.setupListeners();
+ this.hide = this.hide.bind(this);
}
show(event = null) {
import autoSuggest from "./auto-suggest.js"
import backToTop from "./back-to-top.js"
import bookSort from "./book-sort.js"
-import breadcrumbListing from "./breadcrumb-listing.js"
import chapterToggle from "./chapter-toggle.js"
import codeEditor from "./code-editor.js"
import codeHighlighter from "./code-highlighter.js"
import customCheckbox from "./custom-checkbox.js"
import detailsHighlighter from "./details-highlighter.js"
import dropdown from "./dropdown.js"
+import dropdownSearch from "./dropdown-search.js"
import dropzone from "./dropzone.js"
import editorToolbox from "./editor-toolbox.js"
import entityPermissionsEditor from "./entity-permissions-editor.js"
import templateManager from "./template-manager.js"
import toggleSwitch from "./toggle-switch.js"
import triLayout from "./tri-layout.js"
+import userSelect from "./user-select.js"
import wysiwygEditor from "./wysiwyg-editor.js"
const componentMapping = {
"auto-suggest": autoSuggest,
"back-to-top": backToTop,
"book-sort": bookSort,
- "breadcrumb-listing": breadcrumbListing,
"chapter-toggle": chapterToggle,
"code-editor": codeEditor,
"code-highlighter": codeHighlighter,
"custom-checkbox": customCheckbox,
"details-highlighter": detailsHighlighter,
"dropdown": dropdown,
+ "dropdown-search": dropdownSearch,
"dropzone": dropzone,
"editor-toolbox": editorToolbox,
"entity-permissions-editor": entityPermissionsEditor,
"template-manager": templateManager,
"toggle-switch": toggleSwitch,
"tri-layout": triLayout,
+ "user-select": userSelect,
"wysiwyg-editor": wysiwygEditor,
};
this.pageId = this.$opts.pageId;
this.textDirection = this.$opts.textDirection;
+ this.imageUploadErrorText = this.$opts.imageUploadErrorText;
this.markdown = new MarkdownIt({html: true});
this.markdown.use(mdTasksLists, {label: true});
this.displayStylesLoaded = false;
this.input = this.elem.querySelector('textarea');
- this.htmlInput = this.elem.querySelector('input[name=html]');
this.cm = code.markdownEditor(this.input);
this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
// Set body content
this.displayDoc.body.className = 'page-content';
this.displayDoc.body.innerHTML = html;
- this.htmlInput.value = html;
// Copy styles from page head and set custom styles for editor
this.loadStylesIntoDisplay();
const newContent = `[](${resp.data.url})`;
replaceContent(placeHolderText, newContent);
}).catch(err => {
- window.$events.emit('error', trans('errors.image_upload_error'));
+ window.$events.emit('error', context.imageUploadErrorText);
replaceContent(placeHolderText, selectedText);
console.log(err);
});
this.cm.focus();
DrawIO.close();
}).catch(err => {
- window.$events.emit('error', trans('errors.image_upload_error'));
+ window.$events.emit('error', this.imageUploadErrorText);
console.log(err);
});
});
Code.highlight();
this.setupPointer();
this.setupNavHighlighting();
+ this.setupDetailsCodeBlockRefresh();
// Check the hash on load
if (window.location.hash) {
});
}
}
+
+ setupDetailsCodeBlockRefresh() {
+ const onToggle = event => {
+ const codeMirrors = [...event.target.querySelectorAll('.CodeMirror')];
+ codeMirrors.forEach(cm => cm.CodeMirror && cm.CodeMirror.refresh());
+ };
+
+ const details = [...this.elem.querySelectorAll('details')];
+ details.forEach(detail => detail.addEventListener('toggle', onToggle));
+ }
}
export default PageDisplay;
this.editorType = this.$opts.editorType;
this.pageId = Number(this.$opts.pageId);
this.isNewDraft = this.$opts.pageNewDraft === 'true';
- this.hasDefaultTitle = this.$opts.isDefaultTitle || false;
+ this.hasDefaultTitle = this.$opts.hasDefaultTitle || false;
// Elements
this.container = this.$el;
--- /dev/null
+import {onChildEvent} from "../services/dom";
+
+class UserSelect {
+
+ setup() {
+
+ this.input = this.$refs.input;
+ this.userInfoContainer = this.$refs.userInfo;
+
+ this.hide = this.$el.components.dropdown.hide;
+
+ onChildEvent(this.$el, 'a.dropdown-search-item', 'click', this.selectUser.bind(this));
+ }
+
+ selectUser(event, userEl) {
+ const id = userEl.getAttribute('data-id');
+ this.input.value = id;
+ this.userInfoContainer.innerHTML = userEl.innerHTML;
+ this.hide();
+ }
+
+}
+
+export default UserSelect;
\ No newline at end of file
editor.dom.replace(newEl, id);
}).catch(err => {
editor.dom.remove(id);
- window.$events.emit('error', trans('errors.image_upload_error'));
+ window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
console.log(err);
});
}, 10);
showPopup(editor);
});
- editor.on('SetContent', function () {
+ function parseCodeMirrorInstances() {
// Recover broken codemirror instances
$('.CodeMirrorContainer').filter((index ,elem) => {
Code.wysiwygView(elem);
});
});
+ }
+
+ editor.on('init', function() {
+ // Parse code mirror instances on init, but delay a little so this runs after
+ // initial styles are fetched into the editor.
+ parseCodeMirrorInstances();
+ // Parsed code mirror blocks when content is set but wait before setting this handler
+ // to avoid any init 'SetContent' events.
+ setTimeout(() => {
+ editor.on('SetContent', parseCodeMirrorInstances);
+ }, 200);
});
});
}
-function drawIoPlugin(drawioUrl, isDarkMode, pageId) {
+function drawIoPlugin(drawioUrl, isDarkMode, pageId, wysiwygComponent) {
let pageEditor = null;
let currentNode = null;
pageEditor.dom.setAttrib(imgElem, 'src', img.url);
pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
} catch (err) {
- window.$events.emit('error', trans('errors.image_upload_error'));
+ window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
console.log(err);
}
return;
pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
} catch (err) {
pageEditor.dom.remove(id);
- window.$events.emit('error', trans('errors.image_upload_error'));
+ window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
console.log(err);
}
}, 5);
class WysiwygEditor {
-
setup() {
this.elem = this.$el;
this.pageId = this.$opts.pageId;
this.textDirection = this.$opts.textDirection;
+ this.imageUploadErrorText = this.$opts.imageUploadErrorText;
this.isDarkMode = document.documentElement.classList.contains('dark-mode');
- this.plugins = "image table textcolor paste link autolink fullscreen code customhr autosave lists codeeditor media";
+ this.plugins = "image imagetools table textcolor paste link autolink fullscreen code customhr autosave lists codeeditor media";
this.loadPlugins();
this.tinyMceConfig = this.getTinyMceConfig();
const drawioUrlElem = document.querySelector('[drawio-url]');
if (drawioUrlElem) {
const url = drawioUrlElem.getAttribute('drawio-url');
- drawIoPlugin(url, this.isDarkMode, this.pageId);
+ drawIoPlugin(url, this.isDarkMode, this.pageId, this);
this.plugins += ' drawio';
}
theme: getTheme(),
readOnly: true
});
- setTimeout(() => {
- cm.refresh();
- }, 300);
+
return {wrap: newWrap, editor: cm};
}
// Bookshelves
'bookshelf_create' => 'تم إنشاء رف الكتب',
- 'bookshelf_create_notification' => 'Bookshelf Successfully Created',
- 'bookshelf_update' => 'updated bookshelf',
- 'bookshelf_update_notification' => 'Bookshelf Successfully Updated',
- 'bookshelf_delete' => 'deleted bookshelf',
- 'bookshelf_delete_notification' => 'Bookshelf Successfully Deleted',
+ 'bookshelf_create_notification' => 'تم إنشاء الرف بنجاح',
+ 'bookshelf_update' => 'تم تحديث الرف',
+ 'bookshelf_update_notification' => 'تم تحديث الرف بنجاح',
+ 'bookshelf_delete' => 'تم تحديث الرف',
+ 'bookshelf_delete_notification' => 'تم حذف الرف بنجاح',
// Other
'commented_on' => 'تم التعليق',
+ 'permissions_update' => 'تحديث الأذونات',
];
'remember_me' => 'تذكرني',
'ldap_email_hint' => 'الرجاء إدخال عنوان بريد إلكتروني لاستخدامه مع الحساب.',
'create_account' => 'إنشاء حساب',
- 'already_have_account' => 'Already have an account?',
- 'dont_have_account' => 'Don\'t have an account?',
+ 'already_have_account' => 'لديك حساب بالفعل؟',
+ 'dont_have_account' => 'ليس لديك حساب؟',
'social_login' => 'تسجيل الدخول باستخدام حسابات التواصل الاجتماعي',
'social_registration' => 'إنشاء حساب باستخدام حسابات التواصل الاجتماعي',
'social_registration_text' => 'إنشاء حساب والدخول باستخدام خدمة أخرى.',
'reset_password' => 'استعادة كلمة المرور',
'reset_password_send_instructions' => 'أدخل بريدك الإلكتروني بالأسفل وسيتم إرسال رسالة برابط لاستعادة كلمة المرور.',
'reset_password_send_button' => 'أرسل رابط الاستعادة',
- 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+ 'reset_password_sent' => 'سيتم إرسال رابط إعادة تعيين كلمة المرور إلى عنوان البريد الإلكتروني هذا إذا كان موجودًا في النظام.',
'reset_password_success' => 'تمت استعادة كلمة المرور بنجاح.',
'email_reset_subject' => 'استعد كلمة المرور الخاصة بتطبيق :appName',
'email_reset_text' => 'تم إرسال هذه الرسالة بسبب تلقينا لطلب استعادة كلمة المرور الخاصة بحسابكم.',
'email_not_confirmed_resend_button' => 'إعادة إرسال رسالة التأكيد',
// User Invite
- 'user_invite_email_subject' => 'You have been invited to join :appName!',
- 'user_invite_email_greeting' => 'An account has been created for you on :appName.',
- 'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
- 'user_invite_email_action' => 'Set Account Password',
- 'user_invite_page_welcome' => 'Welcome to :appName!',
- 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
- 'user_invite_page_confirm_button' => 'Confirm Password',
- 'user_invite_success' => 'Password set, you now have access to :appName!'
+ 'user_invite_email_subject' => 'تم دعوتك للإنضمام إلى صفحة الحالة الخاصة بـ :app_name!',
+ 'user_invite_email_greeting' => 'تم إنشاء حساب مستخدم لك على %site%.',
+ 'user_invite_email_text' => 'انقر على الزر أدناه لتعيين كلمة مرور الحساب والحصول على الوصول:',
+ 'user_invite_email_action' => 'كلمة سر المستخدم',
+ 'user_invite_page_welcome' => 'مرحبا بكم في :appName!',
+ 'user_invite_page_text' => 'لإكمال حسابك والحصول على حق الوصول تحتاج إلى تعيين كلمة مرور سيتم استخدامها لتسجيل الدخول إلى :appName في الزيارات المستقبلية.',
+ 'user_invite_page_confirm_button' => 'تأكيد كلمة المرور',
+ 'user_invite_success' => 'مجموعة كلمات المرور، لديك الآن حق الوصول إلى :appName!'
];
\ No newline at end of file
'save' => 'حفظ',
'continue' => 'استمرار',
'select' => 'تحديد',
- 'toggle_all' => 'Toggle All',
+ 'toggle_all' => 'تبديل الكل',
'more' => 'المزيد',
// Form Labels
// Actions
'actions' => 'إجراءات',
'view' => 'عرض',
- 'view_all' => 'View All',
+ 'view_all' => 'عرض الكل',
'create' => 'إنشاء',
'update' => 'تحديث',
'edit' => 'تعديل',
'copy' => 'نسخ',
'reply' => 'رد',
'delete' => 'حذف',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'تأكيد الحذف',
'search' => 'بحث',
'search_clear' => 'مسح البحث',
'reset' => 'إعادة تعيين',
'remove' => 'إزالة',
'add' => 'إضافة',
- 'fullscreen' => 'Fullscreen',
+ 'fullscreen' => 'شاشة كاملة',
// Sort Options
- 'sort_options' => 'Sort Options',
- 'sort_direction_toggle' => 'Sort Direction Toggle',
- 'sort_ascending' => 'Sort Ascending',
- 'sort_descending' => 'Sort Descending',
- 'sort_name' => 'Name',
- 'sort_created_at' => 'Created Date',
- 'sort_updated_at' => 'Updated Date',
+ 'sort_options' => 'خيارات الترتيب',
+ 'sort_direction_toggle' => 'الترتيب وفق الإتجاه',
+ 'sort_ascending' => 'فرز تصاعدي',
+ 'sort_descending' => 'فرز تنازلي',
+ 'sort_name' => 'الاسم',
+ 'sort_created_at' => 'تاريخ الإنشاء',
+ 'sort_updated_at' => 'تاريخ التحديث',
// Misc
'deleted_user' => 'حذف مستخدم',
'details' => 'التفاصيل',
'grid_view' => 'عرض شبكي',
'list_view' => 'عرض منسدل',
- 'default' => 'Default',
- 'breadcrumb' => 'Breadcrumb',
+ 'default' => 'افتراضي',
+ 'breadcrumb' => 'شريط التنقل',
// Header
- 'profile_menu' => 'Profile Menu',
+ 'profile_menu' => 'قائمة ملف التعريف',
'view_profile' => 'عرض الملف الشخصي',
'edit_profile' => 'تعديل الملف الشخصي',
- 'dark_mode' => 'Dark Mode',
- 'light_mode' => 'Light Mode',
+ 'dark_mode' => 'الوضع المظلم',
+ 'light_mode' => 'الوضع المضيء',
// Layout tabs
- 'tab_info' => 'Info',
- 'tab_content' => 'Content',
+ 'tab_info' => 'معلومات',
+ 'tab_content' => 'المحتوى',
// Email Content
'email_action_help' => 'إذا واجهتكم مشكلة بضغط زر ":actionText" فبإمكانكم نسخ الرابط أدناه ولصقه بالمتصفح:',
'image_load_more' => 'المزيد',
'image_image_name' => 'اسم الصورة',
'image_delete_used' => 'هذه الصورة مستخدمة بالصفحات أدناه.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'هل أنت متأكد من أنك تريد حذف هذه الصورة ؟',
'image_select_image' => 'تحديد الصورة',
'image_dropzone' => 'قم بإسقاط الصورة أو اضغط هنا للرفع',
'images_deleted' => 'تم حذف الصور',
'code_editor' => 'تعديل الشفرة',
'code_language' => 'لغة الشفرة',
'code_content' => 'محتويات الشفرة',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'سجل الدورة',
'code_save' => 'حفظ الشفرة',
];
'meta_created_name' => 'أنشئ :timeLength بواسطة :user',
'meta_updated' => 'مُحدث :timeLength',
'meta_updated_name' => 'مُحدث :timeLength بواسطة :user',
- 'entity_select' => 'Entity Select',
+ 'meta_owned_name' => 'Owned by :user',
+ 'entity_select' => 'اختيار الكيان',
'images' => 'صور',
'my_recent_drafts' => 'مسوداتي الحديثة',
'my_recently_viewed' => 'ما عرضته مؤخراً',
'permissions_intro' => 'في حال التفعيل, ستتم تبدية هذه الأذونات على أذونات الأدوار.',
'permissions_enable' => 'تفعيل الأذونات المخصصة',
'permissions_save' => 'حفظ الأذونات',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'نتائج البحث',
'shelves_long' => 'أرفف الكتب',
'shelves_empty' => 'لم يتم إنشاء أي أرفف',
'shelves_create' => 'إنشاء رف جديد',
- 'shelves_popular' => 'Popular Shelves',
+ 'shelves_popular' => 'أرفف شعبية',
'shelves_new' => 'أرفف جديدة',
'shelves_new_action' => 'رف جديد',
- 'shelves_popular_empty' => 'The most popular shelves will appear here.',
- 'shelves_new_empty' => 'The most recently created shelves will appear here.',
+ 'shelves_popular_empty' => 'ستظهر هنا الأرفف الأكثر رواجًا.',
+ 'shelves_new_empty' => 'ستظهر هنا الأرفف التي تم إنشاؤها مؤخرًا.',
'shelves_save' => 'حفظ الرف',
- 'shelves_books' => 'Books on this shelf',
+ 'shelves_books' => 'كتب على هذا الرف',
'shelves_add_books' => 'إضافة كتب لهذا الرف',
'shelves_drag_books' => 'اسحب الكتب هنا لإضافتها لهذا الرف',
'shelves_empty_contents' => 'لا توجد كتب مخصصة لهذا الرف',
- 'shelves_edit_and_assign' => 'Edit shelf to assign books',
- 'shelves_edit_named' => 'Edit Bookshelf :name',
- 'shelves_edit' => 'Edit Bookshelf',
- 'shelves_delete' => 'Delete Bookshelf',
- 'shelves_delete_named' => 'Delete Bookshelf :name',
- 'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
- 'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
- 'shelves_permissions' => 'Bookshelf Permissions',
- 'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
- 'shelves_permissions_active' => 'Bookshelf Permissions Active',
- 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
- 'shelves_copy_permissions' => 'Copy Permissions',
- 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
- 'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books',
+ 'shelves_edit_and_assign' => 'تحرير الرف لإدراج كتب',
+ 'shelves_edit_named' => 'تحرير رف الكتب: الاسم',
+ 'shelves_edit' => 'تحرير رف الكتب',
+ 'shelves_delete' => 'حذف رف الكتب',
+ 'shelves_delete_named' => 'حذف رف الكتب: الاسم',
+ 'shelves_delete_explain' => "سيؤدي هذا إلى حذف رف الكتب مع الاسم ':المُسمى به'. لن يتم حذف الكتب المتضمنة.",
+ 'shelves_delete_confirmation' => 'هل أنت متأكد من أنك تريد حذف هذا الرف؟',
+ 'shelves_permissions' => 'أذونات رف الكتب',
+ 'shelves_permissions_updated' => 'تم تحديث أذونات رف الكتب',
+ 'shelves_permissions_active' => 'أذونات رف الكتب نشطة',
+ 'shelves_copy_permissions_to_books' => 'نسخ أذونات الوصول إلى الكتب',
+ 'shelves_copy_permissions' => 'نسخ الأذونات',
+ 'shelves_copy_permissions_explain' => 'سيؤدي هذا إلى تطبيق إعدادات الأذونات الحالية لهذا الرف على جميع الكتب المتضمنة فيه. قبل التفعيل، تأكد من حفظ أي تغييرات في أذونات هذا الرف.',
+ 'shelves_copy_permission_success' => 'تم نسخ أذونات رف الكتب إلى: عد الكتب',
// Books
'book' => 'كتاب',
'books_sort_named' => 'فرز كتاب :bookName',
'books_sort_name' => 'ترتيب حسب الإسم',
'books_sort_created' => 'ترتيب حسب تاريخ الإنشاء',
- 'books_sort_updated' => 'Sort by Updated Date',
- 'books_sort_chapters_first' => 'Chapters First',
- 'books_sort_chapters_last' => 'Chapters Last',
+ 'books_sort_updated' => 'فرز حسب تاريخ التحديث',
+ 'books_sort_chapters_first' => 'الفصول الأولى',
+ 'books_sort_chapters_last' => 'الفصول الأخيرة',
'books_sort_show_other' => 'عرض كتب أخرى',
'books_sort_save' => 'حفظ الترتيب الجديد',
'chapters_create' => 'إنشاء فصل جديد',
'chapters_delete' => 'حذف الفصل',
'chapters_delete_named' => 'حذف فصل :chapterName',
- 'chapters_delete_explain' => 'سيتم حذف فصل \':chapterName\'. جميع الصفحات ستزال وستتم إضافتها مباشرة للكتاب الرئيسي.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'تأكيد حذف الفصل؟',
'chapters_edit' => 'تعديل الفصل',
'chapters_edit_named' => 'تعديل فصل :chapterName',
'pages_delete_confirm' => 'تأكيد حذف الصفحة؟',
'pages_delete_draft_confirm' => 'تأكيد حذف المسودة؟',
'pages_editing_named' => ':pageName قيد التعديل',
- 'pages_edit_draft_options' => 'Draft Options',
+ 'pages_edit_draft_options' => 'خيارات المسودة',
'pages_edit_save_draft' => 'حفظ المسودة',
'pages_edit_draft' => 'تعديل مسودة الصفحة',
'pages_editing_draft' => 'المسودة قيد التعديل',
'pages_md_editor' => 'المحرر',
'pages_md_preview' => 'معاينة',
'pages_md_insert_image' => 'إدخال صورة',
- 'pages_md_insert_link' => 'Insert Entity Link',
+ 'pages_md_insert_link' => 'إدراج ارتباط الكيان',
'pages_md_insert_drawing' => 'إدخال رسمة',
'pages_not_in_chapter' => 'صفحة ليست في فصل',
'pages_move' => 'نقل الصفحة',
'pages_revisions' => 'مراجعات الصفحة',
'pages_revisions_named' => 'مراجعات صفحة :pageName',
'pages_revision_named' => 'مراجعة صفحة :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'أنشئ بواسطة',
'pages_revisions_date' => 'تاريخ المراجعة',
'pages_revisions_number' => '#',
- 'pages_revisions_numbered' => 'Revision #:id',
- 'pages_revisions_numbered_changes' => 'Revision #:id Changes',
+ 'pages_revisions_numbered' => 'مراجعة #: رقم تعريفي',
+ 'pages_revisions_numbered_changes' => 'مراجعة #: رقم تعريفي التغييرات',
'pages_revisions_changelog' => 'سجل التعديل',
'pages_revisions_changes' => 'التعديلات',
'pages_revisions_current' => 'النسخة الحالية',
'start_b' => ':userName بدأ بتعديل هذه الصفحة',
'time_a' => 'منذ أن تم تحديث هذه الصفحة',
'time_b' => 'في آخر :minCount دقيقة/دقائق',
- 'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+ 'message' => 'وقت البدء: احرص على عدم الكتابة فوق تحديثات بعضنا البعض!',
],
'pages_draft_discarded' => 'تم التخلص من المسودة. تم تحديث المحرر بمحتوى الصفحة الحالي',
- 'pages_specific' => 'Specific Page',
- 'pages_is_template' => 'Page Template',
+ 'pages_specific' => 'صفحة محددة',
+ 'pages_is_template' => 'قالب الصفحة',
// Editor Sidebar
'page_tags' => 'وسوم الصفحة',
'chapter_tags' => 'وسوم الفصل',
'book_tags' => 'وسوم الكتاب',
- 'shelf_tags' => 'Shelf Tags',
+ 'shelf_tags' => 'علامات الرف',
'tag' => 'وسم',
'tags' => 'وسوم',
- 'tag_name' => 'Tag Name',
+ 'tag_name' => 'اسم العلامة',
'tag_value' => 'قيمة الوسم (اختياري)',
'tags_explain' => "إضافة الوسوم تساعد بترتيب وتقسيم المحتوى. \n من الممكن وضع قيمة لكل وسم لترتيب أفضل وأدق.",
'tags_add' => 'إضافة وسم آخر',
- 'tags_remove' => 'Remove this tag',
+ 'tags_remove' => 'إزالة هذه العلامة',
'attachments' => 'المرفقات',
'attachments_explain' => 'ارفع بعض الملفات أو أرفق بعض الروابط لعرضها بصفحتك. ستكون الملفات والروابط معروضة في الشريط الجانبي للصفحة.',
'attachments_explain_instant_save' => 'سيتم حفظ التغييرات هنا بلحظتها',
'attachments_upload' => 'رفع ملف',
'attachments_link' => 'إرفاق رابط',
'attachments_set_link' => 'تحديد الرابط',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'هل أنت متأكد من أنك تريد حذف هذا المرفق؟',
'attachments_dropzone' => 'أسقط الملفات أو اضغط هنا لإرفاق ملف',
'attachments_no_files' => 'لم يتم رفع أي ملفات',
'attachments_explain_link' => 'بالإمكان إرفاق رابط في حال عدم تفضيل رفع ملف. قد يكون الرابط لصفحة أخرى أو لملف في أحد خدمات التخزين السحابي.',
'attachments_link_name' => 'اسم الرابط',
'attachment_link' => 'رابط المرفق',
- 'attachments_link_url' => 'Link to file',
+ 'attachments_link_url' => 'رابط الملف',
'attachments_link_url_hint' => 'رابط الموقع أو الملف',
'attach' => 'إرفاق',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'إضافة رابط مرفق إلى الصفحة',
'attachments_edit_file' => 'تعديل الملف',
'attachments_edit_file_name' => 'اسم الملف',
'attachments_edit_drop_upload' => 'أسقط الملفات أو اضغط هنا للرفع والاستبدال',
'templates_set_as_template' => 'هذه الصفحة عبارة عن قالب',
'templates_explain_set_as_template' => 'يمكنك تعيين هذه الصفحة كقالب بحيث تستخدم محتوياتها عند إنشاء صفحات أخرى. سيتمكن المستخدمون الآخرون من استخدام هذا القالب إذا كان لديهم أذونات عرض لهذه الصفحة.',
'templates_replace_content' => 'استبدال محتوى الصفحة',
- 'templates_append_content' => 'Append to page content',
- 'templates_prepend_content' => 'Prepend to page content',
+ 'templates_append_content' => 'تذييل محتوى الصفحة',
+ 'templates_prepend_content' => 'بادئة محتوى الصفحة',
// Profile View
- 'profile_user_for_x' => 'User for :time',
+ 'profile_user_for_x' => 'المستخدم لـ : الوقت',
'profile_created_content' => 'المحتوى المنشأ',
'profile_not_created_pages' => 'لم يتم إنشاء أي صفحات بواسطة :userName',
'profile_not_created_chapters' => 'لم يتم إنشاء أي فصول بواسطة :userName',
'profile_not_created_books' => 'لم يتم إنشاء أي كتب بواسطة :userName',
- 'profile_not_created_shelves' => ':userName has not created any shelves',
+ 'profile_not_created_shelves' => 'لم يقم "اسم المستخدم"بإنشاء أي أرفف',
// Comments
'comment' => 'تعليق',
'email_already_confirmed' => 'تم تأكيد البريد الإلكتروني من قبل, الرجاء محاولة تسجيل الدخول.',
'email_confirmation_invalid' => 'رابط التأكيد غير صحيح أو قد تم استخدامه من قبل, الرجاء محاولة التسجيل من جديد.',
'email_confirmation_expired' => 'صلاحية رابط التأكيد انتهت, تم إرسال رسالة تأكيد جديدة لعنوان البريد الإلكتروني.',
- 'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+ 'email_confirmation_awaiting' => 'عنوان البريد الإلكتروني للحساب قيد الاستخدام يحتاج إلى تأكيد',
'ldap_fail_anonymous' => 'فشل الوصول إلى LDAP باستخدام الربط المجهول',
'ldap_fail_authed' => 'فشل الوصول إلى LDAP باستخدام dn و password المعطاة',
'ldap_extension_not_installed' => 'لم يتم تثبيت إضافة LDAP PHP',
'ldap_cannot_connect' => 'لا يمكن الاتصال بخادم ldap, فشل الاتصال المبدئي',
- 'saml_already_logged_in' => 'Already logged in',
- 'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
- 'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
- 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
- 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+ 'saml_already_logged_in' => 'تم تسجيل الدخول بالفعل',
+ 'saml_user_not_registered' => 'المستخدم :name غير مسجل ويتم تعطيل التسجيل التلقائي',
+ 'saml_no_email_address' => 'تعذر العثور على عنوان بريد إلكتروني، لهذا المستخدم، في البيانات المقدمة من نظام المصادقة الخارجي',
+ 'saml_invalid_response_id' => 'لم يتم التعرف على الطلب من نظام التوثيق الخارجي من خلال عملية تبدأ بهذا التطبيق. العودة بعد تسجيل الدخول يمكن أن يسبب هذه المشكلة.',
+ 'saml_fail_authed' => 'تسجيل الدخول باستخدام :system فشل، النظام لم يوفر التفويض الناجح',
'social_no_action_defined' => 'لم يتم تعريف أي إجراء',
'social_login_bad_response' => "حصل خطأ خلال تسجيل الدخول باستخدام :socialAccount \n:error",
'social_account_in_use' => 'حساب :socialAccount قيد الاستخدام حالياً, الرجاء محاولة الدخول باستخدام خيار :socialAccount.',
'social_account_already_used_existing' => 'حساب :socialAccount مستخدَم من قبل مستخدم آخر.',
'social_account_not_used' => 'حساب :socialAccount غير مرتبط بأي مستخدم. الرجاء ربطه من خلال إعدادات ملفكم. ',
'social_account_register_instructions' => 'إذا لم يكن لديكم حساب فيمكنكم التجسيل باستخدام خيار :socialAccount.',
- 'social_driver_not_found' => 'Social driver not found',
- 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
- 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+ 'social_driver_not_found' => 'لم يتم العثور على السوشيال درايفر "Social driver"',
+ 'social_driver_not_configured' => 'لم يتم تهيئة إعدادات حسابك الاجتماعي بشكل صحيح.',
+ 'invite_token_expired' => 'انتهت صلاحية رابط هذه الدعوة. يمكنك بدلاً من ذلك محاولة إعادة تعيين كلمة مرور حسابك.',
// System
'path_not_writable' => 'لا يمكن الرفع إلى مسار :filePath. الرجاء التأكد من قابلية الكتابة إلى الخادم.',
'page_custom_home_deletion' => 'لا يمكن حذف الصفحة إذا كانت محددة كصفحة رئيسية',
// Entities
- 'entity_not_found' => 'Entity not found',
- 'bookshelf_not_found' => 'Bookshelf not found',
+ 'entity_not_found' => 'الكيان غير موجود',
+ 'bookshelf_not_found' => 'رف الكتب غير موجود',
'book_not_found' => 'لم يتم العثور على الكتاب',
'page_not_found' => 'لم يتم العثور على الصفحة',
'chapter_not_found' => 'لم يتم العثور على الفصل',
'role_cannot_be_edited' => 'لا يمكن تعديل هذا الدور',
'role_system_cannot_be_deleted' => 'هذا الدور خاص بالنظام ولا يمكن حذفه',
'role_registration_default_cannot_delete' => 'لا يمكن حذف الدور إذا كان مسجل كالدور الأساسي بعد تسجيل الحساب',
- 'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+ 'role_cannot_remove_only_admin' => 'هذا المستخدم هو المستخدم الوحيد المعين لدور المسؤول. قم بتعيين دور المسؤول لمستخدم آخر قبل محاولة إزالته هنا.',
// Comments
'comment_list' => 'حصل خطأ خلال جلب التعليقات.',
// Error pages
'404_page_not_found' => 'لم يتم العثور على الصفحة',
'sorry_page_not_found' => 'عفواً, لا يمكن العثور على الصفحة التي تبحث عنها.',
- 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+ 'sorry_page_not_found_permission_warning' => 'إذا كنت تتوقع أن تكون هذه الصفحة موجودة، قد لا يكون لديك تصريح بمشاهدتها.',
'return_home' => 'العودة للصفحة الرئيسية',
'error_occurred' => 'حدث خطأ',
'app_down' => ':appName لا يعمل حالياً',
'back_soon' => 'سيعود للعمل قريباً.',
// API errors
- 'api_no_authorization_found' => 'No authorization token found on the request',
- 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
- 'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
- 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
- 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
- 'api_user_token_expired' => 'The authorization token used has expired',
+ 'api_no_authorization_found' => 'لم يتم العثور على رمز ترخيص مميز في الطلب',
+ 'api_bad_authorization_format' => 'تم العثور على رمز ترخيص مميز في الطلب ولكن يبدو أن التنسيق غير صحيح',
+ 'api_user_token_not_found' => 'لم يتم العثور على رمز API مطابق لرمز الترخيص المُقدم',
+ 'api_incorrect_token_secret' => 'الشفرة المُقدمة لرمز API المستخدم المحدد غير صحيحة',
+ 'api_user_no_api_permission' => 'مالك رمز API المستخدم ليس لديه الصلاحية لإجراء مكالمات API',
+ 'api_user_token_expired' => 'انتهت صلاحية رمز الترخيص المستخدم',
// Settings & Maintenance
- 'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+ 'maintenance_test_email_failure' => 'حدث خطأ عند إرسال بريد إلكتروني تجريبي:',
];
'password' => 'يجب أن تتكون كلمة المرور من ستة أحرف على الأقل وأن تطابق التأكيد.',
'user' => "لم يتم العثور على مستخدم بعنوان البريد الإلكتروني المعطى.",
- 'token' => 'The password reset token is invalid for this email address.',
+ 'token' => 'رمز إعادة تعيين كلمة المرور غير صالح لعنوان هذا البريد الإلكتروني.',
'sent' => 'تم إرسال رابط تجديد كلمة المرور إلى بريدكم الإلكتروني!',
'reset' => 'تم تجديد كلمة المرور الخاصة بكم!',
'settings_save_success' => 'تم حفظ الإعدادات',
// App Settings
- 'app_customization' => 'Customization',
- 'app_features_security' => 'Features & Security',
+ 'app_customization' => 'تخصيص',
+ 'app_features_security' => 'الميزات و الأمان',
'app_name' => 'اسم التطبيق',
'app_name_desc' => 'سيتم عرض هذا الاسم في الترويسة وفي أي رسالة بريد إلكتروني.',
'app_name_header' => 'عرض اسم التطبيق في الترويسة؟',
- 'app_public_access' => 'Public Access',
- 'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
- 'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
- 'app_public_access_toggle' => 'Allow public access',
+ 'app_public_access' => 'الوصول العام',
+ 'app_public_access_desc' => 'تمكين هذا الخيار سيسمح للزوار، الذين لم يتم تسجيل دخولهم، بالوصول إلى المحتوى في مثيل مكدس الكتب الخاص بك.',
+ 'app_public_access_desc_guest' => 'يمكن التحكم في وصول الزوار العموميين من خلال المستخدم "الضيف".',
+ 'app_public_access_toggle' => 'السماح بالوصول العام',
'app_public_viewing' => 'السماح بالعرض على العامة؟',
'app_secure_images' => 'تفعيل حماية أكبر لرفع الصور؟',
- 'app_secure_images_toggle' => 'Enable higher security image uploads',
+ 'app_secure_images_toggle' => 'لمزيد من الحماية',
'app_secure_images_desc' => 'لتحسين أداء النظام, ستكون جميع الصور متاحة للعامة. هذا الخيار يضيف سلسلة من الحروف والأرقام العشوائية صعبة التخمين إلى رابط الصورة. الرجاء التأكد من تعطيل فهرسة المسارات لمنع الوصول السهل.',
'app_editor' => 'محرر الصفحة',
'app_editor_desc' => 'الرجاء اختيار محرر النص الذي سيستخدم من قبل جميع المستخدمين لتحرير الصفحات.',
'app_custom_html' => 'Custom HTML head content',
- 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
- 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+ 'app_custom_html_desc' => 'سيتم إدراج أي محتوى مضاف هنا في الجزء السفلي من قسم <head> من كل صفحة. هذا أمر مفيد لتجاوز الأنماط أو إضافة رمز التحليل.',
+ 'app_custom_html_disabled_notice' => 'تم تعطيل محتوى HTML الرئيسي المخصص في صفحة الإعدادات هذه لضمان عكس أي تغييرات متتالية.',
'app_logo' => 'شعار التطبيق',
'app_logo_desc' => 'يجب أن تكون الصورة بارتفاع 43 بكسل. <br>سيتم تصغير الصور الأكبر من ذلك.',
'app_primary_color' => 'اللون الأساسي للتطبيق',
'app_primary_color_desc' => 'يجب أن تكون القيمة من نوع hex. <br>اترك الخانة فارغة للرجوع للون الافتراضي.',
'app_homepage' => 'الصفحة الرئيسية للتطبيق',
'app_homepage_desc' => 'الرجاء اختيار صفحة لتصبح الصفحة الرئيسية بدل من الافتراضية. سيتم تجاهل جميع الأذونات الخاصة بالصفحة المختارة.',
- 'app_homepage_select' => 'Select a page',
+ 'app_homepage_select' => 'اختر صفحة',
'app_disable_comments' => 'تعطيل التعليقات',
- 'app_disable_comments_toggle' => 'Disable comments',
+ 'app_disable_comments_toggle' => 'تعطيل التعليقات',
'app_disable_comments_desc' => 'تعطيل التعليقات على جميع الصفحات داخل التطبيق. التعليقات الموجودة من الأصل لن تكون ظاهرة.',
// Color settings
- 'content_colors' => 'Content Colors',
- 'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
- 'bookshelf_color' => 'Shelf Color',
- 'book_color' => 'Book Color',
- 'chapter_color' => 'Chapter Color',
- 'page_color' => 'Page Color',
- 'page_draft_color' => 'Page Draft Color',
+ 'content_colors' => 'ألوان المحتوى',
+ 'content_colors_desc' => 'تعيين الألوان لجميع العناصر في التسلسل الهرمي لتنظيم الصفحات. يوصى باختيار الألوان ذات السطوع المماثل للألوان الافتراضية للقراءة.',
+ 'bookshelf_color' => 'لون الرف',
+ 'book_color' => 'لون الكتاب',
+ 'chapter_color' => 'لون الفصل',
+ 'page_color' => 'لون الصفحة',
+ 'page_draft_color' => 'لون مسودة الصفحة',
// Registration Settings
'reg_settings' => 'إعدادات التسجيل',
- 'reg_enable' => 'Enable Registration',
- 'reg_enable_toggle' => 'Enable registration',
- 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
+ 'reg_enable' => 'تمكين التسجيل',
+ 'reg_enable_toggle' => 'تمكين التسجيل',
+ 'reg_enable_desc' => 'عند تمكين التسجيل سيكون المستخدم قادرا على تسجيل نفسه كمستخدم تطبيق. عند التسجيل يعطى لهم دور مستخدم افتراضي وحيد.',
'reg_default_role' => 'دور المستخدم الأساسي بعد التسجيل',
- 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
- 'reg_email_confirmation' => 'Email Confirmation',
- 'reg_email_confirmation_toggle' => 'Require email confirmation',
+ 'reg_enable_external_warning' => 'يتم تجاهل الخيار أعلاه بينما يتم تفعيل مصادقة LDAP الخارجية أو SAML. حسابات المستخدم للأعضاء غير الحاليين سيتم إنشاؤها تلقائياً إذا كانت المصادقة، مقابل النظام الخارجي المستخدم، ناجحة.',
+ 'reg_email_confirmation' => 'تأكيد البريد الإلكتروني',
+ 'reg_email_confirmation_toggle' => 'يتطلب تأكيد البريد الإلكتروني',
'reg_confirm_email_desc' => 'إذا تم استخدام قيود للمجال سيصبح التأكيد عن طريق البريد الإلكتروني إلزامي وسيتم تجاهل القيمة أسفله.',
'reg_confirm_restrict_domain' => 'تقييد التسجيل على مجال محدد',
- 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
+ 'reg_confirm_restrict_domain_desc' => 'أدخل قائمة مفصولة بفواصل لنطاقات البريد الإلكتروني التي ترغب في تقييد التسجيل إليها. سيتم إرسال بريد إلكتروني للمستخدمين لتأكيد عنوانهم قبل السماح لهم بالتفاعل مع التطبيق. <br> لاحظ أن المستخدمين سيكونون قادرين على تغيير عناوين البريد الإلكتروني الخاصة بهم بعد التسجيل بنجاح.',
'reg_confirm_restrict_domain_placeholder' => 'لم يتم اختيار أي قيود',
// Maintenance settings
'maint' => 'الصيانة',
'maint_image_cleanup' => 'تنظيف الصور',
- 'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'تجاهل الصور في المراجعات',
+ 'maint_image_cleanup_desc' => "مسح الصفحة ومراجعة المحتوى للتحقق من أي الصور والرسوم المستخدمة حاليًا وأي الصور زائدة عن الحاجة. تأكد من إنشاء قاعدة بيانات كاملة و نسخة احتياطية للصور قبل تشغيل هذا.",
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'بدء التنظيف',
'maint_image_cleanup_warning' => 'يوجد عدد :count من الصور المحتمل عدم استخدامها. تأكيد حذف الصور؟',
'maint_image_cleanup_success' => 'تم إيجاد وحذف عدد :count من الصور المحتمل عدم استخدامها!',
'maint_image_cleanup_nothing_found' => 'لم يتم حذف أي شيء لعدم وجود أي صور غير مسمتخدمة',
- 'maint_send_test_email' => 'Send a Test Email',
- 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
- 'maint_send_test_email_run' => 'Send test email',
- 'maint_send_test_email_success' => 'Email sent to :address',
- 'maint_send_test_email_mail_subject' => 'Test Email',
- 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
- 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+ 'maint_send_test_email' => 'إرسال بريد إلكتروني تجريبي',
+ 'maint_send_test_email_desc' => 'يرسل هذا بريدًا إلكترونيًا تجريبيًا إلى عنوان بريدك الإلكتروني المحدد في ملفك الشخصي.',
+ 'maint_send_test_email_run' => 'إرسال بريد إليكتروني تجريبي',
+ 'maint_send_test_email_success' => 'تم إرسال البريد الإلكتروني إلى:العنوان',
+ 'maint_send_test_email_mail_subject' => 'اختبار البريد الإلكتروني',
+ 'maint_send_test_email_mail_greeting' => 'يبدو أن تسليم البريد الإلكتروني يعمل!',
+ 'maint_send_test_email_mail_text' => 'تهانينا! كما تلقيت إشعار هذا البريد الإلكتروني، يبدو أن إعدادات البريد الإلكتروني الخاص بك قد تم تكوينها بشكل صحيح.',
+ 'maint_recycle_bin_desc' => 'تُرسل الأرفف والكتب والفصول والصفحات المحذوفة إلى سلة المحذوفات حتى يمكن استعادتها أو حذفها نهائيًا. قد يتم إزالة العناصر الأقدم في سلة المحذوفات تلقائيًا بعد فترة اعتمادًا على تكوين النظام.',
+ 'maint_recycle_bin_open' => 'افتح سلة المحذوفات',
+
+ // Recycle Bin
+ 'recycle_bin' => 'سلة المحذوفات',
+ 'recycle_bin_desc' => 'هنا يمكنك استعادة العناصر التي تم حذفها أو اختيار إزالتها نهائيا من النظام. هذه القائمة غير مصفاة خلافاً لقوائم الأنشطة المماثلة في النظام حيث يتم تطبيق عوامل تصفية الأذونات.',
+ 'recycle_bin_deleted_item' => 'عنصر محذوف',
+ 'recycle_bin_deleted_by' => 'حُذف بواسطة',
+ 'recycle_bin_deleted_at' => 'وقت الحذف',
+ 'recycle_bin_permanently_delete' => 'حُذف نهائيًا',
+ 'recycle_bin_restore' => 'استرجاع',
+ 'recycle_bin_contents_empty' => 'سلة المحذوفات فارغة حاليًا',
+ 'recycle_bin_empty' => 'إفراغ سلة المحذوفات',
+ 'recycle_bin_empty_confirm' => 'سيؤدي هذا إلى إتلاف جميع العناصر الموجودة في سلة المحذوفات بشكل دائم بما في ذلك المحتوى الموجود داخل كل عنصر. هل أنت متأكد من أنك تريد إفراغ سلة المحذوفات؟',
+ 'recycle_bin_destroy_confirm' => 'سيؤدي هذا الإجراء إلى حذف هذا العنصر نهائيًا ، إلى جانب أي عناصر فرعية مدرجة أدناه ، من النظام ولن تتمكن من استعادة هذا المحتوى. هل أنت متأكد من أنك تريد حذف هذا العنصر نهائيًا؟',
+ 'recycle_bin_destroy_list' => 'العناصر المراد تدميرها',
+ 'recycle_bin_restore_list' => 'العناصر المراد استرجاعها',
+ 'recycle_bin_restore_confirm' => 'سيعيد هذا الإجراء العنصر المحذوف ، بما في ذلك أي عناصر فرعية ، إلى موقعه الأصلي. إذا تم حذف الموقع الأصلي منذ ذلك الحين ، وهو الآن في سلة المحذوفات ، فسيلزم أيضًا استعادة العنصر الأصلي.',
+ 'recycle_bin_restore_deleted_parent' => 'تم حذف أصل هذا العنصر أيضًا. سيبقى حذفه حتى يتم استعادة ذلك الأصل أيضًا.',
+ 'recycle_bin_destroy_notification' => 'المحذوف: قُم بعد إجمالي العناصر من سلة المحذوفات.',
+ 'recycle_bin_restore_notification' => 'المرتجع: قُم بعد إجمالي العناصر من سلة المحذوفات.',
// Audit Log
- 'audit' => 'Audit Log',
- 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit' => 'سجل المراجعة',
+ 'audit_desc' => 'يعرض هذا السجل قائمة بالأنشطة المتعقبة في النظام. هذه القائمة غير مصفاة خلافاً لقوائم الأنشطة المماثلة في النظام حيث يتم تطبيق عوامل تصفية الأذونات.',
+ 'audit_event_filter' => 'تصفية الحدث',
+ 'audit_event_filter_no_filter' => 'لا يوجد فلتر',
+ 'audit_deleted_item' => 'عنصر محذوف',
+ 'audit_deleted_item_name' => 'الاسم: كتابة الاسم',
+ 'audit_table_user' => 'المستخدم',
+ 'audit_table_event' => 'الحدث',
+ 'audit_table_related' => 'العنصر أو التفاصيل ذات الصلة',
+ 'audit_table_date' => 'تاريخ النشاط',
+ 'audit_date_from' => 'نطاق التاريخ من',
+ 'audit_date_to' => 'نطاق التاريخ إلى',
// Role Settings
'roles' => 'الأدوار',
'role_create_success' => 'تم إنشاء الدور بنجاح',
'role_delete' => 'حذف الدور',
'role_delete_confirm' => 'سيتم حذف الدور المسمى \':roleName\'.',
- 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+ 'role_delete_users_assigned' => 'هذا الدور له: عدد المستخدمين المعينين له. إذا كنت ترغب في ترحيل المستخدمين من هذا الدور ، فحدد دورًا جديدًا أدناه.',
'role_delete_no_migration' => "لا تقم بترجيل المستخدمين",
'role_delete_sure' => 'تأكيد حذف الدور؟',
'role_delete_success' => 'تم حذف الدور بنجاح',
'role_details' => 'تفاصيل الدور',
'role_name' => 'اسم الدور',
'role_desc' => 'وصف مختصر للدور',
- 'role_external_auth_id' => 'External Authentication IDs',
+ 'role_external_auth_id' => 'ربط الحساب بمواقع التواصل',
'role_system' => 'أذونات النظام',
'role_manage_users' => 'إدارة المستخدمين',
'role_manage_roles' => 'إدارة الأدوار وأذوناتها',
'role_manage_entity_permissions' => 'إدارة جميع أذونات الكتب والفصول والصفحات',
'role_manage_own_entity_permissions' => 'إدارة الأذونات الخاصة بكتابك أو فصلك أو صفحاتك',
- 'role_manage_page_templates' => 'Manage page templates',
- 'role_access_api' => 'Access system API',
+ 'role_manage_page_templates' => 'إدارة قوالب الصفحة',
+ 'role_access_api' => 'الوصول إلى واجهة برمجة تطبيقات النظام API',
'role_manage_settings' => 'إدارة إعدادات التطبيق',
- 'role_asset' => 'Asset Permissions',
- 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
- 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
- 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+ 'role_asset' => 'أذونات الأصول',
+ 'roles_system_warning' => 'اعلم أن الوصول إلى أي من الأذونات الثلاثة المذكورة أعلاه يمكن أن يسمح للمستخدم بتغيير امتيازاته الخاصة أو امتيازات الآخرين في النظام. قم بتعيين الأدوار مع هذه الأذونات فقط للمستخدمين الموثوق بهم.',
+ 'role_asset_desc' => 'تتحكم هذه الأذونات في الوصول الافتراضي إلى الأصول داخل النظام. ستتجاوز الأذونات الخاصة بالكتب والفصول والصفحات هذه الأذونات.',
+ 'role_asset_admins' => 'يُمنح المسؤولين حق الوصول تلقائيًا إلى جميع المحتويات ولكن هذه الخيارات قد تعرض خيارات واجهة المستخدم أو تخفيها.',
'role_all' => 'الكل',
- 'role_own' => 'Own',
- 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+ 'role_own' => 'ما يخص',
+ 'role_controlled_by_asset' => 'يتحكم فيها الأصول التي يتم رفعها إلى',
'role_save' => 'حفظ الدور',
'role_update_success' => 'تم تحديث الدور بنجاح',
'role_users' => 'مستخدمون داخل هذا الدور',
'user_profile' => 'ملف المستخدم',
'users_add_new' => 'إضافة مستخدم جديد',
'users_search' => 'بحث عن مستخدم',
- 'users_details' => 'User Details',
- 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
- 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
+ 'users_latest_activity' => 'أحدث نشاط',
+ 'users_details' => 'بيانات المستخدم',
+ 'users_details_desc' => 'قم بتعيين اسم عرض وعنوان بريد إلكتروني لهذا المستخدم. سيتم استخدام عنوان البريد الإلكتروني لتسجيل الدخول إلى التطبيق.',
+ 'users_details_desc_no_email' => 'قم بتعيين اسم عرض لهذا المستخدم حتى يتمكن الآخرون من التعرف عليه.',
'users_role' => 'أدوار المستخدمين',
- 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
- 'users_password' => 'User Password',
- 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
- 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
- 'users_send_invite_option' => 'Send user invite email',
- 'users_external_auth_id' => 'External Authentication ID',
- 'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+ 'users_role_desc' => 'حدد الأدوار التي سيتم تعيين هذا المستخدم لها. إذا تم تعيين مستخدم لأدوار متعددة ، فسيتم تكديس الأذونات من هذه الأدوار وسيتلقى كل قدرات الأدوار المعينة.',
+ 'users_password' => 'كلمة مرور المستخدم',
+ 'users_password_desc' => 'قم بتعيين كلمة مرور مستخدمة لتسجيل الدخول إلى التطبيق. يجب ألا يقل طول هذه الكلمة عن 6 أحرف.',
+ 'users_send_invite_text' => 'يمكنك اختيار إرسال دعوة بالبريد الإلكتروني إلى هذا المستخدم مما يسمح له بتعيين كلمة المرور الخاصة به أو يمكنك تعيين كلمة المرور الخاصة به بنفسك.',
+ 'users_send_invite_option' => 'أرسل بريدًا إلكترونيًا لدعوة المستخدم',
+ 'users_external_auth_id' => 'ربط الحساب بمواقع التواصل',
+ 'users_external_auth_id_desc' => 'تستخدم هذه الهوية لإثبات شخصية المستخدم عند الدخول إلى مواقع التواصل الخاصة بك.',
'users_password_warning' => 'الرجاء ملئ الحقل أدناه فقط في حال أردتم تغيير كلمة المرور:',
'users_system_public' => 'هذا المستخدم يمثل أي ضيف يقوم بزيارة شيء يخصك. لا يمكن استخدامه لتسجيل الدخول ولكن يتم تعيينه تلقائياً.',
'users_delete' => 'حذف المستخدم',
'users_delete_named' => 'حذف المستخدم :userName',
'users_delete_warning' => 'سيتم حذف المستخدم \':userName\' بشكل تام من النظام.',
'users_delete_confirm' => 'تأكيد حذف المستخدم؟',
- 'users_delete_success' => 'تم حذف المستخدم بنجاح',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'تعديل المستخدم',
'users_edit_profile' => 'تعديل الملف',
'users_edit_success' => 'تم تحديث المستخدم بنجاح',
'users_avatar' => 'صورة المستخدم',
'users_avatar_desc' => 'يجب أن تكون الصورة مربعة ومقاربة لحجم 256 بكسل',
'users_preferred_language' => 'اللغة المفضلة',
- 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+ 'users_preferred_language_desc' => 'سيؤدي هذا الخيار إلى تغيير اللغة المستخدمة لواجهة المستخدم الخاصة بالتطبيق. لن يؤثر هذا على أي محتوى قد أنشائه المستخدم.',
'users_social_accounts' => 'الحسابات الاجتماعية',
'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
'users_social_connect' => 'ربط الحساب',
'users_social_disconnect' => 'فصل الحساب',
'users_social_connected' => 'تم ربط حساب :socialAccount بملفك بنجاح.',
'users_social_disconnected' => 'تم فصل حساب :socialAccount من ملفك بنجاح.',
- 'users_api_tokens' => 'API Tokens',
- 'users_api_tokens_none' => 'No API tokens have been created for this user',
- 'users_api_tokens_create' => 'Create Token',
- 'users_api_tokens_expires' => 'Expires',
- 'users_api_tokens_docs' => 'API Documentation',
+ 'users_api_tokens' => 'رموز الـ API',
+ 'users_api_tokens_none' => 'لم يتم إنشاء رموز API لهذا المستخدم',
+ 'users_api_tokens_create' => 'قم بإنشاء رمز مميز',
+ 'users_api_tokens_expires' => 'انتهاء مدة الصلاحية',
+ 'users_api_tokens_docs' => 'وثائق API',
// API Tokens
- 'user_api_token_create' => 'Create API Token',
- 'user_api_token_name' => 'Name',
- 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
- 'user_api_token_expiry' => 'Expiry Date',
- 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
- 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
- 'user_api_token_create_success' => 'API token successfully created',
- 'user_api_token_update_success' => 'API token successfully updated',
- 'user_api_token' => 'API Token',
- 'user_api_token_id' => 'Token ID',
- 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
- 'user_api_token_secret' => 'Token Secret',
- 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
- 'user_api_token_created' => 'Token created :timeAgo',
- 'user_api_token_updated' => 'Token updated :timeAgo',
- 'user_api_token_delete' => 'Delete Token',
- 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
- 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
- 'user_api_token_delete_success' => 'API token successfully deleted',
+ 'user_api_token_create' => 'قم بإنشاء رمز API',
+ 'user_api_token_name' => 'الاسم',
+ 'user_api_token_name_desc' => 'اعطي الرمز الخاص بك اسمًا يمكن قراءته للتذكير مستقبلًا بالغرض المقصود منه.',
+ 'user_api_token_expiry' => 'تاريخ انتهاء الصلاحية',
+ 'user_api_token_expiry_desc' => 'حدد التاريخ الذي تنتهي فيه صلاحية هذا الرمز. بعد هذا التاريخ ، لن تعمل الطلبات المقدمة باستخدام هذا الرمز. سيؤدي ترك هذا الحقل فارغًا إلى تعيين انتهاء صلاحية لمدة 100 عام في المستقبل.',
+ 'user_api_token_create_secret_message' => 'عقب إنشاء هذا الرمز مباشرة، سيتم إنشاء "مُعرّف الرمز" و "رمز سري" وعرضهما. وسيتم عرض الرمز السري لمرة واحدة فقط ، لذا تأكد من نسخ قيمة هذا الرمز إلى مكان آمن ومضمون قبل المتابعة.',
+ 'user_api_token_create_success' => 'تم إنشاء رمز الـ API بنجاح',
+ 'user_api_token_update_success' => 'تم تحديث رمز الـ API بنجاح',
+ 'user_api_token' => 'رمز الـ API',
+ 'user_api_token_id' => 'مُعرّف الرمز',
+ 'user_api_token_id_desc' => 'هذا مُعرّف تم إنشاؤه بواسطة النظام غير قابل للتحرير لهذا الرمز والذي يجب توفيره في طلبات API.',
+ 'user_api_token_secret' => 'الرمز السري',
+ 'user_api_token_secret_desc' => 'هذا الرمز السري تم إنشاؤه بواسطة النظام والذي يجب توفيره ضمن طلبات API. سيتم عرضه لمرة واحدة فقط ، لذا انسخ قيمة هذا الرمز إلى مكان آمن ومضمون.',
+ 'user_api_token_created' => 'تم إنشاء رمز :الوقت الزمني',
+ 'user_api_token_updated' => 'تم تحديث الرمز :الوقت الزمني',
+ 'user_api_token_delete' => 'حذف الرمز',
+ 'user_api_token_delete_warning' => 'سيؤدي هذا إلى حذف رمز API المُشار إليه بالكامل باسم \'اسم الرمز\' من النظام.',
+ 'user_api_token_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف رمز API؟',
+ 'user_api_token_delete_success' => 'تم حذف رمز الـ API بنجاح',
//! If editing translations files directly please ignore this in all
//! languages apart from en. Content will be auto-copied from en.
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'alpha' => 'يجب أن يقتصر :attribute على الحروف فقط.',
'alpha_dash' => 'يجب أن يقتصر :attribute على حروف أو أرقام أو شرطات فقط.',
'alpha_num' => 'يجب أن يقتصر :attribute على الحروف والأرقام فقط.',
- 'array' => 'The :attribute must be an array.',
+ 'array' => 'يجب أن تكون السمة مصفوفة.',
'before' => 'يجب أن يكون التاريخ :attribute قبل :date.',
'between' => [
'numeric' => 'يجب أن يكون :attribute بين :min و :max.',
'string' => 'يجب أن يكون :attribute بين :min و :max حرف / حروف.',
'array' => 'يجب أن يكون :attribute بين :min و :max عنصر / عناصر.',
],
- 'boolean' => 'The :attribute field must be true or false.',
+ 'boolean' => 'يجب أن يحتمل حقل السمة الصحة أو الخطأ.',
'confirmed' => ':attribute غير مطابق.',
'date' => ':attribute ليس تاريخ صالح.',
'date_format' => ':attribute لا يطابق الصيغة :format.',
'digits' => 'يجب أن يكون :attribute بعدد :digits خانات.',
'digits_between' => 'يجب أن يكون :attribute بعدد خانات بين :min و :max.',
'email' => 'يجب أن يكون :attribute عنوان بريد إلكتروني صالح.',
- 'ends_with' => 'The :attribute must end with one of the following: :values',
+ 'ends_with' => 'يجب أن تنتهي السمة بأحد القيم التالية',
'filled' => 'حقل :attribute مطلوب.',
'gt' => [
- 'numeric' => 'The :attribute must be greater than :value.',
- 'file' => 'The :attribute must be greater than :value kilobytes.',
- 'string' => 'The :attribute must be greater than :value characters.',
- 'array' => 'The :attribute must have more than :value items.',
+ 'numeric' => 'يجب أن تكون السمة أكبر من: القيمة.',
+ 'file' => 'يجب أن تكون السمة أكبر من: القيمة كيلوبايت.',
+ 'string' => 'يجب أن تكون السمة أكبر من: أحرف القيمة.',
+ 'array' => 'يجب أن تحتوي السمة على أكثر من: عناصر القيمة.',
],
'gte' => [
- 'numeric' => 'The :attribute must be greater than or equal :value.',
- 'file' => 'The :attribute must be greater than or equal :value kilobytes.',
- 'string' => 'The :attribute must be greater than or equal :value characters.',
- 'array' => 'The :attribute must have :value items or more.',
+ 'numeric' => 'يجب أن تكون السمة أكبر من أو تساوي: القيمة.',
+ 'file' => 'يجب أن تكون السمة أكبر من أو تساوي: القيمة كيلوبايت.',
+ 'string' => 'يجب أن تكون السمة أكبر من أو تساوي: أحرف القيمة.',
+ 'array' => 'يجب أن تحتوي السمة على: عناصر القيمة أو أكثر.',
],
'exists' => ':attribute المحدد غير صالح.',
'image' => 'يجب أن يكون :attribute صورة.',
- 'image_extension' => 'The :attribute must have a valid & supported image extension.',
+ 'image_extension' => 'يجب أن تحتوي السمة على امتداد صورة صالح ومدعوم.',
'in' => ':attribute المحدد غير صالح.',
'integer' => 'يجب أن يكون :attribute عدد صحيح.',
'ip' => 'يجب أن يكون :attribute عنوان IP صالح.',
- 'ipv4' => 'The :attribute must be a valid IPv4 address.',
- 'ipv6' => 'The :attribute must be a valid IPv6 address.',
- 'json' => 'The :attribute must be a valid JSON string.',
+ 'ipv4' => 'يجب أن تكون السمة: عنوان IPv4 صالحًا.',
+ 'ipv6' => 'يجب أن تكون السمة: عنوان IPv6 صالحًا.',
+ 'json' => 'يجب أن تكون السمة: سلسلة من نوع جسون JSON صالح.',
'lt' => [
- 'numeric' => 'The :attribute must be less than :value.',
- 'file' => 'The :attribute must be less than :value kilobytes.',
- 'string' => 'The :attribute must be less than :value characters.',
- 'array' => 'The :attribute must have less than :value items.',
+ 'numeric' => 'يجب أن تكون السمة أقل من: القيمة.',
+ 'file' => 'يجب أن تكون السمة أقل من: القيمة كيلوبايت.',
+ 'string' => 'يجب أن تكون السمة أقل من: أحرف القيمة.',
+ 'array' => 'يجب أن تحتوي السمة على أقل من: عناصر القيمة.',
],
'lte' => [
- 'numeric' => 'The :attribute must be less than or equal :value.',
- 'file' => 'The :attribute must be less than or equal :value kilobytes.',
- 'string' => 'The :attribute must be less than or equal :value characters.',
- 'array' => 'The :attribute must not have more than :value items.',
+ 'numeric' => 'يجب أن تكون السمة أقل من أو تساوي: القيمة.',
+ 'file' => 'يجب أن تكون السمة أقل من أو تساوي: القيمة كيلوبايت.',
+ 'string' => 'يجب أن تكون السمة أقل من أو تساوي: أحرف القيمة.',
+ 'array' => 'يجب ألا تحتوي السمة على أكثر من: عناصر القيمة.',
],
'max' => [
'numeric' => 'يجب ألا يكون :attribute أكبر من :max.',
'string' => 'يجب أن يكون :attribute على الأقل :min حرف / حروف.',
'array' => 'يجب أن يحتوي :attribute على :min عنصر / عناصر كحد أدنى.',
],
- 'no_double_extension' => 'The :attribute must only have a single file extension.',
+ 'no_double_extension' => 'يجب أن يكون للسمة: امتداد ملف واحد فقط.',
'not_in' => ':attribute المحدد غير صالح.',
- 'not_regex' => 'The :attribute format is invalid.',
+ 'not_regex' => 'صيغة السمة: غير صالحة.',
'numeric' => 'يجب أن يكون :attribute رقم.',
'regex' => 'صيغة :attribute غير صالحة.',
'required' => 'حقل :attribute مطلوب.',
'required_without' => 'حقل :attribute مطلوب عندما تكون :values غير موجودة.',
'required_without_all' => 'حقل :attribute مطلوب عندما لا يكون أي من :values موجودة.',
'same' => 'يجب تطابق :attribute مع :other.',
+ 'safe_url' => 'قد لايكون الرابط المتوفر آمنا.',
'size' => [
'numeric' => 'يجب أن يكون :attribute بحجم :size.',
'file' => 'يجب أن يكون :attribute بحجم :size كيلو بايت.',
'string' => 'يجب أن يكون :attribute بعدد :size حرف / حروف.',
'array' => 'يجب أن يحتوي :attribute على :size عنصر / عناصر.',
],
- 'string' => 'The :attribute must be a string.',
+ 'string' => 'يجب أن تكون السمة: سلسلة.',
'timezone' => 'يجب أن تكون :attribute منطقة صالحة.',
'unique' => 'تم حجز :attribute من قبل.',
'url' => 'صيغة :attribute غير صالحة.',
- 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.',
+ 'uploaded' => 'تعذر تحميل الملف. قد لا يقبل الخادم ملفات بهذا الحجم.',
// Custom validation lines
'custom' => [
// Other
'commented_on' => 'коментирано на',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Създадено преди :timeLength от :user',
'meta_updated' => 'Актуализирано :timeLength',
'meta_updated_name' => 'Актуализирано преди :timeLength от :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Избор на обект',
'images' => 'Изображения',
'my_recent_drafts' => 'Моите скорошни драфтове',
'permissions_intro' => 'Веднъж добавени, тези права ще вземат приоритет над всички други установени права.',
'permissions_enable' => 'Разреши уникални права',
'permissions_save' => 'Запази права',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Резултати от търсенето',
'chapters_create' => 'Създай нова глава',
'chapters_delete' => 'Изтрий глава',
'chapters_delete_named' => 'Изтрий глава :chapterName',
- 'chapters_delete_explain' => 'Ще бъде изтрита глава с име \':chapterName\'. Всички страници в нея ще бъдат премахнати и добавени в основната книга.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази глава?',
'chapters_edit' => 'Редактирай глава',
'chapters_edit_named' => 'Актуализирай глава :chapterName',
'pages_revisions' => 'Ревизии на страницата',
'pages_revisions_named' => 'Ревизии на страницата :pageName',
'pages_revision_named' => 'Ревизия на страницата :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Създадено от',
'pages_revisions_date' => 'Дата на ревизията',
'pages_revisions_number' => '№',
'maint' => 'Maintenance',
'maint_image_cleanup' => 'Cleanup Images',
'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Run Cleanup',
'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
'maint_send_test_email_mail_subject' => 'Test Email',
'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'User Profile',
'users_add_new' => 'Add New User',
'users_search' => 'Search Users',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'User Details',
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
'users_delete_named' => 'Delete user :userName',
'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
'users_delete_confirm' => 'Are you sure you want to delete this user?',
- 'users_delete_success' => 'Users successfully removed',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Edit User',
'users_edit_profile' => 'Edit Profile',
'users_edit_success' => 'User successfully updated',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
// Other
'commented_on' => 'okomentoval/a',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Vytvořeno :timeLength uživatelem :user',
'meta_updated' => 'Aktualizováno :timeLength',
'meta_updated_name' => 'Aktualizováno :timeLength uživatelem :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Výběr entity',
'images' => 'Obrázky',
'my_recent_drafts' => 'Mé nedávné koncepty',
'permissions_intro' => 'Pokud je povoleno, tato oprávnění budou mít přednost před všemi nastavenými oprávněními role.',
'permissions_enable' => 'Povolit vlastní oprávnění',
'permissions_save' => 'Uložit oprávnění',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Výsledky hledání',
'chapters_create' => 'Vytvořit novou kapitolu',
'chapters_delete' => 'Smazat kapitolu',
'chapters_delete_named' => 'Smazat kapitolu :chapterName',
- 'chapters_delete_explain' => 'Kapitola \':chapterName\' bude smazána. Všechny stránky v ní obsažené budou přesunuty přímo pod samotnou knihu.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Opravdu chcete tuto kapitolu smazat?',
'chapters_edit' => 'Upravit kapitolu',
'chapters_edit_named' => 'Upravit kapitolu :chapterName',
'pages_revisions' => 'Revize stránky',
'pages_revisions_named' => 'Revize stránky pro :pageName',
'pages_revision_named' => 'Revize stránky pro :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Vytvořeno uživatelem',
'pages_revisions_date' => 'Datum revize',
'pages_revisions_number' => 'Č.',
'maint' => 'Údržba',
'maint_image_cleanup' => 'Pročistění obrázků',
'maint_image_cleanup_desc' => "Prohledá stránky a jejich revize, aby zjistil, které obrázky a kresby jsou momentálně používány a které jsou zbytečné. Zajistěte plnou zálohu databáze a obrázků než se do toho pustíte.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorovat obrázky v revizích',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Spustit pročištění',
'maint_image_cleanup_warning' => 'Nalezeno :count potenciálně nepoužitých obrázků. Jste si jistí, že je chcete smazat?',
'maint_image_cleanup_success' => 'Potenciálně nepoužité obrázky byly smazány. Celkem :count.',
'maint_send_test_email_mail_subject' => 'Testovací e-mail',
'maint_send_test_email_mail_greeting' => 'Zdá se, že posílání e-mailů funguje!',
'maint_send_test_email_mail_text' => 'Gratulujeme! Protože jste dostali tento e-mail, zdá se, že nastavení e-mailů je v pořádku.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Protokol auditu',
'audit_deleted_item_name' => 'Jméno: :name',
'audit_table_user' => 'Uživatel',
'audit_table_event' => 'Událost',
- 'audit_table_item' => 'Související položka',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Datum aktivity',
'audit_date_from' => 'Časový rozsah od',
'audit_date_to' => 'Časový rozsah do',
'user_profile' => 'Profil uživatele',
'users_add_new' => 'Přidat nového uživatele',
'users_search' => 'Vyhledávání uživatelů',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Údaje o uživateli',
'users_details_desc' => 'Nastavte zobrazované jméno a e-mailovou adresu pro tohoto uživatele. E-mailová adresa bude použita pro přihlášení do aplikace.',
'users_details_desc_no_email' => 'Nastavte zobrazované jméno pro tohoto uživatele, aby jej ostatní uživatele poznali.',
'users_delete_named' => 'Odstranit uživatele :userName',
'users_delete_warning' => 'Uživatel \':userName\' bude zcela smazán ze systému.',
'users_delete_confirm' => 'Opravdu chcete tohoto uživatele smazat?',
- 'users_delete_success' => 'Uživatel byl úspěšně smazán',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Upravit uživatele',
'users_edit_profile' => 'Upravit profil',
'users_edit_success' => 'Uživatel byl úspěšně aktualizován',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute musí být vyplněno pokud :values není vyplněno.',
'required_without_all' => ':attribute musí být vyplněno pokud není žádné z :values zvoleno.',
'same' => ':attribute a :other se musí shodovat.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute musí být přesně :size.',
'file' => ':attribute musí mít přesně :size Kilobytů.',
// Other
'commented_on' => 'kommenterede til',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Oprettet :timeLength af :user',
'meta_updated' => 'Opdateret :timeLength',
'meta_updated_name' => 'Opdateret :timeLength af :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Vælg emne',
'images' => 'Billeder',
'my_recent_drafts' => 'Mine seneste kladder',
'permissions_intro' => 'Når de er aktiveret, vil disse tilladelser have prioritet over alle indstillede rolletilladelser.',
'permissions_enable' => 'Aktivér tilpassede tilladelser',
'permissions_save' => 'Gem tilladelser',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Søgeresultater',
'chapters_create' => 'Opret nyt kapitel',
'chapters_delete' => 'Slet kapitel',
'chapters_delete_named' => 'Slet kapitel :chapterName',
- 'chapters_delete_explain' => 'Dette vil slette kapitlet med navnet \':chapterName\'. Alle sider fjernes og tilføjes direkte til den tilhørende bog.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Er du sikker på du vil slette dette kapitel?',
'chapters_edit' => 'Rediger kapitel',
'chapters_edit_named' => 'Rediger kapitel :chapterName',
'pages_revisions' => 'Sidserevisioner',
'pages_revisions_named' => 'Siderevisioner for :pageName',
'pages_revision_named' => 'Siderevision for :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Oprettet af',
'pages_revisions_date' => 'Revisionsdato',
'pages_revisions_number' => '#',
'maint' => 'Vedligeholdelse',
'maint_image_cleanup' => 'Ryd op i billeder',
'maint_image_cleanup_desc' => "Scanner side & revisionsindhold for at kontrollere, hvilke billeder og tegninger, der i øjeblikket er i brug, og hvilke billeder, der er overflødige. Sørg for, at du opretter en komplet database og billedbackup, før du kører dette.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorer billeder i revisioner',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Kør Oprydning',
'maint_image_cleanup_warning' => 'der blev fundet :count potentielt ubrugte billeder. Er du sikker på, at du vil slette disse billeder?',
'maint_image_cleanup_success' => ':count: potentielt ubrugte billeder fundet og slettet!',
'maint_send_test_email_mail_subject' => 'Test E-Mail',
'maint_send_test_email_mail_greeting' => 'E-Mail levering ser ud til at virke!',
'maint_send_test_email_mail_text' => 'Tillykke! Da du har modtaget denne mailnotifikation, ser det ud som om, at dine mailindstillinger er opsat korrekt.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Brugerprofil',
'users_add_new' => 'Tilføj ny bruger',
'users_search' => 'Søg efter brugere',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Brugeroplysninger',
'users_details_desc' => 'Angiv et visningsnavn og en E-Mail-adresse for denne bruger. E-Mail-adressen bruges til at logge ind på applikationen.',
'users_details_desc_no_email' => 'Sætter et visningsnavn for denne bruger, så andre kan genkende dem.',
'users_delete_named' => 'Slet bruger :userName',
'users_delete_warning' => 'Dette vil helt slette denne bruger med navnet \':userName\' fra systemet.',
'users_delete_confirm' => 'Er du sikker på, at du vil slette denne bruger?',
- 'users_delete_success' => 'Brugere blev fjernet',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Rediger bruger',
'users_edit_profile' => 'Rediger profil',
'users_edit_success' => 'Bruger suscesfuldt opdateret',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute skal udfyldes når :values ikke er udfyldt.',
'required_without_all' => ':attribute skal udfyldes når ingen af :values er udfyldt.',
'same' => ':attribute og :other skal være ens.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute skal være :size.',
'file' => ':attribute skal være :size kilobytes.',
return [
// Pages
- 'page_create' => 'erstellte Seite',
- 'page_create_notification' => 'Die Seite wurde erfolgreich erstellt.',
- 'page_update' => 'aktualisierte Seite',
- 'page_update_notification' => 'Die Seite wurde erfolgreich aktualisiert.',
- 'page_delete' => 'gelöschte Seite',
- 'page_delete_notification' => 'Die Seite wurde erfolgreich gelöscht.',
- 'page_restore' => 'wiederhergestellte Seite',
- 'page_restore_notification' => 'Die Seite wurde erfolgreich wiederhergestellt.',
- 'page_move' => 'Seite verschoben',
+ 'page_create' => 'hat die Seite erstellt',
+ 'page_create_notification' => 'Die Seite wurde erfolgreich erstellt',
+ 'page_update' => 'hat die Seite aktualisiert',
+ 'page_update_notification' => 'Die Seite wurde erfolgreich aktualisiert',
+ 'page_delete' => 'hat die Seite gelöscht',
+ 'page_delete_notification' => 'Die Seite wurde erfolgreich gelöscht',
+ 'page_restore' => 'hat die Seite wiederhergestellt',
+ 'page_restore_notification' => 'Die Seite wurde erfolgreich wiederhergestellt',
+ 'page_move' => 'hat die Seite verschoben',
// Chapters
- 'chapter_create' => 'erstellte Kapitel',
- 'chapter_create_notification' => 'Das Kapitel wurde erfolgreich erstellt.',
- 'chapter_update' => 'aktualisierte Kapitel',
- 'chapter_update_notification' => 'Das Kapitel wurde erfolgreich aktualisiert.',
- 'chapter_delete' => 'löschte Kapitel',
- 'chapter_delete_notification' => 'Das Kapitel wurde erfolgreich gelöscht.',
- 'chapter_move' => 'verschob Kapitel',
+ 'chapter_create' => 'hat das Kapitel erstellt',
+ 'chapter_create_notification' => 'Das Kapitel wurde erfolgreich erstellt',
+ 'chapter_update' => 'hat das Kapitel geändert',
+ 'chapter_update_notification' => 'Das Kapitel wurde erfolgreich aktualisiert',
+ 'chapter_delete' => 'hat das Kapitel gelöscht',
+ 'chapter_delete_notification' => 'Das Kapitel wurde erfolgreich gelöscht',
+ 'chapter_move' => 'hat das Kapitel verschoben',
// Books
- 'book_create' => 'erstellte Buch',
- 'book_create_notification' => 'Das Buch wurde erfolgreich erstellt.',
- 'book_update' => 'aktualisierte Buch',
- 'book_update_notification' => 'Das Buch wurde erfolgreich aktualisiert.',
- 'book_delete' => 'löschte Buch',
- 'book_delete_notification' => 'Das Buch wurde erfolgreich gelöscht.',
- 'book_sort' => 'sortierte Buch',
- 'book_sort_notification' => 'Das Buch wurde erfolgreich umsortiert.',
+ 'book_create' => 'hat das Buch erstellt',
+ 'book_create_notification' => 'Das Buch wurde erfolgreich erstellt',
+ 'book_update' => 'hat das Buch aktualisiert',
+ 'book_update_notification' => 'Das Buch wurde erfolgreich aktualisiert',
+ 'book_delete' => 'hat das Buch gelöscht',
+ 'book_delete_notification' => 'Das Buch wurde erfolgreich gelöscht',
+ 'book_sort' => 'hat die Buch-Sortierung geändert',
+ 'book_sort_notification' => 'Das Buch wurde erfolgreich umsortiert',
// Bookshelves
- 'bookshelf_create' => 'erstellt Bücherregal',
+ 'bookshelf_create' => 'hat das Bücherregal erstellt',
'bookshelf_create_notification' => 'Das Bücherregal wurde erfolgreich erstellt',
- 'bookshelf_update' => 'aktualisiert Bücherregal',
- 'bookshelf_update_notification' => 'Das Bücherregal wurde erfolgreich aktualisiert',
- 'bookshelf_delete' => 'löscht Bücherregal',
+ 'bookshelf_update' => 'hat das Bücherregal geändert',
+ 'bookshelf_update_notification' => 'Das Bücherregal wurde erfolgreich geändert',
+ 'bookshelf_delete' => 'hat das Bücherregal gelöscht',
'bookshelf_delete_notification' => 'Das Bücherregal wurde erfolgreich gelöscht',
// Other
- 'commented_on' => 'kommentiert',
+ 'commented_on' => 'hat einen Kommentar hinzugefügt',
+ 'permissions_update' => 'hat die Berechtigungen aktualisiert',
];
'meta_created_name' => 'Erstellt: :timeLength von :user',
'meta_updated' => 'Zuletzt aktualisiert: :timeLength',
'meta_updated_name' => 'Zuletzt aktualisiert: :timeLength von :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Eintrag auswählen',
'images' => 'Bilder',
'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, überschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
'permissions_save' => 'Berechtigungen speichern',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Suchergebnisse',
'chapters_create' => 'Neues Kapitel anlegen',
'chapters_delete' => 'Kapitel entfernen',
'chapters_delete_named' => 'Kapitel ":chapterName" entfernen',
- 'chapters_delete_explain' => 'Das Kapitel ":chapterName" wird gelöscht und alle zugehörigen Seiten dem übergeordneten Buch zugeordnet.',
+ 'chapters_delete_explain' => 'Dies löscht das Kapitel mit dem Namen \':chapterName\'. Alle Seiten, die innerhalb dieses Kapitels existieren, werden ebenfalls gelöscht.',
'chapters_delete_confirm' => 'Sind Sie sicher, dass Sie dieses Kapitel löschen möchten?',
'chapters_edit' => 'Kapitel bearbeiten',
'chapters_edit_named' => 'Kapitel ":chapterName" bearbeiten',
'pages_revisions' => 'Seitenversionen',
'pages_revisions_named' => 'Seitenversionen von ":pageName"',
'pages_revision_named' => 'Seitenversion von ":pageName"',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Erstellt von',
'pages_revisions_date' => 'Versionsdatum',
'pages_revisions_number' => '#',
'maint' => 'Wartung',
'maint_image_cleanup' => 'Bilder bereinigen',
'maint_image_cleanup_desc' => "Überprüft Seiten- und Versionsinhalte auf ungenutzte und mehrfach vorhandene Bilder. Erstellen Sie vor dem Start ein Backup Ihrer Datenbank und Bilder.",
- 'maint_image_cleanup_ignore_revisions' => 'Bilder in Versionen ignorieren',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Reinigung starten',
'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchten Sie diese Bilder löschen?',
'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.',
'maint_send_test_email_mail_subject' => 'Test E-Mail',
'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
'maint_send_test_email_mail_text' => 'Glückwunsch! Da Sie diese E-Mail Benachrichtigung erhalten haben, scheinen Ihre E-Mail-Einstellungen korrekt konfiguriert zu sein.',
+ 'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Gegenstände im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.',
+ 'maint_recycle_bin_open' => 'Papierkorb öffnen',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Papierkorb',
+ 'recycle_bin_desc' => 'Hier können Sie gelöschte Elemente wiederherstellen oder sie dauerhaft aus dem System entfernen. Diese Liste ist nicht gefiltert, im Gegensatz zu ähnlichen Aktivitätslisten im System, wo Berechtigungsfilter angewendet werden.',
+ 'recycle_bin_deleted_item' => 'Gelöschtes Element',
+ 'recycle_bin_deleted_by' => 'Gelöscht von',
+ 'recycle_bin_deleted_at' => 'Löschzeitpunkt',
+ 'recycle_bin_permanently_delete' => 'Dauerhaft löschen',
+ 'recycle_bin_restore' => 'Wiederherstellen',
+ 'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer',
+ 'recycle_bin_empty' => 'Papierkorb leeren',
+ 'recycle_bin_empty_confirm' => 'Dies wird alle Gegenstände im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Sind Sie sicher, dass Sie den Papierkorb leeren möchten?',
+ 'recycle_bin_destroy_confirm' => 'Diese Aktion wird dieses Element zusammen mit allen unten aufgeführten Unterelementen dauerhaft aus dem System löschen und Sie werden nicht in der Lage sein, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten?',
+ 'recycle_bin_destroy_list' => 'Zu löschende Elemente',
+ 'recycle_bin_restore_list' => 'Zu wiederherzustellende Elemente',
+ 'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird das gelöschte Element einschließlich aller untergeordneten Elemente an seinen ursprünglichen Ort wiederherstellen. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch das übergeordnete Element wiederhergestellt werden.',
+ 'recycle_bin_restore_deleted_parent' => 'Das übergeordnete Elements wurde ebenfalls gelöscht. Dieses Element wird weiterhin als gelöscht zählen, bis auch das übergeordnete Element wiederhergestellt wurde.',
+ 'recycle_bin_destroy_notification' => ':count Elemente wurden aus dem Papierkorb gelöscht.',
+ 'recycle_bin_restore_notification' => ':count Elemente wurden aus dem Papierkorb wiederhergestellt.',
// Audit Log
'audit' => 'Audit-Protokoll',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'Benutzer',
'audit_table_event' => 'Ereignis',
- 'audit_table_item' => 'Verwendetes Objekt',
+ 'audit_table_related' => 'Verknüpftes Element oder Detail',
'audit_table_date' => 'Aktivitätsdatum',
'audit_date_from' => 'Zeitraum von',
'audit_date_to' => 'Zeitraum bis',
'user_profile' => 'Benutzerprofil',
'users_add_new' => 'Benutzer hinzufügen',
'users_search' => 'Benutzer suchen',
+ 'users_latest_activity' => 'Neueste Aktivitäten',
'users_details' => 'Benutzerdetails',
'users_details_desc' => 'Legen Sie für diesen Benutzer einen Anzeigenamen und eine E-Mail-Adresse fest. Die E-Mail-Adresse wird bei der Anmeldung verwendet.',
'users_details_desc_no_email' => 'Legen Sie für diesen Benutzer einen Anzeigenamen fest, damit andere ihn erkennen können.',
'users_delete_named' => 'Benutzer ":userName" löschen',
'users_delete_warning' => 'Der Benutzer ":userName" wird aus dem System gelöscht.',
'users_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?',
- 'users_delete_success' => 'Benutzer erfolgreich gelöscht.',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Benutzer bearbeiten',
'users_edit_profile' => 'Profil bearbeiten',
'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute ist erforderlich, wenn :values nicht vorhanden ist.',
'required_without_all' => ':attribute ist erforderlich, wenn :values nicht vorhanden sind.',
'same' => ':attribute und :other müssen übereinstimmen.',
+ 'safe_url' => 'Der angegebene Link ist möglicherweise nicht sicher.',
'size' => [
'numeric' => ':attribute muss :size sein.',
'file' => ':attribute muss :size Kilobytes groß sein.',
// Other
'commented_on' => 'kommentiert',
+ 'permissions_update' => 'hat die Berechtigungen aktualisiert',
];
'meta_created_name' => 'Erstellt: :timeLength von :user',
'meta_updated' => 'Zuletzt aktualisiert: :timeLength',
'meta_updated_name' => 'Zuletzt aktualisiert: :timeLength von :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Eintrag auswählen',
'images' => 'Bilder',
'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
'permissions_intro' => 'Wenn individuelle Berechtigungen aktiviert werden, überschreiben diese Einstellungen durch Rollen zugewiesene Berechtigungen.',
'permissions_enable' => 'Individuelle Berechtigungen aktivieren',
'permissions_save' => 'Berechtigungen speichern',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Suchergebnisse',
'chapters_create' => 'Neues Kapitel anlegen',
'chapters_delete' => 'Kapitel entfernen',
'chapters_delete_named' => 'Kapitel ":chapterName" entfernen',
- 'chapters_delete_explain' => 'Das Kapitel ":chapterName" wird gelöscht und alle zugehörigen Seiten dem übergeordneten Buch zugeordnet.',
+ 'chapters_delete_explain' => 'Dies löscht das Kapitel mit dem Namen \':chapterName\'. Alle Seiten, die innerhalb dieses Kapitels existieren, werden ebenfalls gelöscht.',
'chapters_delete_confirm' => 'Bist Du sicher, dass Du dieses Kapitel löschen möchtest?',
'chapters_edit' => 'Kapitel bearbeiten',
'chapters_edit_named' => 'Kapitel ":chapterName" bearbeiten',
'pages_revisions' => 'Seitenversionen',
'pages_revisions_named' => 'Seitenversionen von ":pageName"',
'pages_revision_named' => 'Seitenversion von ":pageName"',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Erstellt von',
'pages_revisions_date' => 'Versionsdatum',
'pages_revisions_number' => '#',
'maint' => 'Wartung',
'maint_image_cleanup' => 'Bilder bereinigen',
'maint_image_cleanup_desc' => "Überprüft Seiten- und Versionsinhalte auf ungenutzte und mehrfach vorhandene Bilder. Erstelle vor dem Start ein Backup Deiner Datenbank und Bilder.",
- 'maint_image_cleanup_ignore_revisions' => 'Bilder in Versionen ignorieren',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Reinigung starten',
'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchtest Du diese Bilder löschen?',
'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.',
'maint_send_test_email_mail_subject' => 'Test E-Mail',
'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
'maint_send_test_email_mail_text' => 'Glückwunsch! Da du diese E-Mail Benachrichtigung erhalten hast, scheinen deine E-Mail-Einstellungen korrekt konfiguriert zu sein.',
+ 'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Gegenstände im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.',
+ 'maint_recycle_bin_open' => 'Papierkorb öffnen',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Papierkorb',
+ 'recycle_bin_desc' => 'Hier können Sie gelöschte Elemente wiederherstellen oder sie dauerhaft aus dem System entfernen. Diese Liste ist nicht gefiltert, im Gegensatz zu ähnlichen Aktivitätslisten im System, wo Berechtigungsfilter angewendet werden.',
+ 'recycle_bin_deleted_item' => 'Gelöschtes Element',
+ 'recycle_bin_deleted_by' => 'Gelöscht von',
+ 'recycle_bin_deleted_at' => 'Löschzeitpunkt',
+ 'recycle_bin_permanently_delete' => 'Dauerhaft löschen',
+ 'recycle_bin_restore' => 'Wiederherstellen',
+ 'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer',
+ 'recycle_bin_empty' => 'Papierkorb leeren',
+ 'recycle_bin_empty_confirm' => 'Dies wird alle Gegenstände im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Sind Sie sicher, dass Sie den Papierkorb leeren möchten?',
+ 'recycle_bin_destroy_confirm' => 'Diese Aktion wird dieses Element zusammen mit allen unten aufgeführten Unterelementen dauerhaft aus dem System löschen und Sie werden nicht in der Lage sein, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten?',
+ 'recycle_bin_destroy_list' => 'Zu löschende Elemente',
+ 'recycle_bin_restore_list' => 'Zu wiederherzustellende Elemente',
+ 'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird das gelöschte Element einschließlich aller untergeordneten Elemente an seinen ursprünglichen Ort wiederherstellen. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch das übergeordnete Element wiederhergestellt werden.',
+ 'recycle_bin_restore_deleted_parent' => 'Das übergeordnete Elements wurde ebenfalls gelöscht. Dieses Element wird weiterhin als gelöscht zählen, bis auch das übergeordnete Element wiederhergestellt wurde.',
+ 'recycle_bin_destroy_notification' => ':count Elemente wurden aus dem Papierkorb gelöscht.',
+ 'recycle_bin_restore_notification' => ':count Elemente wurden aus dem Papierkorb wiederhergestellt.',
// Audit Log
'audit' => 'Audit-Protokoll',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'Benutzer',
'audit_table_event' => 'Ereignis',
- 'audit_table_item' => 'Verwendetes Objekt',
+ 'audit_table_related' => 'Verknüpftes Element oder Detail',
'audit_table_date' => 'Aktivitätsdatum',
'audit_date_from' => 'Zeitraum von',
'audit_date_to' => 'Zeitraum bis',
'user_profile' => 'Benutzerprofil',
'users_add_new' => 'Benutzer hinzufügen',
'users_search' => 'Benutzer suchen',
+ 'users_latest_activity' => 'Neueste Aktivitäten',
'users_details' => 'Benutzerdetails',
'users_details_desc' => 'Legen Sie für diesen Benutzer einen Anzeigenamen und eine E-Mail-Adresse fest. Die E-Mail-Adresse wird bei der Anmeldung verwendet.',
'users_details_desc_no_email' => 'Legen Sie für diesen Benutzer einen Anzeigenamen fest, damit andere ihn erkennen können.',
'users_delete_named' => 'Benutzer ":userName" löschen',
'users_delete_warning' => 'Der Benutzer ":userName" wird aus dem System gelöscht.',
'users_delete_confirm' => 'Bist Du sicher, dass Du diesen Benutzer löschen möchtest?',
- 'users_delete_success' => 'Benutzer erfolgreich gelöscht.',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Benutzer bearbeiten',
'users_edit_profile' => 'Profil bearbeiten',
'users_edit_success' => 'Benutzer erfolgreich aktualisisert',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute ist erforderlich, wenn :values nicht vorhanden ist.',
'required_without_all' => ':attribute ist erforderlich, wenn :values nicht vorhanden sind.',
'same' => ':attribute und :other müssen übereinstimmen.',
+ 'safe_url' => 'Der angegebene Link ist möglicherweise nicht sicher.',
'size' => [
'numeric' => ':attribute muss :size sein.',
'file' => ':attribute muss :size Kilobytes groß sein.',
// Other
'commented_on' => 'commented on',
+ 'permissions_update' => 'updated permissions',
];
// Email Content
'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
'email_rights' => 'All rights reserved',
+
+ // Footer Link Options
+ // Not directly used but available for convenience to users.
+ 'privacy_policy' => 'Privacy Policy',
+ 'terms_of_service' => 'Terms of Service',
];
'meta_created_name' => 'Created :timeLength by :user',
'meta_updated' => 'Updated :timeLength',
'meta_updated_name' => 'Updated :timeLength by :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Entity Select',
'images' => 'Images',
'my_recent_drafts' => 'My Recent Drafts',
'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.',
'permissions_enable' => 'Enable Custom Permissions',
'permissions_save' => 'Save Permissions',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Search Results',
'chapters_create' => 'Create New Chapter',
'chapters_delete' => 'Delete Chapter',
'chapters_delete_named' => 'Delete Chapter :chapterName',
- 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages will be removed and added directly to the parent book.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?',
'chapters_edit' => 'Edit Chapter',
'chapters_edit_named' => 'Edit Chapter :chapterName',
'pages_revisions' => 'Page Revisions',
'pages_revisions_named' => 'Page Revisions for :pageName',
'pages_revision_named' => 'Page Revision for :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Created By',
'pages_revisions_date' => 'Revision Date',
'pages_revisions_number' => '#',
'app_homepage' => 'Application Homepage',
'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
'app_homepage_select' => 'Select a page',
+ 'app_footer_links' => 'Footer Links',
+ 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
+ 'app_footer_links_label' => 'Link Label',
+ 'app_footer_links_url' => 'Link URL',
+ 'app_footer_links_add' => 'Add Footer Link',
'app_disable_comments' => 'Disable Comments',
'app_disable_comments_toggle' => 'Disable comments',
'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
'maint' => 'Maintenance',
'maint_image_cleanup' => 'Cleanup Images',
'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Run Cleanup',
'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'User Profile',
'users_add_new' => 'Add New User',
'users_search' => 'Search Users',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'User Details',
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
'users_delete_named' => 'Delete user :userName',
'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
'users_delete_confirm' => 'Are you sure you want to delete this user?',
- 'users_delete_success' => 'Users successfully removed',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Edit User',
'users_edit_profile' => 'Edit Profile',
'users_edit_success' => 'User successfully updated',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
// Other
'commented_on' => 'comentada el',
+ 'permissions_update' => 'permisos actualizados',
];
'meta_created_name' => 'Creado :timeLength por :user',
'meta_updated' => 'Actualizado :timeLength',
'meta_updated_name' => 'Actualizado :timeLength por :user',
+ 'meta_owned_name' => 'Propiedad de :user',
'entity_select' => 'Seleccione entidad',
'images' => 'Imágenes',
'my_recent_drafts' => 'Mis borradores recientes',
'permissions_intro' => 'Una vez habilitado, estos permisos tendrán prioridad por encima de cualquier permiso establecido.',
'permissions_enable' => 'Habilitar permisos personalizados',
'permissions_save' => 'Guardar permisos',
+ 'permissions_owner' => 'Propietario',
// Search
'search_results' => 'Resultados de búsqueda',
'chapters_create' => 'Crear nuevo capítulo',
'chapters_delete' => 'Borrar capítulo',
'chapters_delete_named' => 'Borrar capítulo :chapterName',
- 'chapters_delete_explain' => 'Esto borrará el capítulo con el nombre \':chapterName\', todas las páginas serán eliminadas y agregadas directamente al libro padre.',
+ 'chapters_delete_explain' => 'Esto eliminará el capítulo con el nombre \':chapterName\'. También se eliminarán todas las páginas que existen dentro de este capítulo.',
'chapters_delete_confirm' => '¿Está seguro de borrar este capítulo?',
'chapters_edit' => 'Editar capítulo',
'chapters_edit_named' => 'Editar capítulo :chapterName',
'pages_revisions' => 'Revisiones de página',
'pages_revisions_named' => 'Revisiones de página para :pageName',
'pages_revision_named' => 'Revisión de página para :pageName',
+ 'pages_revision_restored_from' => 'Restaurado de #:id; :summary',
'pages_revisions_created_by' => 'Creado por',
'pages_revisions_date' => 'Fecha de revisión',
'pages_revisions_number' => '#',
'maint' => 'Mantenimiento',
'maint_image_cleanup' => 'Limpiar imágenes',
'maint_image_cleanup_desc' => "Analiza las páginas y sus revisiones para comprobar qué imágenes y dibujos están siendo utilizadas y cuales no son necesarias. Asegúrate de crear una copia completa de la base de datos y de las imágenes antes de lanzar esta opción.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorar imágenes en revisiones',
+ 'maint_delete_images_only_in_revisions' => 'Elimina también imágenes que sólo existen en antiguas revisiones de páginas',
'maint_image_cleanup_run' => 'Lanzar limpieza',
'maint_image_cleanup_warning' => 'Se han encontrado :count imágenes posiblemente no utilizadas . ¿Estás seguro de querer borrar estas imágenes?',
'maint_image_cleanup_success' => '¡Se han encontrado y borrado :count imágenes posiblemente no utilizadas!',
'maint_send_test_email_mail_subject' => 'Probar correo electrónico',
'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
+ 'maint_recycle_bin_desc' => 'Los estantes, libros, capítulos y páginas eliminados se envían a la papelera de reciclaje para que puedan ser restauradas o eliminadas permanentemente. Los elementos más antiguos en la papelera de reciclaje pueden ser eliminados automáticamente después de un tiempo dependiendo de la configuración del sistema.',
+ 'maint_recycle_bin_open' => 'Abrir papelera de reciclaje',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Papelera de Reciclaje',
+ 'recycle_bin_desc' => 'Aquí puede restaurar elementos que hayan sido eliminados o elegir eliminarlos permanentemente del sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+ 'recycle_bin_deleted_item' => 'Elemento Eliminado',
+ 'recycle_bin_deleted_by' => 'Eliminado por',
+ 'recycle_bin_deleted_at' => 'Fecha de eliminación',
+ 'recycle_bin_permanently_delete' => 'Eliminar permanentemente',
+ 'recycle_bin_restore' => 'Restaurar',
+ 'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía',
+ 'recycle_bin_empty' => 'Vaciar Papelera de reciclaje',
+ 'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?',
+ 'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?',
+ 'recycle_bin_destroy_list' => 'Elementos a eliminar',
+ 'recycle_bin_restore_list' => 'Elementos a restaurar',
+ 'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
+ 'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
+ 'recycle_bin_destroy_notification' => 'Eliminados :count artículos de la papelera de reciclaje.',
+ 'recycle_bin_restore_notification' => 'Restaurados :count artículos desde la papelera de reciclaje.',
// Audit Log
'audit' => 'Registro de Auditoría',
'audit_deleted_item_name' => 'Nombre: :name',
'audit_table_user' => 'Usuario',
'audit_table_event' => 'Evento',
- 'audit_table_item' => 'Elemento relacionado',
+ 'audit_table_related' => 'Elemento o detalle relacionados',
'audit_table_date' => 'Fecha de la actividad',
'audit_date_from' => 'Rango de fecha desde',
'audit_date_to' => 'Rango de fecha hasta',
'user_profile' => 'Perfil de Usuario',
'users_add_new' => 'Agregar Nuevo Usuario',
'users_search' => 'Buscar usuarios',
+ 'users_latest_activity' => 'Actividad Reciente',
'users_details' => 'Detalles de Usuario',
'users_details_desc' => 'Ajusta un nombre público y email para este usuario. El email será empleado para acceder a la aplicación.',
'users_details_desc_no_email' => 'Ajusta un nombre público para este usuario para que pueda ser reconocido por otros.',
'users_delete_named' => 'Borrar usuario :userName',
'users_delete_warning' => 'Se borrará completamente el usuario con el nombre \':userName\' del sistema.',
'users_delete_confirm' => '¿Está seguro que desea borrar este usuario?',
- 'users_delete_success' => 'Usuarios removidos éxitosamente',
+ 'users_migrate_ownership' => 'Cambiar Propietario',
+ 'users_migrate_ownership_desc' => 'Seleccione un usuario aquí si desea que otro usuario se convierta en el dueño de todos los elementos que actualmente son propiedad de este usuario.',
+ 'users_none_selected' => 'Usuario no seleccionado',
+ 'users_delete_success' => 'El usuario se ha eliminado correctamente',
'users_edit' => 'Editar Usuario',
'users_edit_profile' => 'Editar perfil',
'users_edit_success' => 'Usuario actualizado',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'El :attribute es requerido cuando no se encuentre entre los valores :values.',
'required_without_all' => 'El :attribute es requerido cuando ninguno de los valores :values están presentes.',
'same' => 'El :attribute y :other deben coincidir.',
+ 'safe_url' => 'El enlace proporcionado puede no ser seguro.',
'size' => [
'numeric' => ':attribute debe ser :size.',
'file' => ':attribute debe ser :size kilobytes.',
// Other
'commented_on' => 'comentado',
+ 'permissions_update' => 'permisos actualizados',
];
'meta_created_name' => 'Creado el :timeLength por :user',
'meta_updated' => 'Actualizado el :timeLength',
'meta_updated_name' => 'Actualizado el :timeLength por :user',
+ 'meta_owned_name' => 'Propiedad de :user',
'entity_select' => 'Seleccione entidad',
'images' => 'Imágenes',
'my_recent_drafts' => 'Mis borradores recientes',
'permissions_intro' => 'una vez habilitado, Estos permisos tendrán prioridad por encima de cualquier permiso establecido.',
'permissions_enable' => 'Habilitar permisos custom',
'permissions_save' => 'Guardar permisos',
+ 'permissions_owner' => 'Propietario',
// Search
'search_results' => 'Buscar resultados',
'chapters_create' => 'Crear nuevo capítulo',
'chapters_delete' => 'Borrar capítulo',
'chapters_delete_named' => 'Borrar capítulo :chapterName',
- 'chapters_delete_explain' => 'Esto borrará el capítulo con el nombre \':chapterName\', todas las páginas serán removidas y agregadas directamente al libro padre.',
+ 'chapters_delete_explain' => 'Esto eliminará el capítulo con el nombre \':chapterName\'. También se eliminarán todas las páginas que existen dentro de este capítulo.',
'chapters_delete_confirm' => '¿Está seguro de borrar este capítulo?',
'chapters_edit' => 'Editar capítulo',
'chapters_edit_named' => 'Editar capítulo :chapterName',
'pages_revisions' => 'Revisiones de página',
'pages_revisions_named' => 'Revisiones de página para :pageName',
'pages_revision_named' => 'Revisión de ágina para :pageName',
+ 'pages_revision_restored_from' => 'Restaurado de #:id; :summary',
'pages_revisions_created_by' => 'Creado por',
'pages_revisions_date' => 'Fecha de revisión',
'pages_revisions_number' => '#',
'attachments_link_url' => 'Enlace a archivo',
'attachments_link_url_hint' => 'URL del sitio o archivo',
'attach' => 'Adjuntar',
- 'attachments_insert_link' => 'Añadir enlace al adjunto en la página',
+ 'attachments_insert_link' => 'Agregar el Enlace del adjunto a la Página',
'attachments_edit_file' => 'Editar archivo',
'attachments_edit_file_name' => 'Nombre del archivo',
'attachments_edit_drop_upload' => 'Arrastre los archivos o presione aquí para subir o sobreescribir',
'maint' => 'Mantenimiento',
'maint_image_cleanup' => 'Limpiar imágenes',
'maint_image_cleanup_desc' => "Analizar contenido de páginas y revisiones para detectar cuáles imágenes y dibujos están en uso y cuáles son redundantes. Asegúrese de crear un respaldo completo de imágenes y base de datos antes de ejecutar esta tarea.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorar imágenes en revisión',
+ 'maint_delete_images_only_in_revisions' => 'Elimina también imágenes que sólo existen en antiguas revisiones de páginas',
'maint_image_cleanup_run' => 'Ejecutar limpieza',
'maint_image_cleanup_warning' => 'Se encontraron :count imágenes pontencialmente sin uso. Está seguro de que quiere eliminarlas?',
'maint_image_cleanup_success' => 'Se encontraron y se eliminaron :count imágenes pontencialmente sin uso!',
'maint_send_test_email_mail_subject' => 'Probar correo electrónico',
'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
+ 'maint_recycle_bin_desc' => 'Los estantes, libros, capítulos y páginas eliminados se envían a la papelera de reciclaje para que puedan ser restauradas o eliminadas permanentemente. Los elementos más antiguos en la papelera de reciclaje pueden ser eliminados automáticamente después de un tiempo dependiendo de la configuración del sistema.',
+ 'maint_recycle_bin_open' => 'Abrir papelera de reciclaje',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Papelera de Reciclaje',
+ 'recycle_bin_desc' => 'Aquí puede restaurar elementos que hayan sido eliminados o elegir eliminarlos permanentemente del sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+ 'recycle_bin_deleted_item' => 'Elemento Eliminado',
+ 'recycle_bin_deleted_by' => 'Eliminado por',
+ 'recycle_bin_deleted_at' => 'Fecha de eliminación',
+ 'recycle_bin_permanently_delete' => 'Eliminar permanentemente',
+ 'recycle_bin_restore' => 'Restaurar',
+ 'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía',
+ 'recycle_bin_empty' => 'Vaciar Papelera de reciclaje',
+ 'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?',
+ 'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?',
+ 'recycle_bin_destroy_list' => 'Elementos a eliminar',
+ 'recycle_bin_restore_list' => 'Elementos a restaurar',
+ 'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
+ 'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
+ 'recycle_bin_destroy_notification' => 'Eliminados :count artículos de la papelera de reciclaje.',
+ 'recycle_bin_restore_notification' => 'Restaurados :count artículos desde la papelera de reciclaje.',
// Audit Log
'audit' => 'Registro de Auditoría',
- 'audit_desc' => 'Este registro de auditoría muestra una lista de actividades registradas en el sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
- 'audit_event_filter' => 'Filtro de eventos',
- 'audit_event_filter_no_filter' => 'Sin filtro',
- 'audit_deleted_item' => 'Elemento eliminado',
+ 'audit_desc' => 'Este registro de auditoría muestra una lista de actividades rastreadas en el sistema. Esta lista no tiene filtrado a diferencia de listas de actividad similares en el sistema en los que se aplican filtros de permisos.',
+ 'audit_event_filter' => 'Filtro de Eventos',
+ 'audit_event_filter_no_filter' => 'Sin Filtro',
+ 'audit_deleted_item' => 'Elemento borrado',
'audit_deleted_item_name' => 'Nombre: :name',
'audit_table_user' => 'Usuario',
'audit_table_event' => 'Evento',
- 'audit_table_item' => 'Elemento relacionado',
- 'audit_table_date' => 'Fecha de la actividad',
- 'audit_date_from' => 'Rango de fecha desde',
- 'audit_date_to' => 'Rango de fecha hasta',
+ 'audit_table_related' => 'Elemento o detalle relacionados',
+ 'audit_table_date' => 'Fecha de la Actividad',
+ 'audit_date_from' => 'Inicio del Rango de Fecha',
+ 'audit_date_to' => 'Final del Rango de Fecha',
// Role Settings
'roles' => 'Roles',
'role_access_api' => 'API de sistema de acceso',
'role_manage_settings' => 'Gestionar ajustes de activos',
'role_asset' => 'Permisos de activos',
- 'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario alterar sus propios privilegios o los privilegios de otros en el sistema. Sólo asignar roles con estos permisos a usuarios de confianza.',
- 'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos a Libros, Capítulos y Páginas sobreescribiran estos permisos.',
+ 'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario modificar sus propios privilegios o los privilegios de otros usuarios en el sistema. Asignar roles con estos permisos sólo a usuarios de comfianza.',
+ 'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos definidos en Libros, Capítulos y Páginas ignorarán estos permisos.',
'role_asset_admins' => 'Los administradores reciben automáticamente acceso a todo el contenido pero estas opciones pueden mostrar u ocultar opciones de UI.',
'role_all' => 'Todo',
'role_own' => 'Propio',
'user_profile' => 'Perfil de usuario',
'users_add_new' => 'Agregar nuevo usuario',
'users_search' => 'Buscar usuarios',
+ 'users_latest_activity' => 'Actividad Reciente',
'users_details' => 'Detalles del usuario',
'users_details_desc' => 'Asigne un nombre de visualización y una dirección de correo electrónico para este usuario. La dirección de correo electrónico se usará pra ingresar a la aplicación.',
'users_details_desc_no_email' => 'Asigne un nombre de visualización a este usuario para que los demás puedan reconocerlo.',
'users_delete_named' => 'Borrar usuario :userName',
'users_delete_warning' => 'Se borrará completamente el usuario con el nombre \':userName\' del sistema.',
'users_delete_confirm' => '¿Está seguro que desea borrar este usuario?',
- 'users_delete_success' => 'Usuarios removidos exitosamente',
+ 'users_migrate_ownership' => 'Cambiar Propietario',
+ 'users_migrate_ownership_desc' => 'Seleccione un usuario aquí si desea que otro usuario se convierta en el dueño de todos los elementos que actualmente son propiedad de este usuario.',
+ 'users_none_selected' => 'Usuario no seleccionado',
+ 'users_delete_success' => 'El usuario se ha eliminado correctamente',
'users_edit' => 'Editar Usuario',
'users_edit_profile' => 'Editar perfil',
'users_edit_success' => 'Usuario actualizado',
'user_api_token_name_desc' => 'Dale a tu token un nombre legible como un recordatorio futuro de su propósito.',
'user_api_token_expiry' => 'Fecha de expiración',
'user_api_token_expiry_desc' => 'Establece una fecha en la que este token expira. Después de esta fecha, las solicitudes realizadas usando este token ya no funcionarán. Dejar este campo en blanco fijará un vencimiento de 100 años en el futuro.',
- 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+ 'user_api_token_create_secret_message' => 'Luego de crear este token, inmediatamente se generará y mostrará el "Token ID" y el "Token Secret" correspondientes. El "Token Secret" se mostrará por única vez, asegúrese de copiar el valor a un lugar seguro antes de continuar.',
'user_api_token_create_success' => 'Token API creado correctamente',
'user_api_token_update_success' => 'Token API actualizado correctamente',
'user_api_token' => 'Token API',
'user_api_token_id_desc' => 'Este es un identificador no editable generado por el sistema y único para este token que necesitará ser proporcionado en solicitudes de API.',
'user_api_token_secret' => 'Clave de Token',
'user_api_token_secret_desc' => 'Esta es una clave no editable generada por el sistema que necesitará ser proporcionada en solicitudes de API. Solo se monstraré esta vez así que guarde su valor en un lugar seguro.',
- 'user_api_token_created' => 'Token created :timeAgo',
- 'user_api_token_updated' => 'Token updated :timeAgo',
+ 'user_api_token_created' => 'Token creado :timeAgo',
+ 'user_api_token_updated' => 'Token actualizado :timeAgo',
'user_api_token_delete' => 'Borrar token',
'user_api_token_delete_warning' => 'Esto eliminará completamente este token API con el nombre \':tokenName\' del sistema.',
'user_api_token_delete_confirm' => '¿Está seguro de que desea borrar este API token?',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute es requerido cuando no se encuentre entre los valores :values.',
'required_without_all' => ':attribute es requerido cuando ninguno de los valores :values están presentes.',
'same' => ':attribute y :other deben coincidir.',
+ 'safe_url' => 'El enlace proporcionado puede no ser seguro.',
'size' => [
'numeric' => ':attribute debe ser :size.',
'file' => ':attribute debe ser :size kilobytes.',
// Other
'commented_on' => 'a commenté',
+ 'permissions_update' => 'mettre à jour les autorisations',
];
'recently_update' => 'Mis à jour récemment',
'recently_viewed' => 'Vus récemment',
'recent_activity' => 'Activité récente',
- 'create_now' => 'En créer un maintenant',
+ 'create_now' => 'En créer une maintenant',
'revisions' => 'Révisions',
'meta_revision' => 'Révision #:revisionCount',
'meta_created' => 'Créé :timeLength',
'meta_created_name' => 'Créé :timeLength par :user',
'meta_updated' => 'Mis à jour :timeLength',
'meta_updated_name' => 'Mis à jour :timeLength par :user',
+ 'meta_owned_name' => 'Possédé par :user',
'entity_select' => 'Sélectionner l\'entité',
'images' => 'Images',
'my_recent_drafts' => 'Mes brouillons récents',
'permissions_intro' => 'Une fois activées ces permissions prendront la priorité sur tous les sets de permissions préexistants.',
'permissions_enable' => 'Activer les permissions personnalisées',
'permissions_save' => 'Enregistrer les permissions',
+ 'permissions_owner' => 'Propriétaire',
// Search
'search_results' => 'Résultats de recherche',
'chapters_create' => 'Créer un nouveau chapitre',
'chapters_delete' => 'Supprimer le chapitre',
'chapters_delete_named' => 'Supprimer le chapitre :chapterName',
- 'chapters_delete_explain' => 'Ceci va supprimer le chapitre \':chapterName\', toutes les pages seront déplacées dans le livre parent.',
+ 'chapters_delete_explain' => 'Ceci supprimera le chapitre portant le nom \':chapterName\'. Toutes les pages qui existent dans ce chapitre seront également supprimées.',
'chapters_delete_confirm' => 'Etes-vous sûr(e) de vouloir supprimer ce chapitre ?',
'chapters_edit' => 'Modifier le chapitre',
'chapters_edit_named' => 'Modifier le chapitre :chapterName',
'pages_revisions' => 'Révisions de la page',
'pages_revisions_named' => 'Révisions pour :pageName',
'pages_revision_named' => 'Révision pour :pageName',
+ 'pages_revision_restored_from' => 'Restauré à partir de #:id; :summary',
'pages_revisions_created_by' => 'Créé par',
'pages_revisions_date' => 'Date de révision',
'pages_revisions_number' => '#',
'maint' => 'Maintenance',
'maint_image_cleanup' => 'Nettoyer les images',
'maint_image_cleanup_desc' => "Scan le contenu des pages et des révisions pour vérifier les images et les dessins en cours d'utilisation et lesquels sont redondant. Veuillez à faire une sauvegarde de la base de données et des images avant de lancer ceci.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorer les images dans les révisions',
+ 'maint_delete_images_only_in_revisions' => 'Supprimer également les images qui n\'existent que dans les anciennes révisions de page',
'maint_image_cleanup_run' => 'Lancer le nettoyage',
'maint_image_cleanup_warning' => ':count images potentiellement inutilisées trouvées. Etes-vous sûr de vouloir supprimer ces images ?',
'maint_image_cleanup_success' => ':count images potentiellement inutilisées trouvées et supprimées !',
'maint_send_test_email_mail_subject' => 'Email de test',
'maint_send_test_email_mail_greeting' => 'La livraison d\'email semble fonctionner !',
'maint_send_test_email_mail_text' => 'Félicitations ! Lorsque vous avez reçu cette notification par courriel, vos paramètres d\'email semblent être configurés correctement.',
+ 'maint_recycle_bin_desc' => 'Les étagères, livres, chapitres et pages supprimés sont envoyés dans la corbeille afin qu\'ils puissent être restaurés ou supprimés définitivement. Les éléments plus anciens de la corbeille peuvent être supprimés automatiquement après un certain temps selon la configuration du système.',
+ 'maint_recycle_bin_open' => 'Ouvrir la corbeille',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Corbeille',
+ 'recycle_bin_desc' => 'Ici, vous pouvez restaurer les éléments qui ont été supprimés ou choisir de les effacer définitivement du système. Cette liste n\'est pas filtrée contrairement aux listes d\'activités similaires dans le système pour lesquelles les filtres d\'autorisation sont appliqués.',
+ 'recycle_bin_deleted_item' => 'Élément supprimé',
+ 'recycle_bin_deleted_by' => 'Supprimé par',
+ 'recycle_bin_deleted_at' => 'Date de suppression',
+ 'recycle_bin_permanently_delete' => 'Supprimer définitivement',
+ 'recycle_bin_restore' => 'Restaurer',
+ 'recycle_bin_contents_empty' => 'La corbeille est vide',
+ 'recycle_bin_empty' => 'Vider la Corbeille',
+ 'recycle_bin_empty_confirm' => 'Cela détruira définitivement tous les éléments de la corbeille, y compris le contenu contenu de chaque élément. Êtes-vous sûr de vouloir vider la corbeille ?',
+ 'recycle_bin_destroy_confirm' => 'Cette action supprimera définitivement cet élément, ainsi que tous les éléments enfants listés ci-dessous du système et vous ne pourrez pas restaurer ce contenu. Êtes-vous sûr de vouloir supprimer définitivement cet élément ?',
+ 'recycle_bin_destroy_list' => 'Éléments à détruire',
+ 'recycle_bin_restore_list' => 'Éléments à restaurer',
+ 'recycle_bin_restore_confirm' => 'Cette action restaurera l\'élément supprimé, y compris tous les éléments enfants, à leur emplacement d\'origine. Si l\'emplacement d\'origine a été supprimé depuis et est maintenant dans la corbeille, l\'élément parent devra également être restauré.',
+ 'recycle_bin_restore_deleted_parent' => 'Le parent de cet élément a également été supprimé. Ceux-ci resteront supprimés jusqu\'à ce que ce parent soit également restauré.',
+ 'recycle_bin_destroy_notification' => ':count éléments totaux supprimés de la corbeille.',
+ 'recycle_bin_restore_notification' => ':count éléments totaux restaurés de la corbeille.',
// Audit Log
'audit' => 'Journal d\'audit',
'audit_deleted_item_name' => 'Nom: :name',
'audit_table_user' => 'Utilisateur',
'audit_table_event' => 'Evènement',
- 'audit_table_item' => 'Élément Associé',
+ 'audit_table_related' => 'Élément ou détail lié',
'audit_table_date' => 'Date d\'activation',
'audit_date_from' => 'À partir du',
'audit_date_to' => 'Jusqu\'au',
'user_profile' => 'Profil d\'utilisateur',
'users_add_new' => 'Ajouter un nouvel utilisateur',
'users_search' => 'Chercher les utilisateurs',
+ 'users_latest_activity' => 'Dernière activité',
'users_details' => 'Informations de l\'utilisateur',
'users_details_desc' => 'Définissez un nom et une adresse e-mail pour cet utilisateur. L\'adresse e-mail sera utilisée pour se connecter à l\'application.',
'users_details_desc_no_email' => 'Définissez un nom d\'affichage pour cet utilisateur afin que les autres puissent le reconnaître.',
'users_delete_named' => 'Supprimer l\'utilisateur :userName',
'users_delete_warning' => 'Ceci va supprimer \':userName\' du système.',
'users_delete_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer cet utilisateur ?',
- 'users_delete_success' => 'Utilisateurs supprimés avec succès',
+ 'users_migrate_ownership' => 'Migré propriété',
+ 'users_migrate_ownership_desc' => 'Sélectionnez un utilisateur ici si vous voulez qu\'un autre utilisateur devienne le propriétaire de tous les éléments actuellement détenus par cet utilisateur.',
+ 'users_none_selected' => 'Aucun utilisateur n\'a été séléctionné',
+ 'users_delete_success' => 'Utilisateur supprimé avec succès',
'users_edit' => 'Modifier l\'utilisateur',
'users_edit_profile' => 'Modifier le profil',
'users_edit_success' => 'Utilisateur mis à jour avec succès',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norvegien',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute est requis si:values n\'est pas présent.',
'required_without_all' => ':attribute est requis si aucun des valeurs :values n\'est présente.',
'same' => ':attribute et :other doivent être identiques.',
+ 'safe_url' => 'Le lien fourni peut ne pas être sûr.',
'size' => [
'numeric' => ':attribute doit avoir la taille :size.',
'file' => ':attribute doit peser :size kilobytes.',
// Other
'commented_on' => 'commented on',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'נוצר :timeLength על ידי :user',
'meta_updated' => 'עודכן :timeLength',
'meta_updated_name' => 'עודכן :timeLength על ידי :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'בחר יישות',
'images' => 'תמונות',
'my_recent_drafts' => 'הטיוטות האחרונות שלי',
'permissions_intro' => 'ברגע שמסומן, הרשאות אלו יגברו על כל הרשאת תפקיד שקיימת',
'permissions_enable' => 'הפעל הרשאות מותאמות אישית',
'permissions_save' => 'שמור הרשאות',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'תוצאות חיפוש',
'chapters_create' => 'צור פרק חדש',
'chapters_delete' => 'מחק פרק',
'chapters_delete_named' => 'מחק את פרק :chapterName',
- 'chapters_delete_explain' => 'פעולה זו תמחוק את הפרק בשם \':chapterName\'. כל הדפים יועברו אוטומטית לספר עצמו',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'האם ברצונך למחוק פרק זה?',
'chapters_edit' => 'ערוך פרק',
'chapters_edit_named' => 'ערוך פרק :chapterName',
'pages_revisions' => 'נוסחי דף',
'pages_revisions_named' => 'נוסחי דף עבור :pageName',
'pages_revision_named' => 'נוסח דף עבור :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'נוצר על ידי',
'pages_revisions_date' => 'תאריך נוסח',
'pages_revisions_number' => '#',
'maint' => 'תחזוקה',
'maint_image_cleanup' => 'ניקוי תמונות',
'maint_image_cleanup_desc' => "סורק את הדפים והגרסאות על מנת למצוא אילו תמונות לא בשימוש. יש לוודא גיבוי מלא של מסד הנתונים והתמונות לפני הרצה",
- 'maint_image_cleanup_ignore_revisions' => 'התעלם מהתמונות בגרסאות',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'הפעל ניקוי תמונות',
'maint_image_cleanup_warning' => 'נמצאו כ :count תמונות אשר לא בשימוש האם ברצונך להמשיך?',
'maint_image_cleanup_success' => ':count תמונות שלא בשימוש נמחקו',
'maint_send_test_email_mail_subject' => 'Test Email',
'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'פרופיל משתמש',
'users_add_new' => 'הוסף משתמש חדש',
'users_search' => 'חפש משתמשים',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'פרטי משתמש',
'users_details_desc' => 'הגדר שם לתצוגה ומייל עבור משתמש זה. כתובת המייל תשמש על מנת להתחבר למערכת',
'users_details_desc_no_email' => 'הגדר שם לתצוגה כדי שאחרים יוכלו לזהות',
'users_delete_named' => 'מחק משתמש :userName',
'users_delete_warning' => 'פעולה זו תמחק את המשתמש \':userName\' מהמערכת',
'users_delete_confirm' => 'האם ברצונך למחוק משתמש זה?',
- 'users_delete_success' => 'המשתמש נמחק בהצלחה',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'עריכת משתמש',
'users_edit_profile' => 'עריכת פרופיל',
'users_edit_success' => 'המשתמש עודכן בהצלחה',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'שדה :attribute נחוץ כאשר :values לא בנמצא.',
'required_without_all' => 'שדה :attribute נחוץ כאשר אף אחד מ-:values נמצאים.',
'same' => 'שדה :attribute ו-:other חייבים להיות זהים.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => 'שדה :attribute חייב להיות :size.',
'file' => 'שדה :attribute חייב להיות :size קילובייטים.',
// Other
'commented_on' => 'megjegyzést fűzött hozzá:',
+ 'permissions_update' => 'updated permissions',
];
'copy' => 'Másolás',
'reply' => 'Válasz',
'delete' => 'Törlés',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'Törlés megerősítése',
'search' => 'Keresés',
'search_clear' => 'Keresés törlése',
'reset' => 'Visszaállítás',
'profile_menu' => 'Profil menü',
'view_profile' => 'Profil megtekintése',
'edit_profile' => 'Profil szerkesztése',
- 'dark_mode' => 'Dark Mode',
- 'light_mode' => 'Light Mode',
+ 'dark_mode' => 'Sötét mód',
+ 'light_mode' => 'Világos mód',
// Layout tabs
'tab_info' => 'Információ',
'image_load_more' => 'Több betöltése',
'image_image_name' => 'Kép neve',
'image_delete_used' => 'Ez a kép a lenti oldalakon van használatban.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'Biztosan törölhető ez a kép?',
'image_select_image' => 'Kép kiválasztása',
'image_dropzone' => 'Képek feltöltése ejtéssel vagy kattintással',
'images_deleted' => 'Képek törölve',
'code_editor' => 'Kód szerkesztése',
'code_language' => 'Kód nyelve',
'code_content' => 'Kód tartalom',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'Munkamenet előzményei',
'code_save' => 'Kód mentése',
];
'meta_created_name' => ':user hozta létre :timeLength',
'meta_updated' => 'Frissítve :timeLength',
'meta_updated_name' => ':user frissítette :timeLength',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Entitás kiválasztása',
'images' => 'Képek',
'my_recent_drafts' => 'Legutóbbi vázlataim',
'permissions_intro' => 'Ha engedélyezett, ezek a jogosultságok elsőbbséget élveznek bármely beállított szerepkör jogosultsággal szemben.',
'permissions_enable' => 'Egyéni jogosultságok engedélyezése',
'permissions_save' => 'Jogosultságok mentése',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Keresési eredmények',
'search_no_pages' => 'Nincsenek a keresésnek megfelelő oldalak',
'search_for_term' => ':term keresése',
'search_more' => 'További eredmények',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => 'Részletes keresés',
+ 'search_terms' => 'Keresési kifejezések',
'search_content_type' => 'Tartalomtípus',
'search_exact_matches' => 'Pontos egyezések',
'search_tags' => 'Címke keresések',
'chapters_create' => 'Új fejezet létrehozása',
'chapters_delete' => 'Fejezet törlése',
'chapters_delete_named' => ':chapterName fejezet törlése',
- 'chapters_delete_explain' => '\':chapterName\' nevű fejezet törölve lesz. Minden oldal el lesz távolítva és közvetlenül a szülő könyvhöz lesz hozzáadva.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Biztosan törölhető ez a fejezet?',
'chapters_edit' => 'Fejezet szerkesztése',
'chapters_edit_named' => ':chapterName fejezet szerkesztése',
'pages_revisions' => 'Oldal változatai',
'pages_revisions_named' => ':pageName oldal változatai',
'pages_revision_named' => ':pageName oldal változata',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Létrehozta:',
'pages_revisions_date' => 'Változat dátuma',
'pages_revisions_number' => '#',
'attachments_upload' => 'Fájlfeltöltés',
'attachments_link' => 'Hivatkozás csatolása',
'attachments_set_link' => 'Hivatkozás beállítása',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'Biztosan törölhető ez a melléklet?',
'attachments_dropzone' => 'Fájlok csatolása ejtéssel vagy kattintással',
'attachments_no_files' => 'Nincsenek fájlok feltöltve',
'attachments_explain_link' => 'Fájl feltöltése helyett hozzá lehet kapcsolni egy hivatkozást. Ez egy hivatkozás lesz egy másik oldalra vagy egy fájlra a felhőben.',
'attachments_link_url' => 'Hivatkozás fájlra',
'attachments_link_url_hint' => 'Weboldal vagy fájl webcíme',
'attach' => 'Csatolás',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Melléklet hivatkozás hozzáadása oldalhoz',
'attachments_edit_file' => 'Fájl szerkesztése',
'attachments_edit_file_name' => 'Fájl neve',
'attachments_edit_drop_upload' => 'Feltöltés és felülírás ejtéssel vagy kattintással',
'maint' => 'Karbantartás',
'maint_image_cleanup' => 'Képek tisztítása',
'maint_image_cleanup_desc' => "Végigolvassa az oldalakat és a tartalmak változatait, hogy leellenőrizze jelenleg mely képek és rajzok vannak használatban, és mely képek szerepelnek többször. A futtatása előtt feltétlen készíteni kell egy teljes adatbázis és lemezkép mentést.",
- 'maint_image_cleanup_ignore_revisions' => 'Képek figyelmen kívül hagyása a változatokban',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Tisztítás futtatása',
'maint_image_cleanup_warning' => ':count potenciálisan nem használt képet találtam. Biztosan törölhetőek ezek a képek?',
'maint_image_cleanup_success' => ':count potenciálisan nem használt kép megtalálva és törölve!',
'maint_send_test_email_mail_subject' => 'Teszt e-mail',
'maint_send_test_email_mail_greeting' => 'Az email kézbesítés működőképesnek tűnik!',
'maint_send_test_email_mail_text' => 'Gratulálunk! Mivel ez az email figyelmeztetés megérkezett az email beállítások megfelelőek.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Lomtár megnyitása',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Lomtár',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Törölt elem',
+ 'recycle_bin_deleted_by' => 'Törölte',
+ 'recycle_bin_deleted_at' => 'Törlés ideje',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Visszaállítás',
+ 'recycle_bin_contents_empty' => 'A lomtár jelenleg üres',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_event_filter' => 'Eseményszűrő',
+ 'audit_event_filter_no_filter' => 'Nincs szűrő',
+ 'audit_deleted_item' => 'Törölt elem',
+ 'audit_deleted_item_name' => 'Név: :name',
+ 'audit_table_user' => 'Felhasználó',
+ 'audit_table_event' => 'Esemény',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Felhasználói profil',
'users_add_new' => 'Új felhasználó hozzáadása',
'users_search' => 'Felhasználók keresése',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Felhasználó részletei',
'users_details_desc' => 'Egy megjelenítendő név és email cím beállítása ennek a felhasználónak. Az email cím az alkalmazásba történő bejelentkezéshez lesz használva.',
'users_details_desc_no_email' => 'Egy megjelenítendő név beállítása ennek a felhasználónak amiről mások felismerik.',
'users_delete_named' => ':userName felhasználó törlése',
'users_delete_warning' => '\':userName\' felhasználó teljesen törölve lesz a rendszerből.',
'users_delete_confirm' => 'Biztosan törölhető ez a felhasználó?',
- 'users_delete_success' => 'Felhasználó sikeresen eltávolítva',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Felhasználó szerkesztése',
'users_edit_profile' => 'Profil szerkesztése',
'users_edit_success' => 'Felhasználó sikeresen frissítve',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute mező kötelező ha :values nincs beállítva.',
'required_without_all' => ':attribute mező kötelező ha egyik :values sincs beállítva.',
'same' => ':attribute és :other értékének egyeznie kell.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute :size méretű kell legyen.',
'file' => ':attribute :size kilobájt méretű kell legyen.',
// Other
'commented_on' => 'ha commentato in',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Creato :timeLength da :user',
'meta_updated' => 'Aggiornato :timeLength',
'meta_updated_name' => 'Aggiornato :timeLength da :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Selezione Entità',
'images' => 'Immagini',
'my_recent_drafts' => 'Bozze Recenti',
'permissions_intro' => 'Una volta abilitati, questi permessi avranno la priorità su tutti gli altri.',
'permissions_enable' => 'Abilita Permessi Custom',
'permissions_save' => 'Salva Permessi',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Risultati Ricerca',
'chapters_create' => 'Crea un nuovo capitolo',
'chapters_delete' => 'Elimina Capitolo',
'chapters_delete_named' => 'Elimina il capitolo :chapterName',
- 'chapters_delete_explain' => 'Questo eliminerà il capitolo \':chapterName\'. Tutte le pagine verranno spostate nel libro.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Sei sicuro di voler eliminare questo capitolo?',
'chapters_edit' => 'Elimina Capitolo',
'chapters_edit_named' => 'Modifica il capitolo :chapterName',
'pages_revisions' => 'Versioni Pagina',
'pages_revisions_named' => 'Versioni della pagina :pageName',
'pages_revision_named' => 'Versione della pagina :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Creata Da',
'pages_revisions_date' => 'Data Versione',
'pages_revisions_number' => '#',
'maint' => 'Manutenzione',
'maint_image_cleanup' => 'Pulizia Immagini',
'maint_image_cleanup_desc' => "Esegue la scansione del contenuto delle pagine e delle revisioni per verificare quali immagini e disegni sono attualmente in uso e quali immagini sono ridondanti. Assicurati di creare backup completo del database e delle immagini prima di eseguire la pulizia.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignora le immagini nelle revisioni',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Esegui Pulizia',
'maint_image_cleanup_warning' => ':count immagini potenzialmente inutilizzate sono state trovate. Sei sicuro di voler eliminare queste immagini?',
'maint_image_cleanup_success' => ':count immagini potenzialmente inutilizzate trovate e eliminate!',
'maint_send_test_email_mail_subject' => 'Email di Test',
'maint_send_test_email_mail_greeting' => 'L\'invio delle email sembra funzionare!',
'maint_send_test_email_mail_text' => 'Congratulazioni! Siccome hai ricevuto questa notifica email, le tue impostazioni sembrano essere configurate correttamente.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Apri il Cestino',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Cestino',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Cancellato da',
+ 'recycle_bin_deleted_at' => 'Orario Cancellazione',
+ 'recycle_bin_permanently_delete' => 'Elimina Definitivamente',
+ 'recycle_bin_restore' => 'Ripristina',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Svuota Cestino',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_event_filter_no_filter' => 'No Filter',
'audit_deleted_item' => 'Deleted Item',
'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_user' => 'Utente',
+ 'audit_table_event' => 'Evento',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Profilo Utente',
'users_add_new' => 'Aggiungi Nuovo Utente',
'users_search' => 'Cerca Utenti',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Dettagli Utente',
'users_details_desc' => 'Imposta un nome e un indirizzo email per questo utente. L\'indirizzo email verrà utilizzato per accedere all\'applicazione.',
'users_details_desc_no_email' => 'Imposta un nome per questo utente così gli altri possono riconoscerlo.',
'users_delete_named' => 'Elimina l\'utente :userName',
'users_delete_warning' => 'Questo eliminerà completamente l\'utente \':userName\' dal sistema.',
'users_delete_confirm' => 'Sei sicuro di voler eliminare questo utente?',
- 'users_delete_success' => 'Utenti rimossi correttamente',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'Nessun utente selezionato',
+ 'users_delete_success' => 'Utente rimosso con successo',
'users_edit' => 'Modifica Utente',
'users_edit_profile' => 'Modifica Profilo',
'users_edit_success' => 'Utente aggiornato correttamente',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Il campo :attribute è richiesto quando :values non è presente.',
'required_without_all' => 'Il campo :attribute è richiesto quando nessuno dei :values sono presenti.',
'same' => ':attribute e :other devono corrispondere.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => 'Il campo :attribute deve essere :size.',
'file' => 'Il campo :attribute deve essere :size kilobytes.',
// Other
'commented_on' => 'コメントする',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => '作成: :timeLength (:user)',
'meta_updated' => '更新: :timeLength',
'meta_updated_name' => '更新: :timeLength (:user)',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'エンティティ選択',
'images' => '画像',
'my_recent_drafts' => '最近の下書き',
'permissions_intro' => 'この設定は各ユーザの役割よりも優先して適用されます。',
'permissions_enable' => 'カスタム権限設定を有効にする',
'permissions_save' => '権限を保存',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => '検索結果',
'chapters_create' => 'チャプターを作成',
'chapters_delete' => 'チャプターを削除',
'chapters_delete_named' => 'チャプター「:chapterName」を削除',
- 'chapters_delete_explain' => 'チャプター「:chapterName」を削除すると、チャプター内のすべてのページはブック内に直接追加されます。',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'チャプターを削除してよろしいですか?',
'chapters_edit' => 'チャプターを編集',
'chapters_edit_named' => 'チャプター「:chapterName」を編集',
'pages_revisions' => '編集履歴',
'pages_revisions_named' => ':pageName のリビジョン',
'pages_revision_named' => ':pageName のリビジョン',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => '作成者',
'pages_revisions_date' => '日付',
'pages_revisions_number' => 'リビジョン',
'maint' => 'メンテナンス',
'maint_image_cleanup' => 'Cleanup Images',
'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'クリーンアップを実行',
'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
'maint_send_test_email_mail_subject' => 'テストメール',
'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'ユーザプロフィール',
'users_add_new' => 'ユーザを追加',
'users_search' => 'ユーザ検索',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'User Details',
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
'users_delete_named' => 'ユーザ「:userName」を削除',
'users_delete_warning' => 'ユーザ「:userName」を完全に削除します。',
'users_delete_confirm' => '本当にこのユーザを削除してよろしいですか?',
- 'users_delete_success' => 'ユーザを削除しました',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'ユーザ編集',
'users_edit_profile' => 'プロフィール編集',
'users_edit_success' => 'ユーザを更新しました',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':valuesが設定されていない場合、:attributeは必須です。',
'required_without_all' => ':valuesが設定されていない場合、:attributeは必須です。',
'same' => ':attributeと:otherは一致している必要があります。',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attributeは:sizeである必要があります。',
'file' => ':attributeは:sizeキロバイトである必要があります。',
// Other
'commented_on' => '댓글 쓰기',
+ 'permissions_update' => 'updated permissions',
];
'copy' => '복사',
'reply' => '답글',
'delete' => '삭제',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => '삭제 요청 확인',
'search' => '검색',
'search_clear' => '검색 지우기',
'reset' => '리셋',
'image_load_more' => '더 로드하기',
'image_image_name' => '이미지 이름',
'image_delete_used' => '이 이미지는 다음 문서들이 쓰고 있습니다.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => '이 이미지를 정말 삭제하시겠습니까?',
'image_select_image' => '이미지 선택',
'image_dropzone' => '여기에 이미지를 드롭하거나 여기를 클릭하세요. 이미지를 올릴 수 있습니다.',
'images_deleted' => '이미지 삭제함',
'code_editor' => '코드 수정',
'code_language' => '언어',
'code_content' => '내용',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => '세션 기록',
'code_save' => '저장',
];
'meta_created_name' => '만듦 :timeLength, :user',
'meta_updated' => '수정함 :timeLength',
'meta_updated_name' => '수정함 :timeLength, :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => '항목 선택',
'images' => '이미지',
'my_recent_drafts' => '내 최근의 초안 문서',
'permissions_intro' => '한번 허용하면 이 설정은 사용자 권한에 우선합니다.',
'permissions_enable' => '설정 허용',
'permissions_save' => '권한 저장',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => '검색 결과',
'search_no_pages' => '결과 없음',
'search_for_term' => ':term 검색',
'search_more' => '더 많은 결과',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => '고급 검색',
+ 'search_terms' => '용어 검색',
'search_content_type' => '형식',
'search_exact_matches' => '정확히 일치',
'search_tags' => '꼬리표 일치',
'chapters_create' => '챕터 만들기',
'chapters_delete' => '챕터 삭제하기',
'chapters_delete_named' => ':chapterName(을)를 지웁니다.',
- 'chapters_delete_explain' => ':chapterName에 있는 모든 문서는 챕터에서 벗어날 뿐 지우지 않습니다.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => '이 챕터를 지울 건가요?',
'chapters_edit' => '챕터 바꾸기',
'chapters_edit_named' => ':chapterName 바꾸기',
'pages_revisions' => '문서 수정본',
'pages_revisions_named' => ':pageName 수정본',
'pages_revision_named' => ':pageName 수정본',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => '만든 사용자',
'pages_revisions_date' => '수정한 날짜',
'pages_revisions_number' => 'No.',
'attachments_upload' => '파일 올리기',
'attachments_link' => '링크로 첨부',
'attachments_set_link' => '링크 설정',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => '이 첨부파일을 정말 삭제하시겠습니까?',
'attachments_dropzone' => '여기에 파일을 드롭하거나 여기를 클릭하세요.',
'attachments_no_files' => '올린 파일 없음',
'attachments_explain_link' => '파일을 올리지 않고 링크로 첨부할 수 있습니다.',
'attachments_link_url' => '파일로 링크',
'attachments_link_url_hint' => '파일 주소',
'attach' => '파일 첨부',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => '페이지에 첨부파일 링크 추가',
'attachments_edit_file' => '파일 수정',
'attachments_edit_file_name' => '파일 이름',
'attachments_edit_drop_upload' => '여기에 파일을 드롭하거나 여기를 클릭하세요. 파일을 올리거나 덮어쓸 수 있습니다.',
'maint' => '데이터',
'maint_image_cleanup' => '이미지 정리',
'maint_image_cleanup_desc' => "중복한 이미지를 찾습니다. 실행하기 전에 이미지를 백업하세요.",
- 'maint_image_cleanup_ignore_revisions' => '수정본에 있는 이미지 제외',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => '실행',
'maint_image_cleanup_warning' => '이미지 :count개를 지울 건가요?',
'maint_image_cleanup_success' => '이미지 :count개 삭제함',
'maint_send_test_email_mail_subject' => '테스트 메일',
'maint_send_test_email_mail_greeting' => '이메일 전송이 성공하였습니다.',
'maint_send_test_email_mail_text' => '축하합니다! 이 메일을 받음으로 이메일 설정이 정상적으로 되었음을 확인하였습니다.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
- 'audit' => 'Audit Log',
+ 'audit' => '감사 기록',
'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit_event_filter' => '이벤트 필터',
+ 'audit_event_filter_no_filter' => '필터 없음',
+ 'audit_deleted_item' => '삭제된 항목',
+ 'audit_deleted_item_name' => '이름: :name',
+ 'audit_table_user' => '사용자',
+ 'audit_table_event' => '이벤트',
+ 'audit_table_related' => 'Related Item or Detail',
+ 'audit_table_date' => '활동 날짜',
+ 'audit_date_from' => '날짜 범위 시작',
+ 'audit_date_to' => '날짜 범위 끝',
// Role Settings
'roles' => '권한',
'user_profile' => '사용자 프로필',
'users_add_new' => '사용자 만들기',
'users_search' => '사용자 검색',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => '사용자 정보',
'users_details_desc' => '메일 주소로 로그인합니다.',
'users_details_desc_no_email' => '사용자 이름을 바꿉니다.',
'users_delete_named' => ':userName 삭제',
'users_delete_warning' => ':userName에 관한 데이터를 지웁니다.',
'users_delete_confirm' => '이 사용자를 지울 건가요?',
- 'users_delete_success' => '사용자 삭제함',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => '사용자 수정',
'users_edit_profile' => '프로필 바꾸기',
'users_edit_success' => '프로필 바꿈',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':values(이)가 없을 때 :attribute(을)를 구성해야 합니다.',
'required_without_all' => ':values(이)가 모두 없을 때 :attribute(을)를 구성해야 합니다.',
'same' => ':attribute(와)과 :other(을)를 똑같이 구성하세요.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute(을)를 :size(으)로 구성하세요.',
'file' => ':attribute(을)를 :size킬로바이트로 구성하세요.',
--- /dev/null
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+ // Pages
+ 'page_create' => 'opprettet side',
+ 'page_create_notification' => 'Siden ble opprettet',
+ 'page_update' => 'oppdaterte side',
+ 'page_update_notification' => 'Siden ble oppdatert',
+ 'page_delete' => 'slettet side',
+ 'page_delete_notification' => 'Siden ble slettet',
+ 'page_restore' => 'gjenopprettet side',
+ 'page_restore_notification' => 'Siden ble gjenopprettet',
+ 'page_move' => 'flyttet side',
+
+ // Chapters
+ 'chapter_create' => 'opprettet kapittel',
+ 'chapter_create_notification' => 'Kapittelet ble opprettet',
+ 'chapter_update' => 'oppdaterte kapittel',
+ 'chapter_update_notification' => 'Kapittelet ble oppdatert',
+ 'chapter_delete' => 'slettet kapittel',
+ 'chapter_delete_notification' => 'Kapittelet ble slettet',
+ 'chapter_move' => 'flyttet kapittel
+ ',
+
+ // Books
+ 'book_create' => 'opprettet bok',
+ 'book_create_notification' => 'Boken ble opprettet',
+ 'book_update' => 'oppdaterte bok',
+ 'book_update_notification' => 'Boken ble oppdatert',
+ 'book_delete' => 'slettet bok',
+ 'book_delete_notification' => 'Boken ble slettet',
+ 'book_sort' => 'sorterte bok',
+ 'book_sort_notification' => 'Boken ble omsortert',
+
+ // Bookshelves
+ 'bookshelf_create' => 'opprettet bokhylle',
+ 'bookshelf_create_notification' => 'Bokhyllen ble opprettet',
+ 'bookshelf_update' => 'oppdaterte bokhylle',
+ 'bookshelf_update_notification' => 'Bokhyllen ble oppdatert',
+ 'bookshelf_delete' => 'slettet bokhylle',
+ 'bookshelf_delete_notification' => 'Bokhyllen ble slettet',
+
+ // Other
+ 'commented_on' => 'kommenterte på',
+ 'permissions_update' => 'updated permissions',
+];
--- /dev/null
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+ 'failed' => 'Disse detaljene samsvarer ikke med det vi har på bok.',
+ 'throttle' => 'For mange forsøk, prøv igjen om :seconds sekunder.',
+
+ // Login & Register
+ 'sign_up' => 'Registrer deg',
+ 'log_in' => 'Logg inn',
+ 'log_in_with' => 'Logg inn med :socialDriver',
+ 'sign_up_with' => 'Registrer med :socialDriver',
+ 'logout' => 'Logg ut',
+
+ 'name' => 'Navn',
+ 'username' => 'Brukernavn',
+ 'email' => 'E-post',
+ 'password' => 'Passord',
+ 'password_confirm' => 'Bekreft passord',
+ 'password_hint' => 'Må inneholde 7 tegn',
+ 'forgot_password' => 'Glemt passord?',
+ 'remember_me' => 'Husk meg',
+ 'ldap_email_hint' => 'Oppgi en e-post for denne kontoen.',
+ 'create_account' => 'Opprett konto',
+ 'already_have_account' => 'Har du allerede en konto?',
+ 'dont_have_account' => 'Mangler du en konto?',
+ 'social_login' => 'Sosiale kontoer',
+ 'social_registration' => 'Registrer via sosiale kontoer',
+ 'social_registration_text' => 'Bruk en annen tjeneste for å registrere deg.',
+
+ 'register_thanks' => 'Takk for at du registrerte deg!',
+ 'register_confirm' => 'Sjekk e-posten din for informasjon som gir deg tilgang til :appName.',
+ 'registrations_disabled' => 'Registrering er deaktivert.',
+ 'registration_email_domain_invalid' => 'Du kan ikke bruke det domenet for å registrere en konto.',
+ 'register_success' => 'Takk for registreringen! Du kan nå logge inn på tjenesten.',
+
+
+ // Password Reset
+ 'reset_password' => 'Nullstille passord',
+ 'reset_password_send_instructions' => 'Oppgi e-posten som er koblet til kontoen din, så sender vi en epost hvor du kan nullstille passordet.',
+ 'reset_password_send_button' => 'Send nullstillingslenke',
+ 'reset_password_sent' => 'En nullstillingslenke ble sendt til :email om den eksisterer i systemet.',
+ 'reset_password_success' => 'Passordet ble nullstilt.',
+ 'email_reset_subject' => 'Nullstill ditt :appName passord',
+ 'email_reset_text' => 'Du mottar denne eposten fordi det er blitt bedt om en nullstilling av passord på denne kontoen.',
+ 'email_reset_not_requested' => 'Om det ikke var deg, så trenger du ikke foreta deg noe.',
+
+
+ // Email Confirmation
+ 'email_confirm_subject' => 'Bekreft epost-adressen for :appName',
+ 'email_confirm_greeting' => 'Takk for at du registrerte deg for :appName!',
+ 'email_confirm_text' => 'Bekreft e-posten din ved å trykke på knappen nedenfor:',
+ 'email_confirm_action' => 'Bekreft e-post',
+ 'email_confirm_send_error' => 'Bekreftelse er krevd av systemet, men systemet kan ikke sende disse. Kontakt admin for å løse problemet.',
+ 'email_confirm_success' => 'E-posten din er bekreftet!',
+ 'email_confirm_resent' => 'Bekreftelsespost ble sendt, sjekk innboksen din.',
+
+ 'email_not_confirmed' => 'E-posten er ikke bekreftet.',
+ 'email_not_confirmed_text' => 'Epost-adressen er ennå ikke bekreftet.',
+ 'email_not_confirmed_click_link' => 'Trykk på lenken i e-posten du fikk vedrørende din registrering.',
+ 'email_not_confirmed_resend' => 'Om du ikke finner den i innboksen eller søppelboksen, kan du få tilsendt ny ved å trykke på knappen under.',
+ 'email_not_confirmed_resend_button' => 'Send bekreftelsespost på nytt',
+
+ // User Invite
+ 'user_invite_email_subject' => 'Du har blitt invitert til :appName!',
+ 'user_invite_email_greeting' => 'En konto har blitt opprettet for deg på :appName.',
+ 'user_invite_email_text' => 'Trykk på knappen under for å opprette et sikkert passord:',
+ 'user_invite_email_action' => 'Angi passord',
+ 'user_invite_page_welcome' => 'Velkommen til :appName!',
+ 'user_invite_page_text' => 'For å fullføre prosessen må du oppgi et passord som sikrer din konto på :appName for fremtidige besøk.',
+ 'user_invite_page_confirm_button' => 'Bekreft passord',
+ 'user_invite_success' => 'Passordet er angitt, du kan nå bruke :appName!'
+];
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+ // Buttons
+ 'cancel' => 'Avbryt',
+ 'confirm' => 'Bekreft',
+ 'back' => 'Tilbake',
+ 'save' => 'Lagre',
+ 'continue' => 'Fortsett',
+ 'select' => 'Velg',
+ 'toggle_all' => 'Bytt alle',
+ 'more' => 'Mer',
+
+ // Form Labels
+ 'name' => 'Navn',
+ 'description' => 'Beskrivelse',
+ 'role' => 'Rolle',
+ 'cover_image' => 'Bokomslag',
+ 'cover_image_description' => 'Bildet bør være ca. 440x250px.',
+
+ // Actions
+ 'actions' => 'Handlinger',
+ 'view' => 'Vis',
+ 'view_all' => 'Vis alle',
+ 'create' => 'Opprett',
+ 'update' => 'Oppdater',
+ 'edit' => 'Rediger',
+ 'sort' => 'Sorter',
+ 'move' => 'Flytt',
+ 'copy' => 'Kopier',
+ 'reply' => 'Svar',
+ 'delete' => 'Slett',
+ 'delete_confirm' => 'Bekreft sletting',
+ 'search' => 'Søk',
+ 'search_clear' => 'Nullstill søk',
+ 'reset' => 'Nullstill',
+ 'remove' => 'Fjern',
+ 'add' => 'Legg til',
+ 'fullscreen' => 'Fullskjerm',
+
+ // Sort Options
+ 'sort_options' => 'Sorteringsalternativer',
+ 'sort_direction_toggle' => 'Sorteringsretning',
+ 'sort_ascending' => 'Stigende sortering',
+ 'sort_descending' => 'Synkende sortering',
+ 'sort_name' => 'Navn',
+ 'sort_created_at' => 'Dato opprettet',
+ 'sort_updated_at' => 'Dato oppdatert',
+
+ // Misc
+ 'deleted_user' => 'Slett bruker',
+ 'no_activity' => 'Ingen aktivitet å vise',
+ 'no_items' => 'Ingen ting å vise',
+ 'back_to_top' => 'Hopp til toppen',
+ 'toggle_details' => 'Vis/skjul detaljer',
+ 'toggle_thumbnails' => 'Vis/skjul miniatyrbilder',
+ 'details' => 'Detaljer',
+ 'grid_view' => 'Rutenettvisning',
+ 'list_view' => 'Listevisning',
+ 'default' => 'Standard',
+ 'breadcrumb' => 'Brødsmuler',
+
+ // Header
+ 'profile_menu' => 'Profilmeny',
+ 'view_profile' => 'Vis profil',
+ 'edit_profile' => 'Endre Profile',
+ 'dark_mode' => 'Kveldsmodus',
+ 'light_mode' => 'Dagmodus',
+
+ // Layout tabs
+ 'tab_info' => 'Informasjon',
+ 'tab_content' => 'Innhold',
+
+ // Email Content
+ 'email_action_help' => 'Om du har problemer med å trykke på «:actionText»-knappen, bruk nettadressen under for å gå direkte dit:',
+ 'email_rights' => 'Kopibeskyttet',
+];
--- /dev/null
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+ // Image Manager
+ 'image_select' => 'Velg bilde',
+ 'image_all' => 'Alle',
+ 'image_all_title' => 'Vis alle bilder',
+ 'image_book_title' => 'Vis bilder som er lastet opp i denne boken',
+ 'image_page_title' => 'Vis bilder lastet opp til denne siden',
+ 'image_search_hint' => 'Søk på bilder etter navn',
+ 'image_uploaded' => 'Opplastet :uploadedDate',
+ 'image_load_more' => 'Last in flere',
+ 'image_image_name' => 'Bildenavn',
+ 'image_delete_used' => 'Dette bildet er brukt på sidene nedenfor.',
+ 'image_delete_confirm_text' => 'Vil du slette dette bildet?',
+ 'image_select_image' => 'Velg bilde',
+ 'image_dropzone' => 'Dra og slipp eller trykk her for å laste opp bilder',
+ 'images_deleted' => 'Bilder slettet',
+ 'image_preview' => 'Hurtigvisning av bilder',
+ 'image_upload_success' => 'Bilde ble lastet opp',
+ 'image_update_success' => 'Bildedetaljer ble oppdatert',
+ 'image_delete_success' => 'Bilde ble slettet',
+ 'image_upload_remove' => 'Fjern',
+
+ // Code Editor
+ 'code_editor' => 'Endre kode',
+ 'code_language' => 'Kodespråk',
+ 'code_content' => 'Kodeinnhold',
+ 'code_session_history' => 'Sesjonshistorikk',
+ 'code_save' => 'Lagre kode',
+];
--- /dev/null
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+ // Shared
+ 'recently_created' => 'Nylig opprettet',
+ 'recently_created_pages' => 'Nylig opprettede sider',
+ 'recently_updated_pages' => 'Nylig oppdaterte sider',
+ 'recently_created_chapters' => 'Nylig opprettede kapitler',
+ 'recently_created_books' => 'Nylig opprettede bøker',
+ 'recently_created_shelves' => 'Nylig opprettede bokhyller',
+ 'recently_update' => 'Nylig oppdatert',
+ 'recently_viewed' => 'Nylig vist',
+ 'recent_activity' => 'Nylig aktivitet',
+ 'create_now' => 'Opprett en nå',
+ 'revisions' => 'Revisjoner',
+ 'meta_revision' => 'Revisjon #:revisionCount',
+ 'meta_created' => 'Opprettet :timeLength',
+ 'meta_created_name' => 'Opprettet :timeLength av :user',
+ 'meta_updated' => 'Oppdatert :timeLength',
+ 'meta_updated_name' => 'Oppdatert :timeLength av :user',
+ 'meta_owned_name' => 'Owned by :user',
+ 'entity_select' => 'Velg entitet',
+ 'images' => 'Bilder',
+ 'my_recent_drafts' => 'Mine nylige utkast',
+ 'my_recently_viewed' => 'Mine nylige visninger',
+ 'no_pages_viewed' => 'Du har ikke sett på noen sider',
+ 'no_pages_recently_created' => 'Ingen sider har nylig blitt opprettet',
+ 'no_pages_recently_updated' => 'Ingen sider har nylig blitt oppdatert',
+ 'export' => 'Eksporter',
+ 'export_html' => 'Nettside med alt',
+ 'export_pdf' => 'PDF Fil',
+ 'export_text' => 'Tekstfil',
+
+ // Permissions and restrictions
+ 'permissions' => 'Tilganger',
+ 'permissions_intro' => 'Når disse er tillatt, vil disse tillatelsene ha prioritet over alle angitte rolletillatelser.',
+ 'permissions_enable' => 'Aktiver egendefinerte tillatelser',
+ 'permissions_save' => 'Lagre tillatelser',
+ 'permissions_owner' => 'Owner',
+
+ // Search
+ 'search_results' => 'Søkeresultater',
+ 'search_total_results_found' => ':count resultater funnet|:count totalt',
+ 'search_clear' => 'Nullstill søk',
+ 'search_no_pages' => 'Ingen sider passer med søket',
+ 'search_for_term' => 'Søk etter :term',
+ 'search_more' => 'Flere resultater',
+ 'search_advanced' => 'Avansert søk',
+ 'search_terms' => 'Søkeord',
+ 'search_content_type' => 'Innholdstype',
+ 'search_exact_matches' => 'Eksakte ord',
+ 'search_tags' => 'Søk på merker',
+ 'search_options' => 'ALternativer',
+ 'search_viewed_by_me' => 'Sett av meg',
+ 'search_not_viewed_by_me' => 'Ikke sett av meg',
+ 'search_permissions_set' => 'Tilganger er angitt',
+ 'search_created_by_me' => 'Opprettet av meg',
+ 'search_updated_by_me' => 'Oppdatert av meg',
+ 'search_date_options' => 'Datoalternativer',
+ 'search_updated_before' => 'Oppdatert før',
+ 'search_updated_after' => 'Oppdatert etter',
+ 'search_created_before' => 'Opprettet før',
+ 'search_created_after' => 'Opprettet etter',
+ 'search_set_date' => 'Angi dato',
+ 'search_update' => 'Oppdater søk',
+
+ // Shelves
+ 'shelf' => 'Hylle',
+ 'shelves' => 'Hyller',
+ 'x_shelves' => ':count hylle|:count hyller',
+ 'shelves_long' => 'Bokhyller',
+ 'shelves_empty' => 'Ingen bokhyller er opprettet',
+ 'shelves_create' => 'Opprett ny bokhylle',
+ 'shelves_popular' => 'Populære bokhyller',
+ 'shelves_new' => 'Nye bokhyller',
+ 'shelves_new_action' => 'Ny bokhylle',
+ 'shelves_popular_empty' => 'De mest populære bokhyllene blir vist her.',
+ 'shelves_new_empty' => 'Nylig opprettede bokhyller vises her.',
+ 'shelves_save' => 'Lagre hylle',
+ 'shelves_books' => 'Bøker på denne hyllen',
+ 'shelves_add_books' => 'Legg til bøker på denne hyllen',
+ 'shelves_drag_books' => 'Dra bøker hit for å stable dem i denne hylla',
+ 'shelves_empty_contents' => 'INgen bøker er stablet i denne hylla',
+ 'shelves_edit_and_assign' => 'Endre hylla for å legge til bøker',
+ 'shelves_edit_named' => 'Endre hyllen :name',
+ 'shelves_edit' => 'Endre bokhylle',
+ 'shelves_delete' => 'Fjern bokhylle',
+ 'shelves_delete_named' => 'Fjern bokhyllen :name',
+ 'shelves_delete_explain' => "Dette vil fjerne bokhyllen ':name'. Bøkene vil ikke fjernes fra systemet.",
+ 'shelves_delete_confirmation' => 'Er du helt sikker på at du vil skru ned hylla?',
+ 'shelves_permissions' => 'Tilganger til hylla',
+ 'shelves_permissions_updated' => 'Hyllas tilganger er oppdatert',
+ 'shelves_permissions_active' => 'Hyllas tilganger er aktive',
+ 'shelves_copy_permissions_to_books' => 'Kopier tilganger til bøkene på hylla',
+ 'shelves_copy_permissions' => 'Kopier tilganger',
+ 'shelves_copy_permissions_explain' => 'Dette vil angi gjeldende tillatelsesinnstillinger for denne bokhyllen på alle bøkene som finnes på den. Før du aktiverer, må du forsikre deg om at endringer i tillatelsene til denne bokhyllen er lagret.',
+ 'shelves_copy_permission_success' => 'Tilgangene ble overført til :count bøker',
+
+ // Books
+ 'book' => 'Bok',
+ 'books' => 'Bøker',
+ 'x_books' => ':count bok|:count bøker',
+ 'books_empty' => 'Ingen bøker er skrevet',
+ 'books_popular' => 'Populære bøker',
+ 'books_recent' => 'Nylige bøker',
+ 'books_new' => 'Nye bøker',
+ 'books_new_action' => 'Ny bok',
+ 'books_popular_empty' => 'De mest populære bøkene',
+ 'books_new_empty' => 'Siste utgivelser vises her.',
+ 'books_create' => 'Skriv ny bok',
+ 'books_delete' => 'Brenn bok',
+ 'books_delete_named' => 'Brenn boken :bookName',
+ 'books_delete_explain' => 'Dette vil brenne boken «:bookName». Alle sider i boken vil fordufte for godt.',
+ 'books_delete_confirmation' => 'Er du sikker på at du vil brenne boken?',
+ 'books_edit' => 'Endre bok',
+ 'books_edit_named' => 'Endre boken :bookName',
+ 'books_form_book_name' => 'Boktittel',
+ 'books_save' => 'Lagre bok',
+ 'books_permissions' => 'Boktilganger',
+ 'books_permissions_updated' => 'Boktilganger oppdatert',
+ 'books_empty_contents' => 'Ingen sider eller kapitler finnes i denne boken.',
+ 'books_empty_create_page' => 'Skriv en ny side',
+ 'books_empty_sort_current_book' => 'Sorter innholdet i boken',
+ 'books_empty_add_chapter' => 'Start på nytt kapittel',
+ 'books_permissions_active' => 'Boktilganger er aktive',
+ 'books_search_this' => 'Søk i boken',
+ 'books_navigation' => 'Boknavigasjon',
+ 'books_sort' => 'Sorter bokinnhold',
+ 'books_sort_named' => 'Sorter boken :bookName',
+ 'books_sort_name' => 'Sorter på navn',
+ 'books_sort_created' => 'Sorter på opprettet dato',
+ 'books_sort_updated' => 'Sorter på oppdatert dato',
+ 'books_sort_chapters_first' => 'Kapitler først',
+ 'books_sort_chapters_last' => 'Kapitler sist',
+ 'books_sort_show_other' => 'Vis andre bøker',
+ 'books_sort_save' => 'Lagre sortering',
+
+ // Chapters
+ 'chapter' => 'Kapittel',
+ 'chapters' => 'Kapitler',
+ 'x_chapters' => ':count Kapittel|:count Kapitler',
+ 'chapters_popular' => 'Populære kapittler',
+ 'chapters_new' => 'Nytt kapittel',
+ 'chapters_create' => 'Skriv nytt kapittel',
+ 'chapters_delete' => 'Riv ut kapittel',
+ 'chapters_delete_named' => 'Riv ut kapittelet :chapterName',
+ 'chapters_delete_explain' => 'Du ønsker å rive ut kapittelet «:chapterName». Alle sidene vil bli flyttet ut av kapittelet og vil ligge direkte i boka.',
+ 'chapters_delete_confirm' => 'Er du sikker på at du vil rive ut dette kapittelet?',
+ 'chapters_edit' => 'Endre kapittel',
+ 'chapters_edit_named' => 'Endre kapittelet :chapterName',
+ 'chapters_save' => 'Lagre kapittel',
+ 'chapters_move' => 'Flytt kapittel',
+ 'chapters_move_named' => 'Flytt kapittelet :chapterName',
+ 'chapter_move_success' => 'Kapittelet ble flyttet til :bookName',
+ 'chapters_permissions' => 'Kapitteltilganger',
+ 'chapters_empty' => 'Det finnes ingen sider i dette kapittelet.',
+ 'chapters_permissions_active' => 'Kapitteltilganger er aktivert',
+ 'chapters_permissions_success' => 'Kapitteltilgager er oppdatert',
+ 'chapters_search_this' => 'Søk i dette kapittelet',
+
+ // Pages
+ 'page' => 'Side',
+ 'pages' => 'Sider',
+ 'x_pages' => ':count side|:count sider',
+ 'pages_popular' => 'Populære sider',
+ 'pages_new' => 'Ny side',
+ 'pages_attachments' => 'Vedlegg',
+ 'pages_navigation' => 'Sidenavigasjon',
+ 'pages_delete' => 'Riv ut side',
+ 'pages_delete_named' => 'Riv ut siden :pageName',
+ 'pages_delete_draft_named' => 'Kast sideutkast :pageName',
+ 'pages_delete_draft' => 'Kast sideutkast',
+ 'pages_delete_success' => 'Siden er revet ut',
+ 'pages_delete_draft_success' => 'Sideutkast er kastet',
+ 'pages_delete_confirm' => 'Er du sikker på at du vil rive ut siden?',
+ 'pages_delete_draft_confirm' => 'Er du sikker på at du vil forkaste utkastet?',
+ 'pages_editing_named' => 'Endrer :pageName',
+ 'pages_edit_draft_options' => 'Utkastsalternativer',
+ 'pages_edit_save_draft' => 'Lagre utkast',
+ 'pages_edit_draft' => 'Endre utkast',
+ 'pages_editing_draft' => 'Redigerer utkast',
+ 'pages_editing_page' => 'Redigerer side',
+ 'pages_edit_draft_save_at' => 'Ukast lagret under ',
+ 'pages_edit_delete_draft' => 'Forkast utkast',
+ 'pages_edit_discard_draft' => 'Gi opp utkast',
+ 'pages_edit_set_changelog' => 'Angi endringslogg',
+ 'pages_edit_enter_changelog_desc' => 'Gi en kort beskrivelse av endringene dine',
+ 'pages_edit_enter_changelog' => 'Se endringslogg',
+ 'pages_save' => 'Lagre side',
+ 'pages_title' => 'Sidetittel',
+ 'pages_name' => 'Sidenavn',
+ 'pages_md_editor' => 'Tekstbehandler',
+ 'pages_md_preview' => 'Forhåndsvisning',
+ 'pages_md_insert_image' => 'Lim inn bilde',
+ 'pages_md_insert_link' => 'Lim in lenke',
+ 'pages_md_insert_drawing' => 'Lim inn tegning',
+ 'pages_not_in_chapter' => 'Siden tilhører ingen kapittel',
+ 'pages_move' => 'Flytt side',
+ 'pages_move_success' => 'Siden ble flyttet til ":parentName"',
+ 'pages_copy' => 'Kopier side',
+ 'pages_copy_desination' => 'Destinasjon',
+ 'pages_copy_success' => 'Siden ble flyttet',
+ 'pages_permissions' => 'Sidetilganger',
+ 'pages_permissions_success' => 'Sidens tilganger ble endret',
+ 'pages_revision' => 'Revisjon',
+ 'pages_revisions' => 'Sidens revisjoner',
+ 'pages_revisions_named' => 'Revisjoner for :pageName',
+ 'pages_revision_named' => 'Revisjoner for :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
+ 'pages_revisions_created_by' => 'Skrevet av',
+ 'pages_revisions_date' => 'Revideringsdato',
+ 'pages_revisions_number' => '#',
+ 'pages_revisions_numbered' => 'Revisjon #:id',
+ 'pages_revisions_numbered_changes' => 'Endringer på revisjon #:id',
+ 'pages_revisions_changelog' => 'Endringslogg',
+ 'pages_revisions_changes' => 'Endringer',
+ 'pages_revisions_current' => 'Siste versjon',
+ 'pages_revisions_preview' => 'Forhåndsvisning',
+ 'pages_revisions_restore' => 'Gjenopprett',
+ 'pages_revisions_none' => 'Denne siden har ingen revisjoner',
+ 'pages_copy_link' => 'Kopier lenke',
+ 'pages_edit_content_link' => 'Endre innhold',
+ 'pages_permissions_active' => 'Sidetilganger er aktive',
+ 'pages_initial_revision' => 'Første publisering',
+ 'pages_initial_name' => 'Ny side',
+ 'pages_editing_draft_notification' => 'Du skriver på et utkast som sist ble lagret :timeDiff.',
+ 'pages_draft_edited_notification' => 'Siden har blitt endret siden du startet. Det anbefales at du forkaster dine endringer.',
+ 'pages_draft_edit_active' => [
+ 'start_a' => ':count forfattere har begynt å endre denne siden.',
+ 'start_b' => ':userName skriver på siden for øyeblikket',
+ 'time_a' => 'siden sist siden ble oppdatert',
+ 'time_b' => 'i løpet av de siste :minCount minuttene',
+ 'message' => ':start :time. Prøv å ikke overskriv hverandres endringer!',
+ ],
+ 'pages_draft_discarded' => 'Forkastet, viser nå siste endringer fra siden slik den er lagret.',
+ 'pages_specific' => 'Bestemt side',
+ 'pages_is_template' => 'Sidemal',
+
+ // Editor Sidebar
+ 'page_tags' => 'Sidemerker',
+ 'chapter_tags' => 'Kapittelmerker',
+ 'book_tags' => 'Bokmerker',
+ 'shelf_tags' => 'Hyllemerker',
+ 'tag' => 'Merke',
+ 'tags' => 'Merker',
+ 'tag_name' => 'Merketittel',
+ 'tag_value' => 'Merkeverdi (Valgfritt)',
+ 'tags_explain' => "Legg til merker for å kategorisere innholdet ditt. \n Du kan legge til merkeverdier for å beskrive dem ytterligere.",
+ 'tags_add' => 'Legg til flere merker',
+ 'tags_remove' => 'Fjern merke',
+ 'attachments' => 'Vedlegg',
+ 'attachments_explain' => 'Last opp vedlegg eller legg til lenker for å berike innholdet. Disse vil vises i sidestolpen på siden.',
+ 'attachments_explain_instant_save' => 'Endringer her blir lagret med en gang.',
+ 'attachments_items' => 'Vedlegg',
+ 'attachments_upload' => 'Last opp vedlegg',
+ 'attachments_link' => 'Fest lenke',
+ 'attachments_set_link' => 'Angi lenke',
+ 'attachments_delete' => 'Er du sikker på at du vil fjerne vedlegget?',
+ 'attachments_dropzone' => 'Dra og slipp eller trykk her for å feste vedlegg',
+ 'attachments_no_files' => 'Ingen vedlegg er lastet opp',
+ 'attachments_explain_link' => 'Du kan feste lenker til denne. Det kan være henvisning til andre sider, bøker etc. eller lenker fra nettet.',
+ 'attachments_link_name' => 'Lenkenavn',
+ 'attachment_link' => 'Vedleggslenke',
+ 'attachments_link_url' => 'Lenke til vedlegg',
+ 'attachments_link_url_hint' => 'Adresse til lenke eller vedlegg',
+ 'attach' => 'Fest',
+ 'attachments_insert_link' => 'Fest vedleggslenke',
+ 'attachments_edit_file' => 'Endre vedlegg',
+ 'attachments_edit_file_name' => 'Vedleggsnavn',
+ 'attachments_edit_drop_upload' => 'Dra og slipp eller trykk her for å oppdatere eller overskrive',
+ 'attachments_order_updated' => 'Vedleggssortering endret',
+ 'attachments_updated_success' => 'Vedleggsdetaljer endret',
+ 'attachments_deleted' => 'Vedlegg fjernet',
+ 'attachments_file_uploaded' => 'Vedlegg ble lastet opp',
+ 'attachments_file_updated' => 'Vedlegget ble oppdatert',
+ 'attachments_link_attached' => 'Lenken ble festet til siden',
+ 'templates' => 'Maler',
+ 'templates_set_as_template' => 'Siden er en mal',
+ 'templates_explain_set_as_template' => 'Du kan angi denne siden som en mal slik at innholdet kan brukes når du oppretter andre sider. Andre brukere vil kunne bruke denne malen hvis de har visningstillatelser for denne siden.',
+ 'templates_replace_content' => 'Bytt sideinnhold',
+ 'templates_append_content' => 'Legg til neders på siden',
+ 'templates_prepend_content' => 'Legg til øverst på siden',
+
+ // Profile View
+ 'profile_user_for_x' => 'Medlem i :time',
+ 'profile_created_content' => 'Har skrevet',
+ 'profile_not_created_pages' => ':userName har ikke forfattet noen sider',
+ 'profile_not_created_chapters' => ':userName har ikke opprettet noen kapitler',
+ 'profile_not_created_books' => ':userName har ikke laget noen bøker',
+ 'profile_not_created_shelves' => ':userName har ikke hengt opp noen hyller',
+
+ // Comments
+ 'comment' => 'Kommentar',
+ 'comments' => 'Kommentarer',
+ 'comment_add' => 'Skriv kommentar',
+ 'comment_placeholder' => 'Skriv en kommentar her',
+ 'comment_count' => '{0} Ingen kommentarer|{1} 1 kommentar|[2,*] :count kommentarer',
+ 'comment_save' => 'Publiser kommentar',
+ 'comment_saving' => 'Publiserer ...',
+ 'comment_deleting' => 'Fjerner...',
+ 'comment_new' => 'Ny kommentar',
+ 'comment_created' => 'kommenterte :createDiff',
+ 'comment_updated' => 'Oppdatert :updateDiff av :username',
+ 'comment_deleted_success' => 'Kommentar fjernet',
+ 'comment_created_success' => 'Kommentar skrevet',
+ 'comment_updated_success' => 'Kommentar endret',
+ 'comment_delete_confirm' => 'Er du sikker på at du vil fjerne kommentaren?',
+ 'comment_in_reply_to' => 'Som svar til :commentId',
+
+ // Revision
+ 'revision_delete_confirm' => 'Vil du slette revisjonen?',
+ 'revision_restore_confirm' => 'Vil du gjenopprette revisjonen? Innholdet på siden vil bli overskrevet med denne revisjonen.',
+ 'revision_delete_success' => 'Revisjonen ble slettet',
+ 'revision_cannot_delete_latest' => 'CKan ikke slette siste revisjon.'
+];
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+ // Permissions
+ 'permission' => 'Du har ikke tilgang til å se denne siden.',
+ 'permissionJson' => 'Du har ikke tilgang til å utføre denne handlingen.',
+
+ // Auth
+ 'error_user_exists_different_creds' => 'En konto med :email finnes allerede, men har andre detaljer.',
+ 'email_already_confirmed' => 'E-posten er allerede bekreftet, du kan forsøke å logge inn.',
+ 'email_confirmation_invalid' => 'Denne bekreftelseskoden er allerede benyttet eller utgått. Prøv å registrere på nytt.',
+ 'email_confirmation_expired' => 'Bekreftelseskoden er allerede utgått, en ny e-post er sendt.',
+ 'email_confirmation_awaiting' => 'Du må bekrefte e-posten for denne kontoen.',
+ 'ldap_fail_anonymous' => 'LDAP kan ikke benyttes med anonym tilgang for denne tjeneren.',
+ 'ldap_fail_authed' => 'LDAP tilgang feilet med angitt DN',
+ 'ldap_extension_not_installed' => 'LDAP PHP modulen er ikke installert.',
+ 'ldap_cannot_connect' => 'Klarer ikke koble til LDAP på denne adressen',
+ 'saml_already_logged_in' => 'Allerede logget inn',
+ 'saml_user_not_registered' => 'Kontoen med navn :name er ikke registert, registrering er også deaktivert.',
+ 'saml_no_email_address' => 'Denne kontoinformasjonen finnes ikke i det eksterne autentiseringssystemet.',
+ 'saml_invalid_response_id' => 'Forespørselen fra det eksterne autentiseringssystemet gjenkjennes ikke av en prosess som startes av dette programmet. Å navigere tilbake etter pålogging kan forårsake dette problemet.',
+ 'saml_fail_authed' => 'Innlogging gjennom :system feilet. Fikk ikke kontakt med autentiseringstjeneren.',
+ 'social_no_action_defined' => 'Ingen handlinger er definert',
+ 'social_login_bad_response' => "Feilmelding mottat fra :socialAccount innloggingstjeneste: \n:error",
+ 'social_account_in_use' => 'Denne :socialAccount kontoen er allerede registrert, Prøv å logge inn med :socialAccount alternativet.',
+ 'social_account_email_in_use' => 'E-posten :email er allerede i bruk. Har du allerede en konto hos :socialAccount kan dette angis fra profilsiden din.',
+ 'social_account_existing' => 'Denne :socialAccount er allerede koblet til din konto.',
+ 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.',
+ 'social_account_not_used' => 'Denne :socialAccount konten er ikke koblet til noen konto, angi denne i profilinnstillingene dine. ',
+ 'social_account_register_instructions' => 'Har du ikke en konto her ennå, kan du benytte :socialAccount alternativet for å registrere deg.',
+ 'social_driver_not_found' => 'Autentiseringstjeneste fra sosiale medier er ikke installert',
+ 'social_driver_not_configured' => 'Dine :socialAccount innstilliner er ikke angitt.',
+ 'invite_token_expired' => 'Invitasjonslenken har utgått, du kan forsøke å be om nytt passord istede.',
+
+ // System
+ 'path_not_writable' => 'Filstien :filePath aksepterer ikke filer, du må sjekke filstitilganger i systemet.',
+ 'cannot_get_image_from_url' => 'Kan ikke hente bilde fra :url',
+ 'cannot_create_thumbs' => 'Kan ikke opprette miniatyrbilder. GD PHP er ikke installert.',
+ 'server_upload_limit' => 'Vedlegget er for stort, forsøk med et mindre vedlegg.',
+ 'uploaded' => 'Tjenesten aksepterer ikke vedlegg som er så stor.',
+ 'image_upload_error' => 'Bildet kunne ikke lastes opp, forsøk igjen.',
+ 'image_upload_type_error' => 'Bildeformatet støttes ikke, forsøk med et annet format.',
+ 'file_upload_timeout' => 'Opplastingen gikk ut på tid.',
+
+ // Attachments
+ 'attachment_not_found' => 'Vedlegget ble ikke funnet',
+
+ // Pages
+ 'page_draft_autosave_fail' => 'Kunne ikke lagre utkastet, forsikre deg om at du er tilkoblet tjeneren (Har du nettilgang?)',
+ 'page_custom_home_deletion' => 'Kan ikke slette en side som er satt som forside.',
+
+ // Entities
+ 'entity_not_found' => 'Entitet ble ikke funnet',
+ 'bookshelf_not_found' => 'Bokhyllen ble ikke funnet',
+ 'book_not_found' => 'Boken ble ikke funnet',
+ 'page_not_found' => 'Siden ble ikke funnet',
+ 'chapter_not_found' => 'Kapittel ble ikke funnet',
+ 'selected_book_not_found' => 'Den valgte boken eksisterer ikke',
+ 'selected_book_chapter_not_found' => 'Den valgte boken eller kapittelet eksisterer ikke',
+ 'guests_cannot_save_drafts' => 'Gjester kan ikke lagre utkast',
+
+ // Users
+ 'users_cannot_delete_only_admin' => 'Du kan ikke kaste ut den eneste administratoren',
+ 'users_cannot_delete_guest' => 'Du kan ikke slette gjestebrukeren (Du kan deaktivere offentlig visning istede)',
+
+ // Roles
+ 'role_cannot_be_edited' => 'Denne rollen kan ikke endres',
+ 'role_system_cannot_be_deleted' => 'Denne systemrollen kan ikke slettes',
+ 'role_registration_default_cannot_delete' => 'Du kan ikke slette en rolle som er satt som registreringsrolle (rollen nye kontoer får når de registrerer seg)',
+ 'role_cannot_remove_only_admin' => 'Denne brukeren er den eneste brukeren som er tildelt administratorrollen. Tilordne administratorrollen til en annen bruker før du prøver å fjerne den her.',
+
+ // Comments
+ 'comment_list' => 'Det oppstod en feil under henting av kommentarene.',
+ 'cannot_add_comment_to_draft' => 'Du kan ikke legge til kommentarer i et utkast.',
+ 'comment_add' => 'Det oppsto en feil da kommentaren skulle legges til / oppdateres.',
+ 'comment_delete' => 'Det oppstod en feil under sletting av kommentaren.',
+ 'empty_comment' => 'Kan ikke legge til en tom kommentar.',
+
+ // Error pages
+ '404_page_not_found' => 'Siden finnes ikke',
+ 'sorry_page_not_found' => 'Beklager, siden du leter etter ble ikke funnet.',
+ 'sorry_page_not_found_permission_warning' => 'Hvis du forventet at denne siden skulle eksistere, har du kanskje ikke tillatelse til å se den.',
+ 'return_home' => 'Gå til hovedside',
+ 'error_occurred' => 'En feil oppsto',
+ 'app_down' => ':appName er nede for øyeblikket',
+ 'back_soon' => 'Den vil snart komme tilbake.',
+
+ // API errors
+ 'api_no_authorization_found' => 'Ingen autorisasjonstoken ble funnet på forespørselen',
+ 'api_bad_authorization_format' => 'Det ble funnet et autorisasjonstoken på forespørselen, men formatet virket feil',
+ 'api_user_token_not_found' => 'Ingen samsvarende API-token ble funnet for det angitte autorisasjonstokenet',
+ 'api_incorrect_token_secret' => 'Hemmeligheten som er gitt for det gitte brukte API-tokenet er feil',
+ 'api_user_no_api_permission' => 'Eieren av det brukte API-tokenet har ikke tillatelse til å ringe API-samtaler',
+ 'api_user_token_expired' => 'Autorisasjonstokenet som er brukt, har utløpt',
+
+ // Settings & Maintenance
+ 'maintenance_test_email_failure' => 'Feil kastet når du sendte en test-e-post:',
+
+];
--- /dev/null
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+ 'previous' => '« Forrige',
+ 'next' => 'Neste »',
+
+];
--- /dev/null
+<?php
+/**
+ * Password Reminder Language Lines
+ * The following language lines are the default lines which match reasons
+ * that are given by the password broker for a password update attempt has failed.
+ */
+return [
+
+ 'password' => 'Passord må inneholde minst åtte tegn og samsvarer med bekreftelsen.',
+ 'user' => "Vi finner ikke en bruker med den e-postadressen.",
+ 'token' => 'Passordet for tilbakestilling av passord er ugyldig for denne e-postadressen.',
+ 'sent' => 'Vi har sendt e-postadressen til tilbakestilling av passordet ditt!',
+ 'reset' => 'Passordet ditt har blitt tilbakestilt!',
+
+];
--- /dev/null
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+ // Common Messages
+ 'settings' => 'Innstillinger',
+ 'settings_save' => 'Lagre innstillinger',
+ 'settings_save_success' => 'Innstillinger lagret',
+
+ // App Settings
+ 'app_customization' => 'Tilpassing',
+ 'app_features_security' => 'Funksjoner og sikkerhet',
+ 'app_name' => 'Applikasjonsnavn',
+ 'app_name_desc' => 'Dette navnet vises i overskriften og i alle e-postmeldinger som sendes av systemet.',
+ 'app_name_header' => 'Vis navn i topptekst',
+ 'app_public_access' => 'Offentlig tilgang',
+ 'app_public_access_desc' => 'Hvis du aktiverer dette alternativet, kan besøkende, som ikke er logget på, få tilgang til innhold i din BookStack-forekomst.',
+ 'app_public_access_desc_guest' => 'Tilgang for offentlige besøkende kan kontrolleres gjennom "Gjest" -brukeren.',
+ 'app_public_access_toggle' => 'Tillat offentlig tilgang',
+ 'app_public_viewing' => 'Tillat offentlig visning?',
+ 'app_secure_images' => 'Høyere sikkerhet på bildeopplastinger',
+ 'app_secure_images_toggle' => 'Enable høyere sikkerhet på bildeopplastinger',
+ 'app_secure_images_desc' => 'Av ytelsesgrunner er alle bilder offentlige. Dette alternativet legger til en tilfeldig streng som er vanskelig å gjette foran bildets nettadresser. Forsikre deg om at katalogindekser ikke er aktivert for å forhindre enkel tilgang.',
+ 'app_editor' => 'Tekstbehandler',
+ 'app_editor_desc' => 'Velg hvilken tekstbehandler som skal brukes av alle brukere til å redigere sider.',
+ 'app_custom_html' => 'Tilpasset HTML-hodeinnhold',
+ 'app_custom_html_desc' => 'Alt innhold som legges til her, blir satt inn i bunnen av <head> -delen på hver side. Dette er praktisk for å overstyre stiler eller legge til analysekode.',
+ 'app_custom_html_disabled_notice' => 'Tilpasset HTML-hodeinnhold er deaktivert på denne innstillingssiden for å sikre at eventuelle endringer ødelegger noe, kan tilbakestilles.',
+ 'app_logo' => 'Applikasjonslogo',
+ 'app_logo_desc' => 'Dette bildet skal være 43 px høyt. <br> Store bilder blir nedskalert.',
+ 'app_primary_color' => 'Applikasjonens primærfarge',
+ 'app_primary_color_desc' => 'Angir primærfargen for applikasjonen inkludert banner, knapper og lenker.',
+ 'app_homepage' => 'Applikasjonens hjemmeside',
+ 'app_homepage_desc' => 'Velg en visning som skal vises på hjemmesiden i stedet for standardvisningen. Sidetillatelser ignoreres for utvalgte sider.',
+ 'app_homepage_select' => 'Velg en side',
+ 'app_disable_comments' => 'Deaktiver kommentarer',
+ 'app_disable_comments_toggle' => 'Deaktiver kommentarer',
+ 'app_disable_comments_desc' => 'Deaktiver kommentarer på tvers av alle sidene i applikasjonen. <br> Eksisterende kommentarer vises ikke.',
+
+ // Color settings
+ 'content_colors' => 'Innholdsfarger',
+ 'content_colors_desc' => 'Angir farger for alle elementene i sideorganisasjonshierarkiet. Det anbefales å lese farger med en lignende lysstyrke som standardfargene for lesbarhet.',
+ 'bookshelf_color' => 'Hyllefarge',
+ 'book_color' => 'Bokfarge',
+ 'chapter_color' => 'Kapittelfarge',
+ 'page_color' => 'Sidefarge',
+ 'page_draft_color' => 'Sideutkastsfarge',
+
+ // Registration Settings
+ 'reg_settings' => 'Registrering',
+ 'reg_enable' => 'Tillat registrering',
+ 'reg_enable_toggle' => 'Tillat registrering',
+ 'reg_enable_desc' => 'Når registrering er aktivert vil brukeren kunne registrere seg som applikasjonsbruker. Ved registrering får de en standard brukerrolle.',
+ 'reg_default_role' => 'Standard brukerrolle etter registrering',
+ 'reg_enable_external_warning' => 'Alternativet ovenfor ignoreres mens ekstern LDAP- eller SAML-autentisering er aktiv. Brukerkontoer for ikke-eksisterende medlemmer blir automatisk opprettet hvis autentisering mot det eksterne systemet i bruk lykkes.',
+ 'reg_email_confirmation' => 'E-postbekreftelse',
+ 'reg_email_confirmation_toggle' => 'Krev e-postbekreftelse',
+ 'reg_confirm_email_desc' => 'Hvis domenebegrensning brukes, vil e-postbekreftelse være nødvendig, og dette alternativet vil bli ignorert.',
+ 'reg_confirm_restrict_domain' => 'Domenebegrensning',
+ 'reg_confirm_restrict_domain_desc' => 'Skriv inn en kommaseparert liste over e-postdomener du vil begrense registreringen til. Brukerne vil bli sendt en e-post for å bekrefte adressen deres før de får lov til å kommunisere med applikasjonen. <br> Vær oppmerksom på at brukere vil kunne endre e-postadressene sine etter vellykket registrering.',
+ 'reg_confirm_restrict_domain_placeholder' => 'Ingen begrensninger er satt',
+
+ // Maintenance settings
+ 'maint' => 'Maintenance',
+ 'maint_image_cleanup' => 'Bildeopprydding',
+ 'maint_image_cleanup_desc' => "Skanner side og revisjonsinnhold for å sjekke hvilke bilder og tegninger som for øyeblikket er i bruk, og hvilke bilder som er overflødige. Forsikre deg om at du lager en full database og sikkerhetskopiering av bilder før du kjører denne.",
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+ 'maint_image_cleanup_run' => 'Kjør opprydding',
+ 'maint_image_cleanup_warning' => ':count potensielt ubrukte bilder ble funnet. Er du sikker på at du vil slette disse bildene?',
+ 'maint_image_cleanup_success' => ':count potensielt ubrukte bilder funnet og slettet!',
+ 'maint_image_cleanup_nothing_found' => 'Ingen ubrukte bilder funnet, ingenting slettet!',
+ 'maint_send_test_email' => 'Send en test-e-post',
+ 'maint_send_test_email_desc' => 'Dette sender en test-e-post til din e-postadresse som er angitt i profilen din.',
+ 'maint_send_test_email_run' => 'Send en test-e-post',
+ 'maint_send_test_email_success' => 'Send en test-e-post til :address',
+ 'maint_send_test_email_mail_subject' => 'Test-e-post',
+ 'maint_send_test_email_mail_greeting' => 'E-postsending ser ut til å fungere!',
+ 'maint_send_test_email_mail_text' => 'Gratulerer! Da du mottok dette e-postvarselet, ser det ut til at e-postinnstillingene dine er konfigurert riktig.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+
+ // Audit Log
+ 'audit' => 'Revisjonslogg',
+ 'audit_desc' => 'Denne revisjonsloggen viser en liste over aktiviteter som spores i systemet. Denne listen er ufiltrert i motsetning til lignende aktivitetslister i systemet der tillatelsesfiltre brukes.',
+ 'audit_event_filter' => 'Hendelsesfilter',
+ 'audit_event_filter_no_filter' => 'Ingen filter',
+ 'audit_deleted_item' => 'Slettet ting',
+ 'audit_deleted_item_name' => 'Navn: :name',
+ 'audit_table_user' => 'Kontoholder',
+ 'audit_table_event' => 'Hendelse',
+ 'audit_table_related' => 'Related Item or Detail',
+ 'audit_table_date' => 'Aktivitetsdato',
+ 'audit_date_from' => 'Datoperiode fra',
+ 'audit_date_to' => 'Datoperiode til',
+
+ // Role Settings
+ 'roles' => 'Roller',
+ 'role_user_roles' => 'Kontoroller',
+ 'role_create' => 'Opprett ny rolle',
+ 'role_create_success' => 'Rolle opprettet',
+ 'role_delete' => 'Rolle slettet',
+ 'role_delete_confirm' => 'Dette vil slette rollen «:roleName».',
+ 'role_delete_users_assigned' => 'Denne rollen har :userCount kontoer koblet opp mot seg. Velg hvilke rolle du vil flytte disse til.',
+ 'role_delete_no_migration' => "Ikke flytt kontoer",
+ 'role_delete_sure' => 'Er du sikker på at du vil slette rollen?',
+ 'role_delete_success' => 'Rollen ble slettet',
+ 'role_edit' => 'Endre rolle',
+ 'role_details' => 'Rolledetaljer',
+ 'role_name' => 'Rollenavn',
+ 'role_desc' => 'Kort beskrivelse av rolle',
+ 'role_external_auth_id' => 'Ekstern godkjennings-ID',
+ 'role_system' => 'Systemtilganger',
+ 'role_manage_users' => 'Behandle kontoer',
+ 'role_manage_roles' => 'Behandle roller og rolletilganger',
+ 'role_manage_entity_permissions' => 'Behandle bok-, kapittel- og sidetilganger',
+ 'role_manage_own_entity_permissions' => 'Behandle tilganger på egne verk',
+ 'role_manage_page_templates' => 'Behandle sidemaler',
+ 'role_access_api' => 'Systemtilgang API',
+ 'role_manage_settings' => 'Behandle applikasjonsinnstillinger',
+ 'role_asset' => 'Eiendomstillatelser',
+ 'roles_system_warning' => 'Vær oppmerksom på at tilgang til noen av de ovennevnte tre tillatelsene kan tillate en bruker å endre sine egne rettigheter eller rettighetene til andre i systemet. Bare tildel roller med disse tillatelsene til pålitelige brukere.',
+ 'role_asset_desc' => 'Disse tillatelsene kontrollerer standard tilgang til eiendelene i systemet. Tillatelser til bøker, kapitler og sider overstyrer disse tillatelsene.',
+ 'role_asset_admins' => 'Administratorer får automatisk tilgang til alt innhold, men disse alternativene kan vise eller skjule UI-alternativer.',
+ 'role_all' => 'Alle',
+ 'role_own' => 'Egne',
+ 'role_controlled_by_asset' => 'Kontrollert av eiendelen de er lastet opp til',
+ 'role_save' => 'Lagre rolle',
+ 'role_update_success' => 'Rollen ble oppdatert',
+ 'role_users' => 'Kontoholdere med denne rollen',
+ 'role_users_none' => 'Ingen kontoholdere er gitt denne rollen',
+
+ // Users
+ 'users' => 'Users',
+ 'user_profile' => 'Profil',
+ 'users_add_new' => 'Register ny konto',
+ 'users_search' => 'Søk i kontoer',
+ 'users_latest_activity' => 'Latest Activity',
+ 'users_details' => 'Kontodetaljer',
+ 'users_details_desc' => 'Angi et visningsnavn og en e-postadresse for denne kontoholderen. E-postadressen vil bli brukt til å logge på applikasjonen.',
+ 'users_details_desc_no_email' => 'Angi et visningsnavn for denne kontoholderen slik at andre kan gjenkjenne dem.',
+ 'users_role' => 'Roller',
+ 'users_role_desc' => 'Velg hvilke roller denne kontoholderen vil bli tildelt. Hvis en kontoholderen er tildelt flere roller, vil tillatelsene fra disse rollene stable seg, og de vil motta alle evnene til de tildelte rollene.',
+ 'users_password' => 'Passord',
+ 'users_password_desc' => 'Angi et passord som brukes til å logge på applikasjonen. Dette må bestå av minst 6 tegn.',
+ 'users_send_invite_text' => 'Du kan velge å sende denne kontoholderen en invitasjons-e-post som lar dem angi sitt eget passord, ellers kan du selv angi passordet.',
+ 'users_send_invite_option' => 'Send invitasjonsmelding',
+ 'users_external_auth_id' => 'Ekstern godkjennings-ID',
+ 'users_external_auth_id_desc' => 'Dette er ID-en som brukes til å matche denne kontoholderen når de kommuniserer med det eksterne autentiseringssystemet.',
+ 'users_password_warning' => 'Fyll bare ut nedenfor hvis du vil endre passordet ditt.',
+ 'users_system_public' => 'Denne brukeren representerer alle gjester som besøker appliaksjonen din. Den kan ikke brukes til å logge på, men tildeles automatisk.',
+ 'users_delete' => 'Slett konto',
+ 'users_delete_named' => 'Slett kontoen :userName',
+ 'users_delete_warning' => 'Dette vil fullstendig slette denne brukeren med navnet «:userName» fra systemet.',
+ 'users_delete_confirm' => 'Er du sikker på at du vil slette denne kontoen?',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'Konto slettet',
+ 'users_edit' => 'Rediger konto',
+ 'users_edit_profile' => 'Rediger profil',
+ 'users_edit_success' => 'Kontoen ble oppdatert',
+ 'users_avatar' => 'Kontobilde',
+ 'users_avatar_desc' => 'Velg et bilde for å representere denne kontoholderen. Dette skal være omtrent 256px kvadrat.',
+ 'users_preferred_language' => 'Foretrukket språk',
+ 'users_preferred_language_desc' => 'Dette alternativet vil endre språket som brukes til brukergrensesnittet til applikasjonen. Dette påvirker ikke noe brukeropprettet innhold.',
+ 'users_social_accounts' => 'Sosiale kontoer',
+ 'users_social_accounts_info' => 'Her kan du koble andre kontoer for raskere og enklere pålogging. Hvis du frakobler en konto her, tilbakekaller ikke dette tidligere autorisert tilgang. Tilbakekall tilgang fra profilinnstillingene dine på den tilkoblede sosiale kontoen.',
+ 'users_social_connect' => 'Koble til konto',
+ 'users_social_disconnect' => 'Koble fra konto',
+ 'users_social_connected' => ':socialAccount ble lagt til din konto.',
+ 'users_social_disconnected' => ':socialAccount ble koblet fra din konto.',
+ 'users_api_tokens' => 'API-nøkler',
+ 'users_api_tokens_none' => 'Ingen API-nøkler finnes for denne kontoen',
+ 'users_api_tokens_create' => 'Opprett nøkkel',
+ 'users_api_tokens_expires' => 'Utløper',
+ 'users_api_tokens_docs' => 'API-dokumentasjon',
+
+ // API Tokens
+ 'user_api_token_create' => 'Opprett API-nøkkel',
+ 'user_api_token_name' => 'Navn',
+ 'user_api_token_name_desc' => 'Gi nøkkelen et lesbart navn som en fremtidig påminnelse om det tiltenkte formålet.',
+ 'user_api_token_expiry' => 'Utløpsdato',
+ 'user_api_token_expiry_desc' => 'Angi en dato da denne nøkkelen utløper. Etter denne datoen vil forespørsler som er gjort med denne nøkkelen ikke lenger fungere. Å la dette feltet stå tomt vil sette utløpsdato 100 år inn i fremtiden.',
+ 'user_api_token_create_secret_message' => 'Umiddelbart etter å ha opprettet denne nøkkelen vil en identifikator og hemmelighet bli generert og vist. Hemmeligheten vil bare vises en gang, så husk å kopiere verdien til et trygt sted før du fortsetter.',
+ 'user_api_token_create_success' => 'API-nøkkel ble opprettet',
+ 'user_api_token_update_success' => 'API-nøkkel ble oppdatert',
+ 'user_api_token' => 'API-nøkkel',
+ 'user_api_token_id' => 'Identifikator',
+ 'user_api_token_id_desc' => 'Dette er en ikke-redigerbar systemgenerert identifikator for denne nøkkelen som må oppgis i API-forespørsler.',
+ 'user_api_token_secret' => 'Hemmelighet',
+ 'user_api_token_secret_desc' => 'Dette er en systemgenerert hemmelighet for denne nøkkelen som må leveres i API-forespørsler. Dette vises bare denne gangen, så kopier denne verdien til et trygt sted.',
+ 'user_api_token_created' => 'Nøkkel opprettet :timeAgo',
+ 'user_api_token_updated' => 'Nøkkel oppdatert :timeAgo',
+ 'user_api_token_delete' => 'Slett nøkkel',
+ 'user_api_token_delete_warning' => 'Dette vil slette API-nøkkelen \':tokenName\' fra systemet.',
+ 'user_api_token_delete_confirm' => 'Sikker på at du vil slette nøkkelen?',
+ 'user_api_token_delete_success' => 'API-nøkkelen ble slettet',
+
+ //! If editing translations files directly please ignore this in all
+ //! languages apart from en. Content will be auto-copied from en.
+ //!////////////////////////////////
+ 'language_select' => [
+ 'en' => 'English',
+ 'ar' => 'العربية',
+ 'bg' => 'Bǎlgarski',
+ 'cs' => 'Česky',
+ 'da' => 'Dansk',
+ 'de' => 'Deutsch (Sie)',
+ 'de_informal' => 'Deutsch (Du)',
+ 'es' => 'Español',
+ 'es_AR' => 'Español Argentina',
+ 'fr' => 'Français',
+ 'he' => 'עברית',
+ 'hu' => 'Magyar',
+ 'it' => 'Italian',
+ 'ja' => '日本語',
+ 'ko' => '한국어',
+ 'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
+ 'pl' => 'Polski',
+ 'pt_BR' => 'Português do Brasil',
+ 'ru' => 'Русский',
+ 'sk' => 'Slovensky',
+ 'sl' => 'Slovenščina',
+ 'sv' => 'Svenska',
+ 'tr' => 'Türkçe',
+ 'uk' => 'Українська',
+ 'vi' => 'Tiếng Việt',
+ 'zh_CN' => '简体中文',
+ 'zh_TW' => '繁體中文',
+ ]
+ //!////////////////////////////////
+];
--- /dev/null
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+ // Standard laravel validation lines
+ 'accepted' => ':attribute må aksepteres.',
+ 'active_url' => ':attribute er ikke en godkjent URL.',
+ 'after' => ':attribute må være en dato etter :date.',
+ 'alpha' => ':attribute kan kun inneholde bokstaver.',
+ 'alpha_dash' => ':attribute kan kunne inneholde bokstaver, tall, bindestreker eller understreker.',
+ 'alpha_num' => ':attribute kan kun inneholde bokstaver og tall.',
+ 'array' => ':attribute må være en liste.',
+ 'before' => ':attribute må være en dato før :date.',
+ 'between' => [
+ 'numeric' => ':attribute må være mellom :min og :max.',
+ 'file' => ':attribute må være mellom :min og :max kilobytes.',
+ 'string' => ':attribute må være mellom :min og :max tegn.',
+ 'array' => ':attribute må være mellom :min og :max ting.',
+ ],
+ 'boolean' => ':attribute feltet kan bare være sann eller falsk.',
+ 'confirmed' => ':attribute bekreftelsen samsvarer ikke.',
+ 'date' => ':attribute er ikke en gyldig dato.',
+ 'date_format' => ':attribute samsvarer ikke med :format.',
+ 'different' => ':attribute og :other må være forskjellige.',
+ 'digits' => ':attribute må være :digits tall.',
+ 'digits_between' => ':attribute må være mellomg :min og :max tall.',
+ 'email' => ':attribute må være en gyldig e-post.',
+ 'ends_with' => ':attribute må slutte med en av verdiene: :values',
+ 'filled' => ':attribute feltet er påkrevd.',
+ 'gt' => [
+ 'numeric' => ':attribute må være større enn :value.',
+ 'file' => ':attribute må være større enn :value kilobytes.',
+ 'string' => ':attribute må være større enn :value tegn.',
+ 'array' => ':attribute må ha mer en :value ting.',
+ ],
+ 'gte' => [
+ 'numeric' => ':attribute må være større enn eller lik :value.',
+ 'file' => ':attribute må være større enn eller lik :value kilobytes.',
+ 'string' => ':attribute må være større enn eller lik :value tegn.',
+ 'array' => ':attribute må ha :value eller flere ting.',
+ ],
+ 'exists' => 'Den valgte :attribute er ugyldig.',
+ 'image' => ':attribute må være et bilde.',
+ 'image_extension' => ':attribute må ha støttet formattype.',
+ 'in' => 'Den valgte :attribute er ugyldig.',
+ 'integer' => ':attribute må være et heltall',
+ 'ip' => ':attribute må være en gyldig IP adresse.',
+ 'ipv4' => ':attribute må være en gyldig IPv4 adresse.',
+ 'ipv6' => ':attribute må være en gyldig IPv6 adresse.',
+ 'json' => ':attribute må være en gyldig JSON tekststreng.',
+ 'lt' => [
+ 'numeric' => ':attribute må være mindre enn :value.',
+ 'file' => ':attribute må være mindre enn :value kilobytes.',
+ 'string' => ':attribute må være mindre enn :value tegn.',
+ 'array' => ':attribute må ha mindre enn :value ting.',
+ ],
+ 'lte' => [
+ 'numeric' => ':attribute må være mindre enn eller lik :value.',
+ 'file' => ':attribute må være mindre enn eller lik :value kilobytes.',
+ 'string' => ':attribute må være mindre enn eller lik :value characters.',
+ 'array' => ':attribute må ha mindre enn eller lik :value ting.',
+ ],
+ 'max' => [
+ 'numeric' => ':attribute kan ikke være større enn :max.',
+ 'file' => ':attribute kan ikke være større enn :max kilobytes.',
+ 'string' => ':attribute kan ikke være større enn :max tegn.',
+ 'array' => ':attribute kan ikke inneholde mer enn :max ting.',
+ ],
+ 'mimes' => ':attribute må være en fil av typen: :values.',
+ 'min' => [
+ 'numeric' => ':attribute må være på minst :min.',
+ 'file' => ':attribute må være på minst :min kilobytes.',
+ 'string' => ':attribute må være på minst :min tegn.',
+ 'array' => ':attribute må minst ha :min ting.',
+ ],
+ 'no_double_extension' => ':attribute kan bare ha en formattype spesifisert.',
+ 'not_in' => 'Den valgte :attribute er ugyldig.',
+ 'not_regex' => ':attribute format er ugyldig.',
+ 'numeric' => ':attribute må være et nummer.',
+ 'regex' => ':attribute format er ugyldig.',
+ 'required' => ':attribute feltet er påkrevt.',
+ 'required_if' => ':attribute feltet er påkrevt når :other er :value.',
+ 'required_with' => ':attribute feltet er påkrevt når :values er tilgjengelig.',
+ 'required_with_all' => ':attribute feltet er påkrevt når :values er tilgjengelig',
+ 'required_without' => ':attribute feltet er påkrevt når :values ikke er tilgjengelig.',
+ 'required_without_all' => ':attribute feltet er påkrevt når ingen av :values er tilgjengelig.',
+ 'same' => ':attribute og :other må samsvare.',
+ 'safe_url' => 'The provided link may not be safe.',
+ 'size' => [
+ 'numeric' => ':attribute må være :size.',
+ 'file' => ':attribute må være :size kilobytes.',
+ 'string' => ':attribute må være :size tegn.',
+ 'array' => ':attribute må inneholde :size ting.',
+ ],
+ 'string' => ':attribute må være en tekststreng.',
+ 'timezone' => ':attribute må være en tidssone.',
+ 'unique' => ':attribute har allerede blitt tatt.',
+ 'url' => ':attribute format er ugyldig.',
+ 'uploaded' => 'kunne ikke lastes opp, tjeneren støtter ikke filer av denne størrelsen.',
+
+ // Custom validation lines
+ 'custom' => [
+ 'password-confirm' => [
+ 'required_with' => 'passordbekreftelse er påkrevd',
+ ],
+ ],
+
+ // Custom validation attributes
+ 'attributes' => [],
+];
// Other
'commented_on' => 'reactie op',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Aangemaakt: :timeLength door :user',
'meta_updated' => ':timeLength Aangepast',
'meta_updated_name' => 'Aangepast: :timeLength door :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Entiteit Selecteren',
'images' => 'Afbeeldingen',
'my_recent_drafts' => 'Mijn Concepten',
'permissions_intro' => 'Als je dit aanzet, dan gelden rol-permissies niet meer voor deze pagina.',
'permissions_enable' => 'Custom Permissies Aanzetten',
'permissions_save' => 'Permissies Opslaan',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Zoekresultaten',
'chapters_create' => 'Hoofdstuk Toevoegen',
'chapters_delete' => 'Hoofdstuk Verwijderen',
'chapters_delete_named' => 'Verwijder Hoofdstuk :chapterName',
- 'chapters_delete_explain' => 'Dit verwijdert het hoofdstuk \':chapterName\', Alle pagina\'s zullen verwijdert worden.
- en toegevoegd worden aan het bijbehorende boek.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Weet je zeker dat je dit boek wilt verwijderen?',
'chapters_edit' => 'Hoofdstuk Aanpassen',
'chapters_edit_named' => 'Hoofdstuk :chapterName Aanpassen',
'pages_revisions' => 'Pagina Revisies',
'pages_revisions_named' => 'Pagina Revisies voor :pageName',
'pages_revision_named' => 'Pagina Revisie voor :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Aangemaakt door',
'pages_revisions_date' => 'Revisiedatum',
'pages_revisions_number' => '#',
'maint' => 'Onderhoud',
'maint_image_cleanup' => 'Afbeeldingen opschonen',
'maint_image_cleanup_desc' => "Scant pagina- en revisie inhoud om te controleren welke afbeeldingen en tekeningen momenteel worden gebruikt en welke afbeeldingen overbodig zijn. Zorg ervoor dat je een volledige database en afbeelding backup maakt voordat je dit uitvoert.",
- 'maint_image_cleanup_ignore_revisions' => 'Afbeeldingen in revisies negeren',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Opschonen uitvoeren',
'maint_image_cleanup_warning' => ':count potentieel ongebruikte afbeeldingen gevonden. Weet u zeker dat u deze afbeeldingen wilt verwijderen?',
'maint_image_cleanup_success' => ':count potentieel ongebruikte afbeeldingen gevonden en verwijderd!',
'maint_send_test_email_mail_subject' => 'Test E-mail',
'maint_send_test_email_mail_greeting' => 'E-mailbezorging lijkt te werken!',
'maint_send_test_email_mail_text' => 'Gefeliciteerd! Nu je deze e-mailmelding hebt ontvangen, lijken je e-mailinstellingen correct te zijn geconfigureerd.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Gebruikersprofiel',
'users_add_new' => 'Gebruiker toevoegen',
'users_search' => 'Gebruiker zoeken',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Gebruiker details',
'users_details_desc' => 'Stel een weergavenaam en e-mailadres in voor deze gebruiker. Het e-mailadres zal worden gebruikt om in te loggen.',
'users_details_desc_no_email' => 'Stel een weergavenaam in voor deze gebruiker zodat anderen deze kunnen herkennen.',
'users_delete_named' => 'Verwijder gebruiker :userName',
'users_delete_warning' => 'Dit zal de gebruiker \':userName\' volledig uit het systeem verwijderen.',
'users_delete_confirm' => 'Weet je zeker dat je deze gebruiker wilt verwijderen?',
- 'users_delete_success' => 'Gebruiker succesvol verwijderd',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Bewerk Gebruiker',
'users_edit_profile' => 'Bewerk Profiel',
'users_edit_success' => 'Gebruiker succesvol bijgewerkt',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute veld is verplicht wanneer :values niet ingesteld is.',
'required_without_all' => ':attribute veld is verplicht wanneer geen van :values ingesteld zijn.',
'same' => ':attribute en :other moeten overeenkomen.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute moet :size zijn.',
'file' => ':attribute moet :size kilobytes zijn.',
// Other
'commented_on' => 'skomentował',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Utworzono :timeLength przez :user',
'meta_updated' => 'Zaktualizowano :timeLength',
'meta_updated_name' => 'Zaktualizowano :timeLength przez :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Wybór obiektu',
'images' => 'Obrazki',
'my_recent_drafts' => 'Moje ostatnie wersje robocze',
'permissions_intro' => 'Jeśli włączone są indywidualne uprawnienia, to te uprawnienia będą miały priorytet względem pozostałych ustawionych uprawnień ról.',
'permissions_enable' => 'Włącz własne uprawnienia',
'permissions_save' => 'Zapisz uprawnienia',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Wyniki wyszukiwania',
'chapters_create' => 'Utwórz nowy rozdział',
'chapters_delete' => 'Usuń rozdział',
'chapters_delete_named' => 'Usuń rozdział :chapterName',
- 'chapters_delete_explain' => 'To spowoduje usunięcie rozdziału \':chapterName\', Wszystkie strony zostaną usunięte
- i dodane bezpośrednio do książki.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Czy na pewno chcesz usunąć ten rozdział?',
'chapters_edit' => 'Edytuj rozdział',
'chapters_edit_named' => 'Edytuj rozdział :chapterName',
'pages_revisions' => 'Wersje strony',
'pages_revisions_named' => 'Wersje strony :pageName',
'pages_revision_named' => 'Wersja strony :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Utworzona przez',
'pages_revisions_date' => 'Data wersji',
'pages_revisions_number' => '#',
'maint' => 'Konserwacja',
'maint_image_cleanup' => 'Czyszczenie obrazków',
'maint_image_cleanup_desc' => "Skanuje zawartość strony i poprzednie wersje, aby sprawdzić, które obrazy i rysunki są aktualnie używane, a które obrazy są zbędne. Przed uruchomieniem tej opcji należy utworzyć pełną kopię zapasową bazy danych i obrazków.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignoruje obrazki w poprzednich wersjach',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Uruchom czyszczenie',
'maint_image_cleanup_warning' => 'Znaleziono :count potencjalnie niepotrzebnych obrazków. Czy na pewno chcesz je usunąć?',
'maint_image_cleanup_success' => ':count potencjalnie nieużywane obrazki zostały znalezione i usunięte!',
'maint_send_test_email_mail_subject' => 'E-mail testowy',
'maint_send_test_email_mail_greeting' => 'Wygląda na to, że wysyłka wiadomości e-mail działa!',
'maint_send_test_email_mail_text' => 'Gratulacje! Otrzymałeś tego e-maila więc Twoje ustawienia poczty elektronicznej wydają się być prawidłowo skonfigurowane.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Profil użytkownika',
'users_add_new' => 'Dodaj użytkownika',
'users_search' => 'Wyszukaj użytkownika',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Szczegóły użytkownika',
'users_details_desc' => 'Ustaw wyświetlaną nazwę i adres e-mail dla tego użytkownika. Adres e-mail zostanie wykorzystany do zalogowania się do aplikacji.',
'users_details_desc_no_email' => 'Ustaw wyświetlaną nazwę dla tego użytkownika, aby inni mogli go rozpoznać.',
'users_delete_named' => 'Usuń :userName',
'users_delete_warning' => 'To usunie użytkownika \':userName\' z systemu.',
'users_delete_confirm' => 'Czy na pewno chcesz usunąć tego użytkownika?',
- 'users_delete_success' => 'Użytkownik usunięty pomyślnie',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Edytuj użytkownika',
'users_edit_profile' => 'Edytuj profil',
'users_edit_success' => 'Użytkownik zaktualizowany pomyślnie',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Pole :attribute jest wymagane jeśli :values nie zostało wprowadzone.',
'required_without_all' => 'Pole :attribute jest wymagane jeśli żadna z wartości :values nie została podana.',
'same' => 'Pole :attribute i :other muszą być takie same.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute musi mieć długość :size.',
'file' => ':attribute musi mieć :size kilobajtów.',
// Other
'commented_on' => 'comentou em',
+ 'permissions_update' => 'updated permissions',
];
'meta_created_name' => 'Criado :timeLength por :user',
'meta_updated' => 'Atualizado :timeLength',
'meta_updated_name' => 'Atualizado :timeLength por :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Seleção de Entidade',
'images' => 'Imagens',
'my_recent_drafts' => 'Meus Rascunhos Recentes',
'permissions_intro' => 'Uma vez habilitadas, estas permissões terão prioridade sobre outro conjunto de permissões.',
'permissions_enable' => 'Habilitar Permissões Customizadas',
'permissions_save' => 'Salvar Permissões',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Resultado(s) da Pesquisa',
'chapters_create' => 'Criar Novo Capítulo',
'chapters_delete' => 'Excluir Capítulo',
'chapters_delete_named' => 'Excluir Capítulo :chapterName',
- 'chapters_delete_explain' => 'A ação vai excluir o capítulo de nome \':chapterName\'. Todas as páginas do capítulo serão removidas e adicionadas diretamente ao livro pai.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Tem certeza que deseja excluir o capítulo?',
'chapters_edit' => 'Editar Capítulo',
'chapters_edit_named' => 'Editar Capítulo :chapterName',
'pages_revisions' => 'Revisões da Página',
'pages_revisions_named' => 'Revisões de Página para :pageName',
'pages_revision_named' => 'Revisão de Página para :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Criada por',
'pages_revisions_date' => 'Data da Revisão',
'pages_revisions_number' => '#',
'maint' => 'Manutenção',
'maint_image_cleanup' => 'Limpeza de Imagens',
'maint_image_cleanup_desc' => "Examina páginas e revisa seus conteúdos para verificar quais imagens e desenhos estão atualmente em uso e quais são redundantes. Certifique-se de criar um backup completo do banco de dados e imagens antes de executar esta ação.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorar imagens em revisões',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Executar Limpeza',
'maint_image_cleanup_warning' => ':count imagens potencialmente não utilizadas foram encontradas. Tem certeza de que deseja excluir estas imagens?',
'maint_image_cleanup_success' => ':count imagens potencialmente não utilizadas foram encontradas e excluídas!',
'maint_send_test_email_mail_subject' => 'E-mail de Teste',
'maint_send_test_email_mail_greeting' => 'O envio de e-mails parece funcionar!',
'maint_send_test_email_mail_text' => 'Parabéns! Já que você recebeu esta notificação, suas opções de e-mail parecem estar configuradas corretamente.',
+ 'maint_recycle_bin_desc' => 'Prateleiras, livros, capítulos e páginas deletados são mandados para a lixeira podendo assim ser restaurados ou excluídos permanentemente. Itens mais antigos da lixeira podem vir a ser automaticamente removidos da lixeira após um tempo dependendo da configuração do sistema.',
+ 'maint_recycle_bin_open' => 'Abrir Lixeira',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Lixeira',
+ 'recycle_bin_desc' => 'Aqui você pode restaurar itens que foram excluídos ou escolher removê-los permanentemente do sistema. Esta lista não é filtrada diferentemente de listas de atividades similares no sistema onde filtros de permissão são aplicados.',
+ 'recycle_bin_deleted_item' => 'Item excluído',
+ 'recycle_bin_deleted_by' => 'Excluído por',
+ 'recycle_bin_deleted_at' => 'Momento de Exclusão',
+ 'recycle_bin_permanently_delete' => 'Excluir permanentemente',
+ 'recycle_bin_restore' => 'Restaurar',
+ 'recycle_bin_contents_empty' => 'A lixeira está vazia',
+ 'recycle_bin_empty' => 'Esvaziar Lixeira',
+ 'recycle_bin_empty_confirm' => 'Isso irá destruir permanentemente todos os itens na lixeira inclusive o conteúdo de cada item. Tem certeza de que quer esvaziar a lixeira?',
+ 'recycle_bin_destroy_confirm' => 'Esta ação irá excluir permanentemente do sistema este item junto com todos os elementos filhos listados abaixo. Você não poderá restaurar esse conteúdo. Tem certeza de que deseja excluir permanentemente este item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na lixeira, o item pai também precisará ser restaurado.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
- 'audit' => 'Audit Log',
+ 'audit' => 'Registro de auditoria',
'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
+ 'audit_event_filter_no_filter' => 'Sem filtro',
+ 'audit_deleted_item' => 'Item excluído',
+ 'audit_deleted_item_name' => 'Nome: :name',
+ 'audit_table_user' => 'Usuário',
+ 'audit_table_event' => 'Evento',
+ 'audit_table_related' => 'Related Item or Detail',
+ 'audit_table_date' => 'Data da Atividade',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Perfil do Usuário',
'users_add_new' => 'Adicionar Novo Usuário',
'users_search' => 'Pesquisar Usuários',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Detalhes do Usuário',
'users_details_desc' => 'Defina um nome de exibição e um endereço de e-mail para este usuário. O endereço de e-mail será usado para fazer login na aplicação.',
'users_details_desc_no_email' => 'Defina um nome de exibição para este usuário para que outros usuários possam reconhecê-lo',
'users_delete_named' => 'Excluir :userName',
'users_delete_warning' => 'A ação vai excluir completamente o usuário de nome \':userName\' do sistema.',
'users_delete_confirm' => 'Tem certeza que deseja excluir esse usuário?',
- 'users_delete_success' => 'Usuários excluídos com sucesso',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Editar Usuário',
'users_edit_profile' => 'Editar Perfil',
'users_edit_success' => 'Usuário atualizado com sucesso',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'O campo :attribute é requerido quando os valores :values não estiverem presentes.',
'required_without_all' => 'O campo :attribute é requerido quando nenhum dos valores :values estiverem presentes.',
'same' => 'O campo :attribute e o campo :other devem ser iguais.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => 'O tamanho do campo :attribute deve ser :size.',
'file' => 'O tamanho do arquivo :attribute deve ser de :size kilobytes.',
// Other
'commented_on' => 'прокомментировал',
+ 'permissions_update' => 'обновил разрешения',
];
'meta_created_name' => ':user создал :timeLength',
'meta_updated' => 'Обновлено :timeLength',
'meta_updated_name' => ':user обновил :timeLength',
+ 'meta_owned_name' => 'Владелец :user',
'entity_select' => 'Выбор объекта',
'images' => 'Изображения',
'my_recent_drafts' => 'Мои последние черновики',
'permissions_intro' => 'После включения опции эти разрешения будут иметь приоритет над любыми установленными разрешениями роли.',
'permissions_enable' => 'Включение пользовательских разрешений',
'permissions_save' => 'Сохранить разрешения',
+ 'permissions_owner' => 'Владелец',
// Search
'search_results' => 'Результаты поиска',
'chapters_create' => 'Создать новую главу',
'chapters_delete' => 'Удалить главу',
'chapters_delete_named' => 'Удалить главу :chapterName',
- 'chapters_delete_explain' => 'Это удалит главу с именем \':chapterName\'. Все страницы главы будут удалены и перемещены напрямую в книгу.',
+ 'chapters_delete_explain' => 'Это действие удалит главу с названием \':chapterName\'. Все страницы, которые существуют в этой главе, также будут удалены.',
'chapters_delete_confirm' => 'Вы действительно хотите удалить эту главу?',
'chapters_edit' => 'Редактировать главу',
'chapters_edit_named' => 'Редактировать главу :chapterName',
'pages_revisions' => 'Версии страницы',
'pages_revisions_named' => 'Версии страницы для :pageName',
'pages_revision_named' => 'Версия страницы для :pageName',
+ 'pages_revision_restored_from' => 'Восстановлено из #:id; :summary',
'pages_revisions_created_by' => 'Создана',
'pages_revisions_date' => 'Дата версии',
'pages_revisions_number' => '#',
'maint' => 'Обслуживание',
'maint_image_cleanup' => 'Очистка изображений',
'maint_image_cleanup_desc' => "Сканирует содержимое страниц и предыдущих версий и определяет изображения, которые не используются. Убедитесь, что у вас есть резервная копия базы данных и папки изображений перед запуском этой функции.",
- 'maint_image_cleanup_ignore_revisions' => 'Пропускать изображения в версиях',
+ 'maint_delete_images_only_in_revisions' => 'Также удалять изображения, которые существуют только в старой версии страницы',
'maint_image_cleanup_run' => 'Выполнить очистку',
'maint_image_cleanup_warning' => 'Найдено :count возможно бесполезных изображений. Вы уверены, что хотите удалить эти изображения?',
'maint_image_cleanup_success' => ':count возможно бесполезных изображений было найдено и удалено!',
'maint_send_test_email_mail_subject' => 'Проверка электронной почты',
'maint_send_test_email_mail_greeting' => 'Доставка электронной почты работает!',
'maint_send_test_email_mail_text' => 'Поздравляем! Поскольку вы получили это письмо, электронная почта настроена правильно.',
+ 'maint_recycle_bin_desc' => 'Удаленные полки, книги, главы и страницы отправляются в корзину, чтобы они могли быть восстановлены или удалены навсегда. Более старые элементы в корзине могут быть автоматически удалены через некоторое время в зависимости от системной конфигурации.',
+ 'maint_recycle_bin_open' => 'Открыть корзину',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Корзина',
+ 'recycle_bin_desc' => 'Здесь вы можете восстановить удаленные элементы или навсегда удалить их из системы. Этот список не отфильтрован в отличие от аналогичных списков действий в системе, где применяются фильтры.',
+ 'recycle_bin_deleted_item' => 'Удаленный элемент',
+ 'recycle_bin_deleted_by' => 'Удалён',
+ 'recycle_bin_deleted_at' => 'Время удаления',
+ 'recycle_bin_permanently_delete' => 'Удалить навсегда',
+ 'recycle_bin_restore' => 'Восстановить',
+ 'recycle_bin_contents_empty' => 'На данный момент корзина пуста',
+ 'recycle_bin_empty' => 'Очистить корзину',
+ 'recycle_bin_empty_confirm' => 'Это действие навсегда уничтожит все элементы в корзине, включая содержимое, содержащееся в каждом элементе. Вы уверены, что хотите очистить корзину?',
+ 'recycle_bin_destroy_confirm' => 'Это действие удалит этот элемент навсегда вместе с любыми дочерними элементами, перечисленными ниже, и вы не сможете восстановить этот контент. Вы уверены, что хотите навсегда удалить этот элемент?',
+ 'recycle_bin_destroy_list' => 'Элементы для удаления',
+ 'recycle_bin_restore_list' => 'Элементы для восстановления',
+ 'recycle_bin_restore_confirm' => 'Это действие восстановит удаленный элемент, включая дочерние, в исходное место. Если исходное место было удалено и теперь находится в корзине, родительский элемент также необходимо будет восстановить.',
+ 'recycle_bin_restore_deleted_parent' => 'Родитель этого элемента также был удален. Элементы будут удалены до тех пор, пока этот родитель не будет восстановлен.',
+ 'recycle_bin_destroy_notification' => 'Удалено :count элементов из корзины.',
+ 'recycle_bin_restore_notification' => 'Восстановлено :count элементов из корзины',
// Audit Log
'audit' => 'Журнал аудита',
- 'audit_desc' => 'Этот журнал аудита отображает список действий, отслеживаемых в системе. Этот список не отфильтрован в отличие от аналогичных списков действий в системе, где применяются фильтры разрешений.',
+ 'audit_desc' => 'Этот журнал аудита отображает список действий, отслеживаемых в системе. Этот список не отфильтрован в отличие от аналогичных списков действий в системе, где применяются фильтры.',
'audit_event_filter' => 'Фильтр событий',
'audit_event_filter_no_filter' => 'Без фильтра',
'audit_deleted_item' => 'Удаленный элемент',
'audit_deleted_item_name' => 'Имя: :name',
'audit_table_user' => 'Пользователь',
'audit_table_event' => 'Событие',
- 'audit_table_item' => 'Связанный элемент',
+ 'audit_table_related' => 'Связанный элемент',
'audit_table_date' => 'Дата действия',
'audit_date_from' => 'Диапазон даты от',
'audit_date_to' => 'Диапазон даты до',
'user_profile' => 'Профиль пользователя',
'users_add_new' => 'Добавить пользователя',
'users_search' => 'Поиск пользователей',
+ 'users_latest_activity' => 'Последние действия',
'users_details' => 'Данные пользователя',
'users_details_desc' => 'Укажите имя и адрес электронной почты для этого пользователя. Адрес электронной почты будет использоваться для входа в приложение.',
'users_details_desc_no_email' => 'Задайте имя для этого пользователя, чтобы другие могли его узнать.',
'users_system_public' => 'Этот пользователь представляет любых гостевых пользователей, которые посещают ваше приложение. Он не может использоваться для входа в систему и назначается автоматически.',
'users_delete' => 'Удалить пользователя',
'users_delete_named' => 'Удалить пользователя :userName',
- 'users_delete_warning' => 'Это полностью удалит пользователя с именем \':userName\' из системы.',
+ 'users_delete_warning' => 'Это полностью удалит пользователя \':userName\' из системы.',
'users_delete_confirm' => 'Вы уверены что хотите удалить этого пользователя?',
- 'users_delete_success' => 'Пользователи успешно удалены',
+ 'users_migrate_ownership' => 'Наследник контента',
+ 'users_migrate_ownership_desc' => 'Выберите пользователя, если вы хотите, чтобы он стал владельцем всех элементов, в настоящее время принадлежащих удаляемому пользователю.',
+ 'users_none_selected' => 'Пользователь не выбран',
+ 'users_delete_success' => 'Пользователь успешно удален',
'users_edit' => 'Редактировать пользователя',
'users_edit_profile' => 'Редактировать профиль',
'users_edit_success' => 'Пользователь успешно обновлен',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute обязательное поле когда :values не установлены.',
'required_without_all' => ':attribute обязательное поле когда ни одно из :values не установлены.',
'same' => ':attribute и :other должны совпадать.',
+ 'safe_url' => 'Предоставленная ссылка может быть небезопасной.',
'size' => [
'numeric' => ':attribute должен быть :size.',
'file' => ':attribute должен быть :size килобайт.',
return [
// Pages
- 'page_create' => 'vytvoril stránku',
+ 'page_create' => 'vytvoril(a) stránku',
'page_create_notification' => 'Stránka úspešne vytvorená',
'page_update' => 'aktualizoval stránku',
'page_update_notification' => 'Stránka úspešne aktualizovaná',
- 'page_delete' => 'odstránil stránku',
+ 'page_delete' => 'odstránil(a) stránku',
'page_delete_notification' => 'Stránka úspešne odstránená',
- 'page_restore' => 'obnovil stránku',
+ 'page_restore' => 'obnovil(a) stránku',
'page_restore_notification' => 'Stránka úspešne obnovená',
- 'page_move' => 'presunul stránku',
+ 'page_move' => 'presunul(a) stránku',
// Chapters
- 'chapter_create' => 'vytvoril kapitolu',
+ 'chapter_create' => 'vytvoril(a) kapitolu',
'chapter_create_notification' => 'Kapitola úspešne vytvorená',
- 'chapter_update' => 'aktualizoval kapitolu',
+ 'chapter_update' => 'aktualizoval(a) kapitolu',
'chapter_update_notification' => 'Kapitola úspešne aktualizovaná',
- 'chapter_delete' => 'odstránil kapitolu',
+ 'chapter_delete' => 'odstránil(a) kapitolu',
'chapter_delete_notification' => 'Kapitola úspešne odstránená',
- 'chapter_move' => 'presunul kapitolu',
+ 'chapter_move' => 'presunul(a) kapitolu',
// Books
- 'book_create' => 'vytvoril knihu',
+ 'book_create' => 'vytvoril(a) knihu',
'book_create_notification' => 'Kniha úspešne vytvorená',
- 'book_update' => 'aktualizoval knihu',
+ 'book_update' => 'aktualizoval(a) knihu',
'book_update_notification' => 'Kniha úspešne aktualizovaná',
- 'book_delete' => 'odstránil knihu',
+ 'book_delete' => 'odstránil(a) knihu',
'book_delete_notification' => 'Kniha úspešne odstránená',
- 'book_sort' => 'zoradil knihu',
+ 'book_sort' => 'zoradil(a) knihu',
'book_sort_notification' => 'Kniha úspešne znovu zoradená',
// Bookshelves
- 'bookshelf_create' => 'vytvorená knižnica',
+ 'bookshelf_create' => 'vytvoril(a) knižnicu',
'bookshelf_create_notification' => 'Knižnica úspešne vytvorená',
- 'bookshelf_update' => 'aktualizovaná knižnica',
+ 'bookshelf_update' => 'aktualizoval(a) knižnicu',
'bookshelf_update_notification' => 'Knižnica úspešne aktualizovaná',
- 'bookshelf_delete' => 'odstránená knižnica',
+ 'bookshelf_delete' => 'odstránil(a) knižnicu',
'bookshelf_delete_notification' => 'Knižnica úspešne odstránená',
// Other
- 'commented_on' => 'komentované na',
+ 'commented_on' => 'komentoval(a)',
+ 'permissions_update' => 'aktualizované oprávnenia',
];
*/
return [
- 'failed' => 'Tieto údaje nesedia s našimi záznamami.',
+ 'failed' => 'Tieto údaje sa nezhodujú s našimi záznamami.',
'throttle' => 'Priveľa pokusov o prihlásenie. Skúste znova o :seconds sekúnd.',
// Login & Register
'password_hint' => 'Musí mať viac ako 7 znakov',
'forgot_password' => 'Zabudli ste heslo?',
'remember_me' => 'Zapamätať si ma',
- 'ldap_email_hint' => 'Zadajte prosím email, ktorý sa má použiť pre tento účet.',
+ 'ldap_email_hint' => 'Zadajte prosím e-mail, ktorý sa má použiť pre tento účet.',
'create_account' => 'Vytvoriť účet',
'already_have_account' => 'Už máte svoj účet?',
'dont_have_account' => 'Nemáte účet?',
'social_login' => 'Sociálne prihlásenie',
'social_registration' => 'Sociálna registrácia',
- 'social_registration_text' => 'Registrovať sa a prihlásiť sa použitím inej služby.',
+ 'social_registration_text' => 'Registrácia a prihlásenie pomocou inej služby.',
- 'register_thanks' => 'Ďakujeme zaregistráciu!',
- 'register_confirm' => 'Skontrolujte prosím svoj email a kliknite na potvrdzujúce tlačidlo pre prístup k :appName.',
+ 'register_thanks' => 'Ďakujeme za registráciu!',
+ 'register_confirm' => 'Prosím, skontrolujte svoj e-mail a kliknite na potvrdzujúce tlačidlo pre prístup k :appName.',
'registrations_disabled' => 'Registrácie sú momentálne zablokované',
- 'registration_email_domain_invalid' => 'Táto emailová doména nemá prístup k tejto aplikácii',
+ 'registration_email_domain_invalid' => 'Táto e-mailová doména nemá prístup k tejto aplikácii',
'register_success' => 'Ďakujeme za registráciu! Teraz ste registrovaný a prihlásený.',
// Password Reset
- 'reset_password' => 'Reset hesla',
- 'reset_password_send_instructions' => 'Zadajte svoj email nižšie a bude Vám odoslaný email s odkazom pre reset hesla.',
- 'reset_password_send_button' => 'Poslať odkaz na reset hesla',
- 'reset_password_sent' => 'Odkaz na obnovenie hesla bude odoslaný na :email, ak sa táto e-mailová adresa nachádza v systéme.',
+ 'reset_password' => 'Resetovanie hesla',
+ 'reset_password_send_instructions' => 'Nižšie zadajte svoj e-mail, na ktorý Vám zašleme odkaz pre resetovanie hesla.',
+ 'reset_password_send_button' => 'Poslať odkaz na resetovanie hesla',
+ 'reset_password_sent' => 'Odkaz na resetovanie hesla bude odoslaný na :email, ak sa táto e-mailová adresa nachádza v systéme.',
'reset_password_success' => 'Vaše heslo bolo úspešne resetované.',
- 'email_reset_subject' => 'Reset Vášho :appName hesla',
- 'email_reset_text' => 'Tento email Ste dostali pretože sme dostali požiadavku na reset hesla pre Váš účet.',
- 'email_reset_not_requested' => 'Ak ste nepožiadali o reset hesla, nemusíte nič robiť.',
+ 'email_reset_subject' => 'Resetovanie Vášho hesla do :appName',
+ 'email_reset_text' => 'Tento e-mail ste obdržali, pretože sme dostali požiadavku na resetovanie hesla pre Váš účet.',
+ 'email_reset_not_requested' => 'Ak ste nepožiadali o resetovanie hesla, nemusíte robiť nič.',
// Email Confirmation
- 'email_confirm_subject' => 'Potvrdiť email na :appName',
- 'email_confirm_greeting' => 'Ďakujeme za pridanie sa k :appName!',
- 'email_confirm_text' => 'Prosím potvrďte Vašu emailovú adresu kliknutím na tlačidlo nižšie:',
- 'email_confirm_action' => 'Potvrdiť email',
- 'email_confirm_send_error' => 'Je požadované overenie emailu, ale systém nemohol odoslať email. Kontaktujte administrátora by ste sa uistili, že email je nastavený správne.',
+ 'email_confirm_subject' => 'Potvrdiť e-mail na :appName',
+ 'email_confirm_greeting' => 'Ďakujeme, že ste sa pridali k :appName!',
+ 'email_confirm_text' => 'Prosím, potvrďte Vašu e-mailovú adresu kliknutím na tlačidlo nižšie:',
+ 'email_confirm_action' => 'Potvrdiť e-mail',
+ 'email_confirm_send_error' => 'Je požadované overenie e-mailu, ale systém nemohol e-mail odoslať. Kontaktujte administrátora, aby ste sa uistili, že je e-mail nastavený správne.',
'email_confirm_success' => 'Váš email bol overený!',
- 'email_confirm_resent' => 'Potvrdzujúci email bol poslaný znovu, skontrolujte prosím svoju emailovú schránku.',
+ 'email_confirm_resent' => 'Potvrdzujúci e-mail bol poslaný znovu, skontrolujte prosím svoju e-mailovú schránku.',
- 'email_not_confirmed' => 'Emailová adresa nebola overená',
- 'email_not_confirmed_text' => 'Vaša emailová adresa nebola zatiaľ overená.',
- 'email_not_confirmed_click_link' => 'Prosím, kliknite na odkaz v emaili, ktorý bol poslaný krátko po Vašej registrácii.',
- 'email_not_confirmed_resend' => 'Ak nemôžete násť email, môžete znova odoslať overovací email odoslaním doleuvedeného formulára.',
- 'email_not_confirmed_resend_button' => 'Znova odoslať overovací email',
+ 'email_not_confirmed' => 'E-mailová adresa nebola overená',
+ 'email_not_confirmed_text' => 'Vaša e-mailová adresa nebola zatiaľ overená.',
+ 'email_not_confirmed_click_link' => 'Prosím, kliknite na odkaz v e-maili, ktorý bol poslaný krátko po Vašej registrácii.',
+ 'email_not_confirmed_resend' => 'Ak nemôžete nájsť e-mail, môžete znova odoslať overovací e-mail odoslaním doleuvedeného formulára.',
+ 'email_not_confirmed_resend_button' => 'Znova odoslať overovací e-mail',
// User Invite
'user_invite_email_subject' => 'Dostali ste pozvánku na pripojenie sa k aplikácii :appName!',
'user_invite_email_greeting' => 'Účet pre :appName bol pre vás vytvorený.',
'user_invite_email_text' => 'Kliknutím na tlačidlo nižšie nastavíte heslo k účtu a získate prístup:',
- 'user_invite_email_action' => 'Heslo k účtu',
- 'user_invite_page_welcome' => 'Vitajte na stránke :appName!',
+ 'user_invite_email_action' => 'Nastaviť heslo k účtu',
+ 'user_invite_page_welcome' => 'Vitajte v :appName!',
'user_invite_page_text' => 'Ak chcete dokončiť svoj účet a získať prístup, musíte nastaviť heslo, ktoré sa použije na prihlásenie do aplikácie :appName pri budúcich návštevách.',
'user_invite_page_confirm_button' => 'Potvrdiť heslo',
- 'user_invite_success' => 'Nastavené heslo, teraz máte prístup k :appName!'
+ 'user_invite_success' => 'Heslo bolo nastavené, teraz máte prístup k :appName!'
];
\ No newline at end of file
'meta_created_name' => 'Vytvorené :timeLength používateľom :user',
'meta_updated' => 'Aktualizované :timeLength',
'meta_updated_name' => 'Aktualizované :timeLength používateľom :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Entita vybraná',
'images' => 'Obrázky',
'my_recent_drafts' => 'Moje nedávne koncepty',
'permissions_intro' => 'Ak budú tieto oprávnenia povolené, budú mať prioritu pred oprávneniami roly.',
'permissions_enable' => 'Povoliť vlastné oprávnenia',
'permissions_save' => 'Uložiť oprávnenia',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Výsledky hľadania',
'chapters_create' => 'Vytvoriť novú kapitolu',
'chapters_delete' => 'Zmazať kapitolu',
'chapters_delete_named' => 'Zmazať kapitolu :chapterName',
- 'chapters_delete_explain' => 'Toto zmaže kapitolu menom \':chapterName\', všetky stránky budú ostránené
- a pridané priamo do rodičovskej knihy.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Ste si istý, že chcete zmazať túto kapitolu?',
'chapters_edit' => 'Upraviť kapitolu',
'chapters_edit_named' => 'Upraviť kapitolu :chapterName',
'pages_revisions' => 'Revízie stránky',
'pages_revisions_named' => 'Revízie stránky :pageName',
'pages_revision_named' => 'Revízia stránky :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Vytvoril',
'pages_revisions_date' => 'Dátum revízie',
'pages_revisions_number' => '#',
'maint' => 'Maintenance',
'maint_image_cleanup' => 'Cleanup Images',
'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Run Cleanup',
'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
'maint_send_test_email_mail_subject' => 'Test Email',
'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Profil používateľa',
'users_add_new' => 'Pridať nového používateľa',
'users_search' => 'Hľadať medzi používateľmi',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'User Details',
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
'users_delete_named' => 'Zmazať používateľa :userName',
'users_delete_warning' => ' Toto úplne odstráni používateľa menom \':userName\' zo systému.',
'users_delete_confirm' => 'Ste si istý, že chcete zmazať tohoto používateľa?',
- 'users_delete_success' => 'Používateľ úspešne zmazaný',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Upraviť používateľa',
'users_edit_profile' => 'Upraviť profil',
'users_edit_success' => 'Používateľ úspešne upravený',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Políčko :attribute je povinné aj :values neexistuje.',
'required_without_all' => 'Políčko :attribute je povinné ak ani jedno z :values neexistuje.',
'same' => ':attribute a :other musia byť rovnaké.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute musí byť :size.',
'file' => ':attribute musí mať :size kilobajtov.',
// Pages
'page_create' => 'ustvarjena stran',
- 'page_create_notification' => 'Zapis uspešno ustvarjen',
- 'page_update' => 'nadgrajena stran',
- 'page_update_notification' => 'Uspešno posodobljeno',
+ 'page_create_notification' => 'Stran uspešno ustvarjena',
+ 'page_update' => 'posodobljena stran',
+ 'page_update_notification' => 'Stran uspešno posodobljena',
'page_delete' => 'izbrisana stran',
- 'page_delete_notification' => 'Uspešno izbrisano',
+ 'page_delete_notification' => 'Stran uspešno izbrisana',
'page_restore' => 'obnovljena stran',
- 'page_restore_notification' => 'Uspešna obnovitev',
+ 'page_restore_notification' => 'Stran uspešno obnovljena',
'page_move' => 'premaknjena stran',
// Chapters
'chapter_create' => 'ustvarjeno poglavje',
- 'chapter_create_notification' => 'Zapis uspešno ustvarjen',
- 'chapter_update' => 'nadgradi poglavje',
- 'chapter_update_notification' => 'Uspešno posodobljeno',
+ 'chapter_create_notification' => 'Poglavje uspešno ustvarjeno',
+ 'chapter_update' => 'posodobljeno poglavje',
+ 'chapter_update_notification' => 'Poglavje uspešno posodobljeno',
'chapter_delete' => 'izbrisano poglavje',
- 'chapter_delete_notification' => 'Uspešno izbrisano',
+ 'chapter_delete_notification' => 'Poglavje uspešno izbrisano',
'chapter_move' => 'premaknjeno poglavje',
// Books
'book_create' => 'knjiga ustvarjena',
- 'book_create_notification' => 'Knjiga Uspešno Usvarjena',
+ 'book_create_notification' => 'Knjiga uspešno usvarjena',
'book_update' => 'knjiga posodobljena',
- 'book_update_notification' => 'Uspešno posodobljeno',
+ 'book_update_notification' => 'Knjiga uspešno posodobljena',
'book_delete' => 'izbrisana knjiga',
- 'book_delete_notification' => 'Uspešno izbrisano',
+ 'book_delete_notification' => 'Knjiga uspešno izbrisana',
'book_sort' => 'razvrščena knjiga',
- 'book_sort_notification' => 'Knjiga Uspešno Razvrščena',
+ 'book_sort_notification' => 'Knjiga uspešno razvrščena',
// Bookshelves
'bookshelf_create' => 'knjižna polica izdelana',
- 'bookshelf_create_notification' => 'Knjižna Polica Izdelana',
+ 'bookshelf_create_notification' => 'Knjižna polica uspešno ustvarjena',
'bookshelf_update' => 'knjižna polica posodobljena',
- 'bookshelf_update_notification' => 'Knjižna Polica Uspešno Posodobljena',
+ 'bookshelf_update_notification' => 'Knjižna polica uspešno posodobljena',
'bookshelf_delete' => 'knjižna polica izbrisana',
- 'bookshelf_delete_notification' => 'Knjižna Polica Uspešno Izbrisana',
+ 'bookshelf_delete_notification' => 'Knjižna polica uspešno Izbrisana',
// Other
'commented_on' => 'komentar na',
+ 'permissions_update' => 'pravice so posodobljene',
];
'reset_password' => 'Ponastavi geslo',
'reset_password_send_instructions' => 'Spodaj vpišite vaš e-poštni naslov in prejeli boste e-pošto s povezavo za ponastavitev gesla.',
'reset_password_send_button' => 'Pošlji povezavo za ponastavitev',
- 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+ 'reset_password_sent' => 'V kolikor e-poštni naslov :email obstaja v sistemu, bo nanj poslana povezava za ponastavitev gesla.',
'reset_password_success' => 'Vaše geslo je bilo uspešno spremenjeno.',
'email_reset_subject' => 'Ponastavi svoje :appName geslo',
'email_reset_text' => 'To e-poštno sporočilo ste prejeli, ker smo prejeli zahtevo za ponastavitev gesla za vaš račun.',
'confirm' => 'Potrdi',
'back' => 'Nazaj',
'save' => 'Shrani',
- 'continue' => 'Naprej',
+ 'continue' => 'Nadaljuj',
'select' => 'Izberi',
'toggle_all' => 'Vklopi vse',
'more' => 'Več',
// Form Labels
- 'name' => 'Ime',
+ 'name' => 'Naziv',
'description' => 'Opis',
'role' => 'Vloga',
'cover_image' => 'Naslovna slika',
- 'cover_image_description' => 'Slika naj bo okoli 440x250px velika.',
+ 'cover_image_description' => 'Slika naj bo velika približno 440x250px.',
// Actions
'actions' => 'Dejanja',
'copy' => 'Kopiraj',
'reply' => 'Odgovori',
'delete' => 'Izbriši',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'Potrdi brisanje',
'search' => 'Išči',
- 'search_clear' => 'Počisti iskanje',
+ 'search_clear' => 'Razveljavi iskanje',
'reset' => 'Ponastavi',
- 'remove' => 'Remove',
+ 'remove' => 'Odstrani',
'add' => 'Dodaj',
'fullscreen' => 'Celozaslonski način',
// Misc
'deleted_user' => 'Izbrisan uporabnik',
'no_activity' => 'Ni aktivnosti za prikaz',
- 'no_items' => 'Ni na voljo nobenih elementov',
+ 'no_items' => 'Na voljo ni nobenega elementa',
'back_to_top' => 'Nazaj na vrh',
'toggle_details' => 'Preklopi podrobnosti',
'toggle_thumbnails' => 'Preklopi sličice',
'profile_menu' => 'Meni profila',
'view_profile' => 'Ogled profila',
'edit_profile' => 'Uredi profil',
- 'dark_mode' => 'Dark Mode',
- 'light_mode' => 'Light Mode',
+ 'dark_mode' => 'Način temnega zaslona',
+ 'light_mode' => 'Način svetlega zaslona',
// Layout tabs
'tab_info' => 'Informacije',
'tab_content' => 'Vsebina',
// Email Content
- 'email_action_help' => 'Če imate težave s klikom na ":actionText" gumb, kopirajte im prilepite spodnjo povezavo v vaš brskalnik:',
+ 'email_action_help' => 'V kolikor imate težave s klikom na gumb ":actionText", kopirajte in prilepite spodnjo povezavo v vaš brskalnik:',
'email_rights' => 'Vse pravice pridržane',
];
'image_select' => 'Izberi slike',
'image_all' => 'Vse',
'image_all_title' => 'Prikaži vse slike',
- 'image_book_title' => 'Preglej slike naložene v to knjigo',
+ 'image_book_title' => 'Prikaži slike naložene v to knjigo',
'image_page_title' => 'Preglej slike naložene na to stran',
'image_search_hint' => 'Iskanje po nazivu slike',
'image_uploaded' => 'Naloženo :uploadedDate',
- 'image_load_more' => 'Naloži več',
+ 'image_load_more' => 'Dodatno naloži',
'image_image_name' => 'Ime slike',
'image_delete_used' => 'Ta slika je uporabljena na spodnjih straneh.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'Ste prepričani, da želite izbrisati to sliko?',
'image_select_image' => 'Izberite sliko',
'image_dropzone' => 'Povlecite slike ali kliknite tukaj za nalaganje',
'images_deleted' => 'Slike so bile izbrisane',
'image_preview' => 'Predogled slike',
'image_upload_success' => 'Slika uspešno naložena',
'image_update_success' => 'Podatki slike uspešno posodobljeni',
- 'image_delete_success' => 'Uspešno izbrisano',
+ 'image_delete_success' => 'Slika uspešno izbrisana',
'image_upload_remove' => 'Odstrani',
// Code Editor
'code_editor' => 'Uredi kodo',
'code_language' => 'Koda jezika',
'code_content' => 'Koda vsebine',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'Zgodovina seje',
'code_save' => 'Shrani kodo',
];
// Shared
'recently_created' => 'Nazadnje objavljeno',
'recently_created_pages' => 'Nazadnje objavljene strani',
- 'recently_updated_pages' => 'Nedavno posodobljene strani',
+ 'recently_updated_pages' => 'Nazadnje posodobljene strani',
'recently_created_chapters' => 'Nazadnje objavljena poglavja',
'recently_created_books' => 'Nazadnje objavljene knjige',
- 'recently_created_shelves' => 'Nazadnje objavljene police',
- 'recently_update' => 'Nedavno posodobljeno',
- 'recently_viewed' => 'Nedavno prikazano',
+ 'recently_created_shelves' => 'Nazadnje ustvarjene police',
+ 'recently_update' => 'Nazadnje posodobljeno',
+ 'recently_viewed' => 'Nazadnje prikazano',
'recent_activity' => 'Nedavna dejavnost',
'create_now' => 'Ustvarite eno sedaj',
'revisions' => 'Revizije',
- 'meta_revision' => 'Revizije #:revisionCount',
+ 'meta_revision' => 'Številka revizije #:revisionCount',
'meta_created' => 'Ustvarjeno :timeLength',
- 'meta_created_name' => 'Created :timeLength by :user',
+ 'meta_created_name' => 'Ustvaril :timeLength uporabnik :user',
'meta_updated' => 'Posodobljeno :timeLength',
- 'meta_updated_name' => 'Posodobljeno :timeLength by :user',
+ 'meta_updated_name' => 'Posodobil :timeLength uporabnik :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Izbira entitete',
'images' => 'Slike',
'my_recent_drafts' => 'Moji nedavni osnutki',
'no_pages_recently_updated' => 'Nedavno ni bila posodobljena nobena stran',
'export' => 'Izvozi',
'export_html' => 'Vsebuje spletno datoteko',
- 'export_pdf' => 'Datoteka PDF',
+ 'export_pdf' => 'PDF datoteka (.pdf)',
'export_text' => 'Navadna besedilna datoteka',
// Permissions and restrictions
'permissions' => 'Dovoljenja',
- 'permissions_intro' => 'Ko so enkrat omogočena, bodo ta dovoljenja imela prednost pred dovoljenji za določanje vlog.',
+ 'permissions_intro' => 'V trenutku, ko bodo omogočena, bodo imela ta dovoljenja prednost pred dovoljenji za določanje vlog.',
'permissions_enable' => 'Omogoči dovoljenja po meri',
'permissions_save' => 'Shrani dovoljenja',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Rezultati iskanja',
'search_no_pages' => 'Nobena stran se ne ujema z vašim iskanjem',
'search_for_term' => 'Išči :term',
'search_more' => 'Prikaži več rezultatov',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => 'Napredno iskanje',
+ 'search_terms' => 'Iskalni izrazi',
'search_content_type' => 'Vrsta vsebine',
'search_exact_matches' => 'Natančno ujemanje',
- 'search_tags' => 'Iskanje oznak',
+ 'search_tags' => 'Iskanje po oznakah',
'search_options' => 'Možnosti',
'search_viewed_by_me' => 'Ogledano',
'search_not_viewed_by_me' => 'Neogledano',
'x_shelves' => ':count Polica|:count Police',
'shelves_long' => 'Knjižne police',
'shelves_empty' => 'Ustvarjena ni bila nobena polica',
- 'shelves_create' => 'Izdelaj novo polico',
+ 'shelves_create' => 'Ustvari novo polico',
'shelves_popular' => 'Priljubljene police',
'shelves_new' => 'Nove police',
'shelves_new_action' => 'Nova polica',
'shelves_popular_empty' => 'Najbolj priljubljene police se bodo pojavile tukaj.',
- 'shelves_new_empty' => 'Zadnje ustvarjene police se bodo pojavile tukaj.',
+ 'shelves_new_empty' => 'Nazadnje ustvarjene police se bodo pojavile tukaj.',
'shelves_save' => 'Shrani polico',
'shelves_books' => 'Knjige na tej polici',
'shelves_add_books' => 'Dodaj knjige na to polico',
- 'shelves_drag_books' => 'Povleci knjige sem za jih dodati na to polico',
- 'shelves_empty_contents' => 'Ta polica nima dodeljenih knjig',
- 'shelves_edit_and_assign' => 'Uredi polico za dodajanje knjig',
+ 'shelves_drag_books' => 'Povlecite knjige sem, da jih dodate na to polico',
+ 'shelves_empty_contents' => 'Na tej polici ni nobene knjige',
+ 'shelves_edit_and_assign' => 'Uredi knjižno polico za dodajanje knjig',
'shelves_edit_named' => 'Uredi knjižno polico :name',
'shelves_edit' => 'Uredi knjižno polico',
'shelves_delete' => 'Izbriši knjižno polico',
'shelves_delete_named' => 'Izbriši knjižno polico :name',
- 'shelves_delete_explain' => "To bo izbrisalo knjižno polico z imenom ':name'. Vsebovane knjige ne bodo izbrisane.",
+ 'shelves_delete_explain' => "S tem boste izbrisali knjižno polico z nazivom ':name'. Vsebovane knjige ne bodo izbrisane.",
'shelves_delete_confirmation' => 'Ali ste prepričani, da želite izbrisati ta knjižno polico?',
'shelves_permissions' => 'Dovoljenja knjižnih polic',
'shelves_permissions_updated' => 'Posodobljena dovoljenja knjižnih polic',
'shelves_permissions_active' => 'Aktivna dovoljenja knjižnih polic',
'shelves_copy_permissions_to_books' => 'Kopiraj dovoljenja na knjige',
- 'shelves_copy_permissions' => 'Kopiraj dovoljenja',
- 'shelves_copy_permissions_explain' => 'To bo uporabilo trenutne nastavitve dovoljenj te knjižne police za vse knjige, ki jih vsebuje. Pred aktiviranjem zagotovite, da so shranjene vse spremembe dovoljenj te knjižne police.',
+ 'shelves_copy_permissions' => 'Dovoljenja kopiranja',
+ 'shelves_copy_permissions_explain' => 'To bo uveljavilo trenutne nastavitve dovoljenj na knjižni polici za vse knjige, ki jih vsebuje ta polica. Pred aktiviranjem zagotovite, da so shranjene vse spremembe dovoljenj te knjižne police.',
'shelves_copy_permission_success' => 'Dovoljenja knjižne police kopirana na :count knjig',
// Books
'books_recent' => 'Zadnje knjige',
'books_new' => 'Nove knjige',
'books_new_action' => 'Nova knjiga',
- 'books_popular_empty' => 'Najbolj priljubljene knjige se bodo pojavile tukaj.',
- 'books_new_empty' => 'Zadnje ustvarjene knjige se bodo pojavile tukaj.',
- 'books_create' => 'Izdelaj novo knjigo',
+ 'books_popular_empty' => 'Tukaj bodo prikazane najbolj priljubljene knjige.',
+ 'books_new_empty' => 'Tukaj bodo prikazane nazadnje ustvarjene knjige.',
+ 'books_create' => 'Ustvari novo knjigo',
'books_delete' => 'Izbriši knjigo',
'books_delete_named' => 'Izbriši knjigo :bookName',
- 'books_delete_explain' => 'To bo izbrisalo knjigo z imenom \':bookName\'. Vse strani in poglavja bodo odstranjena.',
+ 'books_delete_explain' => 'S tem boste izbrisali knjigo z nazivom \':bookName\'. Vse strani in poglavja bodo odstranjena.',
'books_delete_confirmation' => 'Ali ste prepričani, da želite izbrisati to knjigo?',
'books_edit' => 'Uredi knjigo',
'books_edit_named' => 'Uredi knjigo :bookName',
'books_save' => 'Shrani knjigo',
'books_permissions' => 'Dovoljenja knjige',
'books_permissions_updated' => 'Posodobljena dovoljenja knjige',
- 'books_empty_contents' => 'Nobena stran ali poglavje ni bilo ustvarjeno za to knjigo.',
+ 'books_empty_contents' => 'V tej knjigi ni bila ustvarjena še nobena stran ali poglavje.',
'books_empty_create_page' => 'Ustvari novo stran',
'books_empty_sort_current_book' => 'Razvrsti trenutno knjigo',
'books_empty_add_chapter' => 'Dodaj poglavje',
'books_permissions_active' => 'Aktivna dovoljenja knjige',
- 'books_search_this' => 'Išči to knjigo',
- 'books_navigation' => 'Navigacija knjige',
- 'books_sort' => 'Razvrsti vsebine knjige',
+ 'books_search_this' => 'Išči v tej knjigi',
+ 'books_navigation' => 'Navigacija po knjigi',
+ 'books_sort' => 'Razvrsti vsebino knjige',
'books_sort_named' => 'Razvrsti knjigo :bookName',
'books_sort_name' => 'Razvrsti po imenu',
'books_sort_created' => 'Razvrsti po datumu nastanka',
'books_sort_chapters_first' => 'Najprej poglavja',
'books_sort_chapters_last' => 'Nazadnje poglavja',
'books_sort_show_other' => 'Prikaži druge knjige',
- 'books_sort_save' => 'Shrani novo naročilo',
+ 'books_sort_save' => 'Shrani novo razvrstitev',
// Chapters
'chapter' => 'Poglavje',
'chapters_create' => 'Ustvari novo poglavje',
'chapters_delete' => 'Izbriši poglavje',
'chapters_delete_named' => 'Izbriši poglavje :chapterName',
- 'chapters_delete_explain' => 'To bo izbrisalo poglavje z \':chapterName\'. Vse strani bodo odstranjene in dodane neposredno v matično knjigo.',
- 'chapters_delete_confirm' => 'Si prepričan, da želiš izbrisati to poglavje?',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+ 'chapters_delete_confirm' => 'Ste prepričani, da želite izbrisati to poglavje?',
'chapters_edit' => 'Uredi poglavje',
'chapters_edit_named' => 'Uredi poglavje :chapterName',
'chapters_save' => 'Shrani poglavje',
'chapter_move_success' => 'Poglavje premaknjeno v :bookName',
'chapters_permissions' => 'Dovoljenja poglavij',
'chapters_empty' => 'V tem poglavju trenutno ni strani.',
- 'chapters_permissions_active' => 'Aktivna dovoljenja poglavij',
+ 'chapters_permissions_active' => 'Dovoljenja poglavij so aktivirana',
'chapters_permissions_success' => 'Posodobljena dovoljenja poglavij',
'chapters_search_this' => 'Išči v tem poglavju',
'x_pages' => ':count Stran|:count Strani',
'pages_popular' => 'Priljubjene strani',
'pages_new' => 'Nova stran',
- 'pages_attachments' => 'Priloge',
+ 'pages_attachments' => 'Priponke',
'pages_navigation' => 'Navigacija po strani',
'pages_delete' => 'Izbriši stran',
'pages_delete_named' => 'Izbriši stran :pageName',
'pages_edit_draft_save_at' => 'Osnutek shranjen ob ',
'pages_edit_delete_draft' => 'Izbriši osnutek',
'pages_edit_discard_draft' => 'Zavrzi osnutek',
- 'pages_edit_set_changelog' => 'Nastavi zgodovino sprememb',
+ 'pages_edit_set_changelog' => 'Opiši spremembe na dokumentu',
'pages_edit_enter_changelog_desc' => 'Vnesite kratek opis sprememb, ki ste jih naredili',
- 'pages_edit_enter_changelog' => 'Vnesite zgodovino sprememb',
+ 'pages_edit_enter_changelog' => 'Vpišite vsebino sprememb',
'pages_save' => 'Shrani stran',
'pages_title' => 'Naslov strani',
'pages_name' => 'Ime strani',
'pages_md_editor' => 'Urejevalnik',
'pages_md_preview' => 'Predogled',
'pages_md_insert_image' => 'Vstavi sliko',
- 'pages_md_insert_link' => 'Vnesi povezavo entitete',
+ 'pages_md_insert_link' => 'Vnesi povezavo do objekta',
'pages_md_insert_drawing' => 'Vstavi risbo',
'pages_not_in_chapter' => 'Stran ni v poglavju',
'pages_move' => 'Premakni stran',
'pages_revisions' => 'Pregled strani',
'pages_revisions_named' => 'Pregledi strani za :pageName',
'pages_revision_named' => 'Pregled strani za :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Ustvaril',
'pages_revisions_date' => 'Datum revizije',
'pages_revisions_number' => '#',
- 'pages_revisions_numbered' => 'Revizija #:id',
- 'pages_revisions_numbered_changes' => 'Revizija #:id Changes',
+ 'pages_revisions_numbered' => 'Revizija št. :id',
+ 'pages_revisions_numbered_changes' => 'Revizija št. #:id Changes',
'pages_revisions_changelog' => 'Dnevnik sprememb',
'pages_revisions_changes' => 'Spremembe',
'pages_revisions_current' => 'Trenutna različica',
'pages_revisions_preview' => 'Predogled',
'pages_revisions_restore' => 'Obnovi',
- 'pages_revisions_none' => 'Ta stran nima revizije',
+ 'pages_revisions_none' => 'Ta stran nima popravkov',
'pages_copy_link' => 'Kopiraj povezavo',
'pages_edit_content_link' => 'Uredi vsebino',
'pages_permissions_active' => 'Aktivna dovoljenja strani',
'pages_initial_revision' => 'Prvotno objavljeno',
'pages_initial_name' => 'Nova stran',
- 'pages_editing_draft_notification' => 'Trenutno urejate osnutek ku je bil nazadnje shranjen :timeDiff.',
- 'pages_draft_edited_notification' => 'Ta stran je od takrat posodobljena. Priporočamo, da zavržete ta osnutek.',
+ 'pages_editing_draft_notification' => 'Trenutno urejate osnutek, ki je bil nazadnje shranjen :timeDiff.',
+ 'pages_draft_edited_notification' => 'Ta stran je odtlej posodobljena. Priporočamo, da zavržete ta osnutek.',
'pages_draft_edit_active' => [
'start_a' => ':count uporabnikov je začelo urejati to stran',
'start_b' => ':userName je začel urejati to stran',
- 'time_a' => 'od kar je bila stran nazandnje posodobljena',
+ 'time_a' => 'odkar je bila stran nazandnje posodobljena',
'time_b' => 'v zadnjih :minCount minutah',
'message' => ':start :time. Pazite, da ne boste prepisali posodobitev drug drugega!',
],
'tags' => 'Oznake',
'tag_name' => 'Ime oznake',
'tag_value' => 'Vrednost oznake (opcijsko)',
- 'tags_explain' => "Dodajte nekaj oznak za boljšo kategorizacijo vaše vsebine.\nDodelite lahko vrednost oznake za boljšo poglobljeno organizacijo.",
+ 'tags_explain' => "Dodajte nekaj oznak za boljšo kategorizacijo vaše vsebine.\nZ dodelitvijo oznake lahko poskrbite za bolj poglobljeno organizacijo.",
'tags_add' => 'Dodaj drugo oznako',
'tags_remove' => 'Odstrani to oznako',
- 'attachments' => 'Priloge',
- 'attachments_explain' => 'Naložite nekaj datotek ali pripnite nekaj povezav, da bo prikazano na vaši strani. Te so vidne v stranski vrstici strani.',
+ 'attachments' => 'Priponke',
+ 'attachments_explain' => 'Naložite nekaj datotek ali pripnite nekaj povezav, da jih prikažete na vaši strani. Vidne so v stranski orodni vrstici.',
'attachments_explain_instant_save' => 'Spremembe tukaj so takoj shranjene.',
- 'attachments_items' => 'Priloženi element',
+ 'attachments_items' => 'Priloženi elementi',
'attachments_upload' => 'Naloži datoteko',
'attachments_link' => 'Pripni povezavo',
'attachments_set_link' => 'Nastavi povezavo',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'Ali ste prepričani, da želite izbrisati to priponko?',
'attachments_dropzone' => 'Spustite datoteke ali kliknite tukaj, če želite priložiti datoteko',
'attachments_no_files' => 'Nobena datoteka ni bila naložena',
'attachments_explain_link' => 'Lahko pripnete povezavo, če ne želite naložiti datoteke. Lahko je povezava na drugo stran ali povezava do dateteke v oblaku.',
'attachments_link_name' => 'Ime povezave',
'attachment_link' => 'Povezava priponke',
'attachments_link_url' => 'Povezava do datoteke',
- 'attachments_link_url_hint' => 'Url mesta ali datoteke',
+ 'attachments_link_url_hint' => 'Url spletnega mesta ali datoteke',
'attach' => 'Pripni',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Dodaj povezavo na priponko na stran',
'attachments_edit_file' => 'Uredi datoteko',
'attachments_edit_file_name' => 'Ime datoteke',
'attachments_edit_drop_upload' => 'Spustite datoteke ali kliknite tukaj, če želite naložiti in prepisati',
- 'attachments_order_updated' => 'Priloga posodobljena',
+ 'attachments_order_updated' => 'Razvrščanje priponk posodobljeno',
'attachments_updated_success' => 'Podrobnosti priloge posodobljene',
- 'attachments_deleted' => 'Priloga izbirsana',
+ 'attachments_deleted' => 'Priponka izbirsana',
'attachments_file_uploaded' => 'Datoteka uspešno naložena',
'attachments_file_updated' => 'Datoteka uspešno posodobljena',
'attachments_link_attached' => 'Povezava uspešno dodana na stran',
'templates' => 'Predloge',
'templates_set_as_template' => 'Stran je predloga',
- 'templates_explain_set_as_template' => 'To stran lahko nastavite kot predlogo tako bo njena vsebina uporabljena pri izdelavi drugih strani. Ostali uporabniki bodo lahko uporabljali to predlogo, če imajo dovoljenja za to stran.',
+ 'templates_explain_set_as_template' => 'To stran lahko nastavite kot predlogo in njeno vsebino uporabite pri izdelavi drugih strani. Ostali uporabniki bodo lahko uporabljali to predlogo, če imajo dovoljenja za to stran.',
'templates_replace_content' => 'Zamenjaj vsebino strani',
- 'templates_append_content' => 'Dodajte vsebini strani',
- 'templates_prepend_content' => 'Dodaj k vsebini strani',
+ 'templates_append_content' => 'Dodajte k vsebini strani',
+ 'templates_prepend_content' => 'Dodaj predpono k vsebini strani',
// Profile View
'profile_user_for_x' => 'Uporabnik že :time',
'profile_created_content' => 'Ustvarjena vsebina',
'profile_not_created_pages' => ':userName ni izdelal nobene strani',
'profile_not_created_chapters' => ':userName ni izdelal nobenega poglavja',
- 'profile_not_created_books' => ':userName ni izdelal nobene knjige',
+ 'profile_not_created_books' => ':userName ni objavil nobene knjige',
'profile_not_created_shelves' => ':userName ni izdelal nobene knjižne police',
// Comments
'error_user_exists_different_creds' => 'Uporabnik z e-pošto :email že obstaja, vendar z drugačnimi poverilnicami.',
'email_already_confirmed' => 'E-naslov je že bil potrjen, poskusite se prijaviti.',
'email_confirmation_invalid' => 'Ta potrditveni žeton ni veljaven ali je že bil uporabljen. Poizkusite znova.',
- 'email_confirmation_expired' => 'Potrditveni žeton je pretečen. Nova potrditvena e-pošta je bila poslana.',
+ 'email_confirmation_expired' => 'Potrditveni žeton je potekel. Nova potrditvena e-pošta je bila poslana.',
'email_confirmation_awaiting' => 'Potrebno je potrditi e-naslov',
'ldap_fail_anonymous' => 'Dostop do LDAP ni uspel z anonimno povezavo',
'ldap_fail_authed' => 'Neuspešen LDAP dostop z danimi podrobnostimi dn & gesla',
- 'ldap_extension_not_installed' => 'PHP razširitev za LDAP ni nameščen',
+ 'ldap_extension_not_installed' => 'PHP razširitev za LDAP ni nameščena',
'ldap_cannot_connect' => 'Ne morem se povezati na LDAP strežnik, neuspešna začetna povezava',
'saml_already_logged_in' => 'Že prijavljen',
'saml_user_not_registered' => 'Uporabniško ime :name ni registrirano in avtomatska registracija je onemogočena',
'saml_no_email_address' => 'Nisem našel e-naslova za tega uporabnika v podatkih iz zunanjega sistema za preverjanje pristnosti',
- 'saml_invalid_response_id' => 'Zahteva iz zunanjega sistema za preverjanje pristnosti ni prepoznana s strani procesa zagnanega s strani te aplikacije. Pomik nazaj po prijavi je lahko povzročil te težave.',
+ 'saml_invalid_response_id' => 'Zahteva iz zunanjega sistema za preverjanje pristnosti ni prepoznana s strani procesa zagnanega s strani te aplikacije. Pomik nazaj po prijavi je lahko vzrok teh težav.',
'saml_fail_authed' => 'Prijava z uporabo :system ni uspela, sistem ni zagotovil uspešne avtorizacije',
'social_no_action_defined' => 'Akcija ni določena',
'social_login_bad_response' => "Napaka pri :socialAccount prijavi:\n:error",
'social_account_register_instructions' => 'Če še nimate računa, se lahko registrirate z uporabo :socialAccount.',
'social_driver_not_found' => 'Socialni vtičnik ni najden',
'social_driver_not_configured' => 'Vaše nastavitve :socialAccount niso pravilo nastavljene.',
- 'invite_token_expired' => 'Ta link je pretečen. Namesto tega lahko ponastavite vaše geslo računa.',
+ 'invite_token_expired' => 'Ta povezava je potekla. Namesto tega lahko ponastavite vaše geslo računa.',
// System
- 'path_not_writable' => 'Poti :filePath ni bilo mogoče naložiti. Prepričajte se da je zapisljiva na strežnik.',
+ 'path_not_writable' => 'Poti :filePath ni bilo mogoče naložiti. Prepričajte se, da je zapisljiva na strežnik.',
'cannot_get_image_from_url' => 'Ne morem pridobiti slike z :url',
'cannot_create_thumbs' => 'Strežnik ne more izdelati sličice. Prosimo preverite če imate GD PHP razširitev nameščeno.',
- 'server_upload_limit' => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite manjšo velikost datoteke.',
- 'uploaded' => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite manjšo velikost datoteke.',
+ 'server_upload_limit' => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite z manjšo velikostjo datoteke.',
+ 'uploaded' => 'Strežnik ne dovoli nalaganj take velikosti. Prosimo poskusite zmanjšati velikost datoteke.',
'image_upload_error' => 'Prišlo je do napake med nalaganjem slike',
'image_upload_type_error' => 'Napačen tip (format) slike',
'file_upload_timeout' => 'Čas nalaganjanja datoteke je potekel.',
// Pages
'page_draft_autosave_fail' => 'Osnutka ni bilo mogoče shraniti. Pred shranjevanjem te strani se prepričajte, da imate internetno povezavo',
- 'page_custom_home_deletion' => 'Ne morem izbrisati strani dokler je nastavljena kot domača stran',
+ 'page_custom_home_deletion' => 'Ne morem izbrisati strani, ki je nastavljena kot domača stran',
// Entities
- 'entity_not_found' => 'Entiteta ni najdena',
+ 'entity_not_found' => 'Ne najdem tega objekta',
'bookshelf_not_found' => 'Knjižna polica ni najdena',
'book_not_found' => 'Knjiga ni najdena',
'page_not_found' => 'Stran ni najdena',
// Roles
'role_cannot_be_edited' => 'Te vloge mi možno urejati',
'role_system_cannot_be_deleted' => 'Ta vloga je sistemska in je ni možno brisati',
- 'role_registration_default_cannot_delete' => 'Te vloge ni možno brisati dokler je nastavljena kot privzeta',
- 'role_cannot_remove_only_admin' => 'Ta uporabnik je edini administrator. Dodelite vlogo administratorja drugemu uporabniku preden ga poskusite brisati.',
+ 'role_registration_default_cannot_delete' => 'Te vloge ni možno brisati, dokler je nastavljena kot privzeta',
+ 'role_cannot_remove_only_admin' => 'Ta uporabnik je edini administrator. Dodelite vlogo administratorja drugemu uporabniku, preden ga poskusite brisati.',
// Comments
'comment_list' => 'Napaka se je pojavila pri pridobivanju komentarjev.',
- 'cannot_add_comment_to_draft' => 'Ni mogoče dodajanje komentarjev v osnutek.',
- 'comment_add' => 'Napaka se je pojavila pri dodajanju / posodobitev komentarjev.',
+ 'cannot_add_comment_to_draft' => 'V osnutek ni možno dodajati komentarjev.',
+ 'comment_add' => 'Napaka se je pojavila pri dodajanju / posodobitvi komentarjev.',
'comment_delete' => 'Napaka se je pojavila pri brisanju komentarja.',
'empty_comment' => 'Praznega komentarja ne morete objaviti.',
// Error pages
'404_page_not_found' => 'Strani ni mogoče najti',
- 'sorry_page_not_found' => 'Oprostite, strani ki jo iščete ni mogoče najti.',
- 'sorry_page_not_found_permission_warning' => 'Če pričakujete, da ta stran obstaja, mogoče nimate pravic, da jo vidite.',
+ 'sorry_page_not_found' => 'Oprostite, strani ki jo iščete, ni mogoče najti.',
+ 'sorry_page_not_found_permission_warning' => 'Če pričakujete, da ta stran obstaja, mogoče nimate pravic ogleda zanjo.',
'return_home' => 'Vrni se domov',
'error_occurred' => 'Prišlo je do napake',
'app_down' => ':appName trenutno ni dosegljiva',
*/
return [
- 'previous' => '« Prejšnje',
+ 'previous' => '« Predhodno',
'next' => 'Naslednje »',
];
*/
return [
- 'password' => 'Gesla morajo biti najmanj osem znakov in se morajo ujemati s potrditvijo.',
+ 'password' => 'Gesla morajo biti najmanj osem znakov dolga in se morajo ujemati s potrditvijo.',
'user' => "Ne moremo najti uporabnika s tem e-poštnim naslovom.",
- 'token' => 'The password reset token is invalid for this email address.',
+ 'token' => 'Žeton za ponastavitev gesla ni veljaven za ta e-poštni naslov.',
'sent' => 'Poslali smo vam povezavo za ponastavitev gesla!',
'reset' => 'Vaše geslo je bilo ponastavljeno!',
// Common Messages
'settings' => 'Nastavitve',
'settings_save' => 'Shrani nastavitve',
- 'settings_save_success' => 'Nastavitve, shranjene',
+ 'settings_save_success' => 'Nastavitve shranjene',
// App Settings
- 'app_customization' => 'Prilagajanje',
+ 'app_customization' => 'Prilagoditev',
'app_features_security' => 'Lastnosti & Varnost',
'app_name' => 'Ime aplikacije',
'app_name_desc' => 'To ime je prikazano v glavi in vsaki sistemski e-pošti.',
// Maintenance settings
'maint' => 'Vzdrževanje',
- 'maint_image_cleanup' => 'odstrani /počisti slike',
- 'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
- 'maint_image_cleanup_run' => 'zaženite čiščenje',
- 'maint_image_cleanup_warning' => ':zaznano je bilo število neuporabljenih slik. Ali si prepričan, da želiš odstraniti izbrane slike?',
- 'maint_image_cleanup_success' => ':najdeno in izbrisano je bilo število neuporabljenih slik!',
- 'maint_image_cleanup_nothing_found' => 'Najdenih ni bilo nobenih neuporabljenih slik!',
+ 'maint_image_cleanup' => 'Odstrani /počisti slike',
+ 'maint_image_cleanup_desc' => "Pregleda vsebino strani in revizij ter ugotovi, katere slike in risbe so v uporabi in katere so odvečne. Preden to poženeš, naredi popolno varnostno kopijo podatkovne zbirke in slik.",
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+ 'maint_image_cleanup_run' => 'Zaženi čiščenje',
+ 'maint_image_cleanup_warning' => 'Najdenih je bilo :count verjetno neuporabljenih slik. Ali si prepričan, da želiš odstraniti izbrane slike?',
+ 'maint_image_cleanup_success' => ':count verjetno neuporavljenih slik je bilo najdenih in izbrisanih!',
+ 'maint_image_cleanup_nothing_found' => 'Ni bilo najdenih neuporabljenih slik, nič ni izbrisano!',
'maint_send_test_email' => 'Pošlji testno e-pismo',
'maint_send_test_email_desc' => 'To pošlje testno e-pošto na vaš e-poštni naslov, naveden v vašem profilu.',
- 'maint_send_test_email_run' => 'Pošlji preizkusno sporočilo',
+ 'maint_send_test_email_run' => 'Pošlji testno sporočilo',
'maint_send_test_email_success' => 'e-pošta poslana na :naslov',
- 'maint_send_test_email_mail_subject' => 'Preizkusno sporočilo',
+ 'maint_send_test_email_mail_subject' => 'Testno e-sporočilo',
'maint_send_test_email_mail_greeting' => 'Zdi se, da dostava e-pošte deluje!',
- 'maint_send_test_email_mail_text' => 'Čestitke! Če ste prejeli e.poštno obvestilo so bile vaše e-poštne nastavitve pravilno konfigurirane.',
+ 'maint_send_test_email_mail_text' => 'Čestitke! Če ste prejeli e-poštno obvestilo so bile vaše e-poštne nastavitve pravilno konfigurirane.',
+ 'maint_recycle_bin_desc' => 'Izbrisane police, knjige, poglavja in strani se pošljejo v koš, da jih je mogoče obnoviti ali trajno izbrisati. Starejše predmete v košu lahko čez nekaj časa samodejno odstranite, odvisno od konfiguracije sistema.',
+ 'maint_recycle_bin_open' => 'Odpri koš',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Koš',
+ 'recycle_bin_desc' => 'Tu lahko obnovite predmete, ki so bili izbrisani, ali pa jih trajno odstranite s sistema. Ta seznam je nefiltriran, za razliko od podobnih seznamov dejavnosti v sistemu, kjer se uporabljajo filtri dovoljenj.',
+ 'recycle_bin_deleted_item' => 'Izbrisan element',
+ 'recycle_bin_deleted_by' => 'Izbrisal uporabnik',
+ 'recycle_bin_deleted_at' => 'Čas izbrisa',
+ 'recycle_bin_permanently_delete' => 'Trajno izbrišem?',
+ 'recycle_bin_restore' => 'Obnovi',
+ 'recycle_bin_contents_empty' => 'Koš je prazen',
+ 'recycle_bin_empty' => 'Izprazni koš',
+ 'recycle_bin_empty_confirm' => 'S tem boste trajno uničili vse predmete v košu, vključno z vsebino vsakega predmeta. Ali ste prepričani, da želite izprazniti koš?',
+ 'recycle_bin_destroy_confirm' => 'S tem dejanjem boste ta element skupaj s spodaj navedenimi podrejenimi elementi trajno izbrisali iz sistema in te vsebine ne boste mogli obnoviti. Ali ste prepričani, da želite trajno izbrisati ta element?',
+ 'recycle_bin_destroy_list' => 'Predmeti, ki naj bodo trajno izbrisani',
+ 'recycle_bin_restore_list' => 'Predmeti, ki naj bodo obnovljeni',
+ 'recycle_bin_restore_confirm' => 'S tem dejanjem boste izbrisani element, vključno z vsemi podrejenimi elementi, obnovili na prvotno mesto. Če je bilo prvotno mesto od takrat izbrisano in je zdaj v košu, bo treba obnoviti tudi nadrejeni element.',
+ 'recycle_bin_restore_deleted_parent' => 'Nadrejeni element je bil prav tako izbrisan. Dokler se ne obnovi nadrejenega elementa, ni mogoče obnoviti njemu podrejenih elementov.',
+ 'recycle_bin_destroy_notification' => 'Izbrisano :count skupno število elementov iz koša.',
+ 'recycle_bin_restore_notification' => 'Obnovljeno :count skupno število elementov iz koša.',
// Audit Log
- 'audit' => 'Audit Log',
- 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit' => 'Dnevnik dogodkov',
+ 'audit_desc' => 'Ta dnevnik dogodkov prikazuje seznam dejavnosti, ki jim sledi sistem. Seznam je nefiltriran, za razliko od podobnih seznamov dejavnosti v sistemu, kjer se uporabljajo filtri dovoljenj.',
+ 'audit_event_filter' => 'Filter dogodkov',
+ 'audit_event_filter_no_filter' => 'Ni filtra',
+ 'audit_deleted_item' => 'Izbrisan element',
+ 'audit_deleted_item_name' => 'Naziv: :name',
+ 'audit_table_user' => 'Uporabnik',
+ 'audit_table_event' => 'Dogodek',
+ 'audit_table_related' => 'Povezani predmet ali podrobnost',
+ 'audit_table_date' => 'Datum zadnje dejavnosti',
+ 'audit_date_from' => 'Časovno obdobje od',
+ 'audit_date_to' => 'Časovno obdobje do',
// Role Settings
'roles' => 'Vloge',
- 'role_user_roles' => 'Pravilo uporabnika',
- 'role_create' => 'Izdelaj novo polico',
- 'role_create_success' => 'Zapis uspešno ustvarjen',
+ 'role_user_roles' => 'Vloge uporabnika',
+ 'role_create' => 'Ustvari novo vlogo',
+ 'role_create_success' => 'Vloga uspešno ustvarjena',
'role_delete' => 'Brisanje vloge',
- 'role_delete_confirm' => 'Izbrisana bo vloga z imenom \':vlogaImena\'.',
- 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
- 'role_delete_no_migration' => "Uporabniki niso prenosljivi",
- 'role_delete_sure' => 'Ali ste prepričani, da želite izbrisati to element?',
- 'role_delete_success' => 'Uspešno izbrisano',
- 'role_edit' => 'Uredi zapis',
- 'role_details' => 'Podrobnosti zapisa',
- 'role_name' => 'Ime zapisa',
- 'role_desc' => 'Kratki opis ',
+ 'role_delete_confirm' => 'Izbrisana bo vloga z imenom \':roleName\'.',
+ 'role_delete_users_assigned' => 'Ta vloga ima dodeljenih :userCount uporabnikov. V kolikor želite uporabnike preseliti iz te vloge, spodaj izberite novo vlogo.',
+ 'role_delete_no_migration' => "Ne prenašaj uporabnikov",
+ 'role_delete_sure' => 'Ali ste prepričani, da želite izbrisati to vlogo?',
+ 'role_delete_success' => 'Vloga uspešno izbrisana',
+ 'role_edit' => 'Uredi vlogo',
+ 'role_details' => 'Podrobnosti vloge',
+ 'role_name' => 'Naziv vloge',
+ 'role_desc' => 'Kratki opis vloge',
'role_external_auth_id' => 'Zunanje dokazilo ID',
'role_system' => 'Sistemska dovoljenja',
'role_manage_users' => 'Upravljanje uporabnikov',
- 'role_manage_roles' => 'Vloga upravljanja & vloga dovoljenj',
- 'role_manage_entity_permissions' => 'Upravljanje vseh knjig, poglavij & dovoljenj',
- 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages',
- 'role_manage_page_templates' => 'Manage page templates',
- 'role_access_api' => 'Access system API',
+ 'role_manage_roles' => 'Upravljanje vlog in dovoljenja vlog',
+ 'role_manage_entity_permissions' => 'Upravljanje dovoljenj vseh knjig, poglavij in strani',
+ 'role_manage_own_entity_permissions' => 'Upravljanje dovoljenj za svojo knjigo, poglavje in strani',
+ 'role_manage_page_templates' => 'Uredi predloge',
+ 'role_access_api' => 'API za dostop do sistema',
'role_manage_settings' => 'Nastavitve za upravljanje',
'role_asset' => 'Sistemska dovoljenja',
- 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
- 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
- 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
+ 'roles_system_warning' => 'Zavedajte se, da lahko dostop do kateregakoli od zgornjih treh dovoljenj uporabniku omogoči, da spremeni lastne privilegije ali privilegije drugih v sistemu. Vloge s temi dovoljenji dodelite samo zaupanja vrednim uporabnikom.',
+ 'role_asset_desc' => 'Ta dovoljenja nadzorujejo privzeti dostop do sredstev v sistemu. Dovoljenja za knjige, poglavja in strani bodo razveljavila ta dovoljenja.',
+ 'role_asset_admins' => 'Skrbniki samodejno pridobijo dostop do vseh vsebin, vendar lahko te možnosti prikažejo ali pa skrijejo možnosti uporabniškega vmesnika.',
'role_all' => 'Vse',
- 'role_own' => 'Own',
- 'role_controlled_by_asset' => '
-46/5000
-Nadzira ga sredstvo, v katerega so naloženi',
+ 'role_own' => 'Lasten',
+ 'role_controlled_by_asset' => 'Nadzira ga sredstvo, v katerega so naloženi',
'role_save' => 'Shrani vlogo',
'role_update_success' => 'Vloga uspešno posodobljena',
'role_users' => 'Uporabniki v tej vlogi',
- 'role_users_none' => '
-V tej vlogi trenutno ni dodeljen noben uporabnik',
+ 'role_users_none' => 'Tej vlogi trenutno ni dodeljen noben uporabnik',
// Users
'users' => 'Uporabniki',
'user_profile' => 'Uporabniški profil',
'users_add_new' => 'Dodaj novega uporabnika',
'users_search' => 'Išči uporabnike',
+ 'users_latest_activity' => 'Zadnja dejavnost',
'users_details' => 'Podatki o uporabniku',
'users_details_desc' => 'Nastavite prikazno ime in e-poštni naslov za tega uporabnika. E-poštni naslov bo uporabljen za prijavo v aplikacijo.',
'users_details_desc_no_email' => ' Nastavite prikazno ime za tega uporabnika, da ga bodo drugi lahko prepoznali.',
'users_role' => 'Vloge uporabnika',
- 'users_role_desc' => 'Izberi katere vloge bodo dodeljene uporabniku. Če je uporabniku dodeljenih več vlog, se dovoljenja združijo in prejmenjo vse sposobnosti dodeljenih vlog.',
+ 'users_role_desc' => 'Izberi vloge, ki bodo dodeljene uporabniku. Če je uporabniku dodeljenih več vlog, se dovoljenja združijo in prejmenjo vsa dovoljenja dodeljenih vlog.',
'users_password' => 'Uporabniško geslo',
- 'users_password_desc' => 'Nastavite geslo, ki se uporablja za prijavo v aplikacijo. Dolg mora biti vsaj 6 znakov.',
- 'users_send_invite_text' => 'Uporabniku lahko pošljete e-poštno sporočilo s povabilom, ki mu omogoča, da nastavi svoje geslo, ali ga nastavite kar sami.',
- 'users_send_invite_option' => 'Pošlji uporabniku e-povabilo',
+ 'users_password_desc' => 'Nastavite geslo, ki se uporablja za prijavo v aplikacijo. Dolgo mora biti vsaj 6 znakov.',
+ 'users_send_invite_text' => 'Uporabniku lahko pošljete e-poštno sporočilo s povabilom, ki mu omogoča, da nastavi svoje geslo, ali pa ga nastavite kar sami.',
+ 'users_send_invite_option' => 'Pošlji uporabniku e-poštno povabilo',
'users_external_auth_id' => 'Zunanje dokazilo ID',
'users_external_auth_id_desc' => 'To je ID, s katerim se ta uporabnik ujema pri komunikaciji z vašim zunanjim sistemom za preverjanje pristnosti.',
'users_password_warning' => 'Spodaj izpolni le, če želiš spremeniti geslo.',
- 'users_system_public' => 'Ta uporabnik predstavlja vse gostujoče uporabnike, ki obiščejo vaš primer. Za prijavo je ni mogoče uporabiti, ampak je dodeljena samodejno.',
+ 'users_system_public' => 'Ta uporabnik predstavlja vse gostujoče uporabnike, ki obiščejo vašo wiki stran. Za prijavo je ni mogoče uporabiti, ampak je dodeljena samodejno.',
'users_delete' => 'Brisanje uporabnika',
'users_delete_named' => 'Brisanje uporabnika :userName',
'users_delete_warning' => 'Iz sistema se bo popolnoma izbrisal uporabnik z imenom \':userName\'',
- 'users_delete_confirm' => 'Ste prepričani, da želite izbrisati izbranega uporabnika?',
- 'users_delete_success' => 'Uporabniki uspešno odstranjeni.',
+ 'users_delete_confirm' => 'Ste prepričani, da želite izbrisati tega uporabnika?',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Uredi uporabnika',
'users_edit_profile' => 'Uredi profil',
'users_edit_success' => 'Uporabnik uspešno posodobljen',
- 'users_avatar' => 'uporabnikov avatar',
+ 'users_avatar' => 'Uporabnikov avatar',
'users_avatar_desc' => 'Izberi sliko, ki predstavlja uporabnika. Velikost mora biti približno 256px.',
'users_preferred_language' => 'Izbrani jezik',
'users_preferred_language_desc' => 'Ta možnost bo spremenila jezik, ki se uporablja za uporabniški vmesnik aplikacije. To ne bo vplivalo na nobeno vsebino, ki jo ustvari uporabnik.',
'users_social_accounts' => 'Družbene ikone / računi',
- 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+ 'users_social_accounts_info' => 'Tu lahko za hitrejšo in lažjo prijavo povežete druge račune. Prekinitev povezave računa tukaj ne prekliče predhodno odobrenega dostopa. Prekličite dostop iz nastavitev profila v povezanem družabnem računu.',
'users_social_connect' => 'Povežite račun',
'users_social_disconnect' => 'Odklop računa',
- 'users_social_connected' => ':socialAccount račun je bil uspešno dodan na vašem profilu',
- 'users_social_disconnected' => ':socialAccount račun je bil uspešno odstranjen iz vašega profila',
+ 'users_social_connected' => ':socialAccount račun je bil uspešno dodan vašemu profilu.',
+ 'users_social_disconnected' => ':socialAccount račun je bil uspešno odstranjen iz vašega profila.',
'users_api_tokens' => 'API žeton',
'users_api_tokens_none' => 'Nič API žetonov ni bilo ustvarjenih za uporabnika',
'users_api_tokens_create' => 'Ustvari žeton',
// API Tokens
'user_api_token_create' => 'Ustvari žeton',
'user_api_token_name' => 'Ime',
- 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+ 'user_api_token_name_desc' => 'Dajte žetonu berljivo ime kot prihodnji opomnik o predvidenem namenu.',
'user_api_token_expiry' => 'Datum poteka',
'user_api_token_expiry_desc' => 'Določi datum izteka uporabnosti žetona. Po tem datumu, zahteve poslane s tem žetonom, ne bodo več delovale.
Če pustite to polje prazno, bo iztek uporabnosti 100.let .',
'user_api_token_update_success' => 'API žeton uspešno posodobljen',
'user_api_token' => 'API žeton',
'user_api_token_id' => 'Žeton ID',
- 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+ 'user_api_token_id_desc' => 'To je sistemski identifikator, ki ga ni mogoče urejati za ta žeton in ga je treba navesti v zahtevah za API.',
'user_api_token_secret' => 'Skrivnost žetona',
- 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+ 'user_api_token_secret_desc' => 'To je sistemsko ustvarjena skrivnost za ta žeton, ki jo bo treba navesti v zahtevah za API. To bo prikazano samo enkrat, zato kopirajte to vrednost na varno mesto.',
'user_api_token_created' => 'Žeton ustvarjen :timeAgo',
'user_api_token_updated' => 'Žeton posodobljen :timeAgo',
'user_api_token_delete' => 'Briši žeton',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Polje atributa je obvezno, če: vrednosti niso prisotne.',
'required_without_all' => 'Polje atributa je obvezno, če nobena od: vrednosti ni prisotna.',
'same' => 'Atribut in: drugi se morajo ujemati.',
+ 'safe_url' => 'Podana povezava morda ni varna.',
'size' => [
'numeric' => ':attribute mora biti :velikost.',
'file' => ':attribute mora biti :velikost KB.',
// Other
'commented_on' => 'kommenterade',
+ 'permissions_update' => 'uppdaterade behörigheter',
];
'reset_password' => 'Återställ lösenord',
'reset_password_send_instructions' => 'Ange din e-postadress nedan så skickar vi ett mail med en länk för att återställa ditt lösenord.',
'reset_password_send_button' => 'Skicka återställningslänk',
- 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+ 'reset_password_sent' => 'En länk för återställning av lösenord kommer att skickas till :email om den e-postadressen finns i systemet.',
'reset_password_success' => 'Ditt lösenord har återställts.',
'email_reset_subject' => 'Återställ ditt lösenord till :appName',
'email_reset_text' => 'Du får detta mail eftersom vi fått en begäran om att återställa lösenordet till ditt konto.',
'copy' => 'Kopiera',
'reply' => 'Svara',
'delete' => 'Ta bort',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'Bekräfta radering',
'search' => 'Sök',
'search_clear' => 'Rensa sökning',
'reset' => 'Återställ',
'profile_menu' => 'Profilmeny',
'view_profile' => 'Visa profil',
'edit_profile' => 'Redigera profil',
- 'dark_mode' => 'Dark Mode',
- 'light_mode' => 'Light Mode',
+ 'dark_mode' => 'Mörkt läge',
+ 'light_mode' => 'Ljust Läge',
// Layout tabs
'tab_info' => 'Information',
'image_load_more' => 'Ladda fler',
'image_image_name' => 'Bildnamn',
'image_delete_used' => 'Den här bilden används på nedanstående sidor.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'Är du säker på att du vill radera denna bild?',
'image_select_image' => 'Välj bild',
'image_dropzone' => 'Släpp bilder här eller klicka för att ladda upp',
'images_deleted' => 'Bilder borttagna',
'code_editor' => 'Redigera kod',
'code_language' => 'Språk',
'code_content' => 'Kod',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'Sessionshistorik',
'code_save' => 'Spara',
];
'meta_created_name' => 'Skapad :timeLength av :user',
'meta_updated' => 'Uppdaterad :timeLength',
'meta_updated_name' => 'Uppdaterad :timeLength av :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Välj enhet',
'images' => 'Bilder',
'my_recent_drafts' => 'Mina nyaste utkast',
'permissions_intro' => 'Dessa rättigheter kommer att överskrida eventuella rollbaserade rättigheter.',
'permissions_enable' => 'Aktivera anpassade rättigheter',
'permissions_save' => 'Spara rättigheter',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Sökresultat',
'search_no_pages' => 'Inga sidor matchade sökningen',
'search_for_term' => 'Sök efter :term',
'search_more' => 'Fler resultat',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => 'Avancerad sök',
+ 'search_terms' => 'Söktermer',
'search_content_type' => 'Innehållstyp',
'search_exact_matches' => 'Exakta matchningar',
'search_tags' => 'Taggar',
'chapters_create' => 'Skapa nytt kapitel',
'chapters_delete' => 'Radera kapitel',
'chapters_delete_named' => 'Radera kapitlet :chapterName',
- 'chapters_delete_explain' => 'Du håller på att ta bort kapitlet \':chapterName\'. Alla sidor kommer att flyttas direkt in i den aktuella boken istället.',
+ 'chapters_delete_explain' => 'Detta kommer att ta bort kapitlet med namnet \':chapterName\'. Alla sidor som finns inom detta kapitel kommer också att raderas.',
'chapters_delete_confirm' => 'Är du säker på att du vill ta bort det här kapitlet?',
'chapters_edit' => 'Redigera kapitel',
'chapters_edit_named' => 'Redigera kapitel :chapterName',
'pages_revisions' => 'Sidrevisioner',
'pages_revisions_named' => 'Sidrevisioner för :pageName',
'pages_revision_named' => 'Sidrevision för :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Skapad av',
'pages_revisions_date' => 'Revisionsdatum',
'pages_revisions_number' => '#',
'attachments_upload' => 'Ladda upp fil',
'attachments_link' => 'Bifoga länk',
'attachments_set_link' => 'Ange länk',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'Är du säker på att du vill ta bort bilagan?',
'attachments_dropzone' => 'Släpp filer här eller klicka för att ladda upp',
'attachments_no_files' => 'Inga filer har laddats upp',
'attachments_explain_link' => 'Du kan bifoga en länk om du inte vill ladda upp en fil. Detta kan vara en länk till en annan sida eller till en fil i molnet.',
'attachments_link_url' => 'Länk till fil',
'attachments_link_url_hint' => 'URL till sida eller fil',
'attach' => 'Bifoga',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Lägg till bilagelänk till sida',
'attachments_edit_file' => 'Redigera fil',
'attachments_edit_file_name' => 'Filnamn',
'attachments_edit_drop_upload' => 'Släpp filer här eller klicka för att ladda upp och skriva över',
'password' => 'Lösenord måste vara minst sex tecken långa och anges likadant två gånger.',
'user' => "Det finns ingen användare med den e-postadressen.",
- 'token' => 'The password reset token is invalid for this email address.',
+ 'token' => 'Lösenordsåterställningstoken är ogiltig för denna e-postadress.',
'sent' => 'Vi har mailat dig en länk för att återställa ditt lösenord!',
'reset' => 'Ditt lösenord har blivit återställt!',
'maint' => 'Underhåll',
'maint_image_cleanup' => 'Rensa bilder',
'maint_image_cleanup_desc' => "Söker igenom innehåll i sidor & revisioner för att se vilka bilder och teckningar som är i bruk och vilka som är överflödiga. Se till att ta en komplett backup av databas och bilder innan du kör detta.",
- 'maint_image_cleanup_ignore_revisions' => 'Ignorera bilder i revisioner',
+ 'maint_delete_images_only_in_revisions' => 'Ta också bort bilder som bara finns i gamla sidrevideringar',
'maint_image_cleanup_run' => 'Kör rensning',
'maint_image_cleanup_warning' => 'Hittade :count bilder som potentiellt inte används. Vill du verkligen ta bort dessa bilder?',
'maint_image_cleanup_success' => 'Hittade och raderade :count bilder som potentiellt inte används!',
'maint_send_test_email_mail_subject' => 'Testmejl',
'maint_send_test_email_mail_greeting' => 'E-postleverans verkar fungera!',
'maint_send_test_email_mail_text' => 'Grattis! Eftersom du fick detta e-postmeddelande verkar dina e-postinställningar vara korrekt konfigurerade.',
+ 'maint_recycle_bin_desc' => 'Borttagna hyllor, böcker, kapitel & sidor skickas till papperskorgen så att de kan återställas eller raderas permanent. Äldre objekt i papperskorgen kan automatiskt tas bort efter ett tag beroende på systemkonfiguration.',
+ 'maint_recycle_bin_open' => 'Öppna papperskorgen',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Papperskorgen',
+ 'recycle_bin_desc' => 'Här kan du återställa objekt som har tagits bort eller välja att permanent ta bort dem från systemet. Denna lista är ofiltrerad till skillnad från liknande aktivitetslistor i systemet där behörighetsfilter tillämpas.',
+ 'recycle_bin_deleted_item' => 'Raderat objekt',
+ 'recycle_bin_deleted_by' => 'Borttagen av',
+ 'recycle_bin_deleted_at' => 'Tid för borttagning',
+ 'recycle_bin_permanently_delete' => 'Radera permanent',
+ 'recycle_bin_restore' => 'Återställ',
+ 'recycle_bin_contents_empty' => 'Papperskorgen är för närvarande tom',
+ 'recycle_bin_empty' => 'Töm papperskorgen',
+ 'recycle_bin_empty_confirm' => 'Detta kommer permanent att förstöra alla objekt i papperskorgen inklusive innehåll som finns i varje objekt. Är du säker du vill tömma papperskorgen?',
+ 'recycle_bin_destroy_confirm' => 'Denna åtgärd kommer att permanent ta bort detta objekt, tillsammans med alla underordnade element som anges nedan, från systemet och du kommer inte att kunna återställa detta innehåll. Är du säker på att du vill ta bort objektet permanent?',
+ 'recycle_bin_destroy_list' => 'Objekt som ska förstöras',
+ 'recycle_bin_restore_list' => 'Objekt som ska återställas',
+ 'recycle_bin_restore_confirm' => 'Denna åtgärd kommer att återställa det raderade objektet, inklusive alla underordnade element, till deras ursprungliga plats. Om den ursprungliga platsen har tagits bort sedan dess, och är nu i papperskorgen, kommer det överordnade objektet också att behöva återställas.',
+ 'recycle_bin_restore_deleted_parent' => 'Föräldern till det här objektet har också tagits bort. Dessa kommer att förbli raderade tills den förälder är återställd.',
+ 'recycle_bin_destroy_notification' => 'Raderade :count totala objekt från papperskorgen.',
+ 'recycle_bin_restore_notification' => 'Återställt :count totala objekt från papperskorgen.',
// Audit Log
- 'audit' => 'Audit Log',
- 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit' => 'Auditlogg',
+ 'audit_desc' => 'Denna granskningslogg visar en lista över aktiviteter som spåras i systemet. Denna lista är ofiltrerad till skillnad från liknande aktivitetslistor i systemet där behörighetsfilter tillämpas.',
+ 'audit_event_filter' => 'Händelse Filter',
+ 'audit_event_filter_no_filter' => 'Inget filter',
+ 'audit_deleted_item' => 'Raderat objekt',
+ 'audit_deleted_item_name' => 'Namn: :name',
+ 'audit_table_user' => 'Användare',
+ 'audit_table_event' => 'Händelse',
+ 'audit_table_related' => 'Relaterat objekt eller detalj',
+ 'audit_table_date' => 'Datum för senaste aktiviteten',
+ 'audit_date_from' => 'Datumintervall från',
+ 'audit_date_to' => 'Datumintervall till',
// Role Settings
'roles' => 'Roller',
'role_access_api' => 'Åtkomst till systemets API',
'role_manage_settings' => 'Hantera appinställningar',
'role_asset' => 'Tillgång till innehåll',
- 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+ 'roles_system_warning' => 'Var medveten om att åtkomst till någon av ovanstående tre behörigheter kan tillåta en användare att ändra sina egna rättigheter eller andras rättigheter i systemet. Tilldela endast roller med dessa behörigheter till betrodda användare.',
'role_asset_desc' => 'Det här är standardinställningarna för allt innehåll i systemet. Eventuella anpassade rättigheter på böcker, kapitel och sidor skriver över dessa inställningar.',
'role_asset_admins' => 'Administratörer har automatisk tillgång till allt innehåll men dessa alternativ kan visa och dölja vissa gränssnittselement',
'role_all' => 'Alla',
'user_profile' => 'Användarprofil',
'users_add_new' => 'Lägg till användare',
'users_search' => 'Sök användare',
+ 'users_latest_activity' => 'Senaste aktivitet',
'users_details' => 'Användarinformation',
'users_details_desc' => 'Ange ett visningsnamn och en e-postadress för den här användaren. E-postadressen kommer att användas vid inloggningen.',
'users_details_desc_no_email' => 'Ange ett visningsnamn för den här användaren så att andra kan känna igen den.',
'users_delete_named' => 'Ta bort användaren :userName',
'users_delete_warning' => 'Detta kommer att ta bort användaren \':userName\' från systemet helt och hållet.',
'users_delete_confirm' => 'Är du säker på att du vill ta bort användaren?',
- 'users_delete_success' => 'Användaren har tagits bort',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Redigera användare',
'users_edit_profile' => 'Redigera profil',
'users_edit_success' => 'Användaren har uppdaterats',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':attribute är obligatoriskt när :values inte finns.',
'required_without_all' => ':attribute är obligatirskt när ingen av :values finns.',
'same' => ':attribute och :other måste stämma överens.',
+ 'safe_url' => 'Den angivna länken kanske inte är säker.',
'size' => [
'numeric' => ':attribute måste vara :size.',
'file' => ':attribute måste vara :size kilobyte.',
// Other
'commented_on' => 'yorum yaptı',
+ 'permissions_update' => 'güncellenmiş izinler',
];
'meta_created_name' => ':user tarafından :timeLength oluşturuldu',
'meta_updated' => ':timeLength güncellendi',
'meta_updated_name' => ':user tarafından :timeLength güncellendi',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Öge Seçimi',
'images' => 'Görseller',
'my_recent_drafts' => 'Son Taslaklarım',
'permissions_intro' => 'Etkinleştirildikten sonra bu izinler, diğer bütün izinlerden öncelikli olacaktır.',
'permissions_enable' => 'Özelleştirilmiş Yetkileri Etkinleştir',
'permissions_save' => 'İzinleri Kaydet',
+ 'permissions_owner' => 'Sahip',
// Search
'search_results' => 'Arama Sonuçları',
'chapters_create' => 'Yeni Bölüm Oluştur',
'chapters_delete' => 'Bölümü Sil',
'chapters_delete_named' => ':chapterName Bölümünü Sil',
- 'chapters_delete_explain' => 'Bu işlem sonunda \':chapterName\' bölümü silinecek ve bu bölüme ait bütün sayfalar direkt olarak ana kitaba aktarılacaktır.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Bölümü silmek istediğinize emin misiniz?',
'chapters_edit' => 'Bölümü Düzenle',
'chapters_edit_named' => ':chapterName Bölümünü Düzenle',
'pages_revisions' => 'Sayfa Revizyonları',
'pages_revisions_named' => ':pageName için Sayfa Revizyonları',
'pages_revision_named' => ':pageName için Sayfa Revizyonu',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Revize Eden',
'pages_revisions_date' => 'Revizyon Tarihi',
'pages_revisions_number' => '#',
'attachments_link_url' => 'Dosya bağlantısı',
'attachments_link_url_hint' => 'Dosyanın veya sitenin url adresi',
'attach' => 'Ekle',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Sayfaya Bağlantı Ekle',
'attachments_edit_file' => 'Dosyayı Düzenle',
'attachments_edit_file_name' => 'Dosya Adı',
'attachments_edit_drop_upload' => 'Üzerine yazılacak dosyaları sürükleyin veya seçin',
'maint' => 'Bakım',
'maint_image_cleanup' => 'Görselleri Temizle',
'maint_image_cleanup_desc' => "Sayfaları ve revizyon içeriklerini tarayarak hangi görsellerin ve çizimlerin kullanımda olduğunu ve hangilerinin gereksiz olduğunu tespit eder. Bunu başlatmadan önce veritabanının ve görsellerin tam bir yedeğinin alındığından emin olun.",
- 'maint_image_cleanup_ignore_revisions' => 'Revizyonlardaki görselleri yoksay',
+ 'maint_delete_images_only_in_revisions' => 'Eski sayfa revizyonlarındaki görselleri de sil',
'maint_image_cleanup_run' => 'Temizliği Başlat',
'maint_image_cleanup_warning' => 'Muhtemelen kullanılmayan :count adet görsel bulundu. Bu görselleri silmek istediğinize emin misiniz?',
'maint_image_cleanup_success' => 'Muhtemelen kullanılmayan :count adet görsel bulundu ve silindi!',
'maint_send_test_email_mail_subject' => 'Deneme E-postası',
'maint_send_test_email_mail_greeting' => 'E-posta iletimi çalışıyor gibi görünüyor!',
'maint_send_test_email_mail_text' => 'Tebrikler! Eğer bu e-posta bildirimini alıyorsanız, e-posta ayarlarınız doğru bir şekilde ayarlanmış demektir.',
+ 'maint_recycle_bin_desc' => 'Silinen raflar, kitaplar, bölümler ve sayfalar geri dönüşüm kutusuna gönderilir, böylece geri yüklenebilir veya kalıcı olarak silinebilir. Geri dönüşüm kutusundaki daha eski öğeler, sistem yapılandırmasına bağlı olarak bir süre sonra otomatik olarak kaldırılabilir.',
+ 'maint_recycle_bin_open' => 'Geri Dönüşüm Kutusunu Aç',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Geri Dönüşüm Kutusu',
+ 'recycle_bin_desc' => 'Burada silinen öğeleri geri yükleyebilir veya bunları sistemden kalıcı olarak kaldırmayı seçebilirsiniz. Bu liste, izin filtrelerinin uygulandığı sistemdeki benzer etkinlik listelerinden farklı olarak filtrelenmez.',
+ 'recycle_bin_deleted_item' => 'Silinen öge',
+ 'recycle_bin_deleted_by' => 'Tarafından silindi',
+ 'recycle_bin_deleted_at' => 'Silinme Zamanı',
+ 'recycle_bin_permanently_delete' => 'Kalıcı Olarak Sil',
+ 'recycle_bin_restore' => 'Geri Yükle',
+ 'recycle_bin_contents_empty' => 'Geri dönüşüm kutusu boş',
+ 'recycle_bin_empty' => 'Geri Dönüşüm Kutusunu Boşalt',
+ 'recycle_bin_empty_confirm' => 'Bu işlem, her bir öğenin içinde bulunan içerik de dahil olmak üzere geri dönüşüm kutusundaki tüm öğeleri kalıcı olarak imha edecektir. Geri dönüşüm kutusunu boşaltmak istediğinizden emin misiniz?',
+ 'recycle_bin_destroy_confirm' => 'Bu işlem, bu öğeyi kalıcı olarak ve aşağıda listelenen alt öğelerle birlikte sistemden silecek ve bu içeriği geri yükleyemeyeceksiniz. Bu öğeyi kalıcı olarak silmek istediğinizden emin misiniz?',
+ 'recycle_bin_destroy_list' => 'Kalıcı Olarak Silinecek Öğeler',
+ 'recycle_bin_restore_list' => 'Geri Yüklenecek Öğeler',
+ 'recycle_bin_restore_confirm' => 'Bu eylem, tüm alt öğeler dahil olmak üzere silinen öğeyi orijinal konumlarına geri yükleyecektir. Orijinal konum o zamandan beri silinmişse ve şimdi geri dönüşüm kutusunda bulunuyorsa, üst öğenin de geri yüklenmesi gerekecektir.',
+ 'recycle_bin_restore_deleted_parent' => 'Bu öğenin üst öğesi de silindi. Bunlar, üst öğe de geri yüklenene kadar silinmiş olarak kalacaktır.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
- 'audit' => 'Audit Log',
- 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit' => 'Denetim Kaydı',
+ 'audit_desc' => 'Bu denetim günlüğü, sistemde izlenen etkinliklerin bir listesini görüntüler. Bu liste, izin filtrelerinin uygulandığı sistemdeki benzer etkinlik listelerinden farklı olarak filtrelenmez.',
+ 'audit_event_filter' => 'Etkinlik Filtresi',
+ 'audit_event_filter_no_filter' => 'Filtre Yok',
+ 'audit_deleted_item' => 'Silinen Öge',
+ 'audit_deleted_item_name' => 'Isim: :name',
+ 'audit_table_user' => 'Kullanıcı',
+ 'audit_table_event' => 'Etkinlik',
+ 'audit_table_related' => 'İlgili Öğe veya Detay',
+ 'audit_table_date' => 'Aktivite Tarihi',
+ 'audit_date_from' => 'Tarih Aralığından',
+ 'audit_date_to' => 'Tarih Aralığına',
// Role Settings
'roles' => 'Roller',
'user_profile' => 'Kullanıcı Profili',
'users_add_new' => 'Yeni Kullanıcı Ekle',
'users_search' => 'Kullanıcı Ara',
+ 'users_latest_activity' => 'Son Etkinlik',
'users_details' => 'Kullanıcı Detayları',
'users_details_desc' => 'Bu kullanıcı için gösterilecek bir isim ve e-posta adresi belirleyin. Buraya yazacağınız e-posta adresi, uygulamaya giriş yaparken kullanılacaktır.',
'users_details_desc_no_email' => 'Diğer kullanıcılar tarafından tanınabilmesi için bir isim belirleyin.',
'users_delete_named' => ':userName kullanıcısını sil ',
'users_delete_warning' => 'Bu işlem \':userName\' kullanıcısını sistemden tamamen silecektir.',
'users_delete_confirm' => 'Bu kullanıcıyı tamamen silmek istediğinize emin misiniz?',
- 'users_delete_success' => 'Kullanıcılar başarıyla silindi.',
+ 'users_migrate_ownership' => 'Sahipliği Taşıyın',
+ 'users_migrate_ownership_desc' => 'Başka bir kullanıcının şu anda bu kullanıcıya ait olan tüm öğelerin sahibi olmasını istiyorsanız buradan bir kullanıcı seçin.',
+ 'users_none_selected' => 'Hiçbir kullanıcı seçilmedi',
+ 'users_delete_success' => 'Kullanıcı başarıyla kaldırıldı',
'users_edit' => 'Kullanıcıyı Düzenle',
'users_edit_profile' => 'Profili Düzenle',
'users_edit_success' => 'Kullanıcı başarıyla güncellendi',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => ':values değerinin bulunmuyor olması, :attribute alanını zorunlu kılar.',
'required_without_all' => ':values değerlerinden hiçbirinin bulunmuyor olması, :attribute alanını zorunlu kılar.',
'same' => ':attribute ve :other eşleşmelidir.',
+ 'safe_url' => 'Sağlanan bağlantı güvenli olmayabilir.',
'size' => [
'numeric' => ':attribute, :size boyutunda olmalıdır.',
'file' => ':attribute, :size kilobayt olmalıdır.',
// Other
'commented_on' => 'прокоментував',
+ 'permissions_update' => 'updated permissions',
];
'copy' => 'Копіювати',
'reply' => 'Відповісти',
'delete' => 'Видалити',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'Підтвердити видалення',
'search' => 'Шукати',
'search_clear' => 'Очистити пошук',
'reset' => 'Скинути',
'image_load_more' => 'Завантажити ще',
'image_image_name' => 'Назва зображення',
'image_delete_used' => 'Це зображення використовується на наступних сторінках.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'Ви дійсно хочете видалити це зображення?',
'image_select_image' => 'Вибрати зображення',
'image_dropzone' => 'Перетягніть зображення, або натисніть тут для завантаження',
'images_deleted' => 'Зображень видалено',
'code_editor' => 'Редагувати код',
'code_language' => 'Мова коду',
'code_content' => 'Вміст коду',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'Історія сесії',
'code_save' => 'Зберегти Код',
];
'meta_created_name' => ':user створив :timeLength',
'meta_updated' => 'Оновлено :timeLength',
'meta_updated_name' => ':user оновив :timeLength',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Вибір об\'єкта',
'images' => 'Зображення',
'my_recent_drafts' => 'Мої останні чернетки',
'permissions_intro' => 'Після ввімкнення ці дозволи будуть мати вищий пріоритет ніж інші дозволи ролей.',
'permissions_enable' => 'Увімкнути спеціальні дозволи',
'permissions_save' => 'Зберегти дозволи',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Результати пошуку',
'search_no_pages' => 'Немає сторінок, які відповідають цьому пошуку',
'search_for_term' => 'Шукати :term',
'search_more' => 'Більше результатів',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => 'Розширений пошук',
+ 'search_terms' => 'Пошукові фрази',
'search_content_type' => 'Тип вмісту',
'search_exact_matches' => 'Точна відповідність',
'search_tags' => 'Пошукові теги',
'chapters_create' => 'Створити новий розділ',
'chapters_delete' => 'Видалити розділ',
'chapters_delete_named' => 'Видалити розділ :chapterName',
- 'chapters_delete_explain' => 'Ця дія видалить розділ з назвою \':chapterName\'. Всі сторінки будуть вилучені, та додані безпосередньо до батьківської книги.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Ви впевнені, що хочете видалити цей розділ?',
'chapters_edit' => 'Редагувати розділ',
'chapters_edit_named' => 'Редагувати розділ :chapterName',
'pages_revisions' => 'Версія сторінки',
'pages_revisions_named' => 'Версії сторінки для :pageName',
'pages_revision_named' => 'Версія сторінки для :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Створена',
'pages_revisions_date' => 'Дата версії',
'pages_revisions_number' => '#',
'attachments_upload' => 'Завантажити файл',
'attachments_link' => 'Приєднати посилання',
'attachments_set_link' => 'Встановити посилання',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'Дійсно хочете видалити це вкладення?',
'attachments_dropzone' => 'Перетягніть файли, або натисніть тут щоб прикріпити файл',
'attachments_no_files' => 'Файли не завантажені',
'attachments_explain_link' => 'Ви можете приєднати посилання, якщо не бажаєте завантажувати файл. Це може бути посилання на іншу сторінку або посилання на файл у хмарі.',
'attachments_link_url' => 'Посилання на файл',
'attachments_link_url_hint' => 'URL-адреса сайту або файлу',
'attach' => 'Приєднати',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Додати посилання на вкладення',
'attachments_edit_file' => 'Редагувати файл',
'attachments_edit_file_name' => 'Назва файлу',
'attachments_edit_drop_upload' => 'Перетягніть файли, або натисніть тут щоб завантажити та перезаписати',
'api_no_authorization_found' => 'У запиті не знайдено токен авторизації',
'api_bad_authorization_format' => 'У запиті знайдено токен авторизації, але формат недійсний',
'api_user_token_not_found' => 'Не знайдено відповідного API-токена для наданого токена авторизації',
- 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
- 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
+ 'api_incorrect_token_secret' => 'Секрет, наданий для даного використовуваного токена API є неправильним',
+ 'api_user_no_api_permission' => 'Власник використовуваного токена API не має дозволу здійснювати виклики API',
'api_user_token_expired' => 'Термін дії токена авторизації закінчився',
// Settings & Maintenance
'maint' => 'Обслуговування',
'maint_image_cleanup' => 'Очищення зображень',
'maint_image_cleanup_desc' => "Сканує вміст сторінки та версій, щоб перевірити, які зображення та малюнки в даний час використовуються, а також які зображення зайві. Переконайтеся, що ви створили повну резервну копію бази даних та зображення, перш ніж запускати це.",
- 'maint_image_cleanup_ignore_revisions' => 'Ігнорувати зображення в версіях',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Запустити очищення',
'maint_image_cleanup_warning' => ':count потенційно невикористаних зображень було знайдено. Ви впевнені, що хочете видалити ці зображення?',
'maint_image_cleanup_success' => ':count потенційно невикористані зображення знайдено і видалено!',
'maint_send_test_email_mail_subject' => 'Перевірка електронної пошти',
'maint_send_test_email_mail_greeting' => 'Доставляння електронної пошти працює!',
'maint_send_test_email_mail_text' => 'Вітаємо! Оскільки ви отримали цього листа, поштова скринька налаштована правильно.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Recycle Bin',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
- 'audit' => 'Audit Log',
- 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
- 'audit_event_filter' => 'Event Filter',
- 'audit_event_filter_no_filter' => 'No Filter',
- 'audit_deleted_item' => 'Deleted Item',
- 'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
- 'audit_date_from' => 'Date Range From',
- 'audit_date_to' => 'Date Range To',
+ 'audit' => 'Журнал аудиту',
+ 'audit_desc' => 'Цей журнал аудиту показує список відстежуваних у системі дій. Цей список нефільтрований, на відміну від подібних списків активності в системі, де застосовуються фільтри дозволів.',
+ 'audit_event_filter' => 'Фільтр подій',
+ 'audit_event_filter_no_filter' => 'Без фільтра',
+ 'audit_deleted_item' => 'Видалений елемент',
+ 'audit_deleted_item_name' => 'Назва: :name',
+ 'audit_table_user' => 'Користувач',
+ 'audit_table_event' => 'Подія',
+ 'audit_table_related' => 'Related Item or Detail',
+ 'audit_table_date' => 'Дата активності',
+ 'audit_date_from' => 'Діапазон дат від',
+ 'audit_date_to' => 'Діапазон дат до',
// Role Settings
'roles' => 'Ролі',
'role_manage_entity_permissions' => 'Керування всіма правами на книги, розділи та сторінки',
'role_manage_own_entity_permissions' => 'Керування дозволами на власну книгу, розділ та сторінки',
'role_manage_page_templates' => 'Управління шаблонами сторінок',
- 'role_access_api' => 'Access system API',
+ 'role_access_api' => 'Доступ до системного API',
'role_manage_settings' => 'Керування налаштуваннями програми',
'role_asset' => 'Дозволи',
- 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+ 'roles_system_warning' => 'Майте на увазі, що доступ до будь-якого з вищезазначених трьох дозволів може дозволити користувачеві змінювати власні привілеї або привілеї інших в системі. Ролі з цими дозволами призначайте лише довіреним користувачам.',
'role_asset_desc' => 'Ці дозволи контролюють стандартні доступи всередині системи. Права на книги, розділи та сторінки перевизначать ці дозволи.',
'role_asset_admins' => 'Адміністратори автоматично отримують доступ до всього вмісту, але ці параметри можуть відображати або приховувати параметри інтерфейсу користувача.',
'role_all' => 'Все',
'user_profile' => 'Профіль користувача',
'users_add_new' => 'Додати нового користувача',
'users_search' => 'Пошук користувачів',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Відомості про користувача',
'users_details_desc' => 'Встановіть ім\'я та електронну адресу для цього користувача. Адреса електронної пошти буде використана для входу до програми.',
'users_details_desc_no_email' => 'Встановіть ім\'я для цього користувача, щоб інші могли його розпізнати.',
'users_send_invite_text' => 'Ви можете надіслати цьому користувачеві лист із запрошенням, що дозволить йому встановити пароль власноруч, або ви можете встановити йому пароль самостійно.',
'users_send_invite_option' => 'Надіслати листа із запрошенням користувачу',
'users_external_auth_id' => 'Зовнішній ID автентифікації',
- 'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+ 'users_external_auth_id_desc' => 'Цей ідентифікатор використовується для ідентифікації цього користувача під час взаємодії із зовнішньою системою автентифікації.',
'users_password_warning' => 'Тільки якщо ви хочете змінити свій пароль, заповніть поля нижче:',
'users_system_public' => 'Цей користувач представляє будь-яких гостьових користувачів, які відвідують ваш екземпляр. Його не можна використовувати для входу, але він призначається автоматично.',
'users_delete' => 'Видалити користувача',
'users_delete_named' => 'Видалити користувача :userName',
'users_delete_warning' => 'Це повне видалення цього користувача з ім\'ям \':userName\' з системи.',
'users_delete_confirm' => 'Ви впевнені, що хочете видалити цього користувача?',
- 'users_delete_success' => 'Користувачі успішно видалені',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Редагувати користувача',
'users_edit_profile' => 'Редагувати профіль',
'users_edit_success' => 'Користувача успішно оновлено',
'users_social_disconnect' => 'Від\'єднати обліковий запис',
'users_social_connected' => 'Обліковий запис :socialAccount успішно додано до вашого профілю.',
'users_social_disconnected' => 'Обліковий запис :socialAccount був успішно відключений від вашого профілю.',
- 'users_api_tokens' => 'API Tokens',
- 'users_api_tokens_none' => 'No API tokens have been created for this user',
- 'users_api_tokens_create' => 'Create Token',
- 'users_api_tokens_expires' => 'Expires',
- 'users_api_tokens_docs' => 'API Documentation',
+ 'users_api_tokens' => 'API токени',
+ 'users_api_tokens_none' => 'Жодного токена API не створено для цього користувача',
+ 'users_api_tokens_create' => 'Створити токен',
+ 'users_api_tokens_expires' => 'Закінчується',
+ 'users_api_tokens_docs' => 'Документація API',
// API Tokens
- 'user_api_token_create' => 'Create API Token',
- 'user_api_token_name' => 'Name',
- 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
- 'user_api_token_expiry' => 'Expiry Date',
- 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
- 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
- 'user_api_token_create_success' => 'API token successfully created',
- 'user_api_token_update_success' => 'API token successfully updated',
- 'user_api_token' => 'API Token',
- 'user_api_token_id' => 'Token ID',
- 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
- 'user_api_token_secret' => 'Token Secret',
- 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
- 'user_api_token_created' => 'Token created :timeAgo',
- 'user_api_token_updated' => 'Token updated :timeAgo',
- 'user_api_token_delete' => 'Delete Token',
- 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
- 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
- 'user_api_token_delete_success' => 'API token successfully deleted',
+ 'user_api_token_create' => 'Створити токен API',
+ 'user_api_token_name' => 'Назва',
+ 'user_api_token_name_desc' => 'Дайте своєму токену читабельну назву як майбутнє нагадування про його пряме призначення.',
+ 'user_api_token_expiry' => 'Дата закінчення',
+ 'user_api_token_expiry_desc' => 'Встановіть дату закінчення терміну дії цього токена. Після цієї дати запити, зроблені за допомогою цього токена, більше не працюватимуть. Якщо залишити це поле порожнім, термін дії токена закінчиться через 100 років.',
+ 'user_api_token_create_secret_message' => 'Відразу після створення цього токена буде створено та показано «Ідентифікатор токена» та «Ключ токена». Ключ буде показано лише один раз, тому перед тим, як продовжити, не забудьте скопіювати значення ключа в надійне та безпечне місце.',
+ 'user_api_token_create_success' => 'Токен API успішно створено',
+ 'user_api_token_update_success' => 'Токен API успішно оновлено',
+ 'user_api_token' => 'Токен API',
+ 'user_api_token_id' => 'Ідентифікатор (ID) токена',
+ 'user_api_token_id_desc' => 'Системний ідентифікатор цього токена, який потрібно буде вказати в запитах API. Його редагування неможливе.',
+ 'user_api_token_secret' => 'Ключ токена',
+ 'user_api_token_secret_desc' => 'Це ключ, згенерований системою для цього токена, його потрібно буде надати в запитах API. Він буде видимий лише цього разу, тому скопіюйте це значення в безпечне та надійне місце.',
+ 'user_api_token_created' => 'Токен створено :timeAgo',
+ 'user_api_token_updated' => 'Токен оновлено :timeAgo',
+ 'user_api_token_delete' => 'Видалити токен',
+ 'user_api_token_delete_warning' => 'Ця дія повністю видалить цей токен API із назвою \':tokenName\' з системи.',
+ 'user_api_token_delete_confirm' => 'Дійсно хочете видалити цей токен API?',
+ 'user_api_token_delete_success' => 'Токен API успішно видалено',
//! If editing translations files directly please ignore this in all
//! languages apart from en. Content will be auto-copied from en.
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Поле :attribute є обов\'язковим для заповнення, коли :values не вказано.',
'required_without_all' => 'Поле :attribute є обов\'язковим для заповнення, коли :values не вказано.',
'same' => 'Поля :attribute та :other мають збігатися.',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => 'Поле :attribute має бути довжини :size.',
'file' => 'Файл в полі :attribute має бути розміром :size кілобайт.',
// Other
'commented_on' => 'đã bình luận về',
+ 'permissions_update' => 'updated permissions',
];
'reset_password' => 'Đặt lại mật khẩu',
'reset_password_send_instructions' => 'Nhập email vào ô dưới đây và bạn sẽ nhận được một email với liên kết để đặt lại mật khẩu.',
'reset_password_send_button' => 'Gửi liên kết đặt lại mật khẩu',
- 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+ 'reset_password_sent' => 'Một đường dẫn đặt lại mật khẩu sẽ được gửi tới :email nếu địa chỉ email đó tồn tại trong hệ thống.',
'reset_password_success' => 'Mật khẩu đã được đặt lại thành công.',
'email_reset_subject' => 'Đặt lại mật khẩu của :appName',
'email_reset_text' => 'Bạn nhận được email này bởi vì chúng tôi nhận được một yêu cầu đặt lại mật khẩu cho tài khoản của bạn.',
'copy' => 'Sao chép',
'reply' => 'Trả lời',
'delete' => 'Xóa',
- 'delete_confirm' => 'Confirm Deletion',
+ 'delete_confirm' => 'Xác nhận Xóa',
'search' => 'Tìm kiếm',
'search_clear' => 'Xoá tìm kiếm',
'reset' => 'Thiết lập lại',
'profile_menu' => 'Menu Hồ sơ',
'view_profile' => 'Xem Hồ sơ',
'edit_profile' => 'Sửa Hồ sơ',
- 'dark_mode' => 'Dark Mode',
- 'light_mode' => 'Light Mode',
+ 'dark_mode' => 'Chế độ Tối',
+ 'light_mode' => 'Chế độ Sáng',
// Layout tabs
'tab_info' => 'Thông tin',
'image_load_more' => 'Hiện thêm',
'image_image_name' => 'Tên Ảnh',
'image_delete_used' => 'Ảnh này được sử dụng trong các trang dưới đây.',
- 'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+ 'image_delete_confirm_text' => 'Bạn có chắc chắn muốn xóa hình ảnh này?',
'image_select_image' => 'Chọn Ảnh',
'image_dropzone' => 'Thả các ảnh hoặc bấm vào đây để tải lên',
'images_deleted' => 'Các ảnh đã được xóa',
'code_editor' => 'Sửa Mã',
'code_language' => 'Ngôn ngữ Mã',
'code_content' => 'Nội dung Mã',
- 'code_session_history' => 'Session History',
+ 'code_session_history' => 'Lịch sử Phiên',
'code_save' => 'Lưu Mã',
];
'meta_created_name' => 'Được tạo :timeLength bởi :user',
'meta_updated' => 'Được cập nhật :timeLength',
'meta_updated_name' => 'Được cập nhật :timeLength bởi :user',
+ 'meta_owned_name' => 'Owned by :user',
'entity_select' => 'Chọn thực thể',
'images' => 'Ảnh',
'my_recent_drafts' => 'Bản nháp gần đây của tôi',
'permissions_intro' => 'Một khi được bật, các quyền này sẽ được ưu tiên trên hết tất cả các quyền hạn khác.',
'permissions_enable' => 'Bật quyền hạn tùy chỉnh',
'permissions_save' => 'Lưu quyền hạn',
+ 'permissions_owner' => 'Owner',
// Search
'search_results' => 'Kết quả Tìm kiếm',
'search_no_pages' => 'Không trang nào khớp với tìm kiếm này',
'search_for_term' => 'Tìm kiếm cho :term',
'search_more' => 'Thêm kết quả',
- 'search_advanced' => 'Advanced Search',
- 'search_terms' => 'Search Terms',
+ 'search_advanced' => 'Tìm kiếm Nâng cao',
+ 'search_terms' => 'Cụm từ Tìm kiếm',
'search_content_type' => 'Kiểu Nội dung',
'search_exact_matches' => 'Hoàn toàn trùng khớp',
'search_tags' => 'Tìm kiếm Tag',
'chapters_create' => 'Tạo Chương mới',
'chapters_delete' => 'Xóa Chương',
'chapters_delete_named' => 'Xóa Chương :chapterName',
- 'chapters_delete_explain' => 'Chức năng này sẽ xóa chương với tên \':chapterName\'. Tất cả các trang sẽ bị loại bỏ và thêm trực tiếp vào sách chứa nó.',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => 'Bạn có chắc chắn muốn xóa chương này?',
'chapters_edit' => 'Sửa Chương',
'chapters_edit_named' => 'Sửa chương :chapterName',
'pages_revisions' => 'Phiên bản Trang',
'pages_revisions_named' => 'Phiên bản Trang cho :pageName',
'pages_revision_named' => 'Phiên bản Trang cho :pageName',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => 'Tạo bởi',
'pages_revisions_date' => 'Ngày của Phiên bản',
'pages_revisions_number' => '#',
'attachments_upload' => 'Tải lên Tập tin',
'attachments_link' => 'Đính kèm Liên kết',
'attachments_set_link' => 'Đặt Liên kết',
- 'attachments_delete' => 'Are you sure you want to delete this attachment?',
+ 'attachments_delete' => 'Bạn có chắc chắn muốn xóa tập tin đính kèm này?',
'attachments_dropzone' => 'Thả các tập tin hoặc bấm vào đây để đính kèm một tập tin',
'attachments_no_files' => 'Không có tập tin nào được tải lên',
'attachments_explain_link' => 'Bạn có thể đính kèm một liên kết nếu bạn lựa chọn không tải lên tập tin. Liên kết này có thể trỏ đến một trang khác hoặc một tập tin ở trên mạng (đám mây).',
'attachments_link_url' => 'Liên kết đến tập tin',
'attachments_link_url_hint' => 'URL của trang hoặc tập tin',
'attach' => 'Đính kèm',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => 'Thêm Đường dẫn Tập tin đính kèm vào Trang',
'attachments_edit_file' => 'Sửa tập tin',
'attachments_edit_file_name' => 'Tên tệp tin',
'attachments_edit_drop_upload' => 'Thả tập tin hoặc bấm vào đây để tải lên và ghi đè',
// Error pages
'404_page_not_found' => 'Không Tìm Thấy Trang',
'sorry_page_not_found' => 'Xin lỗi, Không tìm thấy trang bạn đang tìm kiếm.',
- 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+ 'sorry_page_not_found_permission_warning' => 'Nếu trang bạn tìm kiếm tồn tại, có thể bạn đang không có quyền truy cập.',
'return_home' => 'Quay lại trang chủ',
'error_occurred' => 'Đã xảy ra lỗi',
'app_down' => ':appName hiện đang ngoại tuyến',
'password' => 'Mật khẩu phải có tối thiểu 8 ký tự và và phải trùng với mật khẩu xác nhận.',
'user' => "Chúng tôi không tìm thấy người dùng với địa chỉ email đó.",
- 'token' => 'The password reset token is invalid for this email address.',
+ 'token' => 'Mã token đặt lại mật khẩu cho địa chỉ email này không hợp lệ.',
'sent' => 'Chúng tôi đã gửi email chứa liên kết đặt lại mật khẩu cho bạn!',
'reset' => 'Mật khẩu của bạn đã được đặt lại!',
'maint' => 'Bảo trì',
'maint_image_cleanup' => 'Dọn dẹp ảnh',
'maint_image_cleanup_desc' => "Quét nội dung trang và phiên bản để kiểm tra xem các ảnh và hình vẽ nào đang được sử dụng và ảnh nào dư thừa. Đảm bảo rằng bạn đã tạo bản sao lưu toàn dữ liệu và ảnh trước khi chạy chức năng này.",
- 'maint_image_cleanup_ignore_revisions' => 'Bỏ qua ảnh trong phiên bản chỉnh sửa',
+ 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
'maint_image_cleanup_run' => 'Chạy Dọn dẹp',
'maint_image_cleanup_warning' => 'Đã tìm thấy :count ảnh có thể không được sử dụng. Bạn muốn chắc rằng muốn xóa các ảnh này?',
'maint_image_cleanup_success' => ':count ảnh có thể không được sử dụng đã được tìm thấy và xóa!',
'maint_send_test_email_mail_subject' => 'Thử Email',
'maint_send_test_email_mail_greeting' => 'Chức năng gửi email có vẻ đã hoạt động!',
'maint_send_test_email_mail_text' => 'Chúc mừng! Khi bạn nhận được email thông báo này, cài đặt email của bạn có vẻ đã được cấu hình đúng.',
+ 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+ 'maint_recycle_bin_open' => 'Mở Thùng Rác',
+
+ // Recycle Bin
+ 'recycle_bin' => 'Thùng Rác',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Xóa Bởi',
+ 'recycle_bin_deleted_at' => 'Thời điểm Xóa',
+ 'recycle_bin_permanently_delete' => 'Xóa Vĩnh viễn',
+ 'recycle_bin_restore' => 'Khôi phục',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Dọn dẹp Thùng Rác',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
'audit' => 'Audit Log',
'audit_event_filter_no_filter' => 'No Filter',
'audit_deleted_item' => 'Deleted Item',
'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
+ 'audit_table_user' => 'Người dùng',
'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
+ 'audit_table_related' => 'Related Item or Detail',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => 'Hồ sơ người dùng',
'users_add_new' => 'Thêm người dùng mới',
'users_search' => 'Tìm kiếm người dùng',
+ 'users_latest_activity' => 'Latest Activity',
'users_details' => 'Chi tiết người dùng',
'users_details_desc' => 'Hiển thị tên và địa chỉ email cho người dùng này. Địa chỉ email sẽ được sử dụng để đăng nhập vào ứng dụng.',
'users_details_desc_no_email' => 'Đặt tên cho người dùng này để giúp người dùng khác nhận ra họ.',
'users_delete_named' => 'Xóa người dùng :userName',
'users_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa người dùng với tên \':userName\' từ hệ thống.',
'users_delete_confirm' => 'Bạn có chắc muốn xóa người dùng này không?',
- 'users_delete_success' => 'Người dùng đã được xóa thành công',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => 'No user selected',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => 'Sửa người dùng',
'users_edit_profile' => 'Sửa Hồ sơ',
'users_edit_success' => 'Người dùng được cập nhật thành công',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => 'Trường :attribute là bắt buộc khi :values không tồn tại.',
'required_without_all' => 'Trường :attribute là bắt buộc khi không có bất cứ :values nào tồn tại.',
'same' => ':attribute và :other phải trùng khớp với nhau.',
+ 'safe_url' => 'Đường dẫn cung cấp có thể không an toàn.',
'size' => [
'numeric' => ':attribute phải có cỡ :size.',
'file' => ':attribute phải có cỡ :size KB.',
// Other
'commented_on' => '评论',
+ 'permissions_update' => '权限已更新',
];
'meta_created_name' => '由 :user 创建于 :timeLength',
'meta_updated' => '更新于 :timeLength',
'meta_updated_name' => '由 :user 更新于 :timeLength',
+ 'meta_owned_name' => '拥有者 :user',
'entity_select' => '实体选择',
'images' => '图片',
'my_recent_drafts' => '我最近的草稿',
'permissions_intro' => '本设置优先于每个用户角色本身所具有的权限。',
'permissions_enable' => '启用自定义权限',
'permissions_save' => '保存权限',
+ 'permissions_owner' => '拥有者',
// Search
'search_results' => '搜索结果',
'chapters_create' => '创建章节',
'chapters_delete' => '删除章节',
'chapters_delete_named' => '删除章节「:chapterName」',
- 'chapters_delete_explain' => '这将删除章节「:chapterName」。所有的页面将被删除并添加到其所在的书籍。',
+ 'chapters_delete_explain' => '这将删除名为“:chapterName”的章节。本章节中存在的所有页面也将被删除。',
'chapters_delete_confirm' => '您确定要删除此章节吗?',
'chapters_edit' => '编辑章节',
'chapters_edit_named' => '编辑章节「:chapterName」',
'pages_revisions' => '页面修订',
'pages_revisions_named' => '“:pageName”页面修订',
'pages_revision_named' => '“:pageName”页面修订',
+ 'pages_revision_restored_from' => '从 #:id; :summary 恢复',
'pages_revisions_created_by' => '创建者',
'pages_revisions_date' => '修订日期',
'pages_revisions_number' => '#',
'comment_deleted_success' => '评论已删除',
'comment_created_success' => '评论已添加',
'comment_updated_success' => '评论已更新',
- 'comment_delete_confirm' => '你确定要删除这条评论?',
+ 'comment_delete_confirm' => '您确定要删除这条评论?',
'comment_in_reply_to' => '回复 :commentId',
// Revision
'maint' => '维护',
'maint_image_cleanup' => '清理图像',
'maint_image_cleanup_desc' => "扫描页面和修订内容以检查哪些图像是正在使用的以及哪些图像是多余的。确保在运行前创建完整的数据库和映像备份。",
- 'maint_image_cleanup_ignore_revisions' => '忽略修订记录中的图像',
+ 'maint_delete_images_only_in_revisions' => '同时删除只存在于旧页面版本中的图片',
'maint_image_cleanup_run' => '运行清理',
'maint_image_cleanup_warning' => '发现了 :count 张可能未使用的图像。您确定要删除这些图像吗?',
'maint_image_cleanup_success' => '找到并删除了 :count 张可能未使用的图像!',
'maint_send_test_email_success' => '电子邮件已发送至 :address',
'maint_send_test_email_mail_subject' => '测试电子邮件',
'maint_send_test_email_mail_greeting' => '邮件发送功能看起来工作正常!',
- 'maint_send_test_email_mail_text' => '恭喜!您收到了此邮件通知,你的电子邮件设置看起来配置正确。',
+ 'maint_send_test_email_mail_text' => '恭喜!您收到了此邮件通知,您的电子邮件设置看起来已配置正确。',
+ 'maint_recycle_bin_desc' => '被删除的书架、书籍、章节和页面会被存入回收站,您可以还原或永久删除它们。回收站中较旧的项目可能会在系统设置的一段时间后被自动删除。',
+ 'maint_recycle_bin_open' => '打开回收站',
+
+ // Recycle Bin
+ 'recycle_bin' => '回收站',
+ 'recycle_bin_desc' => '在这里,您可以还原已删除的项目,或选择将其从系统中永久删除。与系统中过滤过的类似的活动记录不同,这个表会显示所有操作。',
+ 'recycle_bin_deleted_item' => '被删除的项目',
+ 'recycle_bin_deleted_by' => '删除者',
+ 'recycle_bin_deleted_at' => '删除时间',
+ 'recycle_bin_permanently_delete' => '永久删除',
+ 'recycle_bin_restore' => '恢复',
+ 'recycle_bin_contents_empty' => '回收站当前为空',
+ 'recycle_bin_empty' => '清空回收站',
+ 'recycle_bin_empty_confirm' => '这将永久性销毁回收站中的所有项目(包括每个项目中包含的内容,例如图片)。您确定要清空回收站吗?',
+ 'recycle_bin_destroy_confirm' => '此操作将从系统中永久删除此项目以及下面列出的所有子元素,并且您将无法还原此内容。您确定要永久删除该项目吗?',
+ 'recycle_bin_destroy_list' => '要销毁的项目',
+ 'recycle_bin_restore_list' => '要恢复的项目',
+ 'recycle_bin_restore_confirm' => '此操作会将已删除的项目及其所有子元素恢复到原始位置。如果项目的原始位置已被删除,并且现在位于回收站中,则要恢复项目的上级项目也需要恢复。',
+ 'recycle_bin_restore_deleted_parent' => '该项目的上级项目也已被删除。这些项目将保持被删除状态,直到上级项目被恢复。',
+ 'recycle_bin_destroy_notification' => '从回收站中删除了 :count 个项目。',
+ 'recycle_bin_restore_notification' => '从回收站中恢复了 :count 个项目。',
// Audit Log
'audit' => '审核日志',
- 'audit_desc' => 'è¯¥å®¡æ ¸æ\97¥å¿\97æ\98¾ç¤ºç³»ç»\9fä¸è·\9f踪ç\9a\84æ´»å\8a¨å\88\97表ã\80\82ä¸\8eç³»ç»\9fä¸åº\94ç\94¨äº\86æ\9d\83é\99\90è¿\87滤å\99¨ç\9a\84类似活å\8a¨å\88\97表ä¸\8då\90\8cï¼\8cè¿\99个表æ\98¯æ\9cªç»\8fè¿\87滤ç\9a\84。',
+ 'audit_desc' => 'è¿\99ä»½å®¡æ ¸æ\97¥å¿\97æ\98¾ç¤ºæ\89\80æ\9c\89被系ç»\9fè·\9f踪ç\9a\84æ´»å\8a¨ã\80\82ä¸\8eç³»ç»\9fä¸è¿\87滤è¿\87ç\9a\84类似ç\9a\84æ´»å\8a¨è®°å½\95ä¸\8då\90\8cï¼\8cè¿\99个表ä¼\9aæ\98¾ç¤ºæ\89\80æ\9c\89æ\93\8dä½\9c。',
'audit_event_filter' => '事件过滤器',
'audit_event_filter_no_filter' => '无过滤器',
'audit_deleted_item' => '被删除的项目',
- 'audit_deleted_item_name' => 'å§\93å\90\8d: :name',
+ 'audit_deleted_item_name' => 'å\90\8dç§°: :name',
'audit_table_user' => '用户',
'audit_table_event' => '事件',
- 'audit_table_item' => '相关项目',
+ 'audit_table_related' => '相关项目或详细信息',
'audit_table_date' => '活动日期',
'audit_date_from' => '日期范围从',
'audit_date_to' => '日期范围至',
'user_profile' => '用户资料',
'users_add_new' => '添加用户',
'users_search' => '搜索用户',
+ 'users_latest_activity' => '最新活动',
'users_details' => '用户详细资料',
'users_details_desc' => '设置该用户的显示名称和电子邮件地址。 该电子邮件地址将用于登录本站。',
'users_details_desc_no_email' => '设置此用户的昵称,以便其他人识别。',
'users_delete_named' => '删除用户 :userName',
'users_delete_warning' => '这将从系统中完全删除名为 \':userName\' 的用户。',
'users_delete_confirm' => '您确定要删除这个用户?',
- 'users_delete_success' => '用户删除成功。',
+ 'users_migrate_ownership' => '迁移拥有权',
+ 'users_migrate_ownership_desc' => '如果您想要当前用户拥有的全部项目转移到另一个用户(更改拥有者),请在此处选择一个用户。',
+ 'users_none_selected' => '没有选中用户',
+ 'users_delete_success' => '已成功移除用户',
'users_edit' => '编辑用户',
'users_edit_profile' => '编辑资料',
'users_edit_success' => '用户更新成功',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => '挪威语 (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => '当:values不存在时,:attribute 字段是必需的。',
'required_without_all' => '当:values均不存在时,:attribute 字段是必需的。',
'same' => ':attribute 与 :other 必须匹配。',
+ 'safe_url' => '提供的链接可能不安全。',
'size' => [
'numeric' => ':attribute 必须为:size。',
'file' => ':attribute 必须为:size KB。',
// Other
'commented_on' => '評論',
+ 'permissions_update' => '更新權限',
];
'meta_created_name' => '由 :user 建立於 :timeLength',
'meta_updated' => '更新於 :timeLength',
'meta_updated_name' => '由 :user 更新於 :timeLength',
+ 'meta_owned_name' => ':user 所擁有',
'entity_select' => '選擇項目',
'images' => '圖片',
'my_recent_drafts' => '我最近的草稿',
'permissions_intro' => '本設定優先權高於每個使用者角色本身所具有的權限。',
'permissions_enable' => '啟用自訂權限',
'permissions_save' => '儲存權限',
+ 'permissions_owner' => '擁有者',
// Search
'search_results' => '搜尋結果',
'chapters_create' => '建立章節',
'chapters_delete' => '刪除章節',
'chapters_delete_named' => '刪除章節「:chapterName」',
- 'chapters_delete_explain' => '這將刪除章節「:chapterName」。所有的頁面將被刪除並加入到其所在的書籍。',
+ 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
'chapters_delete_confirm' => '您確定要刪除此章節嗎?',
'chapters_edit' => '編輯章節',
'chapters_edit_named' => '編輯章節「:chapterName」',
'pages_revisions' => '頁面修訂',
'pages_revisions_named' => '“:pageName”頁面修訂',
'pages_revision_named' => '“:pageName”頁面修訂',
+ 'pages_revision_restored_from' => 'Restored from #:id; :summary',
'pages_revisions_created_by' => '建立者',
'pages_revisions_date' => '修訂日期',
'pages_revisions_number' => '#',
'pages_revisions_numbered' => '修訂編號:id',
'pages_revisions_numbered_changes' => '修訂編號:id 更改',
'pages_revisions_changelog' => '更新說明',
- 'pages_revisions_changes' => '說明',
+ 'pages_revisions_changes' => '更新紀錄',
'pages_revisions_current' => '目前版本',
'pages_revisions_preview' => '預覽',
'pages_revisions_restore' => '恢複',
'attachments_link_url' => '連結到檔案',
'attachments_link_url_hint' => '網站或檔案的網址',
'attach' => '附加',
- 'attachments_insert_link' => 'Add Attachment Link to Page',
+ 'attachments_insert_link' => '將附件連結增加到頁面',
'attachments_edit_file' => '編輯檔案',
'attachments_edit_file_name' => '檔案名稱',
'attachments_edit_drop_upload' => '刪除檔案或點選這裡上傳並覆蓋',
'maint' => '維護',
'maint_image_cleanup' => '清理圖像',
'maint_image_cleanup_desc' => "掃描頁面和修訂內容以檢查哪些圖像是正在使用的以及哪些圖像是多余的。確保在運行前創建完整的數據庫和映像備份。",
- 'maint_image_cleanup_ignore_revisions' => '忽略修訂記錄中的圖像',
+ 'maint_delete_images_only_in_revisions' => '包含刪除僅在舊頁面修訂版中存在的圖像',
'maint_image_cleanup_run' => '運行清理',
'maint_image_cleanup_warning' => '發現了:count 張可能未使用的圖像。您確定要刪除這些圖像嗎?',
'maint_image_cleanup_success' => '找到並刪除了:count 張可能未使用的圖像!',
'maint_send_test_email_mail_subject' => '測試郵件',
'maint_send_test_email_mail_greeting' => '電子郵件傳遞似乎有效!',
'maint_send_test_email_mail_text' => '恭喜你! 收到此電子郵件通知時,您的電子郵件設置已經認證成功。',
+ 'maint_recycle_bin_desc' => '刪除的書架,書籍,章節和頁面將發送到回收站,以便可以還原或永久刪除它們。 回收站中的較舊項目可能會在一段時間後自動刪除,具體取決於系統配置。',
+ 'maint_recycle_bin_open' => 'Open Recycle Bin',
+
+ // Recycle Bin
+ 'recycle_bin' => '資源回收筒',
+ 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+ 'recycle_bin_deleted_item' => 'Deleted Item',
+ 'recycle_bin_deleted_by' => 'Deleted By',
+ 'recycle_bin_deleted_at' => 'Deletion Time',
+ 'recycle_bin_permanently_delete' => 'Permanently Delete',
+ 'recycle_bin_restore' => 'Restore',
+ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+ 'recycle_bin_empty' => 'Empty Recycle Bin',
+ 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
+ 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
+ 'recycle_bin_destroy_list' => 'Items to be Destroyed',
+ 'recycle_bin_restore_list' => 'Items to be Restored',
+ 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
+ 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
+ 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
+ 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
// Audit Log
- 'audit' => 'Audit Log',
+ 'audit' => '稽核記錄',
'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
'audit_event_filter' => 'Event Filter',
'audit_event_filter_no_filter' => 'No Filter',
'audit_deleted_item' => 'Deleted Item',
'audit_deleted_item_name' => 'Name: :name',
- 'audit_table_user' => 'User',
- 'audit_table_event' => 'Event',
- 'audit_table_item' => 'Related Item',
- 'audit_table_date' => 'Activity Date',
+ 'audit_table_user' => '使用者',
+ 'audit_table_event' => '活動',
+ 'audit_table_related' => 'Related Item or Detail',
+ 'audit_table_date' => '最後活動日期',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
'user_profile' => '使用者資料',
'users_add_new' => '加入使用者',
'users_search' => '搜尋使用者',
+ 'users_latest_activity' => '最新活動',
'users_details' => '用戶詳情',
'users_details_desc' => '請設置用戶的顯示名稱和電子郵件地址, 該電子郵件地址將用於登錄該應用。',
'users_details_desc_no_email' => '設置一個用戶的顯示名稱,以便其他人可以認出你。',
'users_delete_named' => '刪除使用者 :userName',
'users_delete_warning' => '這將從系統中完全刪除名為 \':userName\' 的使用者。',
'users_delete_confirm' => '您確定要刪除這個使用者?',
- 'users_delete_success' => '使用者刪除成功。',
+ 'users_migrate_ownership' => 'Migrate Ownership',
+ 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+ 'users_none_selected' => '沒有選定的使用者',
+ 'users_delete_success' => 'User successfully removed',
'users_edit' => '編輯使用者',
'users_edit_profile' => '編輯資料',
'users_edit_success' => '使用者更新成功',
'ja' => '日本語',
'ko' => '한국어',
'nl' => 'Nederlands',
+ 'nb' => 'Norsk (Bokmål)',
'pl' => 'Polski',
'pt_BR' => 'Português do Brasil',
'ru' => 'Русский',
'required_without' => '當:values不存在時,:attribute 字段是必需的。',
'required_without_all' => '當:values均不存在時,:attribute 字段是必需的。',
'same' => ':attribute 與 :other 必須匹配。',
+ 'safe_url' => 'The provided link may not be safe.',
'size' => [
'numeric' => ':attribute 必須為:size。',
'file' => ':attribute 必須為:size KB。',
.sticky-sidebar {
position: sticky;
top: $-m;
+ max-height: calc(100vh - #{$-m});
+ overflow-y: auto;
}
.bg-chapter {
background-color: var(--color-chapter);
}
-.bg-shelf {
+.bg-bookshelf {
background-color: var(--color-bookshelf);
}
.template-item-actions button:first-child {
border-top: 0;
}
+}
+
+.dropdown-search-dropdown {
+ box-shadow: $bs-med;
+ overflow: hidden;
+ min-height: 100px;
+ width: 240px;
+ display: none;
+ position: absolute;
+ z-index: 80;
+ right: -$-m;
+ @include rtl {
+ right: auto;
+ left: -$-m;
+ }
+ .dropdown-search-search .svg-icon {
+ position: absolute;
+ left: $-s;
+ @include rtl {
+ right: $-s;
+ left: auto;
+ }
+ top: 11px;
+ fill: #888;
+ pointer-events: none;
+ }
+ .dropdown-search-list {
+ max-height: 400px;
+ overflow-y: scroll;
+ text-align: start;
+ }
+ .dropdown-search-item {
+ padding: $-s $-m;
+ &:hover,&:focus {
+ background-color: #F2F2F2;
+ text-decoration: none;
+ }
+ }
+ input {
+ padding-inline-start: $-xl;
+ border-radius: 0;
+ border: 0;
+ border-bottom: 1px solid #DDD;
+ }
+}
+
+@include smaller-than($m) {
+ .dropdown-search-dropdown {
+ position: fixed;
+ right: auto;
+ left: $-m;
+ }
+ .dropdown-search-dropdown .dropdown-search-list {
+ max-height: 240px;
+ }
+}
+
+.custom-select-input {
+ max-width: 280px;
+ border: 1px solid #DDD;
+ border-radius: 4px;
}
\ No newline at end of file
--- /dev/null
+/**
+ * Includes the footer links.
+ */
+
+ footer {
+ flex-shrink: 0;
+ padding: 1rem 1rem 2rem 1rem;
+ text-align: center;
+ }
+
+ footer a {
+ margin: 0 .5em;
+ }
+
+ body.flexbox footer {
+ display: none;
+ }
\ No newline at end of file
*/
header .grid {
- grid-template-columns: auto min-content auto;
+ grid-template-columns: minmax(max-content, 2fr) 1fr minmax(max-content, 2fr);
}
@include smaller-than($l) {
}
-.header-search {
- display: inline-block;
-}
header .search-box {
display: inline-block;
margin-top: 10px;
}
}
-.breadcrumb-listing {
+.dropdown-search {
position: relative;
- .breadcrumb-listing-toggle {
+ .dropdown-search-toggle {
padding: 6px;
border: 1px solid transparent;
border-radius: 4px;
}
}
-.breadcrumb-listing-dropdown {
- box-shadow: $bs-med;
- overflow: hidden;
- min-height: 100px;
- width: 240px;
- display: none;
- position: absolute;
- z-index: 80;
- right: -$-m;
- @include rtl {
- right: auto;
- left: -$-m;
- }
- .breadcrumb-listing-search .svg-icon {
- position: absolute;
- left: $-s;
- @include rtl {
- right: $-s;
- left: auto;
- }
- top: 11px;
- fill: #888;
- pointer-events: none;
- }
- .breadcrumb-listing-entity-list {
- max-height: 400px;
- overflow-y: scroll;
- text-align: start;
- }
- input {
- padding-inline-start: $-xl;
- border-radius: 0;
- border: 0;
- border-bottom: 1px solid #DDD;
- }
-}
-
-@include smaller-than($m) {
- .breadcrumb-listing-dropdown {
- position: fixed;
- right: auto;
- left: $-m;
- }
- .breadcrumb-listing-dropdown .breadcrumb-listing-entity-list {
- max-height: 240px;
- }
-}
-
.faded {
a, button, span, span > div {
color: #666;
line-height: 1.6;
@include lightDark(color, #444, #AAA);
-webkit-font-smoothing: antialiased;
-}
\ No newline at end of file
+ background-color: #F2F2F2;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
}
}
+#content {
+ flex: 1 0 auto;
+}
+
/**
* Flexbox layout system
*/
.justify-center {
justify-content: center;
}
+.items-center {
+ align-items: center;
+}
/**
display: none !important;
}
+.fill-height {
+ height: 100%;
+}
+
.float {
float: left;
&.right {
min-height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
+ height: 100%;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
margin-inline-start: 0;
margin-inline-end: 0;
}
-}
\ No newline at end of file
+}
padding: $-s $-s;
vertical-align: middle;
margin: 0;
+ overflow: visible;
}
th {
font-weight: bold;
overflow-wrap: break-word;
}
-.limit-text {
+.text-limit-lines-1 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+.text-limit-lines-2 {
+ // -webkit use here is actually standardised cross-browser:
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+}
+
/**
* Grouping
*/
@import "codemirror";
@import "components";
@import "header";
+@import "footer";
@import "lists";
@import "pages";
@yield('content')
</div>
+ @include('common.footer')
+
<div back-to-top class="primary-background print-hidden">
<div class="inner">
@icon('chevron-up') <span>{{ trans('common.back_to_top') }}</span>
<ul class="contents">
@foreach($bookChildren as $bookChild)
<li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
- @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+ @if($bookChild->isA('chapter') && count($bookChild->visible_pages) > 0)
<ul>
- @foreach($bookChild->pages as $page)
+ @foreach($bookChild->visible_pages as $page)
<li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
@endforeach
</ul>
@if($bookChild->isA('chapter'))
<p>{{ $bookChild->description }}</p>
- @if(count($bookChild->pages) > 0)
- @foreach($bookChild->pages as $page)
+ @if(count($bookChild->visible_pages) > 0)
+ @foreach($bookChild->visible_pages as $page)
<div class="page-break"></div>
<div class="chapter-hint">{{$bookChild->name}}</div>
<h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+++ /dev/null
-<a href="{{$book->getUrl()}}" class="grid-card" data-entity-type="book" data-entity-id="{{$book->id}}">
- <div class="bg-book featured-image-container-wrap">
- <div class="featured-image-container" @if($book->cover) style="background-image: url('{{ $book->getBookCover() }}')"@endif>
- </div>
- @icon('book')
- </div>
- <div class="grid-card-content">
- <h2>{{$book->getShortName(35)}}</h2>
- @if(isset($book->searchSnippet))
- <p class="text-muted">{!! $book->searchSnippet !!}</p>
- @else
- <p class="text-muted">{{ $book->getExcerpt(130) }}</p>
- @endif
- </div>
- <div class="grid-card-footer text-muted ">
- <p>@icon('star')<span title="{{$book->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $book->created_at->diffForHumans()]) }}</span></p>
- <p>@icon('edit')<span title="{{ $book->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $book->updated_at->diffForHumans()]) }}</span></p>
- </div>
-</a>
\ No newline at end of file
<div class="actions mb-xl">
<h5>{{ trans('common.actions') }}</h5>
<div class="icon-list text-primary">
- @if($currentUser->can('book-create-all'))
+ @if(user()->can('book-create-all'))
<a href="{{ url("/create-book") }}" class="icon-list-item">
<span>@icon('add')</span>
<span>{{ trans('entities.books_create') }}</span>
-
<main class="content-wrap mt-m card">
<div class="grid half v-center no-row-gap">
<h1 class="list-heading">{{ trans('entities.books') }}</h1>
@else
<div class="grid third">
@foreach($books as $key => $book)
- @include('books.grid-item', ['book' => $book])
+ @include('partials.entity-grid-item', ['entity' => $book])
@endforeach
</div>
@endif
<ul class="sortable-page-list sort-list">
@foreach($bookChildren as $bookChild)
- <li class="text-{{ $bookChild->getClassName() }}"
- data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getClassName() }}"
+ <li class="text-{{ $bookChild->getType() }}"
+ data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getType() }}"
data-name="{{ $bookChild->name }}" data-created="{{ $bookChild->created_at->timestamp }}"
data-updated="{{ $bookChild->updated_at->timestamp }}">
<div class="entity-list-item">
</div>
@if($bookChild->isA('chapter'))
<ul>
- @foreach($bookChild->pages as $page)
+ @foreach($bookChild->visible_pages as $page)
<li class="text-page"
data-id="{{$page->id}}" data-type="page"
data-name="{{ $page->name }}" data-created="{{ $page->created_at->timestamp }}"
<div class="chapter-child-menu">
<button chapter-toggle type="button" aria-expanded="{{ $isOpen ? 'true' : 'false' }}"
class="text-muted @if($isOpen) open @endif">
- @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
+ @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->visible_pages->count()) }}</span>
</button>
<ul class="sub-menu inset-list @if($isOpen) open @endif" @if($isOpen) style="display: block;" @endif role="menu">
- @foreach($bookChild->pages as $childPage)
+ @foreach($bookChild->visible_pages as $childPage)
<li class="list-item-page {{ $childPage->isA('page') && $childPage->draft ? 'draft' : '' }}" role="presentation">
@include('partials.entity-list-item-basic', ['entity' => $childPage, 'classes' => $current->matches($childPage)? 'selected' : '' ])
</li>
-<a href="{{ $chapter->getUrl() }}" class="chapter entity-list-item @if($chapter->hasChildren()) has-children @endif" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
+{{--This view display child pages in a list if pre-loaded onto a 'visible_pages' property,--}}
+{{--To ensure that the pages have been loaded efficiently with permissions taken into account.--}}
+<a href="{{ $chapter->getUrl() }}" class="chapter entity-list-item @if($chapter->visible_pages->count() > 0) has-children @endif" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
<span class="icon text-chapter">@icon('chapter')</span>
<div class="content">
<h4 class="entity-list-item-name break-text">{{ $chapter->name }}</h4>
</div>
</div>
</a>
-@if ($chapter->hasChildren())
+@if ($chapter->visible_pages->count() > 0)
<div class="chapter chapter-expansion">
<span class="icon text-chapter">@icon('page')</span>
<div class="content">
<button type="button" chapter-toggle
aria-expanded="false"
- class="text-muted chapter-expansion-toggle">@icon('caret-right') <span>{{ trans_choice('entities.x_pages', $chapter->pages->count()) }}</span></button>
+ class="text-muted chapter-expansion-toggle">@icon('caret-right') <span>{{ trans_choice('entities.x_pages', $chapter->visible_pages->count()) }}</span></button>
<div class="inset-list">
<div class="entity-list-item-children">
- @include('partials.entity-list', ['entities' => $chapter->pages])
+ @include('partials.entity-list', ['entities' => $chapter->visible_pages])
</div>
</div>
</div>
--- /dev/null
+@if(count(setting('app-footer-links', [])) > 0)
+<footer>
+ @foreach(setting('app-footer-links', []) as $link)
+ <a href="{{ $link['url'] }}" target="_blank">{{ strpos($link['label'], 'trans::') === 0 ? trans(str_replace('trans::', '', $link['label'])) : $link['label'] }}</a>
+ @endforeach
+</footer>
+@endif
\ No newline at end of file
<div class="mobile-menu-toggle hide-over-l">@icon('more')</div>
</div>
- <div class="header-search hide-under-l">
+ <div class="flex-container-row justify-center hide-under-l">
@if (hasAppAccess())
<form action="{{ url('/search') }}" method="GET" class="search-box" role="search">
<button id="header-search-box-button" type="submit" aria-label="{{ trans('common.search') }}" tabindex="-1">@icon('search') </button>
<div class="links text-center">
@if (hasAppAccess())
<a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
- @if(userCanOnAny('view', \BookStack\Entities\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
+ @if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
<a href="{{ url('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
@endif
<a href="{{ url('/books') }}">@icon('books'){{ trans('entities.books') }}</a>
<div class="actions mb-xl">
<h5>{{ trans('common.actions') }}</h5>
<div class="icon-list text-primary">
+ @if(user()->can('book-create-all'))
+ <a href="{{ url("/create-book") }}" class="icon-list-item">
+ <span>@icon('add')</span>
+ <span>{{ trans('entities.books_create') }}</span>
+ </a>
+ @endif
@include('partials.view-toggle', ['view' => $view, 'type' => 'books'])
@include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
@include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
</div>
</div>
-@stop
\ No newline at end of file
+@stop
<div class="actions mb-xl">
<h5>{{ trans('common.actions') }}</h5>
<div class="icon-list text-primary">
+ @if(user()->can('bookshelf-create-all'))
+ <a href="{{ url("/create-shelf") }}" class="icon-list-item">
+ <span>@icon('add')</span>
+ <span>{{ trans('entities.shelves_new_action') }}</span>
+ </a>
+ @endif
@include('partials.view-toggle', ['view' => $view, 'type' => 'shelves'])
@include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
@include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
</div>
</div>
-@stop
\ No newline at end of file
+@stop
@endif
<div class="mb-xl">
- <h5>{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h5>
+ <h5>{{ trans('entities.' . (auth()->check() ? 'my_recently_viewed' : 'books_recent')) }}</h5>
@include('partials.entity-list', [
'entities' => $recents,
'style' => 'compact',
- 'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+ 'emptyText' => auth()->check() ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
])
</div>
</div>
@endif
- <div id="{{ $signedIn ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
- <h3 class="card-title">{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+ <div id="{{ auth()->check() ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
+ <h3 class="card-title">{{ trans('entities.' . (auth()->check() ? 'my_recently_viewed' : 'books_recent')) }}</h3>
<div class="px-m">
@include('partials.entity-list', [
'entities' => $recents,
'style' => 'compact',
- 'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
+ 'emptyText' => auth()->check() ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
])
</div>
</div>
--}}
<?php $isOpen = setting()->getForCurrentUser('section_expansion#'. $key); ?>
<button type="button" expand-toggle="{{ $target }}"
- expand-toggle-update-endpoint="{{ url('/settings/users/'. $currentUser->id .'/update-expansion-preference/' . $key) }}"
+ expand-toggle-update-endpoint="{{ url('/settings/users/'. user()->id .'/update-expansion-preference/' . $key) }}"
expand-toggle-is-open="{{ $isOpen ? 'yes' : 'no' }}"
class="text-muted icon-list-item text-primary">
<span>@icon('expand-text')</span>
<div page-picker>
<div class="input-base">
<span @if($value) style="display: none" @endif page-picker-default class="text-muted italic">{{ $placeholder }}</span>
- <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Page::find($value)->name : '' }}</a>
+ <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::find($value)->name : '' }}</a>
</div>
<br>
<input type="hidden" value="{{$value}}" name="{{$name}}" id="{{$name}}">
--- /dev/null
+@foreach($users as $user)
+ <a href="#" class="flex-container-row items-center dropdown-search-item" data-id="{{ $user->id }}">
+ <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+ <span>{{ $user->name }}</span>
+ </a>
+@endforeach
\ No newline at end of file
--- /dev/null
+<div class="dropdown-search custom-select-input" components="dropdown dropdown-search user-select"
+ option:dropdown-search:url="/search/users/select"
+>
+ <input refs="user-select@input" type="hidden" name="{{ $name }}" value="{{ $user->id ?? '' }}">
+ <div refs="dropdown@toggle"
+ class="dropdown-search-toggle flex-container-row items-center"
+ aria-haspopup="true" aria-expanded="false" tabindex="0">
+ <div refs="user-select@user-info" class="flex-container-row items-center px-s">
+ @if($user)
+ <img class="avatar mr-m" src="{{ $user->getAvatar(30) }}" alt="{{ $user->name }}">
+ <span>{{ $user->name }}</span>
+ @else
+ <span>{{ trans('settings.users_none_selected') }}</span>
+ @endif
+ </div>
+ <span style="font-size: 1.5rem; margin-left: auto;">
+ @icon('caret-down')
+ </span>
+ </div>
+ <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+ <div class="dropdown-search-search">
+ @icon('search')
+ <input refs="dropdown-search@searchInput"
+ aria-label="{{ trans('common.search') }}"
+ autocomplete="off"
+ placeholder="{{ trans('common.search') }}"
+ type="text">
+ </div>
+ <div refs="dropdown-search@loading" class="text-center">
+ @include('partials.loading-icon')
+ </div>
+ <div refs="dropdown-search@listContainer" class="dropdown-search-list"></div>
+ </div>
+</div>
\ No newline at end of file
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
- <p class="mb-none">{{ trans('entities.permissions_intro') }}</p>
-
- <div class="form-group">
- @include('form.checkbox', [
- 'name' => 'restricted',
- 'label' => trans('entities.permissions_enable'),
- ])
+ <div class="grid half left-focus v-center">
+ <div>
+ <p class="mb-none mt-m">{{ trans('entities.permissions_intro') }}</p>
+ <div>
+ @include('form.checkbox', [
+ 'name' => 'restricted',
+ 'label' => trans('entities.permissions_enable'),
+ ])
+ </div>
+ </div>
+ <div>
+ <div class="form-group">
+ <label for="owner">{{ trans('entities.permissions_owner') }}</label>
+ @include('components.user-select', ['user' => $model->ownedBy, 'name' => 'owned_by'])
+ </div>
+ </div>
</div>
+ <hr>
+
<table permissions-table class="table permissions-table toggle-switch-list" style="{{ !$model->restricted ? 'display: none' : '' }}">
<tr>
<th>{{ trans('common.role') }}</th>
<input type="password" id="{{ $name }}" name="{{ $name }}"
@if($errors->has($name)) class="text-neg" @endif
@if(isset($placeholder)) placeholder="{{$placeholder}}" @endif
+ @if(isset($autocomplete)) autocomplete="{{$autocomplete}}" @endif
@if(old($name)) value="{{ old($name)}}" @endif>
@if($errors->has($name))
<div class="text-neg text-small">{{ $errors->first($name) }}</div>
-@endif
\ No newline at end of file
+@endif
@section('content')
- <div class="flex-fill flex">
+ <div class="flex-fill flex fill-height">
<form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
{{ csrf_field() }}
<div id="markdown-editor" component="markdown-editor"
option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+ option:markdown-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
class="flex-fill flex code-fill">
- @exposeTranslations([
- 'errors.image_upload_error',
- ])
<div class="markdown-editor-wrap active">
<div class="editor-toolbar">
</div>
<iframe src="about:blank" class="markdown-display" sandbox="allow-same-origin"></iframe>
</div>
- <input type="hidden" name="html"/>
-
</div>
<div class="sidebar-page-nav menu">
@foreach($pageNav as $navItem)
<li class="page-nav-item h{{ $navItem['level'] }}">
- <a href="{{ $navItem['link'] }}" class="limit-text block">{{ $navItem['text'] }}</a>
+ <a href="{{ $navItem['link'] }}" class="text-limit-lines-1 block">{{ $navItem['text'] }}</a>
<div class="primary-background sidebar-page-nav-bullet"></div>
</li>
@endforeach
<div component="wysiwyg-editor"
option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+ option:wysiwyg-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
class="flex-fill flex">
- @exposeTranslations([
- 'errors.image_upload_error',
- ])
-
<textarea id="html-editor" name="html" rows="5"
@if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{ old('html') ? old('html') : $model->html }}@endif</textarea>
</div>
@endif
@foreach($sidebarTree as $bookChild)
- <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
+ <li class="list-item-{{ $bookChild->getType() }} {{ $bookChild->getType() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
@include('partials.entity-list-item-basic', ['entity' => $bookChild, 'classes' => $current->matches($bookChild)? 'selected' : ''])
- @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+ @if($bookChild->isA('chapter') && count($bookChild->visible_pages) > 0)
<div class="entity-list-item no-hover">
<span role="presentation" class="icon text-chapter"></span>
<div class="content">
-<div class="breadcrumb-listing" component="dropdown" breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
- <div class="breadcrumb-listing-toggle" refs="dropdown@toggle"
+<div class="dropdown-search" components="dropdown dropdown-search"
+ option:dropdown-search:url="/search/entity/siblings?entity_type={{$entity->getType()}}&entity_id={{ $entity->id }}"
+ option:dropdown-search:local-search-selector=".entity-list-item"
+>
+ <div class="dropdown-search-toggle" refs="dropdown@toggle"
aria-haspopup="true" aria-expanded="false" tabindex="0">
<div class="separator">@icon('chevron-right')</div>
</div>
- <div refs="dropdown@menu" class="breadcrumb-listing-dropdown card" role="menu">
- <div class="breadcrumb-listing-search">
+ <div refs="dropdown@menu" class="dropdown-search-dropdown card" role="menu">
+ <div class="dropdown-search-search">
@icon('search')
- <input autocomplete="off" type="text" name="entity-search" placeholder="{{ trans('common.search') }}" aria-label="{{ trans('common.search') }}">
+ <input refs="dropdown-search@searchInput"
+ aria-label="{{ trans('common.search') }}"
+ autocomplete="off"
+ placeholder="{{ trans('common.search') }}"
+ type="text">
</div>
- @include('partials.loading-icon')
- <div class="breadcrumb-listing-entity-list px-m"></div>
+ <div refs="dropdown-search@loading">
+ @include('partials.loading-icon')
+ </div>
+ <div refs="dropdown-search@listContainer" class="dropdown-search-list px-m"></div>
</div>
</div>
\ No newline at end of file
<?php $breadcrumbCount = 0; ?>
{{-- Show top level books item --}}
- @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof \BookStack\Entities\Book)
+ @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof \BookStack\Entities\Models\Book)
<a href="{{ url('/books') }}" class="text-book icon-list-item outline-hover">
<span>@icon('books')</span>
<span>{{ trans('entities.books') }}</span>
@endif
{{-- Show top level shelves item --}}
- @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof \BookStack\Entities\Bookshelf)
+ @if (count($crumbs) > 0 && ($crumbs[0] ?? null) instanceof \BookStack\Entities\Models\Bookshelf)
<a href="{{ url('/shelves') }}" class="text-bookshelf icon-list-item outline-hover">
<span>@icon('bookshelf')</span>
<span>{{ trans('entities.shelves') }}</span>
@endif
@foreach($crumbs as $key => $crumb)
- <?php $isEntity = ($crumb instanceof \BookStack\Entities\Entity); ?>
+ <?php $isEntity = ($crumb instanceof \BookStack\Entities\Models\Entity); ?>
@if (is_null($crumb))
<?php continue; ?>
--- /dev/null
+<a href="{{ $entity->getUrl() }}" class="grid-card"
+ data-entity-type="{{ $entity->getType() }}" data-entity-id="{{ $entity->id }}">
+ <div class="bg-{{ $entity->getType() }} featured-image-container-wrap">
+ <div class="featured-image-container" @if($entity->cover) style="background-image: url('{{ $entity->getBookCover() }}')"@endif>
+ </div>
+ @icon($entity->getType())
+ </div>
+ <div class="grid-card-content">
+ <h2 class="text-limit-lines-2">{{ $entity->name }}</h2>
+ <p class="text-muted">{{ $entity->getExcerpt(130) }}</p>
+ </div>
+ <div class="grid-card-footer text-muted ">
+ <p>@icon('star')<span title="{{ $entity->created_at->toDayDateTimeString() }}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span></p>
+ <p>@icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span></p>
+ </div>
+</a>
\ No newline at end of file
<div class="entity-meta">
@if($entity->isA('revision'))
- @icon('history'){{ trans('entities.pages_revision') }}
- {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
- <br>
+ <div>
+ @icon('history'){{ trans('entities.pages_revision') }}
+ {{ trans('entities.pages_revisions_number') }}{{ $entity->revision_number == 0 ? '' : $entity->revision_number }}
+ </div>
@endif
@if ($entity->isA('page'))
- @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
- @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br>
+ <div>
+ @if (userCan('page-update', $entity)) <a href="{{ $entity->getUrl('/revisions') }}"> @endif
+ @icon('history'){{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }}
@if (userCan('page-update', $entity))</a>@endif
+ </div>
@endif
+ @if ($entity->ownedBy && $entity->ownedBy->id !== $entity->createdBy->id)
+ <div>
+ @icon('user'){!! trans('entities.meta_owned_name', [
+ 'user' => "<a href='{$entity->ownedBy->getProfileUrl()}'>".e($entity->ownedBy->name). "</a>"
+ ]) !!}
+ </div>
+ @endif
@if ($entity->createdBy)
- @icon('star'){!! trans('entities.meta_created_name', [
+ <div>
+ @icon('star'){!! trans('entities.meta_created_name', [
'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
- 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
+ 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".e($entity->createdBy->name). "</a>"
]) !!}
+ </div>
@else
- @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+ <div>
+ @icon('star')<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
+ </div>
@endif
- <br>
-
@if ($entity->updatedBy)
- @icon('edit'){!! trans('entities.meta_updated_name', [
+ <div>
+ @icon('edit'){!! trans('entities.meta_updated_name', [
'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
- 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
+ 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".e($entity->updatedBy->name). "</a>"
]) !!}
+ </div>
@elseif (!$entity->isA('revision'))
- @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+ <div>
+ @icon('edit')<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
+ </div>
@endif
</div>
\ No newline at end of file
?>
<div class="list-sort-container" list-sort-control>
<div class="list-sort-label">{{ trans('common.sort') }}</div>
- <form action="{{ url("/settings/users/{$currentUser->id}/change-sort/{$type}") }}" method="post">
+ <form action="{{ url("/settings/users/". user()->id ."/change-sort/{$type}") }}" method="post">
{!! csrf_field() !!}
{!! method_field('PATCH') !!}
<div>
- <form action="{{ url("/settings/users/{$currentUser->id}/switch-${type}-view") }}" method="POST" class="inline">
+ <form action="{{ url("/settings/users/". user()->id ."/switch-${type}-view") }}" method="POST" class="inline">
{!! csrf_field() !!}
{!! method_field('PATCH') !!}
<input type="hidden" value="{{ $view === 'list'? 'grid' : 'list' }}" name="view_type">
+++ /dev/null
-<div class="page-list">
- @if(count($pages) > 0)
- @foreach($pages as $pageIndex => $page)
- <div class="anim searchResult" style="animation-delay: {{$pageIndex*50 . 'ms'}};">
- @include('pages.list-item', ['page' => $page])
- <hr>
- </div>
- @endforeach
- @else
- <p class="text-muted">{{ trans('entities.search_no_pages') }}</p>
- @endif
-</div>
-
-@if(count($chapters) > 0)
- <div class="page-list">
- @foreach($chapters as $chapterIndex => $chapter)
- <div class="anim searchResult" style="animation-delay: {{($chapterIndex+count($pages))*50 . 'ms'}};">
- @include('chapters.list-item', ['chapter' => $chapter, 'hidePages' => true])
- <hr>
- </div>
- @endforeach
- </div>
-@endif
-
<button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
<ul refs="dropdown@menu" class="dropdown-menu">
<li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
- @foreach($activityKeys as $key)
- <li @if($key === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $key]) }}">{{ $key }}</a></li>
+ @foreach($activityTypes as $type)
+ <li @if($type === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $type]) }}">{{ $type }}</a></li>
@endforeach
</ul>
</div>
<th>
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
</th>
- <th>{{ trans('settings.audit_table_item') }}</th>
+ <th>{{ trans('settings.audit_table_related') }}</th>
<th>
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
</tr>
<td>
@include('partials.table-user', ['user' => $activity->user, 'user_id' => $activity->user_id])
</td>
- <td>{{ $activity->key }}</td>
- <td>
+ <td>{{ $activity->type }}</td>
+ <td width="40%">
@if($activity->entity)
<a href="{{ $activity->entity->getUrl() }}" class="table-entity-item">
<span role="presentation" class="icon text-{{$activity->entity->getType()}}">@icon($activity->entity->getType())</span>
{{ $activity->entity->name }}
</div>
</a>
- @elseif($activity->extra)
+ @elseif($activity->detail && $activity->isForEntity())
<div class="px-m">
{{ trans('settings.audit_deleted_item') }} <br>
- {{ trans('settings.audit_deleted_item_name', ['name' => $activity->extra]) }}
+ {{ trans('settings.audit_deleted_item_name', ['name' => $activity->detail]) }}
</div>
+ @elseif($activity->detail)
+ <div class="px-m">{{ $activity->detail }}</div>
@endif
</td>
<td>{{ $activity->created_at }}</td>
--- /dev/null
+{{--
+$value - Setting value
+$name - Setting input name
+--}}
+<div components="add-remove-rows"
+ option:add-remove-rows:row-selector=".card"
+ option:add-remove-rows:remove-selector="button.text-neg">
+
+ <div component="sortable-list"
+ option:sortable-list:handle-selector=".handle">
+ @foreach(array_merge($value, [['label' => '', 'url' => '']]) as $index => $link)
+ <div class="card drag-card {{ $loop->last ? 'hidden' : '' }}" @if($loop->last) refs="add-remove-rows@model" @endif>
+ <div class="handle">@icon('grip')</div>
+ @foreach(['label', 'url'] as $prop)
+ <div class="outline">
+ <input value="{{ $link[$prop] ?? '' }}"
+ placeholder="{{ trans('settings.app_footer_links_' . $prop) }}"
+ aria-label="{{ trans('settings.app_footer_links_' . $prop) }}"
+ name="{{ $name }}[{{ $loop->parent->last ? 'randrowid' : $index }}][{{$prop}}]"
+ type="text"
+ autocomplete="off"/>
+ </div>
+ @endforeach
+ <button type="button"
+ aria-label="{{ trans('common.remove') }}"
+ class="text-center drag-card-action text-neg">
+ @icon('close')
+ </button>
+ </div>
+ @endforeach
+ </div>
+
+ <button refs="add-remove-rows@add" type="button" class="text-button">{{ trans('settings.app_footer_links_add') }}</button>
+</div>
\ No newline at end of file
</div>
</div>
+ <div>
+ <label for="setting-app-privacy-link" class="setting-list-label">{{ trans('settings.app_footer_links') }}</label>
+ <p class="small mb-m">{{ trans('settings.app_footer_links_desc') }}</p>
+ @include('settings.footer-links', ['name' => 'setting-app-footer-links', 'value' => setting('app-footer-links', [])])
+ </div>
+
<div>
<label for="setting-app-custom-head" class="setting-list-label">{{ trans('settings.app_custom_html') }}</label>
<input type="hidden" name="ignore_revisions" value="{{ session()->getOldInput('ignore_revisions', 'false') }}">
<input type="hidden" name="confirm" value="true">
@else
- <label>
- <input type="checkbox" name="ignore_revisions" value="true">
- {{ trans('settings.maint_image_cleanup_ignore_revisions') }}
+ <label class="flex-container-row">
+ <div class="mr-s"><input type="checkbox" name="ignore_revisions" value="true"></div>
+ <div>{{ trans('settings.maint_delete_images_only_in_revisions') }}</div>
</label>
@endif
</div>
<nav class="active-link-list">
- @if($currentUser->can('settings-manage'))
+ @if(userCan('settings-manage'))
<a href="{{ url('/settings') }}" @if($selected == 'settings') class="active" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
<a href="{{ url('/settings/maintenance') }}" @if($selected == 'maintenance') class="active" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
@endif
- @if($currentUser->can('settings-manage') && $currentUser->can('users-manage'))
+ @if(userCan('settings-manage') && userCan('users-manage'))
<a href="{{ url('/settings/audit') }}" @if($selected == 'audit') class="active" @endif>@icon('open-book'){{ trans('settings.audit') }}</a>
@endif
- @if($currentUser->can('users-manage'))
+ @if(userCan('users-manage'))
<a href="{{ url('/settings/users') }}" @if($selected == 'users') class="active" @endif>@icon('users'){{ trans('settings.users') }}</a>
@endif
- @if($currentUser->can('user-roles-manage'))
+ @if(userCan('user-roles-manage'))
<a href="{{ url('/settings/roles') }}" @if($selected == 'roles') class="active" @endif>@icon('lock-open'){{ trans('settings.roles') }}</a>
@endif
</nav>
\ No newline at end of file
@section('body')
<div class="container small">
- <div class="grid left-focus v-center no-row-gap">
- <div class="py-m">
- @include('settings.navbar', ['selected' => 'maintenance'])
- </div>
+ <div class="py-m">
+ @include('settings.navbar', ['selected' => 'maintenance'])
</div>
<div class="card content-wrap auto-height">
<button type="submit" class="button">{{ trans('common.delete_confirm') }}</button>
</form>
- @if($deletion->deletable instanceof \BookStack\Entities\Entity)
+ @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
<hr class="mt-m">
<h5>{{ trans('settings.recycle_bin_destroy_list') }}</h5>
@include('settings.recycle-bin.deletable-entity-list', ['entity' => $deletion->deletable])
@section('body')
<div class="container">
- <div class="grid left-focus v-center no-row-gap">
- <div class="py-m">
- @include('settings.navbar', ['selected' => 'maintenance'])
- </div>
+ <div class="py-m">
+ @include('settings.navbar', ['selected' => 'maintenance'])
</div>
<div class="card content-wrap auto-height">
<table class="table">
<tr>
- <th>{{ trans('settings.recycle_bin_deleted_item') }}</th>
- <th>{{ trans('settings.recycle_bin_deleted_by') }}</th>
- <th>{{ trans('settings.recycle_bin_deleted_at') }}</th>
- <th></th>
+ <th width="50%">{{ trans('settings.recycle_bin_deleted_item') }}</th>
+ <th width="20%">{{ trans('settings.recycle_bin_deleted_by') }}</th>
+ <th width="15%">{{ trans('settings.recycle_bin_deleted_at') }}</th>
+ <th width="15%"></th>
</tr>
@if(count($deletions) === 0)
<tr>
{{ $deletion->deletable->name }}
</div>
</div>
- @if($deletion->deletable instanceof \BookStack\Entities\Book || $deletion->deletable instanceof \BookStack\Entities\Chapter)
+ @if($deletion->deletable instanceof \BookStack\Entities\Models\Book || $deletion->deletable instanceof \BookStack\Entities\Models\Chapter)
<div class="mb-m"></div>
@endif
- @if($deletion->deletable instanceof \BookStack\Entities\Book)
+ @if($deletion->deletable instanceof \BookStack\Entities\Models\Book)
<div class="pl-xl block inline">
<div class="text-chapter">
@icon('chapter') {{ trans_choice('entities.x_chapters', $deletion->deletable->chapters()->withTrashed()->count()) }}
</div>
</div>
@endif
- @if($deletion->deletable instanceof \BookStack\Entities\Book || $deletion->deletable instanceof \BookStack\Entities\Chapter)
+ @if($deletion->deletable instanceof \BookStack\Entities\Models\Book || $deletion->deletable instanceof \BookStack\Entities\Models\Chapter)
<div class="pl-xl block inline">
<div class="text-page">
@icon('page') {{ trans_choice('entities.x_pages', $deletion->deletable->pages()->withTrashed()->count()) }}
@section('body')
<div class="container small">
- <div class="grid left-focus v-center no-row-gap">
- <div class="py-m">
- @include('settings.navbar', ['selected' => 'maintenance'])
- </div>
+ <div class="py-m">
+ @include('settings.navbar', ['selected' => 'maintenance'])
</div>
<div class="card content-wrap auto-height">
<button type="submit" class="button">{{ trans('settings.recycle_bin_restore') }}</button>
</form>
- @if($deletion->deletable instanceof \BookStack\Entities\Entity)
+ @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
<hr class="mt-m">
<h5>{{ trans('settings.recycle_bin_restore_list') }}</h5>
@if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed())
<img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}">
</div>
<div>
- @if(userCan('users-manage') || $currentUser->id == $user->id)
+ @if(userCan('users-manage') || user()->id == $user->id)
<a href="{{ url("/settings/users/{$user->id}") }}">
@endif
{{ $user->name }}
- @if(userCan('users-manage') || $currentUser->id == $user->id)
+ @if(userCan('users-manage') || user()->id == $user->id)
</a>
@endif
</div>
+++ /dev/null
-<a href="{{$shelf->getUrl()}}" class="bookshelf-grid-item grid-card"
- data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
- <div class="bg-shelf featured-image-container-wrap">
- <div class="featured-image-container" @if($shelf->cover) style="background-image: url('{{ $shelf->getBookCover() }}')"@endif>
- </div>
- @icon('bookshelf')
- </div>
- <div class="grid-card-content">
- <h2>{{$shelf->getShortName(35)}}</h2>
- @if(isset($shelf->searchSnippet))
- <p class="text-muted">{!! $shelf->searchSnippet !!}</p>
- @else
- <p class="text-muted">{{ $shelf->getExcerpt(130) }}</p>
- @endif
- </div>
- <div class="grid-card-footer text-muted text-small">
- @icon('star')<span title="{{$shelf->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $shelf->created_at->diffForHumans()]) }}</span>
- <br>
- @icon('edit')<span title="{{ $shelf->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $shelf->updated_at->diffForHumans()]) }}</span>
- </div>
-</a>
\ No newline at end of file
<div class="actions mb-xl">
<h5>{{ trans('common.actions') }}</h5>
<div class="icon-list text-primary">
- @if($currentUser->can('bookshelf-create-all'))
+ @if(userCan('bookshelf-create-all'))
<a href="{{ url("/create-shelf") }}" class="icon-list-item">
<span>@icon('add')</span>
<span>{{ trans('entities.shelves_new_action') }}</span>
<a href="{{ $shelf->getUrl() }}" class="shelf entity-list-item" data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
- <div class="entity-list-item-image bg-shelf @if($shelf->image_id) has-image @endif" style="background-image: url('{{ $shelf->getBookCover() }}')">
+ <div class="entity-list-item-image bg-bookshelf @if($shelf->image_id) has-image @endif" style="background-image: url('{{ $shelf->getBookCover() }}')">
@icon('bookshelf')
</div>
<div class="content py-xs">
@else
<div class="grid third">
@foreach($shelves as $key => $shelf)
- @include('shelves.grid-item', ['shelf' => $shelf])
+ @include('partials.entity-grid-item', ['entity' => $shelf])
@endforeach
</div>
@endif
@else
<div class="grid third">
@foreach($shelf->visibleBooks as $key => $book)
- @include('books.grid-item', ['book' => $book])
+ @include('partials.entity-grid-item', ['entity' => $book])
@endforeach
</div>
@endif
</div>
<div class="form-group text-right">
- <a href="{{ url($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+ <a href="{{ url(userCan('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
<button class="button" type="submit">{{ trans('common.save') }}</button>
</div>
<p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
+ <hr class="my-l">
+
+ <div class="grid half gap-xl v-center">
+ <div>
+ <label class="setting-list-label">{{ trans('settings.users_migrate_ownership') }}</label>
+ <p class="small">{{ trans('settings.users_migrate_ownership_desc') }}</p>
+ </div>
+ <div>
+ @include('components.user-select', ['name' => 'new_owner_id', 'user' => null])
+ </div>
+ </div>
+
+ <hr class="my-l">
+
<div class="grid half">
<p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
<div>
</div>
<section class="card content-wrap">
- <h1 class="list-heading">{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
+ <h1 class="list-heading">{{ $user->id === user()->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
<form action="{{ url("/settings/users/{$user->id}") }}" method="post" enctype="multipart/form-data">
{!! csrf_field() !!}
<input type="hidden" name="_method" value="PUT">
</div>
<div class="text-right">
- <a href="{{ url($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+ <a href="{{ url(userCan('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
@if($authMethod !== 'system')
<a href="{{ url("/settings/users/{$user->id}/delete") }}" class="button outline">{{ trans('settings.users_delete') }}</a>
@endif
</form>
</section>
- @if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
+ @if(user()->id === $user->id && count($activeSocialDrivers) > 0)
<section class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.users_social_accounts') }}</h2>
<p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
</section>
@endif
- @if(($currentUser->id === $user->id && userCan('access-api')) || userCan('users-manage'))
+ @if((user()->id === $user->id && userCan('access-api')) || userCan('users-manage'))
@include('users.api-tokens.list', ['user' => $user])
@endif
</div>
<div class="grid half mt-m gap-xl">
<div>
<label for="password">{{ trans('auth.password') }}</label>
- @include('form.password', ['name' => 'password'])
+ @include('form.password', ['name' => 'password', 'autocomplete' => 'new-password'])
</div>
<div>
<label for="password-confirm">{{ trans('auth.password_confirm') }}</label>
<input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
</form>
</div>
- @if(userCan('users-manage'))
- <a href="{{ url("/settings/users/create") }}" style="margin-top: 0;" class="outline button">{{ trans('settings.users_add_new') }}</a>
- @endif
+ <a href="{{ url("/settings/users/create") }}" class="outline button mt-none">{{ trans('settings.users_add_new') }}</a>
</div>
</div>
- {{--TODO - Add last login--}}
<table class="table">
<tr>
<th></th>
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a>
</th>
<th>{{ trans('settings.role_user_roles') }}</th>
+ <th class="text-right">
+ <a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'last_activity_at']) }}">{{ trans('settings.users_latest_activity') }}</a>
+ </th>
</tr>
@foreach($users as $user)
<tr>
<td class="text-center" style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
<td>
- @if(userCan('users-manage') || $currentUser->id == $user->id)
- <a href="{{ url("/settings/users/{$user->id}") }}">
- @endif
- {{ $user->name }} <br> <span class="text-muted">{{ $user->email }}</span>
- @if(userCan('users-manage') || $currentUser->id == $user->id)
- </a>
- @endif
+ <a href="{{ url("/settings/users/{$user->id}") }}">
+ {{ $user->name }} <br> <span class="text-muted">{{ $user->email }}</span>
+ </a>
</td>
<td>
@foreach($user->roles as $index => $role)
<small><a href="{{ url("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
@endforeach
</td>
+ <td class="text-right text-muted">
+ @if($user->last_activity_at)
+ <small title="{{ $user->last_activity_at->format('Y-m-d H:i:s') }}">{{ $user->last_activity_at->diffForHumans() }}</small>
+ @endif
+ </td>
</tr>
@endforeach
</table>
Route::get('chapters/{id}/export/pdf', 'ChapterExportApiController@exportPdf');
Route::get('chapters/{id}/export/plaintext', 'ChapterExportApiController@exportPlainText');
+Route::get('pages', 'PageApiController@list');
+Route::post('pages', 'PageApiController@create');
+Route::get('pages/{id}', 'PageApiController@read');
+Route::put('pages/{id}', 'PageApiController@update');
+Route::delete('pages/{id}', 'PageApiController@delete');
+
+Route::get('pages/{id}/export/html', 'PageExportApiController@exportHtml');
+Route::get('pages/{id}/export/pdf', 'PageExportApiController@exportPdf');
+Route::get('pages/{id}/export/plaintext', 'PageExportApiController@exportPlainText');
+
Route::get('shelves', 'BookshelfApiController@list');
Route::post('shelves', 'BookshelfApiController@create');
Route::get('shelves/{id}', 'BookshelfApiController@read');
<?php
+Route::get('/status', 'StatusController@show');
Route::get('/robots.txt', 'HomeController@getRobots');
// Authenticated routes...
Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
Route::get('/search/entity/siblings', 'SearchController@searchSiblings');
+ // User Search
+ Route::get('/search/users/select', 'UserSearchController@forSelect');
+
Route::get('/templates', 'PageTemplateController@list');
Route::get('/templates/{templateId}', 'PageTemplateController@get');
Route::delete('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@destroy');
// Roles
- Route::get('/roles', 'PermissionController@listRoles');
- Route::get('/roles/new', 'PermissionController@createRole');
- Route::post('/roles/new', 'PermissionController@storeRole');
- Route::get('/roles/delete/{id}', 'PermissionController@showDeleteRole');
- Route::delete('/roles/delete/{id}', 'PermissionController@deleteRole');
- Route::get('/roles/{id}', 'PermissionController@editRole');
- Route::put('/roles/{id}', 'PermissionController@updateRole');
+ Route::get('/roles', 'RoleController@list');
+ Route::get('/roles/new', 'RoleController@create');
+ Route::post('/roles/new', 'RoleController@store');
+ Route::get('/roles/delete/{id}', 'RoleController@showDelete');
+ Route::delete('/roles/delete/{id}', 'RoleController@delete');
+ Route::get('/roles/{id}', 'RoleController@edit');
+ Route::put('/roles/{id}', 'RoleController@update');
});
});
<?php namespace Tests;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
class ActivityTrackingTest extends BrowserKitTest
{
<?php namespace Tests\Api;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
use Tests\TestCase;
class ApiListingTest extends TestCase
<?php namespace Tests\Api;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
use Tests\TestCase;
class BooksApiTest extends TestCase
],
'updated_by' => [
'name' => $book->createdBy->name,
- ]
+ ],
+ 'owned_by' => [
+ 'name' => $book->ownedBy->name
+ ],
]);
}
<?php namespace Tests\Api;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
use Tests\TestCase;
class ChaptersApiTest extends TestCase
'updated_by' => [
'name' => $chapter->createdBy->name,
],
+ 'owned_by' => [
+ 'name' => $chapter->ownedBy->name
+ ],
'pages' => [
[
'id' => $page->id,
--- /dev/null
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class PagesApiTest extends TestCase
+{
+ use TestsApi;
+
+ protected $baseEndpoint = '/api/pages';
+
+ public function test_index_endpoint_returns_expected_page()
+ {
+ $this->actingAsApiEditor();
+ $firstPage = Page::query()->orderBy('id', 'asc')->first();
+
+ $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+ $resp->assertJson(['data' => [
+ [
+ 'id' => $firstPage->id,
+ 'name' => $firstPage->name,
+ 'slug' => $firstPage->slug,
+ 'book_id' => $firstPage->book->id,
+ 'priority' => $firstPage->priority,
+ ]
+ ]]);
+ }
+
+ public function test_create_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $book = Book::query()->first();
+ $details = [
+ 'name' => 'My API page',
+ 'book_id' => $book->id,
+ 'html' => '<p>My new page content</p>',
+ 'tags' => [
+ [
+ 'name' => 'tagname',
+ 'value' => 'tagvalue',
+ ]
+ ]
+ ];
+
+ $resp = $this->postJson($this->baseEndpoint, $details);
+ unset($details['html']);
+ $resp->assertStatus(200);
+ $newItem = Page::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+ $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+ $this->assertDatabaseHas('tags', [
+ 'entity_id' => $newItem->id,
+ 'entity_type' => $newItem->getMorphClass(),
+ 'name' => 'tagname',
+ 'value' => 'tagvalue',
+ ]);
+ $resp->assertSeeText('My new page content');
+ $resp->assertJsonMissing(['book' => []]);
+ $this->assertActivityExists('page_create', $newItem);
+ }
+
+ public function test_page_name_needed_to_create()
+ {
+ $this->actingAsApiEditor();
+ $book = Book::query()->first();
+ $details = [
+ 'book_id' => $book->id,
+ 'html' => '<p>A page created via the API</p>',
+ ];
+
+ $resp = $this->postJson($this->baseEndpoint, $details);
+ $resp->assertStatus(422);
+ $resp->assertJson($this->validationResponse([
+ "name" => ["The name field is required."]
+ ]));
+ }
+
+ public function test_book_id_or_chapter_id_needed_to_create()
+ {
+ $this->actingAsApiEditor();
+ $details = [
+ 'name' => 'My api page',
+ 'html' => '<p>A page created via the API</p>',
+ ];
+
+ $resp = $this->postJson($this->baseEndpoint, $details);
+ $resp->assertStatus(422);
+ $resp->assertJson($this->validationResponse([
+ "book_id" => ["The book id field is required when chapter id is not present."],
+ "chapter_id" => ["The chapter id field is required when book id is not present."]
+ ]));
+
+ $chapter = Chapter::visible()->first();
+ $resp = $this->postJson($this->baseEndpoint, array_merge($details, ['chapter_id' => $chapter->id]));
+ $resp->assertStatus(200);
+
+ $book = Book::visible()->first();
+ $resp = $this->postJson($this->baseEndpoint, array_merge($details, ['book_id' => $book->id]));
+ $resp->assertStatus(200);
+ }
+
+ public function test_markdown_can_be_provided_for_create()
+ {
+ $this->actingAsApiEditor();
+ $book = Book::visible()->first();
+ $details = [
+ 'book_id' => $book->id,
+ 'name' => 'My api page',
+ 'markdown' => "# A new API page \n[link](https://example.com)",
+ ];
+
+ $resp = $this->postJson($this->baseEndpoint, $details);
+ $resp->assertJson(['markdown' => $details['markdown']]);
+
+ $respHtml = $resp->json('html');
+ $this->assertStringContainsString('new API page</h1>', $respHtml);
+ $this->assertStringContainsString('link</a>', $respHtml);
+ $this->assertStringContainsString('href="https://example.com"', $respHtml);
+ }
+
+ public function test_read_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+
+ $resp = $this->getJson($this->baseEndpoint . "/{$page->id}");
+ $resp->assertStatus(200);
+ $resp->assertJson([
+ 'id' => $page->id,
+ 'slug' => $page->slug,
+ 'created_by' => [
+ 'name' => $page->createdBy->name,
+ ],
+ 'book_id' => $page->book_id,
+ 'updated_by' => [
+ 'name' => $page->createdBy->name,
+ ],
+ 'owned_by' => [
+ 'name' => $page->ownedBy->name
+ ],
+ ]);
+ }
+
+ public function test_read_endpoint_provides_rendered_html()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+ $page->html = "<p>testing</p><script>alert('danger')</script><h1>Hello</h1>";
+ $page->save();
+
+ $resp = $this->getJson($this->baseEndpoint . "/{$page->id}");
+ $html = $resp->json('html');
+ $this->assertStringNotContainsString('script', $html);
+ $this->assertStringContainsString('Hello', $html);
+ $this->assertStringContainsString('testing', $html);
+ }
+
+ public function test_update_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+ $details = [
+ 'name' => 'My updated API page',
+ 'html' => '<p>A page created via the API</p>',
+ 'tags' => [
+ [
+ 'name' => 'freshtag',
+ 'value' => 'freshtagval',
+ ]
+ ],
+ ];
+
+ $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+ $page->refresh();
+
+ $resp->assertStatus(200);
+ unset($details['html']);
+ $resp->assertJson(array_merge($details, [
+ 'id' => $page->id, 'slug' => $page->slug, 'book_id' => $page->book_id
+ ]));
+ $this->assertActivityExists('page_update', $page);
+ }
+
+ public function test_providing_new_chapter_id_on_update_will_move_page()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+ $chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
+ $details = [
+ 'name' => 'My updated API page',
+ 'chapter_id' => $chapter->id,
+ 'html' => '<p>A page created via the API</p>',
+ ];
+
+ $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+ $resp->assertStatus(200);
+ $resp->assertJson([
+ 'chapter_id' => $chapter->id,
+ 'book_id' => $chapter->book_id,
+ ]);
+ }
+
+ public function test_providing_move_via_update_requires_page_create_permission_on_new_parent()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+ $chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
+ $this->setEntityRestrictions($chapter, ['view'], [$this->getEditor()->roles()->first()]);
+ $details = [
+ 'name' => 'My updated API page',
+ 'chapter_id' => $chapter->id,
+ 'html' => '<p>A page created via the API</p>',
+ ];
+
+ $resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
+ $resp->assertStatus(403);
+ }
+
+ public function test_delete_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+ $resp = $this->deleteJson($this->baseEndpoint . "/{$page->id}");
+
+ $resp->assertStatus(204);
+ $this->assertActivityExists('page_delete', $page);
+ }
+
+ public function test_export_html_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+
+ $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/html");
+ $resp->assertStatus(200);
+ $resp->assertSee($page->name);
+ $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"');
+ }
+
+ public function test_export_plain_text_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+
+ $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/plaintext");
+ $resp->assertStatus(200);
+ $resp->assertSee($page->name);
+ $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"');
+ }
+
+ public function test_export_pdf_endpoint()
+ {
+ $this->actingAsApiEditor();
+ $page = Page::visible()->first();
+
+ $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/pdf");
+ $resp->assertStatus(200);
+ $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
+ }
+}
\ No newline at end of file
<?php namespace Tests\Api;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
use Tests\TestCase;
class ShelvesApiTest extends TestCase
],
'updated_by' => [
'name' => $shelf->createdBy->name,
- ]
+ ],
+ 'owned_by' => [
+ 'name' => $shelf->ownedBy->name
+ ],
]);
}
use BookStack\Actions\Activity;
use BookStack\Actions\ActivityService;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\UserRepo;
-use BookStack\Entities\Managers\TrashCan;
-use BookStack\Entities\Page;
+use BookStack\Entities\Tools\TrashCan;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use Carbon\Carbon;
class AuditLogTest extends TestCase
{
+ /** @var ActivityService */
+ protected $activityService;
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->activityService = app(ActivityService::class);
+ }
public function test_only_accessible_with_right_permissions()
{
$admin = $this->getAdmin();
$this->actingAs($admin);
$page = Page::query()->first();
- app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+ $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
$activity = Activity::query()->orderBy('id', 'desc')->first();
$resp = $this->get('settings/audit');
$this->actingAs( $this->getAdmin());
$page = Page::query()->first();
$pageName = $page->name;
- app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+ $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
app(PageRepo::class)->destroy($page);
app(TrashCan::class)->empty();
$viewer = $this->getViewer();
$this->actingAs($viewer);
$page = Page::query()->first();
- app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+ $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
$this->actingAs($this->getAdmin());
app(UserRepo::class)->destroy($viewer);
{
$this->actingAs($this->getAdmin());
$page = Page::query()->first();
- app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+ $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
$resp = $this->get('settings/audit');
$resp->assertSeeText($page->name);
{
$this->actingAs($this->getAdmin());
$page = Page::query()->first();
- app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+ $this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
$yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
$tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Notifications\ConfirmEmail;
use BookStack\Notifications\ResetPassword;
use BookStack\Settings\SettingService;
<?php namespace Tests;
-use BookStack\Entities\Entity;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
use BookStack\Auth\Role;
use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\Models\Page;
use BookStack\Settings\SettingService;
+use DB;
use Illuminate\Contracts\Console\Kernel;
+use Illuminate\Foundation\Application;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Laravel\BrowserKitTesting\TestCase;
use Symfony\Component\DomCrawler\Crawler;
public function tearDown() : void
{
- \DB::disconnect();
+ DB::disconnect();
parent::tearDown();
}
/**
* Creates the application.
*
- * @return \Illuminate\Foundation\Application
+ * @return Application
*/
public function createApplication()
{
*/
public function getNormalUser()
{
- return \BookStack\Auth\User::where('system_name', '=', null)->get()->last();
+ return User::where('system_name', '=', null)->get()->last();
}
/**
/**
* Create a group of entities that belong to a specific user.
- * @param $creatorUser
- * @param $updaterUser
- * @return array
*/
- protected function createEntityChainBelongingToUser($creatorUser, $updaterUser = false)
+ protected function createEntityChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
{
- if ($updaterUser === false) $updaterUser = $creatorUser;
- $book = factory(\BookStack\Entities\Book::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id]);
- $chapter = factory(\BookStack\Entities\Chapter::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id]);
- $page = factory(\BookStack\Entities\Page::class)->create(['created_by' => $creatorUser->id, 'updated_by' => $updaterUser->id, 'book_id' => $book->id, 'chapter_id' => $chapter->id]);
+ if (empty($updaterUser)) {
+ $updaterUser = $creatorUser;
+ }
+
+ $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
+ $book = factory(Book::class)->create($userAttrs);
+ $chapter = factory(Chapter::class)->create(array_merge(['book_id' => $book->id], $userAttrs));
+ $page = factory(Page::class)->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
$restrictionService = $this->app[PermissionService::class];
$restrictionService->buildJointPermissionsForEntity($book);
- return [
- 'book' => $book,
- 'chapter' => $chapter,
- 'page' => $page
- ];
+
+ return compact('book', 'chapter', 'page');
}
/**
*/
protected function getNewBlankUser($attributes = [])
{
- $user = factory(\BookStack\Auth\User::class)->create($attributes);
+ $user = factory(User::class)->create($attributes);
return $user;
}
<?php namespace Tests;
+use BookStack\Actions\ActivityType;
use BookStack\Actions\Comment;
use BookStack\Actions\CommentRepo;
use BookStack\Auth\Permissions\JointPermission;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Page;
use BookStack\Auth\User;
use BookStack\Entities\Repos\PageRepo;
use Symfony\Component\Console\Exception\RuntimeException;
{
$this->asEditor();
$page = Page::first();
- \Activity::add($page, 'page_update', $page->book->id);
+ \Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
$this->assertDatabaseHas('activities', [
- 'key' => 'page_update',
+ 'type' => 'page_update',
'entity_id' => $page->id,
'user_id' => $this->getEditor()->id
]);
$this->assertDatabaseMissing('activities', [
- 'key' => 'page_update'
+ 'type' => 'page_update'
]);
}
<?php namespace Tests\Entity;
use BookStack\Auth\User;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
use BookStack\Uploads\Image;
use Illuminate\Support\Str;
use Tests\TestCase;
<?php namespace Tests\Entity;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
use Tests\TestCase;
class BookTest extends TestCase
<?php namespace Tests\Entity;
-use BookStack\Entities\Chapter;
+use BookStack\Entities\Models\Chapter;
use Tests\TestCase;
class ChapterTest extends TestCase
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use Tests\BrowserKitTest;
class CommentSettingTest extends BrowserKitTest
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Actions\Comment;
use Tests\TestCase;
<?php namespace Tests\Entity;
use BookStack\Actions\Tag;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use Tests\TestCase;
class EntitySearchTest extends TestCase
<?php namespace Tests\Entity;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Auth\UserRepo;
use BookStack\Entities\Repos\PageRepo;
use Carbon\Carbon;
<?php namespace Tests\Entity;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
-use BookStack\Uploads\HttpFetcher;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Tests\TestCase;
public function test_page_export_sets_right_data_type_for_svg_embeds()
{
$page = Page::first();
- $page->html = '<img src="http://example.com/image.svg">';
+ Storage::disk('local')->makeDirectory('uploads/images/gallery');
+ Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
+ $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg">';
$page->save();
$this->asEditor();
- $this->mockHttpFetch('<svg></svg>');
$resp = $this->get($page->getUrl('/export/html'));
+ Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg');
+
$resp->assertStatus(200);
$resp->assertSee('<img src="data:image/svg+xml;base64');
}
-}
\ No newline at end of file
+ public function test_page_image_containment_works_on_multiple_images_within_a_single_line()
+ {
+ $page = Page::first();
+ Storage::disk('local')->makeDirectory('uploads/images/gallery');
+ Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
+ Storage::disk('local')->put('uploads/images/gallery/svg_test2.svg', '<svg></svg>');
+ $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg" class="a"><img src="http://localhost/uploads/images/gallery/svg_test2.svg" class="b">';
+ $page->save();
+
+ $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+ Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg');
+ Storage::disk('local')->delete('uploads/images/gallery/svg_test2.svg');
+
+ $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test');
+ }
+
+ public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder()
+ {
+ $page = Page::first();
+ $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg"/>'
+ .'<img src="http://localhost/uploads/svg_test.svg"/>'
+ .'<img src="/uploads/svg_test.svg"/>';
+ $storageDisk = Storage::disk('local');
+ $storageDisk->makeDirectory('uploads/images/gallery');
+ $storageDisk->put('uploads/images/gallery/svg_test.svg', '<svg>good</svg>');
+ $storageDisk->put('uploads/svg_test.svg', '<svg>bad</svg>');
+ $page->save();
+
+ $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+
+ $storageDisk->delete('uploads/images/gallery/svg_test.svg');
+ $storageDisk->delete('uploads/svg_test.svg');
+
+ $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test.svg');
+ $resp->assertSee('http://localhost/uploads/svg_test.svg');
+ $resp->assertSee('src="/uploads/svg_test.svg"');
+ }
+
+}
public function setUp(): void
{
parent::setUp();
- $this->page = \BookStack\Entities\Page::first();
+ $this->page = \BookStack\Entities\Models\Page::first();
}
protected function setMarkdownEditor()
<?php namespace Tests\Entity;
-use BookStack\Entities\Managers\PageContent;
-use BookStack\Entities\Page;
+use BookStack\Entities\Tools\PageContent;
+use BookStack\Entities\Models\Page;
use Tests\TestCase;
class PageContentTest extends TestCase
$page->refresh();
$this->assertEquals('"Hello & welcome"', $page->text);
}
+
+ public function test_page_markdown_table_rendering()
+ {
+ $this->asEditor();
+ $page = Page::query()->first();
+
+ $content = '| Syntax | Description |
+| ----------- | ----------- |
+| Header | Title |
+| Paragraph | Text |';
+ $this->put($page->getUrl(), [
+ 'name' => $page->name, 'markdown' => $content,
+ 'html' => '', 'summary' => ''
+ ]);
+
+ $page->refresh();
+ $this->assertStringContainsString('</tbody>', $page->html);
+
+ $pageView = $this->get($page->getUrl());
+ $pageView->assertElementExists('.page-content table tbody td');
+ }
+
+ public function test_page_markdown_task_list_rendering()
+ {
+ $this->asEditor();
+ $page = Page::query()->first();
+
+ $content = '- [ ] Item a
+- [x] Item b';
+ $this->put($page->getUrl(), [
+ 'name' => $page->name, 'markdown' => $content,
+ 'html' => '', 'summary' => ''
+ ]);
+
+ $page->refresh();
+ $this->assertStringContainsString('input', $page->html);
+ $this->assertStringContainsString('type="checkbox"', $page->html);
+
+ $pageView = $this->get($page->getUrl());
+ $pageView->assertElementExists('.page-content input[type=checkbox]');
+ }
+
+ public function test_page_markdown_strikethrough_rendering()
+ {
+ $this->asEditor();
+ $page = Page::query()->first();
+
+ $content = '~~some crossed out text~~';
+ $this->put($page->getUrl(), [
+ 'name' => $page->name, 'markdown' => $content,
+ 'html' => '', 'summary' => ''
+ ]);
+
+ $page->refresh();
+ $this->assertStringMatchesFormat('%A<s%A>some crossed out text</s>%A', $page->html);
+
+ $pageView = $this->get($page->getUrl());
+ $pageView->assertElementExists('.page-content p > s');
+ }
}
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use Tests\BrowserKitTest;
public function setUp(): void
{
parent::setUp();
- $this->page = \BookStack\Entities\Page::first();
+ $this->page = \BookStack\Entities\Models\Page::first();
$this->pageRepo = app(PageRepo::class);
}
public function test_alert_message_shows_if_someone_else_editing()
{
- $nonEditedPage = \BookStack\Entities\Page::take(10)->get()->last();
+ $nonEditedPage = \BookStack\Entities\Models\Page::take(10)->get()->last();
$addedContent = '<p>test message content</p>';
$this->asAdmin()->visit($this->page->getUrl('/edit'))
->dontSeeInField('html', $addedContent);
public function test_draft_pages_show_on_homepage()
{
- $book = \BookStack\Entities\Book::first();
+ $book = \BookStack\Entities\Models\Book::first();
$this->asAdmin()->visit('/')
->dontSeeInElement('#recent-drafts', 'New Page')
->visit($book->getUrl() . '/create-page')
public function test_draft_pages_not_visible_by_others()
{
- $book = \BookStack\Entities\Book::first();
+ $book = \BookStack\Entities\Models\Book::first();
$chapter = $book->chapters->first();
$newUser = $this->getEditor();
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use Tests\TestCase;
$pageView->assertSee('def456');
}
+ public function test_page_revision_restore_with_markdown_retains_markdown_content()
+ {
+ $this->asEditor();
+
+ $pageRepo = app(PageRepo::class);
+ $page = Page::first();
+ $pageRepo->update($page, ['name' => 'updated page abc123', 'markdown' => '## New Content def456', 'summary' => 'initial page revision testing']);
+ $pageRepo->update($page, ['name' => 'updated page again', 'markdown' => '## New Content Updated', 'summary' => 'page revision testing']);
+ $page = Page::find($page->id);
+
+ $pageView = $this->get($page->getUrl());
+ $pageView->assertDontSee('abc123');
+ $pageView->assertDontSee('def456');
+
+ $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
+ $restoreReq = $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
+ $page = Page::find($page->id);
+
+ $restoreReq->assertStatus(302);
+ $restoreReq->assertRedirect($page->getUrl());
+
+ $pageView = $this->get($page->getUrl());
+ $this->assertDatabaseHas('pages', [
+ 'id' => $page->id,
+ 'markdown' => '## New Content def456',
+ ]);
+ $pageView->assertSee('abc123');
+ $pageView->assertSee('def456');
+ }
+
+ public function test_page_revision_restore_sets_new_revision_with_summary()
+ {
+ $this->asEditor();
+
+ $pageRepo = app(PageRepo::class);
+ $page = Page::first();
+ $pageRepo->update($page, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'My first update']);
+ $pageRepo->update($page, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => '']);
+ $page->refresh();
+
+ $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
+ $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
+ $page->refresh();
+
+ $this->assertDatabaseHas('page_revisions', [
+ 'page_id' => $page->id,
+ 'text' => 'new contente def456',
+ 'type' => 'version',
+ 'summary' => "Restored from #{$revToRestore->id}; My first update",
+ ]);
+ }
+
public function test_page_revision_count_increments_on_update()
{
$page = Page::first();
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use Tests\TestCase;
class PageTemplateTest extends TestCase
<?php namespace Tests\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Page;
use Tests\TestCase;
class PageTest extends TestCase
{
+ public function test_page_creation_with_markdown_content()
+ {
+ $this->setSettings(['app-editor' => 'markdown']);
+ $book = Book::query()->first();
+
+ $this->asEditor()->get($book->getUrl('/create-page'));
+ $draft = Page::query()->where('book_id', '=', $book->id)
+ ->where('draft', '=', true)->first();
+
+ $details = [
+ 'markdown' => '# a title',
+ 'html' => '<h1>a title</h1>',
+ 'name' => 'my page',
+ ];
+ $resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details);
+ $resp->assertRedirect();
+
+ $this->assertDatabaseHas('pages', [
+ 'markdown' => $details['markdown'],
+ 'name' => $details['name'],
+ 'id' => $draft->id,
+ 'draft' => false
+ ]);
+
+ $draft->refresh();
+ $resp = $this->get($draft->getUrl("/edit"));
+ $resp->assertSee("# a title");
+ }
+
public function test_page_delete()
{
$page = Page::query()->first();
$redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
$redirectReq->assertNotificationContains('Page Successfully Deleted');
}
+
+ public function test_page_copy()
+ {
+ $page = Page::first();
+ $page->html = '<p>This is some test content</p>';
+ $page->save();
+
+ $currentBook = $page->book;
+ $newBook = Book::where('id', '!=', $currentBook->id)->first();
+
+ $resp = $this->asEditor()->get($page->getUrl('/copy'));
+ $resp->assertSee('Copy Page');
+
+ $movePageResp = $this->post($page->getUrl('/copy'), [
+ 'entity_selection' => 'book:' . $newBook->id,
+ 'name' => 'My copied test page'
+ ]);
+ $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+ $movePageResp->assertRedirect($pageCopy->getUrl());
+ $this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
+ $this->assertStringContainsString('This is some test content', $pageCopy->html);
+ }
+
+ public function test_page_copy_with_markdown_has_both_html_and_markdown()
+ {
+ $page = Page::first();
+ $page->html = '<h1>This is some test content</h1>';
+ $page->markdown = '# This is some test content';
+ $page->save();
+ $newBook = Book::where('id', '!=', $page->book->id)->first();
+
+ $this->asEditor()->post($page->getUrl('/copy'), [
+ 'entity_selection' => 'book:' . $newBook->id,
+ 'name' => 'My copied test page'
+ ]);
+ $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+ $this->assertStringContainsString('This is some test content', $pageCopy->html);
+ $this->assertEquals('# This is some test content', $pageCopy->markdown);
+ }
+
+ public function test_page_copy_with_no_destination()
+ {
+ $page = Page::first();
+ $currentBook = $page->book;
+
+ $resp = $this->asEditor()->get($page->getUrl('/copy'));
+ $resp->assertSee('Copy Page');
+
+ $movePageResp = $this->post($page->getUrl('/copy'), [
+ 'name' => 'My copied test page'
+ ]);
+
+ $pageCopy = Page::where('name', '=', 'My copied test page')->first();
+
+ $movePageResp->assertRedirect($pageCopy->getUrl());
+ $this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
+ $this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
+ }
+
+ public function test_page_can_be_copied_without_edit_permission()
+ {
+ $page = Page::first();
+ $currentBook = $page->book;
+ $newBook = Book::where('id', '!=', $currentBook->id)->first();
+ $viewer = $this->getViewer();
+
+ $resp = $this->actingAs($viewer)->get($page->getUrl());
+ $resp->assertDontSee($page->getUrl('/copy'));
+
+ $newBook->owned_by = $viewer->id;
+ $newBook->save();
+ $this->giveUserPermissions($viewer, ['page-create-own']);
+ $this->regenEntityPermissions($newBook);
+
+ $resp = $this->actingAs($viewer)->get($page->getUrl());
+ $resp->assertSee($page->getUrl('/copy'));
+
+ $movePageResp = $this->post($page->getUrl('/copy'), [
+ 'entity_selection' => 'book:' . $newBook->id,
+ 'name' => 'My copied test page'
+ ]);
+ $movePageResp->assertRedirect();
+
+ $this->assertDatabaseHas('pages', [
+ 'name' => 'My copied test page',
+ 'created_by' => $viewer->id,
+ 'book_id' => $newBook->id,
+ ]);
+ }
}
\ No newline at end of file
<?php namespace Tests\Entity;
-use BookStack\Entities\SearchOptions;
+use BookStack\Entities\Tools\SearchOptions;
use Tests\TestCase;
class SearchOptionsTest extends TestCase
<?php namespace Tests\Entity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use Tests\TestCase;
$movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id
]);
- $page = Page::find($page->id);
+ $page->refresh();
$movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book');
$checkResp->assertSee($newBook->name);
}
- public function test_page_copy()
- {
- $page = Page::first();
- $currentBook = $page->book;
- $newBook = Book::where('id', '!=', $currentBook->id)->first();
-
- $resp = $this->asEditor()->get($page->getUrl('/copy'));
- $resp->assertSee('Copy Page');
-
- $movePageResp = $this->post($page->getUrl('/copy'), [
- 'entity_selection' => 'book:' . $newBook->id,
- 'name' => 'My copied test page'
- ]);
- $pageCopy = Page::where('name', '=', 'My copied test page')->first();
-
- $movePageResp->assertRedirect($pageCopy->getUrl());
- $this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
- }
-
- public function test_page_copy_with_no_destination()
- {
- $page = Page::first();
- $currentBook = $page->book;
-
- $resp = $this->asEditor()->get($page->getUrl('/copy'));
- $resp->assertSee('Copy Page');
-
- $movePageResp = $this->post($page->getUrl('/copy'), [
- 'name' => 'My copied test page'
- ]);
-
- $pageCopy = Page::where('name', '=', 'My copied test page')->first();
-
- $movePageResp->assertRedirect($pageCopy->getUrl());
- $this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
- $this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
- }
-
- public function test_page_can_be_copied_without_edit_permission()
- {
- $page = Page::first();
- $currentBook = $page->book;
- $newBook = Book::where('id', '!=', $currentBook->id)->first();
- $viewer = $this->getViewer();
-
- $resp = $this->actingAs($viewer)->get($page->getUrl());
- $resp->assertDontSee($page->getUrl('/copy'));
-
- $newBook->created_by = $viewer->id;
- $newBook->save();
- $this->giveUserPermissions($viewer, ['page-create-own']);
- $this->regenEntityPermissions($newBook);
-
- $resp = $this->actingAs($viewer)->get($page->getUrl());
- $resp->assertSee($page->getUrl('/copy'));
-
- $movePageResp = $this->post($page->getUrl('/copy'), [
- 'entity_selection' => 'book:' . $newBook->id,
- 'name' => 'My copied test page'
- ]);
- $movePageResp->assertRedirect();
-
- $this->assertDatabaseHas('pages', [
- 'name' => 'My copied test page',
- 'created_by' => $viewer->id,
- 'book_id' => $newBook->id,
- ]);
- }
-
}
\ No newline at end of file
<?php namespace Tests\Entity;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
use BookStack\Actions\Tag;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
use BookStack\Auth\Permissions\PermissionService;
use Tests\BrowserKitTest;
<?php namespace Tests;
-use BookStack\Entities\Book;
+use BookStack\Entities\Models\Book;
use Illuminate\Support\Facades\Log;
class ErrorTest extends TestCase
--- /dev/null
+<?php
+
+use Tests\TestCase;
+
+class FooterLinksTest extends TestCase
+{
+
+ public function test_saving_setting()
+ {
+ $resp = $this->asAdmin()->post("/settings", [
+ 'setting-app-footer-links' => [
+ ['label' => 'My custom link 1', 'url' => 'https://example.com/1'],
+ ['label' => 'My custom link 2', 'url' => 'https://example.com/2'],
+ ],
+ ]);
+ $resp->assertRedirect('/settings');
+
+ $result = setting('app-footer-links');
+ $this->assertIsArray($result);
+ $this->assertCount(2, $result);
+ $this->assertEquals('My custom link 2', $result[1]['label']);
+ $this->assertEquals('https://example.com/1', $result[0]['url']);
+ }
+
+ public function test_set_options_visible_on_settings_page()
+ {
+ $this->setSettings(['app-footer-links' => [
+ ['label' => 'My custom link', 'url' => 'https://example.com/link-a'],
+ ['label' => 'Another Link', 'url' => 'https://example.com/link-b'],
+ ]]);
+
+ $resp = $this->asAdmin()->get('/settings');
+ $resp->assertSee('value="My custom link"');
+ $resp->assertSee('value="Another Link"');
+ $resp->assertSee('value="https://example.com/link-a"');
+ $resp->assertSee('value="https://example.com/link-b"');
+ }
+
+ public function test_footer_links_show_on_pages()
+ {
+ $this->setSettings(['app-footer-links' => [
+ ['label' => 'My custom link', 'url' => 'https://example.com/link-a'],
+ ['label' => 'Another Link', 'url' => 'https://example.com/link-b'],
+ ]]);
+
+ $this->get('/login')->assertElementContains('footer a[href="https://example.com/link-a"]', 'My custom link');
+ $this->asEditor()->get('/')->assertElementContains('footer a[href="https://example.com/link-b"]', 'Another link');
+ }
+
+ public function test_using_translation_system_for_labels()
+ {
+ $this->setSettings(['app-footer-links' => [
+ ['label' => 'trans::common.privacy_policy', 'url' => 'https://example.com/privacy'],
+ ['label' => 'trans::common.terms_of_service', 'url' => 'https://example.com/terms'],
+ ]]);
+
+ $resp = $this->get('/login');
+ $resp->assertElementContains('footer a[href="https://example.com/privacy"]', 'Privacy Policy');
+ $resp->assertElementContains('footer a[href="https://example.com/terms"]', 'Terms of Service');
+ }
+}
\ No newline at end of file
<?php namespace Tests;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Bookshelf;
class HomepageTest extends TestCase
{
{
$editor = $this->getEditor();
setting()->putUser($editor, 'bookshelves_view_type', 'grid');
+ $shelf = Bookshelf::query()->firstOrFail();
$this->setSettings(['app-homepage-type' => 'bookshelves']);
$this->asEditor();
$homeVisit = $this->get('/');
$homeVisit->assertSee('Shelves');
- $homeVisit->assertSee('bookshelf-grid-item grid-card');
$homeVisit->assertSee('grid-card-content');
- $homeVisit->assertSee('grid-card-footer');
$homeVisit->assertSee('featured-image-container');
+ $homeVisit->assertElementContains('.grid-card', $shelf->name);
$this->setSettings(['app-homepage-type' => false]);
$this->test_default_homepage_visible();
--- /dev/null
+<?php namespace Tests\Permissions;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Str;
+use Tests\TestCase;
+
+class EntityOwnerChangeTest extends TestCase
+{
+
+ public function test_changing_page_owner()
+ {
+ $page = Page::query()->first();
+ $user = User::query()->where('id', '!=', $page->owned_by)->first();
+
+ $this->asAdmin()->put($page->getUrl('permissions'), ['owned_by' => $user->id]);
+ $this->assertDatabaseHas('pages', ['owned_by' => $user->id, 'id' => $page->id]);
+ }
+
+ public function test_changing_chapter_owner()
+ {
+ $chapter = Chapter::query()->first();
+ $user = User::query()->where('id', '!=', $chapter->owned_by)->first();
+
+ $this->asAdmin()->put($chapter->getUrl('permissions'), ['owned_by' => $user->id]);
+ $this->assertDatabaseHas('chapters', ['owned_by' => $user->id, 'id' => $chapter->id]);
+ }
+
+ public function test_changing_book_owner()
+ {
+ $book = Book::query()->first();
+ $user = User::query()->where('id', '!=', $book->owned_by)->first();
+
+ $this->asAdmin()->put($book->getUrl('permissions'), ['owned_by' => $user->id]);
+ $this->assertDatabaseHas('books', ['owned_by' => $user->id, 'id' => $book->id]);
+ }
+
+ public function test_changing_shelf_owner()
+ {
+ $shelf = Bookshelf::query()->first();
+ $user = User::query()->where('id', '!=', $shelf->owned_by)->first();
+
+ $this->asAdmin()->put($shelf->getUrl('permissions'), ['owned_by' => $user->id]);
+ $this->assertDatabaseHas('bookshelves', ['owned_by' => $user->id, 'id' => $shelf->id]);
+ }
+
+}
\ No newline at end of file
<?php namespace Tests\Permissions;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
use BookStack\Auth\User;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
+use Illuminate\Support\Str;
use Tests\BrowserKitTest;
-class RestrictionsTest extends BrowserKitTest
+class EntityPermissionsTest extends BrowserKitTest
{
/**
->dontSee($page->name);
}
+ public function test_restricted_chapter_pages_not_visible_on_book_page()
+ {
+ $chapter = Chapter::query()->first();
+ $this->actingAs($this->user)
+ ->visit($chapter->book->getUrl())
+ ->see($chapter->pages->first()->name);
+
+ foreach ($chapter->pages as $page) {
+ $this->setEntityRestrictions($page, []);
+ }
+
+ $this->actingAs($this->user)
+ ->visit($chapter->book->getUrl())
+ ->dontSee($chapter->pages->first()->name);
+ }
+
public function test_bookshelf_update_restriction_override()
{
$shelf = Bookshelf::first();
public function test_page_visible_if_has_permissions_when_book_not_visible()
{
$book = Book::first();
-
- $this->setEntityRestrictions($book, []);
-
$bookChapter = $book->chapters->first();
$bookPage = $bookChapter->pages->first();
+
+ foreach ([$book, $bookChapter, $bookPage] as $entity) {
+ $entity->name = Str::random(24);
+ $entity->save();
+ }
+
+ $this->setEntityRestrictions($book, []);
$this->setEntityRestrictions($bookPage, ['view']);
$this->actingAs($this->viewer);
--- /dev/null
+<?php namespace Tests\Permissions;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use Illuminate\Support\Str;
+use Tests\TestCase;
+
+class ExportPermissionsTest extends TestCase
+{
+
+ public function test_page_content_without_view_access_hidden_on_chapter_export()
+ {
+ $chapter = Chapter::query()->first();
+ $page = $chapter->pages()->firstOrFail();
+ $pageContent = Str::random(48);
+ $page->html = '<p>' . $pageContent . '</p>';
+ $page->save();
+ $viewer = $this->getViewer();
+ $this->actingAs($viewer);
+ $formats = ['html', 'plaintext'];
+
+ foreach ($formats as $format) {
+ $resp = $this->get($chapter->getUrl("export/{$format}"));
+ $resp->assertStatus(200);
+ $resp->assertSee($page->name);
+ $resp->assertSee($pageContent);
+ }
+
+ $this->setEntityRestrictions($page, []);
+
+ foreach ($formats as $format) {
+ $resp = $this->get($chapter->getUrl("export/{$format}"));
+ $resp->assertStatus(200);
+ $resp->assertDontSee($page->name);
+ $resp->assertDontSee($pageContent);
+ }
+ }
+
+ public function test_page_content_without_view_access_hidden_on_book_export()
+ {
+ $book = Book::query()->first();
+ $page = $book->pages()->firstOrFail();
+ $pageContent = Str::random(48);
+ $page->html = '<p>' . $pageContent . '</p>';
+ $page->save();
+ $viewer = $this->getViewer();
+ $this->actingAs($viewer);
+ $formats = ['html', 'plaintext'];
+
+ foreach ($formats as $format) {
+ $resp = $this->get($book->getUrl("export/{$format}"));
+ $resp->assertStatus(200);
+ $resp->assertSee($page->name);
+ $resp->assertSee($pageContent);
+ }
+
+ $this->setEntityRestrictions($page, []);
+
+ foreach ($formats as $format) {
+ $resp = $this->get($book->getUrl("export/{$format}"));
+ $resp->assertStatus(200);
+ $resp->assertDontSee($page->name);
+ $resp->assertDontSee($pageContent);
+ }
+ }
+
+}
\ No newline at end of file
<?php namespace Tests\Permissions;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Page;
+use BookStack\Actions\Comment;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
use BookStack\Auth\Role;
+use BookStack\Uploads\Image;
use Laravel\BrowserKitTesting\HttpException;
use Tests\BrowserKitTest;
public function test_cannot_delete_admin_role()
{
- $adminRole = \BookStack\Auth\Role::getRole('admin');
+ $adminRole = Role::getRole('admin');
$deletePageUrl = '/settings/roles/delete/' . $adminRole->id;
$this->asAdmin()->visit($deletePageUrl)
->press('Confirm')
public function test_restrictions_manage_all_permission()
{
- $page = \BookStack\Entities\Page::take(1)->get()->first();
+ $page = Page::take(1)->get()->first();
$this->actingAs($this->user)->visit($page->getUrl())
->dontSee('Permissions')
->visit($page->getUrl() . '/permissions')
public function test_restrictions_manage_own_permission()
{
- $otherUsersPage = \BookStack\Entities\Page::first();
+ $otherUsersPage = Page::first();
$content = $this->createEntityChainBelongingToUser($this->user);
+
+ // Set a different creator on the page we're checking to ensure
+ // that the owner fields are checked
+ $page = $content['page']; /** @var Page $page */
+ $page->created_by = $otherUsersPage->id;
+ $page->owned_by = $this->user->id;
+ $page->save();
+
// Check can't restrict other's content
$this->actingAs($this->user)->visit($otherUsersPage->getUrl())
->dontSee('Permissions')
->visit($otherUsersPage->getUrl() . '/permissions')
->seePageIs('/');
// Check can't restrict own content
- $this->actingAs($this->user)->visit($content['page']->getUrl())
+ $this->actingAs($this->user)->visit($page->getUrl())
->dontSee('Permissions')
- ->visit($content['page']->getUrl() . '/permissions')
+ ->visit($page->getUrl() . '/permissions')
->seePageIs('/');
$this->giveUserPermissions($this->user, ['restrictions-manage-own']);
->visit($otherUsersPage->getUrl() . '/permissions')
->seePageIs('/');
// Check can restrict own content
- $this->actingAs($this->user)->visit($content['page']->getUrl())
+ $this->actingAs($this->user)->visit($page->getUrl())
->see('Permissions')
->click('Permissions')
- ->seePageIs($content['page']->getUrl() . '/permissions');
+ ->seePageIs($page->getUrl() . '/permissions');
}
/**
{
$otherShelf = Bookshelf::first();
$ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
- $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+ $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
$this->regenEntityPermissions($ownShelf);
$this->checkAccessPermission('bookshelf-update-own', [
public function test_bookshelves_edit_all_permission()
{
- $otherShelf = \BookStack\Entities\Bookshelf::first();
+ $otherShelf = Bookshelf::first();
$this->checkAccessPermission('bookshelf-update-all', [
$otherShelf->getUrl('/edit')
], [
public function test_bookshelves_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
- $otherShelf = \BookStack\Entities\Bookshelf::first();
+ $otherShelf = Bookshelf::first();
$ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
- $ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
+ $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
$this->regenEntityPermissions($ownShelf);
$this->checkAccessPermission('bookshelf-delete-own', [
public function test_bookshelves_delete_all_permission()
{
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
- $otherShelf = \BookStack\Entities\Bookshelf::first();
+ $otherShelf = Bookshelf::first();
$this->checkAccessPermission('bookshelf-delete-all', [
$otherShelf->getUrl('/delete')
], [
public function test_books_edit_own_permission()
{
- $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+ $otherBook = Book::take(1)->get()->first();
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('book-update-own', [
$ownBook->getUrl() . '/edit'
public function test_books_edit_all_permission()
{
- $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+ $otherBook = Book::take(1)->get()->first();
$this->checkAccessPermission('book-update-all', [
$otherBook->getUrl() . '/edit'
], [
public function test_books_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['book-update-all']);
- $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+ $otherBook = Book::take(1)->get()->first();
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('book-delete-own', [
$ownBook->getUrl() . '/delete'
public function test_books_delete_all_permission()
{
$this->giveUserPermissions($this->user, ['book-update-all']);
- $otherBook = \BookStack\Entities\Book::take(1)->get()->first();
+ $otherBook = Book::take(1)->get()->first();
$this->checkAccessPermission('book-delete-all', [
$otherBook->getUrl() . '/delete'
], [
public function test_chapter_create_own_permissions()
{
- $book = \BookStack\Entities\Book::take(1)->get()->first();
+ $book = Book::take(1)->get()->first();
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('chapter-create-own', [
$ownBook->getUrl('/create-chapter')
public function test_chapter_create_all_permissions()
{
- $book = \BookStack\Entities\Book::take(1)->get()->first();
+ $book = Book::take(1)->get()->first();
$this->checkAccessPermission('chapter-create-all', [
$book->getUrl('/create-chapter')
], [
public function test_chapter_edit_own_permission()
{
- $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+ $otherChapter = Chapter::take(1)->get()->first();
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
$this->checkAccessPermission('chapter-update-own', [
$ownChapter->getUrl() . '/edit'
public function test_chapter_edit_all_permission()
{
- $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+ $otherChapter = Chapter::take(1)->get()->first();
$this->checkAccessPermission('chapter-update-all', [
$otherChapter->getUrl() . '/edit'
], [
public function test_chapter_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['chapter-update-all']);
- $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+ $otherChapter = Chapter::take(1)->get()->first();
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
$this->checkAccessPermission('chapter-delete-own', [
$ownChapter->getUrl() . '/delete'
public function test_chapter_delete_all_permission()
{
$this->giveUserPermissions($this->user, ['chapter-update-all']);
- $otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+ $otherChapter = Chapter::take(1)->get()->first();
$this->checkAccessPermission('chapter-delete-all', [
$otherChapter->getUrl() . '/delete'
], [
public function test_page_create_own_permissions()
{
- $book = \BookStack\Entities\Book::first();
- $chapter = \BookStack\Entities\Chapter::first();
+ $book = Book::first();
+ $chapter = Chapter::first();
$entities = $this->createEntityChainBelongingToUser($this->user);
$ownBook = $entities['book'];
foreach ($accessUrls as $index => $url) {
$this->actingAs($this->user)->visit($url);
- $expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
+ $expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
$this->seePageIs($expectedUrl);
}
public function test_page_create_all_permissions()
{
- $book = \BookStack\Entities\Book::take(1)->get()->first();
- $chapter = \BookStack\Entities\Chapter::take(1)->get()->first();
+ $book = Book::take(1)->get()->first();
+ $chapter = Chapter::take(1)->get()->first();
$baseUrl = $book->getUrl() . '/page';
$createUrl = $book->getUrl('/create-page');
foreach ($accessUrls as $index => $url) {
$this->actingAs($this->user)->visit($url);
- $expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
+ $expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
$this->seePageIs($expectedUrl);
}
public function test_page_edit_own_permission()
{
- $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+ $otherPage = Page::take(1)->get()->first();
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
$this->checkAccessPermission('page-update-own', [
$ownPage->getUrl() . '/edit'
public function test_page_edit_all_permission()
{
- $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+ $otherPage = Page::take(1)->get()->first();
$this->checkAccessPermission('page-update-all', [
$otherPage->getUrl() . '/edit'
], [
public function test_page_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['page-update-all']);
- $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+ $otherPage = Page::take(1)->get()->first();
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
$this->checkAccessPermission('page-delete-own', [
$ownPage->getUrl() . '/delete'
public function test_page_delete_all_permission()
{
$this->giveUserPermissions($this->user, ['page-update-all']);
- $otherPage = \BookStack\Entities\Page::take(1)->get()->first();
+ $otherPage = Page::take(1)->get()->first();
$this->checkAccessPermission('page-delete-all', [
$otherPage->getUrl() . '/delete'
], [
public function test_public_role_visible_in_user_edit_screen()
{
- $user = \BookStack\Auth\User::first();
+ $user = User::first();
$adminRole = Role::getSystemRole('admin');
$publicRole = Role::getSystemRole('public');
$this->asAdmin()->visit('/settings/users/' . $user->id)
public function test_image_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['image-update-all']);
- $page = \BookStack\Entities\Page::first();
- $image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
+ $page = Page::first();
+ $image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
->seeStatusCode(403);
{
$this->giveUserPermissions($this->user, ['image-update-all']);
$admin = $this->getAdmin();
- $page = \BookStack\Entities\Page::first();
- $image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
+ $page = Page::first();
+ $image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
->seeStatusCode(403);
{
// To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
$page = Page::first();
- $viewerRole = \BookStack\Auth\Role::getRole('viewer');
+ $viewerRole = Role::getRole('viewer');
$viewer = $this->getViewer();
$this->actingAs($viewer)->visit($page->getUrl())->assertResponseStatus(200);
{
$admin = $this->getAdmin();
// Book links
- $book = factory(\BookStack\Entities\Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
+ $book = factory(Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
$this->updateEntityPermissions($book);
$this->actingAs($this->getViewer())->visit($book->getUrl())
->dontSee('Create a new page')
->dontSee('Add a chapter');
// Chapter links
- $chapter = factory(\BookStack\Entities\Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
+ $chapter = factory(Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
$this->updateEntityPermissions($chapter);
$this->actingAs($this->getViewer())->visit($chapter->getUrl())
->dontSee('Create a new page')
}
private function addComment($page) {
- $comment = factory(\BookStack\Actions\Comment::class)->make();
+ $comment = factory(Comment::class)->make();
$url = "/comment/$page->id";
$request = [
'text' => $comment->text,
}
private function updateComment($commentId) {
- $comment = factory(\BookStack\Actions\Comment::class)->make();
+ $comment = factory(Comment::class)->make();
$url = "/comment/$commentId";
$request = [
'text' => $comment->text,
use BookStack\Auth\Permissions\RolePermission;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Book;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
class PublicActionTest extends BrowserKitTest
{
<?php namespace Tests;
-use BookStack\Entities\Book;
-use BookStack\Entities\Deletion;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Deletion;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
use DB;
use Illuminate\Support\Carbon;
$redirectReq->assertNotificationContains('Deleted '.$itemCount.' total items from the recycle bin');
}
+ public function test_permanent_delete_for_each_type()
+ {
+ /** @var Entity $entity */
+ foreach ([new Bookshelf, new Book, new Chapter, new Page] as $entity) {
+ $entity = $entity->newQuery()->first();
+ $this->asEditor()->delete($entity->getUrl());
+ $deletion = Deletion::query()->orderBy('id', 'desc')->firstOrFail();
+
+ $deleteReq = $this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
+ $deleteReq->assertRedirect('/settings/recycle-bin');
+ $this->assertDatabaseMissing('deletions', ['id' => $deletion->id]);
+ $this->assertDatabaseMissing($entity->getTable(), ['id' => $entity->id]);
+ }
+ }
+
public function test_permanent_entity_delete_updates_existing_activity_with_entity_name()
{
$page = Page::query()->firstOrFail();
$deletion = $page->deletions()->firstOrFail();
$this->assertDatabaseHas('activities', [
- 'key' => 'page_delete',
+ 'type' => 'page_delete',
'entity_id' => $page->id,
'entity_type' => $page->getMorphClass(),
]);
$this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
$this->assertDatabaseMissing('activities', [
- 'key' => 'page_delete',
+ 'type' => 'page_delete',
'entity_id' => $page->id,
'entity_type' => $page->getMorphClass(),
]);
$this->assertDatabaseHas('activities', [
- 'key' => 'page_delete',
- 'entity_id' => 0,
- 'entity_type' => '',
- 'extra' => $page->name,
+ 'type' => 'page_delete',
+ 'entity_id' => null,
+ 'entity_type' => null,
+ 'detail' => $page->name,
]);
}
--- /dev/null
+<?php namespace Tests;
+
+
+use Illuminate\Support\Str;
+
+class SecurityHeaderTest extends TestCase
+{
+
+ public function test_cookies_samesite_lax_by_default()
+ {
+ $resp = $this->get("/");
+ foreach ($resp->headers->getCookies() as $cookie) {
+ $this->assertEquals("lax", $cookie->getSameSite());
+ }
+ }
+
+ public function test_cookies_samesite_none_when_iframe_hosts_set()
+ {
+ $this->runWithEnv("ALLOWED_IFRAME_HOSTS", "http://example.com", function() {
+ $resp = $this->get("/");
+ foreach ($resp->headers->getCookies() as $cookie) {
+ $this->assertEquals("none", $cookie->getSameSite());
+ }
+ });
+ }
+
+ public function test_secure_cookies_controlled_by_app_url()
+ {
+ $this->runWithEnv("APP_URL", "http://example.com", function() {
+ $resp = $this->get("/");
+ foreach ($resp->headers->getCookies() as $cookie) {
+ $this->assertFalse($cookie->isSecure());
+ }
+ });
+
+ $this->runWithEnv("APP_URL", "https://example.com", function() {
+ $resp = $this->get("/");
+ foreach ($resp->headers->getCookies() as $cookie) {
+ $this->assertTrue($cookie->isSecure());
+ }
+ });
+ }
+
+ public function test_iframe_csp_self_only_by_default()
+ {
+ $resp = $this->get("/");
+ $cspHeaders = collect($resp->headers->get('Content-Security-Policy'));
+ $frameHeaders = $cspHeaders->filter(function ($val) {
+ return Str::startsWith($val, 'frame-ancestors');
+ });
+
+ $this->assertTrue($frameHeaders->count() === 1);
+ $this->assertEquals('frame-ancestors \'self\'', $frameHeaders->first());
+ }
+
+ public function test_iframe_csp_includes_extra_hosts_if_configured()
+ {
+ $this->runWithEnv("ALLOWED_IFRAME_HOSTS", "https://a.example.com https://b.example.com", function() {
+ $resp = $this->get("/");
+ $cspHeaders = collect($resp->headers->get('Content-Security-Policy'));
+ $frameHeaders = $cspHeaders->filter(function($val) {
+ return Str::startsWith($val, 'frame-ancestors');
+ });
+
+ $this->assertTrue($frameHeaders->count() === 1);
+ $this->assertEquals('frame-ancestors \'self\' https://a.example.com https://b.example.com', $frameHeaders->first());
+ });
+
+ }
+
+}
\ No newline at end of file
<?php namespace Tests;
use BookStack\Auth\User;
-use BookStack\Entities\Book;
-use BookStack\Entities\Bookshelf;
-use BookStack\Entities\Chapter;
-use BookStack\Entities\Entity;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Repos\BookshelfRepo;
use BookStack\Entities\Repos\ChapterRepo;
/**
* Give the given user some permissions.
- * @param User $user
- * @param array $permissions
*/
- protected function giveUserPermissions(User $user, $permissions = [])
+ protected function giveUserPermissions(User $user, array $permissions = [])
{
$newRole = $this->createNewRole($permissions);
$user->attachRole($newRole);
$user->load('roles');
- $user->permissions(false);
+ $user->clearPermissionCache();
}
/**
--- /dev/null
+<?php
+
+use Illuminate\Cache\ArrayStore;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Session;
+use Tests\TestCase;
+
+class StatusTest extends TestCase
+{
+ public function test_returns_json_with_expected_results()
+ {
+ $resp = $this->get("/status");
+ $resp->assertStatus(200);
+ $resp->assertJson([
+ 'database' => true,
+ 'cache' => true,
+ 'session' => true,
+ ]);
+ }
+
+ public function test_returns_500_status_and_false_on_db_error()
+ {
+ DB::shouldReceive('table')->andThrow(new Exception());
+
+ $resp = $this->get("/status");
+ $resp->assertStatus(500);
+ $resp->assertJson([
+ 'database' => false,
+ ]);
+ }
+
+ public function test_returns_500_status_and_false_on_wrong_cache_return()
+ {
+ $mockStore = Mockery::mock(new ArrayStore())->makePartial();
+ Cache::swap($mockStore);
+ $mockStore->shouldReceive('get')->andReturn('cat');
+
+ $resp = $this->get("/status");
+ $resp->assertStatus(500);
+ $resp->assertJson([
+ 'cache' => false,
+ ]);
+ }
+
+ public function test_returns_500_status_and_false_on_wrong_session_return()
+ {
+ $session = Session::getFacadeRoot();
+ $mockSession = Mockery::mock($session)->makePartial();
+ Session::swap($mockSession);
+ $mockSession->shouldReceive('get')->andReturn('cat');
+
+ $resp = $this->get("/status");
+ $resp->assertStatus(500);
+ $resp->assertJson([
+ 'session' => false,
+ ]);
+ }
+}
\ No newline at end of file
<?php namespace Tests;
-use BookStack\Entities\Entity;
+use BookStack\Entities\Models\Entity;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
* Assert that an activity entry exists of the given key.
* Checks the activity belongs to the given entity if provided.
*/
- protected function assertActivityExists(string $key, Entity $entity = null)
+ protected function assertActivityExists(string $type, Entity $entity = null)
{
- $detailsToCheck = ['key' => $key];
+ $detailsToCheck = ['type' => $type];
if ($entity) {
$detailsToCheck['entity_type'] = $entity->getMorphClass();
<?php namespace Tests\Uploads;
-use BookStack\Entities\Managers\TrashCan;
+use BookStack\Entities\Tools\TrashCan;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Uploads\Attachment;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Auth\Permissions\PermissionService;
use BookStack\Uploads\AttachmentService;
use Illuminate\Http\UploadedFile;
<?php namespace Tests\Uploads;
use BookStack\Auth\User;
+use BookStack\Exceptions\HttpFetchException;
use BookStack\Uploads\HttpFetcher;
+use Illuminate\Support\Facades\Log;
use Tests\TestCase;
class AvatarTest extends TestCase
protected function createUserRequest($user)
{
- $resp = $this->asAdmin()->post('/settings/users/create', [
+ $this->asAdmin()->post('/settings/users/create', [
'name' => $user->name,
'email' => $user->email,
'password' => 'testing',
protected function assertImageFetchFrom(string $url)
{
- $http = \Mockery::mock(HttpFetcher::class);
- $this->app->instance(HttpFetcher::class, $http);
+ $http = $this->mock(HttpFetcher::class);
$http->shouldReceive('fetch')
->once()->with($url)
public function test_custom_url_used_if_set()
{
config()->set([
+ 'services.disable_services' => false,
'services.avatar_url' => 'https://example.com/${email}/${hash}/${size}',
]);
$user = factory(User::class)->make();
- $http = \Mockery::mock(HttpFetcher::class);
- $this->app->instance(HttpFetcher::class, $http);
+ $http = $this->mock(HttpFetcher::class);
$http->shouldNotReceive('fetch');
$this->createUserRequest($user);
}
+ public function test_no_failure_but_error_logged_on_failed_avatar_fetch()
+ {
+ config()->set([
+ 'services.disable_services' => false,
+ ]);
+
+ $http = $this->mock(HttpFetcher::class);
+ $http->shouldReceive('fetch')->andThrow(new HttpFetchException());
+
+ $logger = $this->withTestLogger();
+
+ $user = factory(User::class)->make();
+ $this->createUserRequest($user);
+ $this->assertTrue($logger->hasError('Failed to save user avatar image'));
+ }
+
}
<?php namespace Tests\Uploads;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Uploads\Image;
use Tests\TestCase;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Uploads\Image;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use BookStack\Uploads\ImageService;
use Illuminate\Support\Str;
use Tests\TestCase;
$relPath = $this->getTestImagePath('gallery', $fileName);
$this->deleteImage($relPath);
- $file = $this->getTestImage($fileName);
+ $file = $this->newTestImageFromBase64('bad-php.base64', $fileName);
$upload = $this->withHeader('Content-Type', 'image/jpeg')->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $file], []);
$upload->assertStatus(302);
$relPath = $this->getTestImagePath('gallery', $fileName);
$this->deleteImage($relPath);
- $file = $this->getTestImage($fileName);
+ $file = $this->newTestImageFromBase64('bad-phtml.base64', $fileName);
$upload = $this->withHeader('Content-Type', 'image/jpeg')->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $file], []);
$upload->assertStatus(302);
$relPath = $this->getTestImagePath('gallery', $fileName);
$this->deleteImage($relPath);
- $file = $this->getTestImage($fileName);
+ $file = $this->newTestImageFromBase64('bad-phtml-png.base64', $fileName);
$upload = $this->withHeader('Content-Type', 'image/png')->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $file], []);
$upload->assertStatus(302);
<?php namespace Tests\Uploads;
-use BookStack\Entities\Page;
+use BookStack\Entities\Models\Page;
use Illuminate\Http\UploadedFile;
trait UsesImages
{
/**
- * Get the path to our basic test image.
- * @return string
+ * Get the path to a file in the test-data-directory.
*/
- protected function getTestImageFilePath(?string $fileName = null)
+ protected function getTestImageFilePath(?string $fileName = null): string
{
if (is_null($fileName)) {
$fileName = 'test-image.png';
return base_path('tests/test-data/' . $fileName);
}
+ /**
+ * Creates a new temporary image file using the given name,
+ * with the content decoded from the given bas64 file name.
+ * Is generally used for testing sketchy files that could trip AV.
+ */
+ protected function newTestImageFromBase64(string $base64FileName, $imageFileName): UploadedFile
+ {
+ $imagePath = implode(DIRECTORY_SEPARATOR, [sys_get_temp_dir(), $imageFileName]);
+ $base64FilePath = $this->getTestImageFilePath($base64FileName);
+ $data = file_get_contents($base64FilePath);
+ $decoded = base64_decode($data);
+ file_put_contents($imagePath, $decoded);
+ return new UploadedFile($imagePath, $imageFileName, 'image/png', null, true);
+ }
+
/**
* Get a test image that can be uploaded
- * @param $fileName
- * @return UploadedFile
*/
- protected function getTestImage($fileName, ?string $testDataFileName = null)
+ protected function getTestImage(string $fileName, ?string $testDataFileName = null): UploadedFile
{
- return new UploadedFile($this->getTestImageFilePath($testDataFileName), $fileName, 'image/png', 5238, null, true);
+ return new UploadedFile($this->getTestImageFilePath($testDataFileName), $fileName, 'image/png', null, true);
}
/**
<?php namespace Tests\User;
+use BookStack\Actions\ActivityType;
use BookStack\Api\ApiToken;
use Carbon\Carbon;
use Tests\TestCase;
$this->assertTrue(strlen($secret) === 32);
$this->assertSessionHas('success');
+ $this->assertActivityExists(ActivityType::API_TOKEN_CREATE);
}
public function test_create_with_no_expiry_sets_expiry_hundred_years_away()
$this->assertDatabaseHas('api_tokens', array_merge($updateData, ['id' => $token->id]));
$this->assertSessionHas('success');
+ $this->assertActivityExists(ActivityType::API_TOKEN_UPDATE);
}
public function test_token_update_with_blank_expiry_sets_to_hundred_years_away()
$resp = $this->delete($tokenUrl);
$resp->assertRedirect($editor->getEditUrl('#api_tokens'));
$this->assertDatabaseMissing('api_tokens', ['id' => $token->id]);
+ $this->assertActivityExists(ActivityType::API_TOKEN_DELETE);
}
public function test_user_manage_can_delete_token_without_api_permission_themselves()
--- /dev/null
+<?php namespace Tests\User;
+
+use BookStack\Actions\ActivityType;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class UserManagementTest extends TestCase
+{
+
+ public function test_delete()
+ {
+ $editor = $this->getEditor();
+ $resp = $this->asAdmin()->delete("settings/users/{$editor->id}");
+ $resp->assertRedirect("/settings/users");
+ $resp = $this->followRedirects($resp);
+
+ $resp->assertSee("User successfully removed");
+ $this->assertActivityExists(ActivityType::USER_DELETE);
+
+ $this->assertDatabaseMissing('users', ['id' => $editor->id]);
+ }
+
+ public function test_delete_offers_migrate_option()
+ {
+ $editor = $this->getEditor();
+ $resp = $this->asAdmin()->get("settings/users/{$editor->id}/delete");
+ $resp->assertSee("Migrate Ownership");
+ $resp->assertSee("new_owner_id");
+ }
+
+ public function test_delete_with_new_owner_id_changes_ownership()
+ {
+ $page = Page::query()->first();
+ $owner = $page->ownedBy;
+ $newOwner = User::query()->where('id', '!=' , $owner->id)->first();
+
+ $this->asAdmin()->delete("settings/users/{$owner->id}", ['new_owner_id' => $newOwner->id]);
+ $this->assertDatabaseHas('pages', [
+ 'id' => $page->id,
+ 'owned_by' => $newOwner->id,
+ ]);
+ }
+}
\ No newline at end of file
<?php namespace Tests\User;
use Activity;
+use BookStack\Actions\ActivityType;
use BookStack\Auth\User;
-use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Models\Bookshelf;
use Tests\BrowserKitTest;
class UserProfileTest extends BrowserKitTest
$newUser = $this->getNewBlankUser();
$this->actingAs($newUser);
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
- Activity::add($entities['book'], 'book_update', $entities['book']->id);
- Activity::add($entities['page'], 'page_create', $entities['book']->id);
+ Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
+ Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
$this->asAdmin()->visit('/user/' . $newUser->id)
->seeInElement('#recent-user-activity', 'updated book')
$newUser = $this->getNewBlankUser();
$this->actingAs($newUser);
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
- Activity::add($entities['book'], 'book_update', $entities['book']->id);
- Activity::add($entities['page'], 'page_create', $entities['book']->id);
+ Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
+ Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
$this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
->seePageIs('/user/' . $newUser->id)
--- /dev/null
+/9j/4AAQSkZJRgABAQEBLAEsAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAEBAQEBAQEBAQEB
+AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBD
+AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
+AQEBAQEBAQH/wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACv/EABQBAQAA
+AAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAT/n/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgB
+AQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAA
+AAAAAP/aAAgBAgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAGPwJ//8QAFBABAAAA
+AAAAAAAAAAAAAAAAAP/aAAgBAQABPyF//9oADAMBAAIAAwAAABAf/8QAFBEBAAAAAAAAAAAAAAAA
+AAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAFBABAAAA
+AAAAAAAAAAAAAAAAAP/aAAgBAQABPxB//9k8P3BocCBlY2hvICdiYWRwaHAnOwo=
--- /dev/null
+iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
+B3RJTUUH4gEcDCo5iYNs+gAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
+AAAAFElEQVQI12O0jN/KgASYGFABqXwAZtoBV6Sl3hIAAAAASUVORK5CYII=
--- /dev/null
+/9j/4AAQSkZJRgABAQEBLAEsAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAEBAQEBAQEBAQEB
+AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBD
+AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
+AQEBAQEBAQH/wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACv/EABQBAQAA
+AAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAT/n/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgB
+AQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAA
+AAAAAP/aAAgBAgEBPwF//8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAGPwJ//8QAFBABAAAA
+AAAAAAAAAAAAAAAAAP/aAAgBAQABPyF//9oADAMBAAIAAwAAABAf/8QAFBEBAAAAAAAAAAAAAAAA
+AAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAFBABAAAA
+AAAAAAAAAAAAAAAAAP/aAAgBAQABPxB//9k8P3BocCBlY2hvICdiYWRwaHAnOwo=