mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-10-26 11:11:56 +00:00
Add attachment preview insertion
This commit is contained in:
parent
146a6c01cc
commit
fdb72ff3e9
@ -92,6 +92,32 @@ class Attachment extends Model implements OwnableInterface
|
||||
return ['text/html' => $this->htmlLink(), 'text/plain' => $this->markdownLink()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this attachment can provide preview content for editor insertion.
|
||||
*/
|
||||
public function hasPreviewContent(): bool
|
||||
{
|
||||
if ($this->external) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$previewExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx'];
|
||||
|
||||
return in_array(strtolower($this->extension), $previewExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preview-friendly content for insertion into the editors.
|
||||
*/
|
||||
public function editorPreviewContent(): array
|
||||
{
|
||||
$html = '<div class="attachment-preview">'
|
||||
. '<iframe class="attachment-preview-frame" src="' . e($this->getUrl(true)) . '" title="' . e($this->name) . '" loading="lazy"></iframe>'
|
||||
. '</div>';
|
||||
|
||||
return ['text/html' => $html, 'text/plain' => $html];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the HTML link to this attachment.
|
||||
*/
|
||||
|
||||
@ -369,6 +369,7 @@ return [
|
||||
'attachments_link_url_hint' => 'Url of site or file',
|
||||
'attach' => 'Attach',
|
||||
'attachments_insert_link' => 'Add Attachment Link to Page',
|
||||
'attachments_insert_preview' => 'Add Attachment Preview to Page',
|
||||
'attachments_edit_file' => 'Edit File',
|
||||
'attachments_edit_file_name' => 'File Name',
|
||||
'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
|
||||
|
||||
@ -33,12 +33,11 @@ export class Attachments extends Component {
|
||||
});
|
||||
|
||||
this.container.addEventListener('event-emit-select-insert', event => {
|
||||
const insertContent = event.target.closest('[data-drag-content]').getAttribute('data-drag-content');
|
||||
const contentTypes = JSON.parse(insertContent);
|
||||
window.$events.emit('editor::insert', {
|
||||
html: contentTypes['text/html'],
|
||||
markdown: contentTypes['text/plain'],
|
||||
});
|
||||
this.insertContentFromCard(event, 'data-drag-content');
|
||||
});
|
||||
|
||||
this.container.addEventListener('event-emit-select-insert-preview', event => {
|
||||
this.insertContentFromCard(event, 'data-preview-content');
|
||||
});
|
||||
|
||||
this.attachLinkButton.addEventListener('click', () => {
|
||||
@ -66,6 +65,28 @@ export class Attachments extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
insertContentFromCard(event, attributeName) {
|
||||
const card = event.target.closest(`[${attributeName}]`);
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
const insertContent = card.getAttribute(attributeName);
|
||||
if (!insertContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentTypes = JSON.parse(insertContent);
|
||||
const html = contentTypes['text/html'] ?? '';
|
||||
const markdown = contentTypes['text/plain'] ?? html;
|
||||
|
||||
if (!html && !markdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.$events.emit('editor::insert', {html, markdown});
|
||||
}
|
||||
|
||||
updateOrder(idOrder) {
|
||||
window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
|
||||
window.$events.emit('success', resp.data.message);
|
||||
|
||||
@ -117,6 +117,19 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
margin: vars.$m 0;
|
||||
}
|
||||
|
||||
.attachment-preview-frame {
|
||||
width: 100%;
|
||||
min-height: 360px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid;
|
||||
@include mixins.lightDark(border-color, #DDD, #444);
|
||||
@include mixins.lightDark(background-color, #FFF, #111);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@ -5,6 +5,9 @@
|
||||
option:ajax-delete-row:url="{{ url('/attachments/' . $attachment->id) }}"
|
||||
data-id="{{ $attachment->id }}"
|
||||
data-drag-content="{{ json_encode($attachment->editorContent()) }}"
|
||||
@if($attachment->hasPreviewContent())
|
||||
data-preview-content="{{ json_encode($attachment->editorPreviewContent()) }}"
|
||||
@endif
|
||||
class="card drag-card">
|
||||
<div class="handle">@icon('grip')</div>
|
||||
<div class="py-s">
|
||||
@ -16,6 +19,13 @@
|
||||
type="button"
|
||||
title="{{ trans('entities.attachments_insert_link') }}"
|
||||
class="drag-card-action text-center text-link">@icon('link')</button>
|
||||
@if($attachment->hasPreviewContent())
|
||||
<button component="event-emit-select"
|
||||
option:event-emit-select:name="insert-preview"
|
||||
type="button"
|
||||
title="{{ trans('entities.attachments_insert_preview') }}"
|
||||
class="drag-card-action text-center text-link">@icon('expand-text')</button>
|
||||
@endif
|
||||
@if(userCan(\BookStack\Permissions\Permission::AttachmentUpdate, $attachment))
|
||||
<button component="event-emit-select"
|
||||
option:event-emit-select:name="edit"
|
||||
|
||||
@ -468,4 +468,37 @@ class AttachmentTest extends TestCase
|
||||
|
||||
$this->files->deleteAllAttachmentFiles();
|
||||
}
|
||||
|
||||
public function test_preview_content_available_for_supported_internal_files()
|
||||
{
|
||||
$attachment = Attachment::factory()->make([
|
||||
'extension' => 'pdf',
|
||||
'external' => false,
|
||||
'name' => 'Preview.pdf',
|
||||
]);
|
||||
$attachment->id = 42;
|
||||
|
||||
$this->assertTrue($attachment->hasPreviewContent());
|
||||
|
||||
$preview = $attachment->editorPreviewContent();
|
||||
$this->assertArrayHasKey('text/html', $preview);
|
||||
$this->assertStringContainsString('<iframe', $preview['text/html']);
|
||||
$this->assertStringContainsString('/attachments/42?open=true', $preview['text/html']);
|
||||
$this->assertSame($preview['text/html'], $preview['text/plain']);
|
||||
}
|
||||
|
||||
public function test_preview_content_not_available_for_external_or_unsupported_files()
|
||||
{
|
||||
$attachment = Attachment::factory()->make([
|
||||
'extension' => 'txt',
|
||||
'external' => false,
|
||||
]);
|
||||
$this->assertFalse($attachment->hasPreviewContent());
|
||||
|
||||
$externalAttachment = Attachment::factory()->make([
|
||||
'extension' => 'pdf',
|
||||
'external' => true,
|
||||
]);
|
||||
$this->assertFalse($externalAttachment->hasPreviewContent());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user