Changes for page View File Macro

Last modified by Frank Fock on 2025/04/04 21:18

From version 2.1
edited by Frank Fock
on 2024/05/23 17:17
Change comment: Install extension [com.xwiki.pro:xwiki-pro-macros/1.17.3]
To version 4.1
edited by Frank Fock
on 2025/04/04 21:18
Change comment: Install extension [com.xwiki.pro:xwiki-pro-macros-ui/1.26.14]

Summary

Details

Page properties
Content
... ... @@ -1,3 +1,35 @@
1 -{{view-file name="Test.ppt" /}}
1 +The view-file macro displays attachments in a document, in place or as thumbnail and offers a preview.
2 2  
3 -{{view-file name="TestPDF.pdf" /}}
3 +Limitation: the thumbnail is actually only an icon for now.
4 +
5 += Parameters =
6 +
7 +|=Parameter|=Description|=Required|=Default
8 +|display|Kind of display. "button" for a button, "thumbnail" for a thumbnail, "full" to render the document in place|no|thumbnail (button in inline mode)
9 +|name|The attachment reference to display|if att--filename is not given|
10 +|page|The page from where you want the attachments to be displayed. Modifying this parameter will reset the selected file value|no|
11 +|width|The width of the view in % or px (e.g. 100%, 100px)|no|100% for the full view or 100px for the thumbnail
12 +|height|The height of the view in % or px (e.g. 100%, 100px)|no|1000px for the full view or 100px for the thumbnail
13 +|att--filename|Alias of name|If name is not given|
14 +
15 += Example Usage =
16 +
17 +Thumbnails side by side:
18 +
19 +{{view-file display="thumbnail" name="Test.ppt"/}} {{view-file display="thumbnail" name="TestPDF.pdf"/}}
20 +
21 +Or standalone:
22 +
23 +{{view-file name="Test.ppt"/}}
24 +
25 +In a paragraph: {{view-file name="Test.ppt"/}}
26 +
27 +
28 +Full PDF:
29 +
30 +{{view-file display="full" name="TestPDF.pdf"/}}
31 +
32 +Full Presentation:
33 +
34 +{{view-file display="full" name="Test.ppt"/}}
35 +
XWiki.WikiMacroClass[0]
Cached
... ... @@ -1,1 +1,0 @@
1 -No
Asynchronous rendering
... ... @@ -1,1 +1,0 @@
1 -No
Macro code
... ... @@ -1,32 +1,0 @@
1 -{{velocity output="false"}}
2 -#macro (executeMacro)
3 - #set($hasPDFViewer = $xwiki.exists("XWiki.PDFViewerMacro"))
4 - #set($officeExtensions = [ 'ppt', 'pptx', 'odp', 'doc', 'docx', 'odt', 'xls', 'xlsx', 'ods' ])
5 - #set($unescapedFilename = $xcontext.macro.params.get('att--filename'))
6 - #if(!$unescapedFilename)
7 - #set($unescapedFilename = $xcontext.macro.params.get('name'))
8 - #end
9 - #set($extension = $unescapedFilename.substring($mathtool.add($unescapedFilename.lastIndexOf('.'), 1)).toLowerCase())
10 - #set($escapedFilename = $services.rendering.escape($unescapedFilename, $xwiki.currentContentSyntaxId))
11 - #if($extension == 'pdf' && $hasPDFViewer)
12 -
13 - {{pdfviewer file="$escapedFilename" /}}
14 - #elseif($officeExtensions.contains($extension))
15 -
16 - {{office reference="attach:$escapedFilename" /}}
17 - #elseif($doc.getAttachment($unescapedFilename))
18 - [[attach:$escapedFilename]]
19 - #end
20 -#end
21 -{{/velocity}}
22 -
23 -{{velocity}}
24 -## We need to check if there is a valid license because the macro is registered even if the user doesn't have view right
25 -## on the macro definition page. See XWIKI-14828: Rendering macros defined in wiki pages are available to users that
26 -## don't have view right on those pages.
27 -#if ($services.licensing.licensor.hasLicensureForEntity($xcontext.macro.doc.documentReference))
28 - #executeMacro
29 -#else
30 - {{missingLicenseMessage extensionName="proMacros.extension.name"/}}
31 -#end
32 -{{/velocity}}
Macro content availability
... ... @@ -1,1 +1,0 @@
1 -Optional
Macro description
... ... @@ -1,1 +1,0 @@
1 -Show files using PDF Viewer Macro or Office Viewer
Macro id
... ... @@ -1,1 +1,0 @@
1 -view-file
Macro name
... ... @@ -1,1 +1,0 @@
1 -View Files
Supports inline mode
... ... @@ -1,1 +1,0 @@
1 -No
Macro visibility
... ... @@ -1,1 +1,0 @@
1 -Current Wiki
XWiki.WikiMacroParameterClass[0]
Parameter mandatory
... ... @@ -1,1 +1,0 @@
1 -Yes
Parameter name
... ... @@ -1,1 +1,0 @@
1 -att--filename
XWiki.JavaScriptExtension[0]
Caching policy
... ... @@ -1,0 +1,1 @@
1 +long
Code
... ... @@ -1,0 +1,98 @@
1 +require.config({
2 + paths: {
3 + 'xwiki-suggestAttachments': "$xwiki.getSkinFile('uicomponents/suggest/suggestAttachments.js', true)" +
4 + "?v=$escapetool.url($xwiki.version)"
5 + }
6 +});
7 +
8 +// As there is no platform implementation to allow the user to dynamically select the page from where the attachments
9 +// are shown, a custom implementation was made to dynamically update the displyed attachments in corelation to the
10 +// selected page parameter. This can be removed after the implementation of https://jira.xwiki.org/browse/XWIKI-22850.
11 +require(['jquery', 'xwiki-meta', 'xwiki-suggestAttachments'], function($, xm) {
12 + const acceptedExtensions = '.ppt,.pptx,.odp,.doc,.docx,.odt,.xls,.xlsx,.ods,.pdf';
13 + const styleObserver = new MutationObserver(function(mutations) {
14 + for (const mutation of mutations) {
15 + if (mutation.target.style.display === 'none') {
16 + styleObserver.disconnect();
17 + pageObserver.observe(document.body, { childList: true, subtree: true });
18 + return;
19 + }
20 + }
21 + });
22 +
23 + const initializeAttachments = function(selectElement) {
24 + let scope = "document:";
25 + if (selectElement.val() != null) {
26 + scope += selectElement.val();
27 + }
28 + var inputElement = $('input[name="name"]');
29 + if (inputElement.length) {
30 + let selectize = inputElement.siblings('.selectize-control');
31 + if (selectize.length) {
32 + selectize.remove();
33 + inputElement.removeAttr("class tabindex style").val('');
34 + const clone = inputElement.clone().appendTo(inputElement.parent());
35 + inputElement.remove();
36 + inputElement = clone;
37 + }
38 + const allowUpload = selectElement.val() == xm.document || !selectElement.val();
39 + inputElement.suggestAttachments({
40 + maxItems: 1,
41 + accept: acceptedExtensions,
42 + searchScope: scope,
43 + uploadAllowed: allowUpload
44 + });
45 + }
46 + }
47 +
48 + const pageObserver = new MutationObserver(function(mutationsList, observer) {
49 + const selectElement = $('select[name="page"]');
50 + if (selectElement.length) {
51 + const modal = selectElement.closest(".macro-editor-modal")[0];
52 + if (modal.style.display !== 'none') {
53 + observer.disconnect();
54 + styleObserver.observe(modal, { attributes: true, attributeFilter: ['style'] });
55 + $(selectElement).change(function() {
56 + initializeAttachments($(this))
57 + });
58 + initializeAttachments(selectElement)
59 + }
60 + }
61 + });
62 +
63 + pageObserver.observe(document.body, { childList: true, subtree: true });
64 +});
65 +
66 +window.addEventListener("DOMContentLoaded", function () {
67 + "use strict";
68 + document.addEventListener("click", async function (e) {
69 + if (("" + document.getElementById("xwikicontent")?.contentEditable) == "true") {
70 + return;
71 + }
72 + const viewFile = e.target?.closest(".viewFileThumbnail");
73 + if (viewFile && viewFile.dataset.preview === 'true') {
74 + const popup = new XWiki.widgets.ModalPopup();
75 + popup.createDialog();
76 + popup.setContent("<span class='fa fa-spinner'></span>");
77 + popup.dialogBox.classList.add("viewFileModal");
78 + popup.dialogBox.style.top = "2.5vh";
79 + popup.dialogBox.style.width = "95vw";
80 + popup.dialogBox.style.height = "95vh";
81 + const a = e.target.closest('a');
82 + const downloadLink = document.createElement("a");
83 + downloadLink.download = true;
84 + downloadLink.textContent = a.textContent;
85 + downloadLink.className = "fa fa-download button button-primary viewFileModal-downloadLink";
86 + downloadLink.href = a.href;
87 + popup.dialogBox.insertBefore(downloadLink, popup.dialogBox.firstChild)
88 + popup.showDialog();
89 + e.preventDefault();
90 + const response = await fetch(XWiki.contextPath + "/wiki/" + XWiki.currentWiki + "/get/Confluence/Macros/ViewFileService?action=render&attachment=" + encodeURIComponent(viewFile.dataset.ref));
91 + popup.setContent(await response.text());
92 + const gallery = popup.dialogBox.querySelector(".gallery");
93 + if (gallery) {
94 + new XWiki.Gallery(gallery);
95 + }
96 + }
97 + });
98 +});
Use this extension
... ... @@ -1,0 +1,1 @@
1 +onDemand
Parse content
... ... @@ -1,0 +1,1 @@
1 +Yes
XWiki.StyleSheetExtension[0]
Caching policy
... ... @@ -1,0 +1,1 @@
1 +long
Code
... ... @@ -1,0 +1,83 @@
1 +.viewFileModal iframe, .viewFileModal .xdialog-content {
2 + height: calc(95vh - 5em);
3 +}
4 +
5 +.viewFileModal .xGallery, .viewFileFull .xGallery {
6 + height: 100%;
7 + width: 100%;
8 +}
9 +
10 +.viewFileModal .xdialog-content:before {
11 + clear: both;
12 +}
13 +
14 +.viewFileModal .xdialog-content {
15 + overflow: auto;
16 + margin:0 0.8em 0.8em 0.8em;
17 + width: calc(100% - 1.6em);
18 +}
19 +
20 +.viewFileContentThumb .modal-dialog {
21 + max-height: 90vh;
22 + width: 80%;
23 +}
24 +
25 +.viewFileContentThumb .modal-body {
26 + max-height: 80vh;
27 + overflow: auto;
28 +}
29 +
30 +.viewFileContentThumb.viewFilePresentation .modal-dialog {
31 + height: 90%;
32 + width: 90%;
33 + display: flex;
34 + flex-direction: column;
35 + align-items: center;
36 +}
37 +
38 +.viewFileThumbnailButton {
39 + display: inline-block;
40 + padding-right: 0.5ex;
41 +}
42 +
43 +.viewFileThumbnailCard, .viewFileFull {
44 + position: relative;
45 + width: 10rem !important;
46 + height: fit-content;
47 + min-height: 10rem !important;
48 + margin-bottom: 1rem !important;
49 +}
50 +
51 +span.viewFileThumbnailCard {
52 + display: inline-block;
53 +}
54 +
55 +.viewFileThumbnailCard a {
56 + display: flex;
57 + gap: 3px;
58 + flex-direction: column;
59 + text-align: center;
60 + width: 100%;
61 + min-height: inherit;
62 +}
63 +
64 +.viewFileThumbnailCard a .attachmentMimeType {
65 + flex: 1;
66 + display: flex;
67 + align-items: center;
68 + align-self: center;
69 + text-align: center;
70 + justify-content: center;
71 + width: 100%;
72 + border: 1px solid;
73 + border-radius: 1rem;
74 + min-height: 5em;
75 +}
76 +
77 +.viewFileModal-downloadLink {
78 + margin-left: 2px;
79 +}
80 +
81 +.viewFileName {
82 + overflow-wrap: break-word;
83 +}
Use this extension
... ... @@ -1,0 +1,1 @@
1 +onDemand
Name
... ... @@ -1,0 +1,1 @@
1 +viewFileCSS
Parse content
... ... @@ -1,0 +1,1 @@
1 +No
Content Type
... ... @@ -1,0 +1,1 @@
1 +CSS