Mundo Racional (ismael.mesquita) :: Portuguese, Brazilian
Zarik (3apuk) :: Russian
Ali Shaatani (a.shaatani) :: Arabic
+ChacMaster :: Portuguese, Brazilian
/**
* Get a URL for this specific deletion.
*/
- public function getUrl($path): string
+ public function getUrl(string $path = 'restore'): string
{
return url("/settings/recycle-bin/{$this->id}/" . ltrim($path, '/'));
}
* @property string $slug
* @property Carbon $created_at
* @property Carbon $updated_at
+ * @property Carbon $deleted_at
* @property int $created_by
* @property int $updated_by
* @property bool $restricted
/**
* Get the url for this revision.
- *
- * @param null|string $path
- *
- * @return string
*/
- public function getUrl($path = null)
+ public function getUrl(string $path = ''): string
{
- $url = $this->page->getUrl() . '/revisions/' . $this->id;
- if ($path) {
- return $url . '/' . trim($path, '/');
- }
-
- return $url;
+ return $this->page->getUrl('/revisions/' . $this->id . '/' . ltrim($path, '/'));
}
/**
public static function fromJson(string $json): self
{
- $map = new static();
+ $map = new BookSortMap();
$mapData = json_decode($json);
foreach ($mapData as $mapDataItem) {
{
/**
* Send a shelf to the recycle bin.
+ *
+ * @throws NotifyException
*/
public function softDestroyShelf(Bookshelf $shelf)
{
+ $this->ensureDeletable($shelf);
Deletion::createForEntity($shelf);
$shelf->delete();
}
*/
public function softDestroyBook(Book $book)
{
+ $this->ensureDeletable($book);
Deletion::createForEntity($book);
foreach ($book->pages as $page) {
public function softDestroyChapter(Chapter $chapter, bool $recordDelete = true)
{
if ($recordDelete) {
+ $this->ensureDeletable($chapter);
Deletion::createForEntity($chapter);
}
public function softDestroyPage(Page $page, bool $recordDelete = true)
{
if ($recordDelete) {
+ $this->ensureDeletable($page);
Deletion::createForEntity($page);
}
- // Check if set as custom homepage & remove setting if not used or throw error if active
- $customHome = setting('app-homepage', '0:');
- if (intval($page->id) === intval(explode(':', $customHome)[0])) {
- if (setting('app-homepage-type') === 'page') {
- throw new NotifyException(trans('errors.page_custom_home_deletion'), $page->getUrl());
+ $page->delete();
+ }
+
+ /**
+ * Ensure the given entity is deletable.
+ * Is not for permissions, but logical conditions within the application.
+ * Will throw if not deletable.
+ *
+ * @throws NotifyException
+ */
+ protected function ensureDeletable(Entity $entity): void
+ {
+ $customHomeId = intval(explode(':', setting('app-homepage', '0:'))[0]);
+ $customHomeActive = setting('app-homepage-type') === 'page';
+ $removeCustomHome = false;
+
+ // Check custom homepage usage for pages
+ if ($entity instanceof Page && $entity->id === $customHomeId) {
+ if ($customHomeActive) {
+ throw new NotifyException(trans('errors.page_custom_home_deletion'), $entity->getUrl());
+ }
+ $removeCustomHome = true;
+ }
+
+ // Check custom homepage usage within chapters or books
+ if ($entity instanceof Chapter || $entity instanceof Book) {
+ if ($entity->pages()->where('id', '=', $customHomeId)->exists()) {
+ if ($customHomeActive) {
+ throw new NotifyException(trans('errors.page_custom_home_deletion'), $entity->getUrl());
+ }
+ $removeCustomHome = true;
}
- setting()->remove('app-homepage');
}
- $page->delete();
+ if ($removeCustomHome) {
+ setting()->remove('app-homepage');
+ }
}
/**
/**
* On a permission error redirect to home and display.
* the error as a notification.
+ *
+ * @return never
*/
protected function showPermissionError()
{
this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
+ this.listContainer.addEventListener('error', event => {
+ event.target.src = baseUrl('loading_error.png');
+ }, true);
+
onSelect(this.selectButton, () => {
if (this.callback) {
this.callback(this.lastSelected);
actionInsertImage() {
const cursorPos = this.cm.getCursor('from');
window.ImageManager.show(image => {
+ const imageUrl = image.thumbs.display || image.url;
let selectedText = this.cm.getSelection();
- let newText = "[](" + image.url + ")";
+ let newText = "[](" + image.url + ")";
this.cm.focus();
this.cm.replaceSelection(newText);
this.cm.setCursor(cursorPos.line, cursorPos.ch + newText.length);
}
// Replace the actively selected content with the linked image
+ const imageUrl = image.thumbs.display || image.url;
let html = `<a href="${image.url}" target="_blank">`;
- html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
+ html += `<img src="${imageUrl}" alt="${image.name}">`;
html += '</a>';
win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html);
}, 'gallery');
tooltip: 'Insert an image',
onclick: function () {
window.ImageManager.show(function (image) {
+ const imageUrl = image.thumbs.display || image.url;
let html = `<a href="${image.url}" target="_blank">`;
- html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
+ html += `<img src="${imageUrl}" alt="${image.name}">`;
html += '</a>';
editor.execCommand('mceInsertContent', false, html);
}, 'gallery');
'status' => 'Stav',
'status_active' => 'Aktivní',
'status_inactive' => 'Neaktivní',
- 'never' => 'Never',
+ 'never' => 'Nikdy',
// Header
'header_menu_expand' => 'Rozbalit menu v záhlaví',
'email_not_confirmed_resend_button' => 'Reenviar Correo Electrónico de confirmación',
// User Invite
- 'user_invite_email_subject' => 'As sido invitado a unirte a :appName!',
+ 'user_invite_email_subject' => 'Has sido invitado a unirte a :appName!',
'user_invite_email_greeting' => 'Se ha creado una cuenta para usted en :appName.',
'user_invite_email_text' => 'Clica en el botón a continuación para ajustar una contraseña y poder acceder:',
'user_invite_email_action' => 'Ajustar la Contraseña de la Cuenta',
'email' => 'メールアドレス',
'password' => 'パスワード',
'password_confirm' => 'パスワード (確認)',
- 'password_hint' => 'Must be at least 8 characters',
+ 'password_hint' => '8文字以上で設定する必要があります',
'forgot_password' => 'パスワードをお忘れですか?',
'remember_me' => 'ログイン情報を保存する',
'ldap_email_hint' => 'このアカウントで使用するEメールアドレスを入力してください。',
'email_confirm_text' => '以下のボタンを押し、メールアドレスを確認してください:',
'email_confirm_action' => 'メールアドレスを確認',
'email_confirm_send_error' => 'Eメールの確認が必要でしたが、システム上でEメールの送信ができませんでした。管理者に連絡し、Eメールが正しく設定されていることを確認してください。',
- 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.',
+ 'email_confirm_success' => 'メールアドレスが確認されました!このメールアドレスでログインできるようになりました。',
'email_confirm_resent' => '確認メールを再送信しました。受信トレイを確認してください。',
'email_not_confirmed' => 'Eメールアドレスが確認できていません',
'user_invite_page_welcome' => ':appNameへようこそ!',
'user_invite_page_text' => 'アカウントの設定を完了してアクセスするには、今後の訪問時に:appNameにログインするためのパスワードを設定する必要があります。',
'user_invite_page_confirm_button' => 'パスワードを確定',
- 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!',
+ 'user_invite_success_login' => 'パスワードが設定されました。設定したパスワードで:appNameにログインできるようになりました!',
// Multi-factor Authentication
'mfa_setup' => '多要素認証を設定',
'light_mode' => 'ライトモード',
// Layout tabs
- 'tab_info' => 'Info',
- 'tab_info_label' => 'Tab: Show Secondary Information',
- 'tab_content' => 'Content',
- 'tab_content_label' => 'Tab: Show Primary Content',
+ 'tab_info' => '情報',
+ 'tab_info_label' => 'タブ: サブコンテンツを表示',
+ 'tab_content' => '内容',
+ 'tab_content_label' => 'タブ: メインコンテンツを表示',
// Email Content
'email_action_help' => '":actionText" をクリックできない場合、以下のURLをコピーしブラウザで開いてください:',
'meta_created_name' => '作成: :timeLength (:user)',
'meta_updated' => '更新: :timeLength',
'meta_updated_name' => '更新: :timeLength (:user)',
- 'meta_owned_name' => 'Owned by :user',
+ 'meta_owned_name' => '所有者: :user',
'entity_select' => 'エンティティ選択',
'images' => '画像',
'my_recent_drafts' => '最近の下書き',
'export_html' => 'Webページ',
'export_pdf' => 'PDF',
'export_text' => 'テキストファイル',
- 'export_md' => 'Markdown File',
+ 'export_md' => 'Markdown',
// Permissions and restrictions
'permissions' => '権限',
'permissions_intro' => 'この設定は各ユーザの役割よりも優先して適用されます。',
'permissions_enable' => 'カスタム権限設定を有効にする',
'permissions_save' => '権限を保存',
- 'permissions_owner' => 'Owner',
+ 'permissions_owner' => '所有者',
// Search
'search_results' => '検索結果',
'books_sort_chapters_last' => 'チャプターを後に',
'books_sort_show_other' => '他のブックを表示',
'books_sort_save' => '並び順を保存',
- 'books_copy' => 'Copy Book',
- 'books_copy_success' => 'Book successfully copied',
+ 'books_copy' => 'ブックをコピー',
+ 'books_copy_success' => 'ブックが正常にコピーされました',
// Chapters
'chapter' => 'チャプター',
'chapters_move' => 'チャプターを移動',
'chapters_move_named' => 'チャプター「:chapterName」を移動',
'chapter_move_success' => 'チャプターを「:bookName」に移動しました',
- 'chapters_copy' => 'Copy Chapter',
- 'chapters_copy_success' => 'Chapter successfully copied',
+ 'chapters_copy' => 'チャプターをコピー',
+ 'chapters_copy_success' => 'チャプターが正常にコピーされました',
'chapters_permissions' => 'チャプター権限',
'chapters_empty' => 'まだチャプター内にページはありません。',
'chapters_permissions_active' => 'チャプターの権限は有効です',
'pages_copy_success' => 'ページが正常にコピーされました',
'pages_permissions' => 'ページの権限設定',
'pages_permissions_success' => 'ページの権限を更新しました',
- 'pages_revision' => 'Revision',
+ 'pages_revision' => '編集履歴',
'pages_revisions' => '編集履歴',
'pages_revisions_named' => ':pageName のリビジョン',
'pages_revision_named' => ':pageName のリビジョン',
- 'pages_revision_restored_from' => 'Restored from #:id; :summary',
+ 'pages_revision_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' => 'リビジョン #:id',
+ 'pages_revisions_numbered_changes' => 'リビジョン #:id の変更',
'pages_revisions_changelog' => '説明',
'pages_revisions_changes' => '変更点',
'pages_revisions_current' => '現在のバージョン',
// Revision
'revision_delete_confirm' => 'このリビジョンを削除しますか?',
- 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+ 'revision_restore_confirm' => 'このリビジョンを復元してよろしいですか?現在のページの内容が置換されます。',
'revision_delete_success' => 'リビジョンを削除しました',
'revision_cannot_delete_latest' => '最新のリビジョンを削除できません。',
// Copy view
- 'copy_consider' => 'Please consider the below when copying content.',
- 'copy_consider_permissions' => 'Custom permission settings will not be copied.',
- 'copy_consider_owner' => 'You will become the owner of all copied content.',
- 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.',
- 'copy_consider_attachments' => 'Page attachments will not be copied.',
- 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.',
+ 'copy_consider' => 'コンテンツをコピーする場合は以下の点にご注意ください。',
+ 'copy_consider_permissions' => 'カスタム権限設定はコピーされません。',
+ 'copy_consider_owner' => 'あなたはコピーされた全てのコンテンツの所有者になります。',
+ 'copy_consider_images' => 'ページの画像ファイルは複製されず、元の画像は最初にアップロードされたページとの関係を保持します。',
+ 'copy_consider_attachments' => 'ページの添付ファイルはコピーされません。',
+ 'copy_consider_access' => '場所、所有者または権限を変更すると、以前アクセスできなかったユーザーがこのコンテンツにアクセスできるようになる可能性があります。',
];
'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_incorrect_token_secret' => '利用されたAPIトークンに対して提供されたシークレットが正しくありません',
'api_user_no_api_permission' => '使用されているAPIトークンの所有者には、API呼び出しを行う権限がありません',
'api_user_token_expired' => '認証トークンが期限切れです。',
'user_api_token_delete_success' => 'APIトークンが正常に削除されました',
// Webhooks
- 'webhooks' => 'Webhooks',
- 'webhooks_create' => 'Create New Webhook',
- 'webhooks_none_created' => 'No webhooks have yet been created.',
- 'webhooks_edit' => 'Edit Webhook',
- 'webhooks_save' => 'Save Webhook',
- 'webhooks_details' => 'Webhook Details',
- 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.',
- 'webhooks_events' => 'Webhook Events',
- 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.',
- 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.',
- 'webhooks_events_all' => 'All system events',
- 'webhooks_name' => 'Webhook Name',
- 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)',
- 'webhooks_endpoint' => 'Webhook Endpoint',
+ 'webhooks' => 'Webhook',
+ 'webhooks_create' => 'Webhookを作成',
+ 'webhooks_none_created' => 'Webhookはまだ作成されていません。',
+ 'webhooks_edit' => 'Webhookを編集',
+ 'webhooks_save' => 'Webhookを保存',
+ 'webhooks_details' => 'Webhookの詳細',
+ 'webhooks_details_desc' => 'ユーザーフレンドリーな名前とWebhookデータの送信先にするPOSTエンドポイントを指定します。',
+ 'webhooks_events' => 'Webhookのイベント',
+ 'webhooks_events_desc' => 'このWebhookの呼び出しをトリガーするすべてのイベントを選択します。',
+ 'webhooks_events_warning' => 'これらのイベントはカスタム権限が適用されている場合でも、選択したすべてのイベントに対してトリガーされることに注意してください。このWebhookの利用により機密コンテンツが公開されないことを確認してください。',
+ 'webhooks_events_all' => '全てのシステムイベント',
+ 'webhooks_name' => 'Webhook名',
+ 'webhooks_timeout' => 'Webhookリクエストタイムアウト (秒)',
+ 'webhooks_endpoint' => 'Webhookエンドポイント',
'webhooks_active' => 'Webhook Active',
'webhook_events_table_header' => 'Events',
'webhooks_delete' => 'Delete Webhook',
'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.',
'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?',
- 'webhooks_format_example' => 'Webhook Format Example',
- 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.',
+ 'webhooks_format_example' => 'Webhookのフォーマット例',
+ 'webhooks_format_example_desc' => 'Webhookのデータは、設定されたエンドポイントにPOSTリクエストにより以下のフォーマットのJSONで送信されます。related_item と url プロパティはオプションであり、トリガーされるイベントの種類によって異なります。',
'webhooks_status' => 'Webhook Status',
'webhooks_last_called' => 'Last Called:',
'webhooks_last_errored' => 'Last Errored:',
// Pages
'page_create' => 'criou a página',
- 'page_create_notification' => 'Page successfully created',
+ 'page_create_notification' => 'Página criada com sucesso',
'page_update' => 'atualizou a página',
- 'page_update_notification' => 'Page successfully updated',
+ 'page_update_notification' => 'Página atualizada com sucesso',
'page_delete' => 'excluiu a página',
- 'page_delete_notification' => 'Page successfully deleted',
+ 'page_delete_notification' => 'Página excluída com sucesso',
'page_restore' => 'restaurou a página',
- 'page_restore_notification' => 'Page successfully restored',
+ 'page_restore_notification' => 'Página restaurada com sucesso',
'page_move' => 'moveu a página',
// Chapters
'chapter_create' => 'criou o capítulo',
- 'chapter_create_notification' => 'Chapter successfully created',
+ 'chapter_create_notification' => 'Capítulo criado com sucesso',
'chapter_update' => 'atualizou o capítulo',
- 'chapter_update_notification' => 'Chapter successfully updated',
+ 'chapter_update_notification' => 'Capítulo atualizado com sucesso',
'chapter_delete' => 'excluiu o capítulo',
- 'chapter_delete_notification' => 'Chapter successfully deleted',
+ 'chapter_delete_notification' => 'Capítulo excluída com sucesso',
'chapter_move' => 'moveu o capítulo',
// Books
'book_create' => 'criou o livro',
- 'book_create_notification' => 'Book successfully created',
+ 'book_create_notification' => 'Livro criado com sucesso',
'book_update' => 'atualizou o livro',
- 'book_update_notification' => 'Book successfully updated',
+ 'book_update_notification' => 'Livro atualizado com sucesso',
'book_delete' => 'excluiu o livro',
- 'book_delete_notification' => 'Book successfully deleted',
+ 'book_delete_notification' => 'Livro excluído com sucesso',
'book_sort' => 'ordenou o livro',
- 'book_sort_notification' => 'Book successfully re-sorted',
+ 'book_sort_notification' => 'Livro reordenado com sucesso',
// Bookshelves
- 'bookshelf_create' => 'created bookshelf',
- 'bookshelf_create_notification' => 'Bookshelf successfully created',
+ 'bookshelf_create' => 'prateleira criada',
+ 'bookshelf_create_notification' => 'Prateleira criada com sucesso',
'bookshelf_update' => 'atualizou a prateleira',
- 'bookshelf_update_notification' => 'Bookshelf successfully updated',
+ 'bookshelf_update_notification' => 'Prateleira atualizada com sucesso',
'bookshelf_delete' => 'excluiu a prateleira',
- 'bookshelf_delete_notification' => 'Bookshelf successfully deleted',
+ 'bookshelf_delete_notification' => 'Prateleira excluída com sucesso',
// Favourites
- 'favourite_add_notification' => '":name" has been added to your favourites',
- 'favourite_remove_notification' => '":name" has been removed from your favourites',
+ 'favourite_add_notification' => '":name" foi adicionada aos seus favoritos',
+ 'favourite_remove_notification' => '":name" foi removida dos seus favoritos',
// MFA
- 'mfa_setup_method_notification' => 'Multi-factor method successfully configured',
- 'mfa_remove_method_notification' => 'Multi-factor method successfully removed',
+ 'mfa_setup_method_notification' => 'Método de multi-fatores configurado com sucesso',
+ 'mfa_remove_method_notification' => 'Método de multi-fatores removido com sucesso',
// Webhooks
- 'webhook_create' => 'created webhook',
- 'webhook_create_notification' => 'Webhook successfully created',
- 'webhook_update' => 'updated webhook',
- 'webhook_update_notification' => 'Webhook successfully updated',
- 'webhook_delete' => 'deleted webhook',
- 'webhook_delete_notification' => 'Webhook successfully deleted',
+ 'webhook_create' => 'webhook criado',
+ 'webhook_create_notification' => 'Webhook criado com sucesso',
+ 'webhook_update' => 'webhook atualizado',
+ 'webhook_update_notification' => 'Webhook atualizado com sucesso',
+ 'webhook_delete' => 'webhook excluído',
+ 'webhook_delete_notification' => 'Webhook excluido com sucesso',
// Other
'commented_on' => 'comentou em',
'reset' => 'Redefinir',
'remove' => 'Remover',
'add' => 'Adicionar',
- 'configure' => 'Configure',
+ 'configure' => 'Configurar',
'fullscreen' => 'Tela cheia',
'favourite' => 'Favoritos',
'unfavourite' => 'Remover dos Favoritos',
'next' => 'Seguinte',
'previous' => 'Anterior',
- 'filter_active' => 'Active Filter:',
- 'filter_clear' => 'Clear Filter',
+ 'filter_active' => 'Filtro Ativo:',
+ 'filter_clear' => 'Limpar Filtro',
// Sort Options
'sort_options' => 'Opções de Ordenação',
'default' => 'Padrão',
'breadcrumb' => 'Caminho',
'status' => 'Status',
- 'status_active' => 'Active',
- 'status_inactive' => 'Inactive',
- 'never' => 'Never',
+ 'status_active' => 'Ativo',
+ 'status_inactive' => 'Inativo',
+ 'never' => 'Nunca',
// Header
- 'header_menu_expand' => 'Expand Header Menu',
+ 'header_menu_expand' => 'Expandir Cabeçalho do Menu',
'profile_menu' => 'Menu de Perfil',
'view_profile' => 'Visualizar Perfil',
'edit_profile' => 'Editar Perfil',
// Layout tabs
'tab_info' => 'Informações',
- 'tab_info_label' => 'Tab: Show Secondary Information',
+ 'tab_info_label' => 'Aba: Mostrar Informação Secundária',
'tab_content' => 'Conteúdo',
- 'tab_content_label' => 'Tab: Show Primary Content',
+ 'tab_content_label' => 'Aba: Mostrar Conteúdo Primário',
// Email Content
'email_action_help' => 'Se você estiver tendo problemas ao clicar o botão ":actionText", copie e cole a URL abaixo no seu navegador:',
'status' => '状态',
'status_active' => '已激活',
'status_inactive' => '未激活',
- 'never' => '永不',
+ 'never' => '从未',
// Header
'header_menu_expand' => '展开标头菜单',
// Webhooks
'webhooks' => 'Webhooks',
'webhooks_create' => '新建 Webhook',
- 'webhooks_none_created' => '不存在已创建的 webhooks',
+ 'webhooks_none_created' => '尚未创建任何 webhook',
'webhooks_edit' => '编辑 Webhook',
'webhooks_save' => '保存 Webhook',
'webhooks_details' => 'Webhook 详情',
<div class="image-manager-viewer">
<a href="{{ $image->url }}" target="_blank" rel="noopener" class="block">
- <img src="{{ $image->thumbs['display'] }}"
+ <img src="{{ $image->thumbs['display'] ?? $image->url }}"
alt="{{ $image->name }}"
class="anim fadeIn"
title="{{ $image->name }}">
$pageDeleteReq->assertSessionMissing('error');
}
+ public function test_custom_homepage_cannot_be_deleted_from_parent_deletion()
+ {
+ /** @var Page $page */
+ $page = Page::query()->first();
+ $this->setSettings([
+ 'app-homepage' => $page->id,
+ 'app-homepage-type' => 'page',
+ ]);
+
+ $this->asEditor()->delete($page->book->getUrl());
+ $this->assertSessionError('Cannot delete a page while it is set as a homepage');
+ $this->assertDatabaseMissing('deletions', ['deletable_id' => $page->book->id]);
+
+ $page->refresh();
+ $this->assertNull($page->deleted_at);
+ $this->assertNull($page->book->deleted_at);
+ }
+
public function test_custom_homepage_renders_includes()
{
$this->asEditor();