--- /dev/null
+>0.25%
+not op_mini all
\ No newline at end of file
use BookStack\Repos\EntityRepo;
use BookStack\Repos\UserRepo;
use BookStack\Services\ExportService;
-use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Views;
* @param string $chapterSlug
* @return Response
* @internal param bool $pageSlug
+ * @throws NotFoundException
*/
public function create($bookSlug, $chapterSlug = null)
{
- $book = $this->entityRepo->getBySlug('book', $bookSlug);
- $chapter = $chapterSlug ? $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug) : null;
+ if ($chapterSlug !== null) {
+ $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+ $book = $chapter->book;
+ } else {
+ $chapter = null;
+ $book = $this->entityRepo->getBySlug('book', $bookSlug);
+ }
+
$parent = $chapter ? $chapter : $book;
$this->checkOwnablePermission('page-create', $parent);
return redirect($draft->getUrl());
}
- // Otherwise show edit view
+ // Otherwise show the edit view if they're a guest
$this->setPageTitle(trans('entities.pages_new'));
return view('pages/guest-create', ['parent' => $parent]);
}
'name' => 'required|string|max:255'
]);
- $book = $this->entityRepo->getBySlug('book', $bookSlug);
- $chapter = $chapterSlug ? $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug) : null;
+ if ($chapterSlug !== null) {
+ $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
+ $book = $chapter->book;
+ } else {
+ $chapter = null;
+ $book = $this->entityRepo->getBySlug('book', $bookSlug);
+ }
+
$parent = $chapter ? $chapter : $book;
$this->checkOwnablePermission('page-create', $parent);
public function editDraft($bookSlug, $pageId)
{
$draft = $this->entityRepo->getById('page', $pageId, true);
- $this->checkOwnablePermission('page-create', $draft->book);
+ $this->checkOwnablePermission('page-create', $draft->parent);
$this->setPageTitle(trans('entities.pages_edit_draft'));
$draftsEnabled = $this->signedIn;
]);
$input = $request->all();
- $book = $this->entityRepo->getBySlug('book', $bookSlug);
-
$draftPage = $this->entityRepo->getById('page', $pageId, true);
+ $book = $draftPage->book;
- $chapterId = intval($draftPage->chapter_id);
- $parent = $chapterId !== 0 ? $this->entityRepo->getById('chapter', $chapterId) : $book;
+ $parent = $draftPage->parent;
$this->checkOwnablePermission('page-create', $parent);
if ($parent->isA('chapter')) {
return $this->belongsTo(Book::class);
}
+ /**
+ * Get the parent item
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function parent()
+ {
+ return $this->chapter_id ? $this->chapter() : $this->book();
+ }
+
/**
* Get the chapter that this page is in, If applicable.
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
this.init();
+
+ // Scroll to text if needed.
+ const queryParams = (new URL(window.location)).searchParams;
+ const scrollText = queryParams.get('content-text');
+ if (scrollText) {
+ this.scrollToText(scrollText);
+ }
}
init() {
});
}
+ // Scroll to a specified text
+ scrollToText(searchText) {
+ if (!searchText) {
+ return;
+ }
+
+ const content = this.cm.getValue();
+ const lines = content.split(/\r?\n/);
+ let lineNumber = lines.findIndex(line => {
+ return line && line.indexOf(searchText) !== -1;
+ });
+
+ if (lineNumber === -1) {
+ return;
+ }
+
+ this.cm.scrollIntoView({
+ line: lineNumber,
+ }, 200);
+ this.cm.focus();
+ // set the cursor location.
+ this.cm.setCursor({
+ line: lineNumber,
+ char: lines[lineNumber].length
+ })
+ }
+
}
module.exports = MarkdownEditor ;
\ No newline at end of file
pointerShowing = false;
});
- let updatePointerContent = () => {
+ let updatePointerContent = ($elem) => {
let inputText = pointerModeLink ? window.baseUrl(`/link/${this.pageId}#${pointerSectionId}`) : `{{@${this.pageId}#${pointerSectionId}}}`;
if (pointerModeLink && inputText.indexOf('http') !== 0) inputText = window.location.protocol + "//" + window.location.host + inputText;
$pointer.find('input').val(inputText);
+
+ // update anchor if present
+ const $editAnchor = $pointer.find('#pointer-edit');
+ if ($editAnchor.length !== 0 && $elem) {
+ const editHref = $editAnchor.data('editHref');
+ const element = $elem[0];
+ const elementId = element.id;
+
+ // get the first 50 characters.
+ let queryContent = element.textContent && element.textContent.substring(0, 50);
+ $editAnchor[0].href = `${editHref}?content-id=${elementId}&content-text=${encodeURIComponent(queryContent)}`;
+ }
};
// Show pointer when selecting a single block of tagged content
// Show pointer and set link
let $elem = $(this);
pointerSectionId = $elem.attr('id');
- updatePointerContent();
+ updatePointerContent($elem);
$elem.before($pointer);
$pointer.show();
}
}
}
-
}
-module.exports = PageDisplay;
\ No newline at end of file
+module.exports = PageDisplay;
},
setup: function (editor) {
- editor.on('init ExecCommand change input NodeChange ObjectResized', editorChange);
+ editor.on('ExecCommand change input NodeChange ObjectResized', editorChange);
+
+ editor.on('init', () => {
+ editorChange();
+ // Scroll to the content if needed.
+ const queryParams = (new URL(window.location)).searchParams;
+ const scrollId = queryParams.get('content-id');
+ if (scrollId) {
+ scrollToText(scrollId);
+ }
+ });
function editorChange() {
let content = editor.getContent();
window.$events.emit('editor-html-change', content);
}
+ function scrollToText(scrollId) {
+ const element = editor.dom.get(encodeURIComponent(scrollId).replace(/!/g, '%21'));
+ if (!element) {
+ return;
+ }
+
+ // scroll the element into the view and put the cursor at the end.
+ element.scrollIntoView();
+ editor.selection.select(element, true);
+ editor.selection.collapse(false);
+ editor.focus();
+ }
+
window.$events.listen('editor-html-update', html => {
editor.setContent(html);
editor.selection.select(editor.getBody(), true);
// Global Polyfills
-import "@babel/polyfill"
import "./services/dom-polyfills"
// Url retrieval function
position: absolute;
top: -60px;
background-color:#FFF;
- width: 272px;
+ width: 275px;
z-index: 55;
+
+ &.is-page-editable {
+ width: 328px;
+ }
+
&:before {
position: absolute;
left: 50%;
width: 172px;
z-index: 40;
}
- input, button {
+ input, button, a {
position: relative;
border-radius: 0;
height: 28px;
font-size: 12px;
vertical-align: top;
+ padding: 5px 16px;
}
> i {
color: #888;
cursor: pointer;
user-select: none;
}
- .button {
+ .input-group .button {
line-height: 1;
margin: 0 0 0 -4px;
box-shadow: none;
}
+ a.button {
+ margin: 0 0 0 0;
+
+ &:hover {
+ fill: #fff;
+ }
+ }
+ .svg-icon {
+ width: 1.2em;
+ height: 1.2em;
+ }
}
// Attribute form
'pages_revisions_restore' => 'Wiederherstellen',
'pages_revisions_none' => 'Diese Seite hat keine älteren Versionen.',
'pages_copy_link' => 'Link kopieren',
+ 'pages_edit_content_link' => 'Inhalt bearbeiten',
'pages_permissions_active' => 'Seiten-Berechtigungen aktiv',
'pages_initial_revision' => 'Erste Veröffentlichung',
'pages_initial_name' => 'Neue Seite',
'pages_revisions_restore' => 'Restore',
'pages_revisions_none' => 'This page has no revisions',
'pages_copy_link' => 'Copy Link',
+ 'pages_edit_content_link' => 'Edit Content',
'pages_permissions_active' => 'Page Permissions Active',
'pages_initial_revision' => 'Initial publish',
'pages_initial_name' => 'New Page',
'pages_revisions_restore' => 'Restaurar',
'pages_revisions_none' => 'Esta página no tiene revisiones',
'pages_copy_link' => 'Copiar Enlace',
+ 'pages_edit_content_link' => 'Contenido editado',
'pages_permissions_active' => 'Permisos de página activos',
'pages_initial_revision' => 'Publicación inicial',
'pages_initial_name' => 'Página nueva',
'chapters_permissions_active' => 'Permisos de capítulo activado',
'chapters_permissions_success' => 'Permisos de capítulo actualizados',
'chapters_search_this' => 'Buscar en este capítulo',
-
+
/**
* Pages
*/
'pages_revisions_restore' => 'Restaurar',
'pages_revisions_none' => 'Esta página no tiene revisiones',
'pages_copy_link' => 'Copiar enlace',
+ 'pages_edit_content_link' => 'Contenido editado',
'pages_permissions_active' => 'Permisos de página activos',
'pages_initial_revision' => 'Publicación inicial',
'pages_initial_name' => 'Página nueva',
'pages_revisions_restore' => 'Restaurer',
'pages_revisions_none' => 'Cette page n\'a aucune révision',
'pages_copy_link' => 'Copier le lien',
+ 'pages_edit_content_link' => 'Modifier le contenu',
'pages_permissions_active' => 'Permissions de page actives',
'pages_initial_revision' => 'Publication initiale',
'pages_initial_name' => 'Nouvelle page',
'pages_revisions_restore' => 'Ripristina',
'pages_revisions_none' => 'Questa pagina non ha versioni',
'pages_copy_link' => 'Copia Link',
+ 'pages_edit_content_link' => 'Modifica contenuto',
'pages_permissions_active' => 'Permessi Pagina Attivi',
'pages_initial_revision' => 'Pubblicazione iniziale',
'pages_initial_name' => 'Nuova Pagina',
'pages_revisions_restore' => '復元',
'pages_revisions_none' => 'このページにはリビジョンがありません',
'pages_copy_link' => 'リンクをコピー',
+ 'pages_edit_content_link' => 'コンテンツの編集',
'pages_permissions_active' => 'ページの権限は有効です',
'pages_initial_revision' => '初回の公開',
'pages_initial_name' => '新規ページ',
'recent_activity' => 'Recente Activiteit',
'create_now' => 'Maak er zelf één',
'revisions' => 'Revisies',
- 'meta_revision' => 'Revisie #:revisionCount',
+ 'meta_revision' => 'Revisie #:revisionCount',
'meta_created' => 'Aangemaakt :timeLength',
'meta_created_name' => 'Aangemaakt: :timeLength door :user',
'meta_updated' => ':timeLength Aangepast',
* Search
*/
'search_results' => 'Zoekresultaten',
- 'search_total_results_found' => ':count resultaten gevonden|:count resultaten gevonden',
+ 'search_total_results_found' => ':count resultaten gevonden|:count resultaten gevonden',
'search_clear' => 'Zoekopdracht wissen',
'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
'search_for_term' => 'Zoeken op :term',
*/
'chapter' => 'Hoofdstuk',
'chapters' => 'Hoofdstukken',
- 'x_chapters' => ':count Hoofdstuk|:count Hoofdstukken',
+ 'x_chapters' => ':count Hoofdstuk|:count Hoofdstukken',
'chapters_popular' => 'Populaire Hoofdstukken',
'chapters_new' => 'Nieuw Hoofdstuk',
'chapters_create' => 'Hoofdstuk Toevoegen',
'chapters_empty' => 'Er zijn geen pagina\'s in dit hoofdstuk aangemaakt.',
'chapters_permissions_active' => 'Hoofdstuk Permissies Actief',
'chapters_permissions_success' => 'Hoofdstuk Permissies Bijgewerkt',
- 'chapters_search_this' => 'Doorzoek dit hoofdstuk',
+ 'chapters_search_this' => 'Doorzoek dit hoofdstuk',
/**
* Pages
*/
'page' => 'Pagina',
'pages' => 'Pagina\'s',
- 'x_pages' => ':count Pagina|:count Pagina\'s',
+ 'x_pages' => ':count Pagina|:count Pagina\'s',
'pages_popular' => 'Populaire Pagina\'s',
'pages_new' => 'Nieuwe Pagina',
'pages_attachments' => 'Bijlages',
'pages_move_success' => 'Pagina verplaatst naar ":parentName"',
'pages_permissions' => 'Pagina Permissies',
'pages_permissions_success' => 'Pagina Permissies bijgwerkt',
- 'pages_revision' => 'Revisie',
+ 'pages_revision' => 'Revisie',
'pages_revisions' => 'Pagina Revisies',
'pages_revisions_named' => 'Pagina Revisies voor :pageName',
'pages_revision_named' => 'Pagina Revisie voor :pageName',
'pages_revisions_restore' => 'Herstellen',
'pages_revisions_none' => 'Deze pagina heeft geen revisies',
'pages_copy_link' => 'Link Kopiëren',
+ 'pages_edit_content_link' => 'Bewerk inhoud',
'pages_permissions_active' => 'Pagina Permissies Actief',
'pages_initial_revision' => 'Eerste publicatie',
'pages_initial_name' => 'Nieuwe Pagina',
'pages_revisions_restore' => 'Przywróć',
'pages_revisions_none' => 'Ta strona nie posiada żadnych rewizji',
'pages_copy_link' => 'Kopiuj link',
+ 'pages_edit_content_link' => 'Edytuj zawartość',
'pages_permissions_active' => 'Uprawnienia strony aktywne',
'pages_initial_revision' => 'Wydanie pierwotne',
'pages_initial_name' => 'Nowa strona',
'role' => 'Regra',
'cover_image' => 'Imagem de capa',
'cover_image_description' => 'Esta imagem deve ser aproximadamente 300x170px.',
+
/**
* Actions
*/
'edit' => 'Editar',
'sort' => 'Ordenar',
'move' => 'Mover',
+ 'copy' => 'Copiar',
'reply' => 'Responder',
'delete' => 'Excluir',
'search' => 'Pesquisar',
'toggle_details' => 'Alternar Detalhes',
'toggle_thumbnails' => 'Alternar Miniaturas',
'details' => 'Detalhes',
+ 'grid_view' => 'Visualização em Grade',
+ 'list_view' => 'Visualização em Lista',
/**
* Header
'pages_revisions_restore' => 'Restaurar',
'pages_revisions_none' => 'Essa página não tem revisões',
'pages_copy_link' => 'Copia Link',
+ 'pages_edit_content_link' => 'Editar conteúdo',
'pages_permissions_active' => 'Permissões de Página Ativas',
'pages_initial_revision' => 'Publicação Inicial',
'pages_initial_name' => 'Nova Página',
'pages_revisions_restore' => 'Восстановить',
'pages_revisions_none' => 'У этой страницы нет других версий',
'pages_copy_link' => 'Копировать ссылку',
+ 'pages_edit_content_link' => 'Изменить содержание',
'pages_permissions_active' => 'Действующие разрешения на страницу',
'pages_initial_revision' => 'Первоначальное издание',
'pages_initial_name' => 'Новая страница',
'pages_revisions_restore' => 'Obnoviť',
'pages_revisions_none' => 'Táto stránka nemá žiadne revízie',
'pages_copy_link' => 'Kopírovať odkaz',
+ 'pages_edit_content_link' => 'Upraviť obsah',
'pages_permissions_active' => 'Oprávnienia stránky aktívne',
'pages_initial_revision' => 'Prvé zverejnenie',
'pages_initial_name' => 'Nová stránka',
'pages_revisions_restore' => 'Återställ',
'pages_revisions_none' => 'Sidan har inga revisioner',
'pages_copy_link' => 'Kopiera länk',
+ 'pages_edit_content_link' => 'Redigera innehåll',
'pages_permissions_active' => 'Anpassade rättigheter är i bruk',
'pages_initial_revision' => 'Första publicering',
'pages_initial_name' => 'Ny sida',
'pages_revisions_restore' => '恢复',
'pages_revisions_none' => '此页面没有修订',
'pages_copy_link' => '复制链接',
+ 'pages_edit_content_link' => '编辑内容',
'pages_permissions_active' => '有效的页面权限',
'pages_initial_revision' => '初始发布',
'pages_initial_name' => '新页面',
'pages_revisions_restore' => '恢複',
'pages_revisions_none' => '此頁面沒有修訂',
'pages_copy_link' => '複製連結',
+ 'pages_edit_content_link' => '编辑内容',
'pages_permissions_active' => '有效的頁面權限',
'pages_initial_revision' => '初次發布',
'pages_initial_name' => '新頁面',
<div class="page-content flex" page-display="{{ $page->id }}">
<div class="pointer-container" id="pointer">
- <div class="pointer anim" >
+ <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
<span class="icon text-primary">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
- <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
- <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}">@icon('copy')</button>
+ <span class="input-group">
+ <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
+ <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}">@icon('copy')</button>
+ </span>
+ @if(userCan('page-update', $page))
+ <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
+ class="button icon heading-edit-icon" title="{{ trans('entities.pages_edit_content_link')}}">@icon('edit')</a>
+ @endif
</div>
</div>
->see('You do not have permission')
->seePageIs('/');
}
+
+ public function test_can_create_page_if_chapter_has_permissions_when_book_not_visible()
+ {
+ $book = Book::first();
+ $this->setEntityRestrictions($book, []);
+ $bookChapter = $book->chapters->first();
+ $this->setEntityRestrictions($bookChapter, ['view']);
+
+ $this->actingAs($this->user)->visit($bookChapter->getUrl())
+ ->dontSee('New Page');
+
+ $this->setEntityRestrictions($bookChapter, ['view', 'create']);
+
+ $this->actingAs($this->user)->visit($bookChapter->getUrl())
+ ->click('New Page')
+ ->seeStatusCode(200)
+ ->type('test page', 'name')
+ ->type('test content', 'html')
+ ->press('Save Page')
+ ->seePageIs($book->getUrl('/page/test-page'))
+ ->seeStatusCode(200);
+ }
}
use: {
loader: 'babel-loader',
options: {
- presets: ['@babel/preset-env']
+ presets: [[
+ '@babel/preset-env', {
+ useBuiltIns: 'usage'
+ }
+ ]]
}
}
},