Änderungen von Dokument View File Macro
Zuletzt geändert von xwikiadmin am 2025/12/11 07:44
Von Version 5.1
bearbeitet von xwikiadmin
am 2023/10/26 10:38
am 2023/10/26 10:38
Änderungskommentar:
Install extension [com.xwiki.pro:xwiki-pro-macros/1.12]
Auf Version 10.1
bearbeitet von xwikiadmin
am 2025/12/11 07:44
am 2025/12/11 07:44
Änderungskommentar:
Install extension [com.xwiki.pro:xwiki-pro-macros-ui/1.28.5]
Zusammenfassung
-
Seiteneigenschaften (1 geändert, 0 hinzugefügt, 0 gelöscht)
-
Objekte (0 geändert, 2 hinzugefügt, 2 gelöscht)
Details
- Seiteneigenschaften
-
- Inhalt
-
... ... @@ -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 -Nein - Asynchrones Rendern
-
... ... @@ -1,1 +1,0 @@ 1 -Nein - Makro-Code
-
... ... @@ -1,36 +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 -{{include reference="Licenses.Code.VelocityMacros"/}} 24 - 25 -{{velocity}} 26 -## We need to check if there is a valid license because the macro is registered even if the user doesn't have view right 27 -## on the macro definition page. See XWIKI-14828: Rendering macros defined in wiki pages are available to users that 28 -## don't have view right on those pages. 29 -#if ($services.licensing.licensor.hasLicensureForEntity($xcontext.macro.doc.documentReference)) 30 - #executeMacro 31 -#else 32 - {{error}} 33 - #getMissingLicenseMessage('proMacros.extension.name') 34 - {{/error}} 35 -#end 36 -{{/velocity}} - Verfügbarkeit von Makroinhalten
-
... ... @@ -1,1 +1,0 @@ 1 -Optional - Standardkategorie
-
... ... @@ -1,1 +1,0 @@ 1 -content - Makrobeschreibung
-
... ... @@ -1,1 +1,0 @@ 1 -Show files using PDF Viewer Macro or Office Viewer - Makro id
-
... ... @@ -1,1 +1,0 @@ 1 -view-file - Makroname
-
... ... @@ -1,1 +1,0 @@ 1 -View Files - Unterstützt Inline-Modus
-
... ... @@ -1,1 +1,0 @@ 1 -Nein - Makro-Sichtbarkeit
-
... ... @@ -1,1 +1,0 @@ 1 -Current Wiki
- XWiki.WikiMacroParameterClass[0]
-
- Parameter-Name
-
... ... @@ -1,1 +1,0 @@ 1 -att--filename
- XWiki.JavaScriptExtension[0]
-
- Pufferstrategie
-
... ... @@ -1,0 +1,1 @@ 1 +long - Code
-
... ... @@ -1,0 +1,185 @@ 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 + require(['collabora-utils'], function(collabora) { 13 + initUI(collabora); 14 + }, function (err) { 15 + console.warn('collabora-utils not found, continuing without it.'); 16 + initUI(null); 17 + }); 18 + 19 + function initUI(collabora) { 20 + const acceptedExtensions = '.ppt,.pptx,.odp,.doc,.docx,.odt,.xls,.xlsx,.ods,.pdf'; 21 + const styleObserver = new MutationObserver(function(mutations) { 22 + for (const mutation of mutations) { 23 + if (mutation.target.style.display === 'none') { 24 + styleObserver.disconnect(); 25 + pageObserver.observe(document.body, { childList: true, subtree: true }); 26 + return; 27 + } 28 + } 29 + }); 30 + 31 + const initializeAttachments = function(selectElement) { 32 + let scope = "document:"; 33 + if (selectElement.val() != null) { 34 + scope += selectElement.val(); 35 + } 36 + var inputElement = $('input[name="name"]'); 37 + if (inputElement.length) { 38 + let selectize = inputElement.siblings('.selectize-control'); 39 + if (selectize.length) { 40 + selectize.remove(); 41 + inputElement.removeAttr("class tabindex style").val(''); 42 + const clone = inputElement.clone().appendTo(inputElement.parent()); 43 + inputElement.remove(); 44 + inputElement = clone; 45 + } 46 + const allowUpload = selectElement.val() == xm.document || !selectElement.val(); 47 + inputElement.suggestAttachments({ 48 + maxItems: 1, 49 + accept: acceptedExtensions, 50 + searchScope: scope, 51 + uploadAllowed: allowUpload 52 + }); 53 + } 54 + }; 55 + 56 + const checkModalAttachment = function(mutationsList, observer) { 57 + const selectElement = $('select[name="page"]'); 58 + if (selectElement.length) { 59 + const modal = selectElement.closest(".macro-editor-modal")[0]; 60 + if (modal && modal.style.display !== 'none') { 61 + observer.disconnect(); 62 + styleObserver.observe(modal, { attributes: true, attributeFilter: ['style'] }); 63 + $(selectElement).change(function() { 64 + initializeAttachments($(this)); 65 + }); 66 + initializeAttachments(selectElement); 67 + } 68 + } 69 + }; 70 + 71 + const pageObserver = new MutationObserver(checkModalAttachment); 72 + pageObserver.observe(document.body, { childList: true, subtree: true }); 73 + checkModalAttachment([], pageObserver); 74 + 75 + const getFileExtension = function(fileName) { 76 + return fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase(); 77 + }; 78 + 79 + const decorateWithCollaboraButton = function(viewFile, fileRef, spanContainer) { 80 + const attachment = XWiki.Model.resolve(fileRef, XWiki.EntityType.ATTACHMENT); 81 + var fileName = attachment.getName(); 82 + var collaboraButton = $(viewFile).find('a.collaboraEdit'); 83 + if (collaboraButton.length != 0) { 84 + const accessRights = collabora.getAccessRights(fileName); 85 + if (!accessRights) { 86 + return; 87 + } 88 + collabora.populateCollaboraButton(collaboraButton, fileName, accessRights, XWiki.Model.serialize(attachment.parent)); 89 + const clonedButton = collaboraButton.clone(true, true); 90 + clonedButton.addClass('btn btn-default'); 91 + clonedButton.addClass('viewfile-header-button'); 92 + clonedButton.attr('target', '_blank'); 93 + $(spanContainer).find('.viewFileModal-downloadLink').after(clonedButton); 94 + } 95 + }; 96 + 97 + // This function has been taken from OnlyOffice Connector Application, as it's being used to determine what 98 + // action the user can take on a file depending on the file extension. 99 + const determineOOAction = function(fileRef) { 100 + var ooCanDoByExt = { }; 101 + const editableFormats = ['docx', 'xlsx', 'pptx']; 102 + const convertibleFormats = ['odt', 'ods', 'odp', 'xls', 'csv', 'doc', 'ppt', 'pps', 'ppsx', 'rtf', 'txt', 'mht', 103 + 'html', 'htm']; 104 + editableFormats.forEach(function(x) { 105 + ooCanDoByExt[x] = 'edit'; 106 + }); 107 + convertibleFormats.forEach(function(x) { 108 + ooCanDoByExt[x] = 'convert'; 109 + }); 110 + ['pdf', 'djvu', 'fb2', 'epub', 'xps'].forEach(function(x) { 111 + ooCanDoByExt[x] = 'view'; 112 + }); 113 + var fileType = getFileExtension(fileRef); 114 + return ooCanDoByExt[fileType]; 115 + }; 116 + 117 + const decorateWithXooButton = function(viewFile, fileRef, spanContainer) { 118 + if ($(viewFile).find('span.oo-installed-check').data('ooexists')) { 119 + var whatCanDo = determineOOAction(fileRef); 120 + if (!whatCanDo) { 121 + return; 122 + } 123 + if (!XWiki.hasEdit) { 124 + whatCanDo = 'view'; 125 + } 126 + const attachment = XWiki.Model.resolve(fileRef, XWiki.EntityType.ATTACHMENT); 127 + var queryString = $.param({ 128 + 'elem': 'oOActionButtons', 129 + 'document': XWiki.Model.serialize(attachment.parent), 130 + 'filename': attachment.getName(), 131 + 'whatCanDo': whatCanDo, 132 + 'asLink': true 133 + }); 134 + 135 + $.post(new XWiki.Document('UI', 'XWikiOnlyOfficeCode').getURL('get', queryString), function(data) { 136 + const $button = $(data); 137 + $button.addClass('btn btn-default'); 138 + $button.addClass('viewfile-header-button'); 139 + $button.attr('target', '_blank'); 140 + $(spanContainer).find('.viewFileModal-downloadLink').after($button); 141 + }); 142 + } 143 + }; 144 + 145 + $(document).on("click", async function (e) { 146 + if (("" + document.getElementById("xwikicontent")?.contentEditable) == "true") { 147 + return; 148 + } 149 + const viewFile = e.target?.closest(".viewFileThumbnail"); 150 + if (viewFile && viewFile.dataset.preview === 'true') { 151 + const fileRef = viewFile.dataset.ref; 152 + const popup = new XWiki.widgets.ModalPopup(); 153 + popup.createDialog(); 154 + popup.setContent("<span class='fa fa-spinner'></span>"); 155 + popup.dialogBox.classList.add("viewFileModal"); 156 + popup.dialogBox.style.top = "2.5vh"; 157 + popup.dialogBox.style.width = "95vw"; 158 + popup.dialogBox.style.height = "95vh"; 159 + const a = e.target.closest('a'); 160 + const downloadLink = document.createElement('a'); 161 + const spanContainer = document.createElement('span'); 162 + spanContainer.className = 'viewfile-actions-header'; 163 + downloadLink.download = true; 164 + downloadLink.textContent = ' ' + $(a).find('.viewFileName').text(); 165 + downloadLink.className = 'fa fa-download button button-primary viewFileModal-downloadLink viewfile-header-button'; 166 + downloadLink.href = a.href; 167 + spanContainer.appendChild(downloadLink); 168 + decorateWithXooButton(viewFile, fileRef, spanContainer); 169 + if (collabora != null) { 170 + decorateWithCollaboraButton(viewFile, fileRef, spanContainer); 171 + } 172 + $(popup.dialogBox).prepend(spanContainer); 173 + popup.showDialog(); 174 + e.preventDefault(); 175 + const response = await fetch(XWiki.contextPath + '/wiki/' + XWiki.currentWiki + 176 + '/get/Confluence/Macros/ViewFileService?action=render&attachment=' + encodeURIComponent(fileRef)); 177 + popup.setContent(await response.text()); 178 + const gallery = popup.dialogBox.querySelector(".gallery"); 179 + if (gallery) { 180 + new XWiki.Gallery(gallery); 181 + } 182 + } 183 + }); 184 + } 185 +}); - Benutze diese Erweiterung
-
... ... @@ -1,0 +1,1 @@ 1 +onDemand - Inhalt parsen
-
... ... @@ -1,0 +1,1 @@ 1 +Ja
- XWiki.StyleSheetExtension[0]
-
- Pufferstrategie
-
... ... @@ -1,0 +1,1 @@ 1 +long - Code
-
... ... @@ -1,0 +1,126 @@ 1 +#template('colorThemeInit.vm') 2 + 3 +.viewFileModal iframe, .viewFileModal .xdialog-content { 4 + height: calc(95vh - 5em); 5 +} 6 + 7 +.viewFileModal .xGallery, .viewFileFull .xGallery { 8 + height: 100%; 9 + width: 100%; 10 +} 11 + 12 +.viewFileModal .xdialog-content:before { 13 + clear: both; 14 +} 15 + 16 +.viewFileModal .xdialog-content { 17 + overflow: auto; 18 + margin:0 0.8em 0.8em 0.8em; 19 + width: calc(100% - 1.6em); 20 +} 21 + 22 +.viewFileContentThumb .modal-dialog { 23 + max-height: 90vh; 24 + width: 80%; 25 +} 26 + 27 +.viewFileContentThumb .modal-body { 28 + max-height: 80vh; 29 + overflow: auto; 30 +} 31 + 32 +.viewFileContentThumb.viewFilePresentation .modal-dialog { 33 + height: 90%; 34 + width: 90%; 35 + display: flex; 36 + flex-direction: column; 37 + align-items: center; 38 +} 39 + 40 +.viewFileThumbnailButton { 41 + display: inline-block; 42 + padding-right: 0.5ex; 43 +} 44 + 45 +.viewFileThumbnailCard, .viewFileFull { 46 + position: relative; 47 + margin-bottom: 1rem !important; 48 +} 49 + 50 +.viewFileThumbnailCard { 51 + width: 10rem !important; 52 + height: fit-content !important; 53 + min-height: 10rem; 54 +} 55 + 56 +span.viewFileThumbnailCard { 57 + display: inline-block; 58 +} 59 + 60 +.viewFileThumbnailCard a { 61 + display: flex; 62 + gap: 3px; 63 + flex-direction: column; 64 + text-align: center; 65 + width: 100%; 66 + min-height: inherit; 67 +} 68 + 69 +.viewFileThumbnailCard a .attachmentMimeType { 70 + flex: 1; 71 + display: flex; 72 + align-items: center; 73 + align-self: center; 74 + text-align: center; 75 + justify-content: center; 76 + width: 100%; 77 + border: 1px solid; 78 + border-radius: 1rem; 79 + min-height: 5em; 80 +} 81 + 82 +.viewFileModal-downloadLink { 83 + margin-left: 2px; 84 +} 85 + 86 +.viewFileName { 87 + overflow-wrap: break-word; 88 +} 89 + 90 +.image-container { 91 + position: relative; 92 +} 93 + 94 +.viewfile-thumbnail-image { 95 + display: block; 96 +} 97 + 98 +.overlay { 99 + position: absolute; 100 + inset: 0px; 101 + opacity: 0; 102 + transition: .7s ease; 103 + background-color: $theme.buttonPrimaryBackgroundColor; 104 +} 105 + 106 +.image-container:hover .overlay { 107 + opacity: 1; 108 +} 109 + 110 +.overlay-text { 111 + color: $theme.buttonPrimaryTextColor; 112 + font-size: 2rem; 113 + position: absolute; 114 + top: 50%; 115 + left: 50%; 116 + transform: translate(-50%, -50%); 117 + text-align: center; 118 +} 119 + 120 +.viewfile-header-button { 121 + margin: 1rem 0.7rem; 122 +} 123 + 124 +.viewfile-actions-header { 125 + margin: 1rem; 126 +} - Benutze diese Erweiterung
-
... ... @@ -1,0 +1,1 @@ 1 +onDemand - Name
-
... ... @@ -1,0 +1,1 @@ 1 +viewFileCSS - Inhalt parsen
-
... ... @@ -1,0 +1,1 @@ 1 +Ja - Content Type
-
... ... @@ -1,0 +1,1 @@ 1 +CSS