Änderungen von Dokument Attachments
Zuletzt geändert von xwikiadmin am 2025/05/21 09:22
Von Version 2.1
bearbeitet von xwikiadmin
am 2023/10/26 09:30
am 2023/10/26 09:30
Änderungskommentar:
Migrated property [type] from class [XWiki.WikiMacroParameterClass]
Auf Version 3.1
bearbeitet von xwikiadmin
am 2023/10/26 10:36
am 2023/10/26 10:36
Änderungskommentar:
Install extension [com.xwiki.pro:xwiki-pro-macros/1.12]
Zusammenfassung
-
Seiteneigenschaften (1 geändert, 0 hinzugefügt, 0 gelöscht)
-
Objekte (3 geändert, 1 hinzugefügt, 0 gelöscht)
Details
- Seiteneigenschaften
-
- Inhalt
-
... ... @@ -10,17 +10,17 @@ 10 10 = Parameters = 11 11 12 12 |=Parameter|=Description|=Required|=Default 13 -|**patterns**|Comma-separated list of regular expressions, used to filter attachments by name.|No| 13 +|**patterns**|Comma-separated list of regular expressions, used to filter attachments by name.|No| 14 14 |**sortBy**|Sort attachments by {{{ date }}}, {{{ size }}} or {{{ name }}}|No|{{{ date }}} 15 15 |**sortOrder**|Sort attachments in {{{ ascending }}} or {{{ descending }}} order|No|{{{ ascending }}} 16 16 |**upload**|Allow users to attach new files|No|{{{ true }}} 17 -|**page**|Pages containing the attachments to display. Current page if empty.|No| 17 +|**page**|Pages containing the attachments to display. Current page if empty.|No| 18 18 19 19 = Example Usage = 20 20 21 21 22 22 {{code}} 23 -{{attachments 23 +{{confluence_attachments 24 24 patterns=".*png" 25 25 sortBy="name" 26 26 /}}
- XWiki.JavaScriptExtension[0]
-
- Code
-
... ... @@ -1,8 +1,95 @@ 1 -require(['jquery', 'xwiki-events-bridge'], function ($) { 2 - $('#xwikiuploadfile').on('xwiki:html5upload:message', function (event, data) { 3 - if (data.content === 'UPLOAD_FINISHED' && data.type === 'done') { 4 - location.reload(); 1 +define('xwiki-confluence-attachments-messages', { 2 + prefix: 'core.viewers.attachments.delete.', 3 + keys: [ 4 + 'inProgress', 5 + 'done', 6 + 'error' 7 + ] 8 +}); 9 + 10 +require(['jquery', 'xwiki-l10n!xwiki-confluence-attachments-messages'], function($, l10n) { 11 + var enhanceUploadInputs = function(liveDataElems) { 12 + $.each(liveDataElems.find('input[type=file]'), function() { 13 + // Since the attachments liveData is refreshed on file upload, there is no need for a response container. 14 + new XWiki.FileUploader(this, { 15 + 'progressAutohide': true, 16 + 'responseContainer' : document.createElement('div'), 17 + 'maxFilesize' : parseInt(this.readAttribute('data-max-file-size')) 18 + }); 19 + }) 20 + }; 21 + 22 + $(function() { 23 + enhanceUploadInputs($('.confluenceAttachmentsMacro')); 24 + }) 25 + 26 + $(document).on('xwiki:dom:updated', function(e, data) { 27 + let liveDataElems = $(data.elements).find('.confluenceAttachmentsMacro'); 28 + enhanceUploadInputs(liveDataElems); 29 + }); 30 + 31 + $(document).on('xwiki:html5upload:done', function(e) { 32 + if ($(e.target).prop('id').startsWith('confluenceAttachments')) { 33 + // Select the livedata above the upload form. 34 + const uploadForm = $(e.target).closest('form'); 35 + // The 'xwiki-livedata' CSS class is present only before XWiki version 14.4. 36 + let associatedLivedata = uploadForm.prevAll('.liveData, .xwiki-livedata').first(); 37 + // We do not have access to a liveData object before XWiki 14.4. 38 + if (associatedLivedata.data('liveData') === undefined) { 39 + location.reload(); 40 + } else { 41 + associatedLivedata.data('liveData').updateEntries(); 42 + } 5 5 } 6 6 }); 45 + 46 + // 47 + // The delete action methods are similar to the ones from the attachments tab, but couldn't be reused. The problem is 48 + // that when the tab is not opened we cannot just load the attachments.js file, since it expects some elements to be 49 + // already loaded and it produces errors. 50 + // 51 + 52 + /** 53 + * Open the delete confirmation modal on liveData attachment delete button. 54 + */ 55 + $(document).on('click', '.confluenceAttachmentsMacro .attachmentActions .actiondelete', function(e) { 56 + e.preventDefault(); 57 + // The 'xwiki-livedata' CSS class is present only before XWiki version 14.4. 58 + let liveData = $(e.currentTarget).closest('.liveData, .xwiki-livedata'); 59 + var modal = liveData.nextAll('.deleteConfluenceAttachment'); 60 + modal.data('relatedTarget', e.currentTarget); 61 + modal.modal('show'); 62 + }); 63 + 64 + /** 65 + * On delete confirmation, delete attachment and refresh liveData. 66 + */ 67 + $(document).on('click', '.confluenceAttachmentsMacro .deleteConfluenceAttachment input.btn-danger', function(e) { 68 + e.preventDefault(); 69 + var modal = $(e.currentTarget).closest('.deleteConfluenceAttachment'); 70 + var button = $(modal.data('relatedTarget')); 71 + // The 'xwiki-livedata' CSS class is present only before XWiki version 14.4. 72 + let liveData = button.closest('.liveData, .xwiki-livedata'); 73 + var notification; 74 + 75 + $.ajax({ 76 + url : button.prop('href'), 77 + beforeSend : function() { 78 + notification = new XWiki.widgets.Notification(l10n['inProgress'], 'inprogress'); 79 + }, 80 + success : function() { 81 + // We do not have access to a liveData object before XWiki 14.4. 82 + if (liveData.data('liveData') !== undefined) { 83 + liveData.data('liveData').updateEntries(); 84 + notification.replace(new XWiki.widgets.Notification(l10n['done'], 'done')); 85 + } else { 86 + location.reload(); 87 + } 88 + }, 89 + error: function() { 90 + notification.replace(new XWiki.widgets.Notification(l10n['failed'], 'error')); 91 + } 92 + }); 93 + }); 7 7 }); 8 8 - Name
-
... ... @@ -1,0 +1,1 @@ 1 +Confluence Attachments Macro
- XWiki.StyleSheetExtension[0]
-
- Code
-
... ... @@ -1,15 +1,15 @@ 1 -.confluence-attachment-macro { 2 - .upload-response { 3 - display: none; 4 - } 5 - 6 - #AddAttachment { 7 - margin-top: 15px; 8 - 9 - #attachform { 10 - .buttonwrapper { 11 - display: none; 12 - } 13 - } 14 - } 1 +.confluenceAttachmentsMacro legend { 2 + font-size: 1em; 15 15 } 4 + /* These should be deleted after upgrading to a XWiki parent >= 14.6. */ 5 +.attachmentMimeType { 6 + color: $theme.textSecondaryColor; 7 + font-size: 32px; 8 + height: 32px; 9 + line-height: 32px; 10 + text-align: center; 11 +} 12 +.attachmentMimeType img { 13 + max-height: 32px; 14 + max-width: 48px; 15 +} - Content Type
-
... ... @@ -1,1 +1,1 @@ 1 - LESS1 +CSS - Name
-
... ... @@ -1,0 +1,1 @@ 1 +Confluence Attachments Macro
- XWiki.WikiMacroClass[0]
-
- Makro-Code
-
... ... @@ -1,48 +1,6 @@ 1 1 {{velocity output="false"}} 2 -#macro (filterAndSortAttachments $attachmentsFiltered $invalidSortBy $invalidSortOrder) 3 - $xwiki.ssx.use('Confluence.Macros.Attachments') 4 - $xwiki.jsx.use('Confluence.Macros.Attachments') 5 - #if ("$!{request.forceTestRights}" == "1")#template("xwikivars.vm")#end 6 - #template('attachment_macros.vm') 7 - ## Get attachments 8 - #if ("$!wikimacro.parameters.tags" != '') 9 - #set ($tags = $!wikimacro.parameters.tags.split(',')) 10 - #set ($pageReferenceSet = $collectiontool.getSet()) 11 - #foreach ($tag in $tags) 12 - #set ($references = $xwiki.tag.getDocumentsWithTag("$tag")) 13 - #set ($discard = $pageReferenceSet.addAll($references)) 14 - #end 15 - #set ($attachments = []) 16 - #foreach ($pageReference in $pageReferenceSet) 17 - #set ($document = $xwiki.getDocument($pageReference)) 18 - #set ($documentAttachments = $document.attachmentList) 19 - #set ($discard = $attachments.addAll($documentAttachments)) 20 - #end 21 - #else 22 - #set ($attachments = $doc.attachmentList) 23 - #if ("$!wikimacro.parameters.page" != '') 24 - #set ($document = $xwiki.getDocument("$!wikimacro.parameters.page")) 25 - #set ($attachments = $document.attachmentList) 26 - #end 27 - #end 28 - ## Filter attachments 29 - #set ($confluencePatterns = "$!wikimacro.parameters.patterns") 30 - #if ($confluencePatterns == '') 31 - #set($attachmentsFiltered = $attachments) 32 - #else 33 - #set ($patterns = $!confluencePatterns.split(',')) 34 - #set($attachmentsFiltered = []) 35 - #foreach ($attachment in $attachments) 36 - #foreach ($pattern in $patterns) 37 - #set ($matches = $attachment.filename.matches("(?i)$!pattern")) 38 - #if ($matches) 39 - #set ($discard = $attachmentsFiltered.add($attachment)) 40 - #break 41 - #end 42 - #end 43 - #end 44 - #end 45 - ## Sort attachments 2 +#macro (getLiveDataSort $return) 3 + ##property 46 46 #set ($confluenceSortBy = "$!wikimacro.parameters.sortBy") 47 47 #set ($invalidSortBy = false) 48 48 #if ($confluenceSortBy == 'date') ... ... @@ -56,6 +56,7 @@ 56 56 #else 57 57 #set ($invalidSortBy = true) 58 58 #end 17 + ## order 59 59 #set ($confluenceSortOrder = "$!wikimacro.parameters.sortOrder") 60 60 #set ($invalidSortOrder = false) 61 61 #set ($reverseSortOrderProperties = ['filesize', 'date']) ... ... @@ -72,101 +72,178 @@ 72 72 #set ($sortOrder = 'desc') 73 73 #end 74 74 #else 75 - #set ($invalidSortOrder = false)34 + #set ($invalidSortOrder = true) 76 76 #end 77 - #set ($attachmentsFiltered = $collectiontool.sort($attachmentsFiltered, "$sortBy:$sortOrder")) 36 + ## return 37 + #if ($invalidSortBy || $invalidSortOrder) 38 + #setVariable("$return", {}) 39 + #else 40 + #setVariable("$return", "$sortBy:$sortOrder") 41 + #end 78 78 #end 79 79 80 -#macro (executeMacro) 81 - #filterAndSortAttachments($attachmentsFiltered $invalidSortBy $invalidSortOrder) 44 +#macro (deleteConfluenceAttachmentModal) 45 + ## Copied from the attachments tab code. The original macro cannot be used, since it will interfere with some js 46 + ## specific to attachments tab. Precisely, if the attachments tab is already opened, everything is working correctly. 47 + ## If it is not opened, we need to load the attachments.js code anyway, to use it's listeners, but there are parts 48 + ## that rightfully assume that some elements are present on the page and errors will occur. 49 + <div class="modal fade deleteConfluenceAttachment" tabindex="-1" role="dialog"> 50 + <div class="modal-dialog"> 51 + <div class="modal-content"> 52 + <div class="modal-header"> 53 + <button type="button" class="close" data-dismiss="modal">×</button> 54 + <div class="modal-title">$services.localization.render('core.viewers.attachments.delete')</div> 55 + </div> 56 + <div class="modal-body"> 57 + <div>$services.localization.render('core.viewers.attachments.delete.confirm')</div> 58 + </div> 59 + <div class="modal-footer"> 60 + <input type="button" class="btn btn-danger" data-dismiss="modal" 61 + value="$escapetool.xml($services.localization.render('core.viewers.attachments.delete'))"> 62 + <input type="button" class="btn btn-default" data-dismiss="modal" 63 + value="$escapetool.xml($services.localization.render('cancel'))"> 64 + </div> 65 + </div> 66 + </div> 67 + </div> 68 +#end 69 + 70 +#set ($confluenceAttachmentMacroIndex = -1) 71 +## Code taken and adapted 72 +#macro (showConfluenceAttachments $document) 73 + #template('display_macros.vm') 74 + #set ($confluenceAttachmentMacroIndex = $confluenceAttachmentMacroIndex + 1) 75 + #set ($confluenceAttachmentMacroId = "confluenceAttachments$confluenceAttachmentMacroIndex") 76 + ## 77 + #showConfluenceAttachmentsLiveData($document $confluenceAttachmentMacroId) 78 +#end 79 + 80 +#macro (uploadFileForm $liveDataId) 81 + #set ($upload = "$!wikimacro.parameters.upload") 82 + #if ($upload == 'true' && ($hasEdit || $hasAdmin) && $xcontext.action == 'view') 83 + <form action="$attachmentsDoc.getURL("upload")" enctype="multipart/form-data" method="post"> 84 + <div> 85 + ## CSRF prevention 86 + <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /> 87 + <fieldset> 88 + <legend>$services.localization.render('promacros.attachments.upload.title')</legend> 89 + <div> 90 + #set ($inputID = "${liveDataId}-uploadFile") 91 + <label class="sr-only" for="${inputID}"> 92 + $services.localization.render('core.viewers.attachments.upload.file') 93 + </label> 94 + <input id="${inputID}" type="file" name="filepath" size="40" class="noitems" 95 + data-max-file-size="$!escapetool.xml($xwiki.getSpacePreference('upload_maxsize'))"/> 96 + </div> 97 + </fieldset> 98 + </div> 99 + </form> 100 + #end 101 +#end 102 + 103 +#macro (supportsAttachJSON $supportsAttachJSON) 104 + ## The attachments.json code uses macros from attachment_macro.vm, so the existence of the attachments livedata macro 105 + ## is an indicator that the template exists and is adapted for livedata. 106 + #template('attachment_macros.vm') 107 + #set ($macroResult = "#showAttachmentsLiveData($doc 'id')") 108 + #set ($supportsAttachJSON = $macroResult.startsWith('{{liveData')) 109 +#end 110 + 111 +## Display a liveData with attachments from the specified document. 112 +#macro (showConfluenceAttachmentsLiveData $attachmentsDoc $liveDataId) 113 + #set ($liveDataConfig = { 114 + 'meta': { 115 + 'propertyDescriptors': [ 116 + { 'id': 'mimeType', 'displayer': 'html'}, 117 + { 'id': 'filename', 'displayer': 'html' }, 118 + { 'id': 'filesize', 'displayer': 'html' }, 119 + { 'id': 'date', 'filter': 'date'}, 120 + { 'id': 'author', 'displayer': 'html' }, 121 + { 'id': 'actions', 'displayer': 'html' } 122 + ], 123 + 'entryDescriptor': { 124 + 'idProperty': 'id' 125 + } 126 + } 127 + }) 128 + 129 + #if ("$!wikimacro.parameters.patterns" != '') 130 + #set ($liveDataConfig.query = {}) 131 + #set ($liveDataConfig.query.filters = [ 132 + { 133 + "property": "filename", 134 + "matchAll": true, 135 + "constraints": [] 136 + } 137 + ]) 138 + #set ($filters = $stringtool.split($wikimacro.parameters.patterns, ',')) 139 + #foreach ($filter in $filters) 140 + #set ($discard = $liveDataConfig.query.filters[0].constraints.add( 141 + { "operator": "contains", "value": "$!filter.trim()" } 142 + )) 143 + #end 144 + #end 145 + #set ($sourceParams = { 146 + 'translationPrefix': 'core.viewers.attachments.livetable.', 147 + 'className': 'XWiki.AllAttachments', 148 + "\$doc": "$attachmentsDoc" 149 + }) 150 + ## Since the correct attachmentsjson.vm was added in XWiki 14.8, we use a copy of its code for backwards compatibility. 151 + #supportsAttachJSON($supportsAttachJSON) 152 + #if ($supportsAttachJSON) 153 + #set ($discard = $sourceParams.put('template', 'xpart.vm')) 154 + #set ($discard = $sourceParams.put('vm', 'attachmentsjson.vm')) 155 + #else 156 + #set ($discard = $sourceParams.put('resultPage', 'Confluence.Macros.AttachmentsJSON')) 157 + #end 158 + #getLiveDataSort($liveDataSort) 82 82 #if ($invalidSortBy) 83 - {{error}} 84 - Attachment macro error: sortBy parameter must be one of 'date', 'size', or 'name' 85 - {{/error}} 86 - #break 160 + {{warning}} 161 + Attachment macro: sortBy parameter must be one of 'date', 'size', or 'name' 162 + {{/warning}} 87 87 #end 88 88 #if ($invalidSortOrder) 89 - {{error}} 90 - Attachment macro error: sortOrder parameter must be one of 'ascending' or 'descending' 91 - {{/error}} 92 - #break 165 + {{warning}} 166 + Attachment macro: sortOrder parameter must be one of 'ascending' or 'descending' 167 + {{/warning}} 93 93 #end 94 - ## Display attachments 95 - {{html clean="false"}} 96 - <div class="confluence-attachment-macro"> 97 - #if ($attachmentsFiltered.size() > 0) 98 - #displayAttachments($attachmentsFiltered) 99 - #deleteAttachmentModal() 100 - #else 101 - <p class="noitems"> 102 - $!escapetool.xml($services.localization.render('core.viewers.attachments.noAttachments')) 103 - </p> 104 - #end 105 105 106 - ## Allow upload 107 - #set ($upload = "$!wikimacro.parameters.upload") 108 - #if ($upload == 'true' && ($hasEdit || $hasAdmin) && $xcontext.action == 'view') 109 - <form 110 - id="AddAttachment" 111 - action="$doc.getURL("upload")" 112 - method="post" 113 - enctype="multipart/form-data" 114 - > 115 - <div> 116 - ## CSRF prevention 117 - <input 118 - type="hidden" 119 - name="form_token" 120 - value="$!{services.csrf.getToken()}" 121 - /> 170 + {{liveData 171 + id="$liveDataId" 172 + properties="mimeType,filename,filesize,date,author,actions" 173 + source='liveTable' 174 + sourceParameters="$escapetool.url($sourceParams)" 175 + sort="$liveDataSort" 176 + limit=5 177 + }}$jsontool.serialize($liveDataConfig){{/liveData}} 122 122 123 - <fieldset id="attachform"> 124 - <legend> 125 - $!escapetool.xml($services.localization.render('core.viewers.attachments.upload.title')) 126 - </legend> 179 + {{html clean="false"}} 180 + #uploadFileForm($liveDataId) 181 + #deleteConfluenceAttachmentModal() 182 + {{/html}} 183 +#end 127 127 128 - <div class="fileupload-field"> 129 - <label 130 - class="hidden" 131 - for="xwikiuploadfile" 132 - > 133 - $!escapetool.xml($services.localization.render('core.viewers.attachments.upload.file')) 134 - </label> 135 135 136 - <input 137 - id="xwikiuploadfile" 138 - class="uploadFileInput noitems" 139 - type="file" 140 - name="filepath" 141 - size="40" 142 - data-max-file-size="$!escapetool.xml($xwiki.getSpacePreference('upload_maxsize'))" 143 - /> 144 - </div> 186 +#macro (executeMacro) 187 + #set ($discard = $xwiki.ssfx.use('js/xwiki/viewers/attachments.css', true)) 188 + #set ($discard = $xwiki.ssfx.use('uicomponents/widgets/upload.css', true)) 189 + #set ($discard = $xwiki.jsfx.use('uicomponents/widgets/upload.js', { 190 + 'forceSkinAction': true, 191 + 'language': $xcontext.locale 192 + })) 193 + #set ($discard = $xwiki.jsx.use("Confluence.Macros.Attachments")) 194 + #set ($discard = $xwiki.ssx.use("Confluence.Macros.Attachments")) 195 + #set ($document = $doc) 196 + #if ("$!wikimacro.parameters.page" != '') 197 + #set ($document = $xwiki.getDocument("$!wikimacro.parameters.page")) 198 + #end 145 145 146 - <div> 147 - <span class="buttonwrapper"> 148 - <input 149 - class="button btn btn-primary" 150 - type="submit" 151 - value="$!escapetool.xml($services.localization.render('core.viewers.attachments.upload.submit'))" 152 - /> 153 - </span> 154 - 155 - <span class="buttonwrapper"> 156 - <a 157 - class="cancel secondary button btn btn-primary" 158 - href="$doc.getURL()" 159 - > 160 - $!escapetool.xml($services.localization.render('core.viewers.attachments.upload.cancel')) 161 - </a> 162 - </span> 163 - </div> 164 - </fieldset> 165 - </div> 166 - </form> 167 - #end 168 - </div> ## .confluence-attachment-macro 200 + {{html clean="false" wiki="true"}} 201 + <div class='confluenceAttachmentsMacro'> 202 + #showConfluenceAttachments($document) 203 + </div> 169 169 {{/html}} 205 + 170 170 #end 171 171 {{/velocity}} 172 172 - Standardkategorie
-
... ... @@ -1,1 +1,0 @@ 1 -confluence
- XWiki.WikiMacroParameterClass[9]
-
- Parameter-Vorgabe
-
... ... @@ -1,0 +1,1 @@ 1 +true - Parameter-Beschreibung
-
... ... @@ -1,0 +1,1 @@ 1 +Display an option for uploading files to the selected page. - Parameter verpflichtend
-
... ... @@ -1,0 +1,1 @@ 1 +Nein - Parameter-Name
-
... ... @@ -1,0 +1,1 @@ 1 +upload - Parameter-Typ
-
... ... @@ -1,0 +1,1 @@ 1 +java.lang.Boolean