Wiki source code of Attachments

Last modified by Manuel Leduc on 2026/06/01 14:37

Show last authors
1 {{velocity output="false"}}
2 #set ($translationPrefix = 'xe.attachmentSelector')
3
4 #if ($request.xaction == 'postUpload')
5 #set ($targetDocument = $xwiki.getDocument($request.get('docname')))
6 #set ($targetAttachDocument = $xwiki.getDocument($request.get('targetdocname')))
7
8 #set ($fieldname = $request.get('fieldname'))
9 #set ($comment = $services.localization.render("${translationPrefix}.postUpload.comment", [$fieldname]))
10 #set ($docAction = $request.get('docAction'))
11 #set ($attachmentList = $targetAttachDocument.getAttachmentList())
12 #if ($attachmentList && $attachmentList.size() > 0)
13 #set ($sortedAttachments = $collectiontool.sort($attachmentList, 'date:desc'))
14 #set ($lastAttachment = $sortedAttachments.get(0))
15 #end
16 $response.sendRedirect($targetDocument.getURL($docAction, $escapetool.url({
17 $fieldname: $lastAttachment.filename,
18 'comment': $comment,
19 'form_token': $request.form_token
20 })))
21 #stop
22 #end
23 {{/velocity}}
24
25 {{velocity output="false"}}
26 ##
27 ## Macros
28 ##
29 #set ($attachmentPickerDocName = 'XWiki.AttachmentSelector')
30
31 $xwiki.ssx.use($attachmentPickerDocName)
32 $xwiki.jsx.use($attachmentPickerDocName)
33
34 #**
35 * Displays the attachment gallery as a list of attachment boxes, starting with special boxes for uploading a new attachment and for setting a default value.
36 *
37 * @param $targetDocument the document to recieve the field value being modified
38 * @param $targetAttachDocument the document to list/save attachments to
39 * @param $options generic picker options
40 *#
41 #macro (_attachmentPicker_displayAttachmentGallery $targetDocument, $targetAttachDocument, $options)
42 #set ($currentValue = $targetDocument.getValue($options.property))
43 #if ("$!{targetAttachDocument.getAttachment($currentValue)}" == '')
44 #set ($currentValue = "$!{options.defaultValue}")
45 #end
46 (% class="gallery" %)(((
47 ## Only display the upload form if they have edit permission on targetAttachDocument
48 #_attachmentPicker_displayUploadForm($targetDocument, $targetAttachDocument, $options)
49 #_attachmentPicker_displayAttachmentGalleryEmptyValue($targetDocument, $targetAttachDocument, $options, $currentValue)
50 #if ("$!services.temporaryAttachments" != '')
51 #set ($unsortedAttachments = $services.temporaryAttachments.listAllAttachments($targetAttachDocument))
52 #set ($sortedAttachments = $collectiontool.sort($unsortedAttachments, "${options.sortAttachmentsBy}"))
53 #else
54 #set ($sortedAttachments = $collectiontool.sort($targetAttachDocument.getAttachmentList(), "${options.sortAttachmentsBy}") )
55 #end
56 #foreach ($attachment in $sortedAttachments)
57 #set ($extension = $attachment.getFilename())
58 #set ($extension = $extension.substring($mathtool.add($extension.lastIndexOf('.'), 1)).toLowerCase())
59 #if ($options.filter.size() == 0 || $options.filter.contains($extension))
60 #_attachmentPicker_displayAttachmentBox($attachment $targetDocument $targetAttachDocument, $options $currentValue)
61 #end
62 #end
63 )))
64 #end
65
66 #**
67 * Displays an attachment box.
68 *
69 * @param $attachment the target attachment to display
70 * @param $targetDocument the document being modified
71 * @param $options generic picker options
72 * @param $currentValue the currently selected file, used for determining if the box should be highlighted as the current value
73 *#
74 #macro (_attachmentPicker_displayAttachmentBox $attachment $targetDocument $targetAttachDocument, $options $currentValue)
75 #set ($hasTemporaryAttachment = "$!services.temporaryAttachments" != '')
76 #set ($canEdit = $xwiki.hasAccessLevel('edit', $xcontext.user, ${targetAttachDocument.fullName}))
77 #set ($isTemporaryAttachment = false)
78 #if(!$hasTemporaryAttachment)
79 #set ($canDeleteAttachment = $canEdit)
80 #else
81 #set ($isTemporaryAttachment = $services.temporaryAttachments.temporaryAttachmentExists($attachment))
82 ## TODO: Update once it is made possible to delete temporary attachments (see XWIKI-20225).
83 #set ($canDeleteAttachment = !$isTemporaryAttachment && $canEdit)
84 #end
85 #set ($cssClasses = [])
86 #if ($options.displayImage && $attachment.isImage())
87 #set ($discard = $cssClasses.add('gallery_image'))
88 #end
89 #if ($isTemporaryAttachment)
90 #set ($discard = $cssClasses.add('temporary_attachment'))
91 #end
92 #_attachmentPicker_displayStartFrame({'value' : $attachment.filename, 'text' : $attachment.filename, 'cssClass' : "${stringtool.join($cssClasses, ' ')}"} $currentValue)
93 #_attachmentPicker_displayAttachmentDetails($attachment $options)
94 #set ($returnURL = $escapetool.url($doc.getURL('view', $request.queryString)))
95 #set ($deleteURL = $targetAttachDocument.getAttachmentURL($attachment.filename, 'delattachment', "xredirect=${returnURL}&form_token=$!{services.csrf.getToken()}") )
96 #set ($viewURL = $targetAttachDocument.getAttachmentURL($attachment.filename) )##{'name' : 'download', 'url' : $viewURL, 'rel' : '__blank'}
97 #set ($selectURL = $targetDocument.getURL(${options.get('docAction')}, $escapetool.url({
98 "${options.get('classname')}_${options.get('object')}_${options.get('property')}": ${attachment.filename},
99 'form_token': $!{services.csrf.getToken()}
100 })))
101 ## Delete action is only proposed for users with the edit right on the document.
102 ## If the temporary attachment is available, the delete action is only allowed for non-temporary attachments.
103 #set ($attachmentActions = [{'name' : 'select', 'url' : $selectURL, 'icon' : 'check', 'extraCssClass' : 'btn btn-xs btn-success'}])
104 #if($canDeleteAttachment)
105 #set ($discard = $attachmentActions.add({'name' : 'delete', 'url' : $deleteURL, 'icon' : 'cross', 'extraCssClass' : 'btn btn-xs btn-danger'}))
106 #end
107 #define($additionalContent)
108 #if ($isTemporaryAttachment)
109 #set ($titleMessage = $services.localization.render('attachment.attachmentSelector.attachmentBox.temporaryAttachmentTitle'))
110 #set ($titleMessage = $services.rendering.escape($titleMessage, 'xwiki/2.1'))
111 (% title="$titleMessage" %)$services.icon.render('clock')(%%)
112 #end
113 #end
114 #_attachmentPicker_displayEndFrame ($attachmentActions $additionalContent)
115 #end
116
117 #**
118 * Writes the wiki code used at the start of an attachment box. Outputs the attachment title bar, and opens the inner frame div.
119 *
120 * @param $boxOptions a map of parameters/options for the current attachment, holding, for example, the attachment name (boxOptions.value),
121 * the title to display (boxOptions.text), optional extra CSS classnames to put on the box (boxOptions.cssClass)
122 * @param $currentValue the currently selected file, used for determining if this attachment should be highlighted as the current value
123 *#
124 #macro (_attachmentPicker_displayStartFrame $boxOptions $currentValue)
125 (% class="gallery_attachmentbox $!{boxOptions.cssClass} #if ("$!{boxOptions.value}" == $currentValue) current#{end}" %)(((
126 (% class="gallery_attachmenttitle" title="$services.rendering.escape($!{boxOptions.value}, 'xwiki/2.1')" %)(((
127 #if($!{boxOptions.cssClass} == 'gallery_upload')$services.icon.render('add') #end##
128 $services.rendering.escape($boxOptions.text, 'xwiki/2.1')
129 )))
130 (% class="gallery_attachmentframe" %)(((
131 #end
132
133 #**
134 * Displays details about an attachment inside the attachment box. If the attachment is an image and the "displayImage" option is on,
135 * then the image is displayed. Otherwise, some basic information is displayed: the version, the size, the date and the author.
136 *
137 * @param $attachment the target attachment to display
138 * @param $options generic picker options
139 *#
140 #macro (_attachmentPicker_displayAttachmentDetails $attachment $options)
141 #if ($attachment)
142 ## Compute the attachment reference because there's no getter.
143 #set ($attachmentReference = $services.model.createAttachmentReference($attachment.document.documentReference,
144 $attachment.filename))
145 #set ($attachmentStringReference = $services.rendering.escape($services.model.serialize($attachmentReference, 'default'), 'xwiki/2.1'))
146 #if ($attachment.isImage() && $options.displayImage)
147 ## We add the version to the query string in order to invalidate the cache when an image attachment is replaced.
148 #set ($queryString = $escapetool.url({'version': $attachment.version}))
149 [[[[image:${attachmentStringReference}||width=180 queryString="$queryString"]]>>attach:$attachmentStringReference]]
150 #else
151 * (% class="mime" %){{html wiki=false clean=false}}#mimetypeimg($attachment.getMimeType().toLowerCase() $attachment.getFilename().toLowerCase()){{/html}}(%%) (% class="filename" %)$services.rendering.escape($attachment.getFilename(), 'xwiki/2.1')(% %)
152 * v$attachment.getVersion() (#dynamicsize($attachment.longSize))
153 * $services.localization.render('core.viewers.attachments.author', [$!{xwiki.getUserName($attachment.author, false)}]) $services.localization.render('core.viewers.attachments.date', [$!{xwiki.formatDate($attachment.date, 'dd/MM/yyyy hh:mm')}])
154 * (% class="buttonwrapper" %)[[${services.localization.render("${translationPrefix}.actions.download")}>>attach:${attachmentStringReference}||title="$services.localization.render("${translationPrefix}.actions.download")" rel="__blank" class="button"]](%%)
155 #end
156 #end
157 #end
158
159 #**
160 * Writes the wiki code used at the end of an attachment box. Closes the inner frame div, and outputs the attachment actions.
161 *
162 * @param $actions a list of maps defining action buttons, where each entry contains a subset of the following:
163 * <dl>
164 * <dt>name</dt>
165 * <dd>identifies the action; the name is used as a CSS classname, and in the translation key for the display text, as "xe.attachmentSelector.actions.<name>"</dd>
166 * <dt>url</dt>
167 * <dd>the destination of the button</dd>
168 * <dt>rel</dt>
169 * <dd>an optional parameter to be used in the "rel" HTML attribute; for example "__blank" can be used to open the link in a new tab/window</dd>
170 * </dl>
171 * @param $additionalContent optional additional content that does not follow the structure of the actions
172 *#
173 #macro (_attachmentPicker_displayEndFrame $actions $additionalContent)
174 )))## attachmentframe
175 (% class="gallery_actions" %)(((
176 #foreach ($action in $actions)
177 #set( $actionname = $services.localization.render("${translationPrefix}.actions.${action.name}") )
178 [[${services.icon.render($action.icon)}(% class="sr-only"%)${actionname}(%%)>>##
179 path:${action.url}||class="tool ${action.name} $!{action.extraCssClass}"##
180 title="${actionname}" #if($action.rel) rel="${action.rel}"#end]]##
181 #end
182 $!additionalContent
183 )))## actions
184 )))## attachmentbox
185 #end
186
187 #**
188 * Displays the upload box used for adding and selecting a new attachment.
189 *
190 * @param $targetDocument the document with the property being modified
191 * @param $targetAttachDocument the document to upload the attachment to
192 * @param $options generic picker options
193 *#
194 #macro (_attachmentPicker_displayUploadForm $targetDocument, $targetAttachDocument, $options)
195 #_attachmentPicker_displayStartFrame({
196 'value' : $services.localization.render("${translationPrefix}.upload.title"),
197 'text' : $services.localization.render("${translationPrefix}.upload.title"),
198 'cssClass' : 'gallery_upload'
199 } $NULL)
200 {{html clean="false"}}
201 <form action="$targetAttachDocument.getURL('upload')" enctype="multipart/form-data" method="post" id="uploadAttachment" class="uploadAttachment xform">
202 <div class="gallery_upload_input">
203 #if (${options.rawfilter} != '')
204 <span class="xHint">$escapetool.xml($services.localization.render("${translationPrefix}.upload.hint", [${options.rawfilter}]))</span>
205 #end
206 <input type="file" name="filepath" id="attachfile" class="noitems" title="$!{escapetool.xml($options.rawfilter)}"/>
207 <input type="hidden" name="xredirect" value="$xwiki.getDocument($attachmentPickerDocName).getURL('get', "xaction=postUpload&amp;docAction=$!{escapetool.url($options.get('docAction'))}&amp;targetdocname=$!{escapetool.url($targetAttachDocument.fullName)}&amp;docname=$!{escapetool.url($targetDocument.fullName)}&amp;fieldname=$!{escapetool.url($options.get('classname'))}_$!{escapetool.url($options.get('object'))}_$!{escapetool.url($options.get('property'))}&amp;form_token=$!{services.csrf.getToken()}")" />
208 <input type="hidden" name="docname" value="$!{escapetool.xml($targetDocument.fullName)}" />
209 <input type="hidden" name="classname" value="$!{escapetool.xml($options.get('classname'))}" />
210 <input type="hidden" name="object" value="$!{escapetool.xml($options.get('object'))}" />
211 <input type="hidden" name="property" value="$!{escapetool.xml($options.get('property'))}" />
212 <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
213 </div>
214 #if ("$!currentValue" != '' && $currentValue != $options.defaultValue)
215 <div>
216 <label>
217 <input type="checkbox" name="filename" value="$!escapetool.xml($currentValue)"
218 />$services.localization.render('attachmentSelector.replace',
219 ["<strong>$!escapetool.xml($currentValue)</strong>"])
220 </label>
221 <span class="xHint">$escapetool.xml($services.localization.render('attachmentSelector.replace.hint'))</span>
222 </div>
223 #end
224 #if ($xwiki.hasEditComment() && $options.versionSummary)
225 <div>
226 #if ($xwiki.isEditCommentFieldHidden())
227 <input type="hidden" name="comment" value="$!escapetool.xml($request.comment)" />
228 #else
229 <label for="commentinput">$services.localization.render('core.comment')</label>
230 <input type="text" name="comment" id="commentinput" value="$!escapetool.xml($request.comment)"
231 title="$services.localization.render('core.comment.tooltip')" />
232 #end
233 </div>
234 #end
235 <div class="buttons">
236 <span class="buttonwrapper">
237 <input type="submit" name="action_upload" class="button " value='$services.localization.render("${translationPrefix}.upload.submit")' title='$services.localization.render("${translationPrefix}.upload.submit")'/>
238 </span>
239 </div>
240 </form>
241 {{/html}}
242 #_attachmentPicker_displayEndFrame ([])
243 #end
244
245 #**
246 * Displays the "empty value" box, used for unsetting the field value.
247 *
248 * @param $targetDocument the document being modified
249 * @param $targetAttachDocument the document that the attachments will the loaded from/saved to
250 * @param $options generic picker options
251 * @param $currentValue the currently selected file, used for determining if the empty box should be highlighted as the current value
252 *#
253 #macro (_attachmentPicker_displayAttachmentGalleryEmptyValue $targetDocument, $targetAttachDocument, $options, $currentValue)
254 #if ("$!{options.get('defaultValue')}" != '')
255 #set ($reference = ${options.get('defaultValue')})
256 #set ($docNameLimit = $reference.indexOf('@'))
257 #if ($docNameLimit > 0)
258 #set ($docName = $reference.substring(0, $docNameLimit))
259 #else
260 #set ($docName = $targetAttachDocument.fullName)
261 #end
262 #set ($attachmentName = $reference.substring($mathtool.add($docNameLimit, 1)))
263 #set ($defaultAttachment = $xwiki.getDocument($docName).getAttachment($attachmentName))
264 #if ($defaultAttachment.isImage())
265 #set($dcssClass = 'gallery_image')
266 #end
267 #end
268 #_attachmentPicker_displayStartFrame({'cssClass' : "gallery_emptyChoice $!{dcssClass}", 'text' : $services.localization.render("${translationPrefix}.default"), 'value' : "${options.defaultValue}"} $currentValue)
269 #_attachmentPicker_displayAttachmentDetails($defaultAttachment $options)
270 #set ($returnURL = $escapetool.url($doc.getURL('view', $request.queryString)))
271 #set ($selectURL = $targetDocument.getURL(${options.get('docAction')}, "${options.get('classname')}_${options.get('object')}_${options.get('property')}=&form_token=$!{services.csrf.getToken()}"))
272 #_attachmentPicker_displayEndFrame ([{'name' : 'select', 'url' : $selectURL, 'icon' : 'check', 'extraCssClass' : 'btn btn-xs btn-success'}])
273 #end
274 {{/velocity}}
275
276 {{velocity}}
277 #if ($request.docname)
278 #set ($targetDocument = $xwiki.getDocument($request.docname))
279 #if ($request.targetdocname)
280 ## Use the target document if it exists.
281 #set ($targetAttachDocument = $xwiki.getDocument($request.targetdocname))
282 #else
283 ## Otherwise, just use the current document as the target to save/load attachments
284 #set ($targetAttachDocument = $targetDocument)
285 #end
286 #if ("$!{request.savemode}" == 'direct')
287 #set($docAction = 'save')
288 #else
289 #set($docAction = $targetAttachDocument.getDefaultEditMode())
290 #end
291 #set ($filter = [])
292 #set ($rawfilter = '')
293 #if ("$!{request.filter}" != '')
294 #foreach ($value in $request.filter.trim().split('\s*+[,|; ]\s*+'))
295 #if ("$!value" != '')
296 #set ($discard = $filter.add($value.toLowerCase()))
297 #set ($rawfilter = "${rawfilter}, ${value}")
298 #end
299 #end
300 #if ($rawfilter != '')
301 #set ($rawfilter = $rawfilter.substring(2))
302 #end
303 #end
304 #if ("$!{request.displayImage}" == 'true')
305 #set ($displayImage = true)
306 #else
307 #set ($displayImage = false)
308 #end
309 ### Determine attachment sorting
310 #set($sortAttachmentsBy = "$!{request.sortAttachmentsBy}")
311 #set ($validAttachmentProperties = ['filename', 'date', 'filesize', 'author', 'version', 'mimeType'])
312 #if($sortAttachmentsBy == '' || $validAttachmentProperties.indexOf($sortAttachmentsBy) == -1)
313 ### Default to sorting by filename, sort not requested.
314 #set($sortAttachmentsBy = "filename")
315 #end
316 ### Set attachment sorting direction
317 #if($sortAttachmentsBy == 'date')
318 ### Sort the date descending
319 #set($sortAttachmentsBy = "date:desc")
320 #else
321 ### Sort everything else ascending
322 #set($sortAttachmentsBy = "${sortAttachmentsBy}:asc")
323 #end
324 #set ($options = {
325 'classname' : ${request.get('classname')},
326 'object' : $!{numbertool.toNumber($request.object).intValue()},
327 'property' : ${request.property},
328 'displayImage' : ${displayImage},
329 'docAction' : ${docAction},
330 'defaultValue' : "$!{request.defaultValue}",
331 'rawfilter': "$!{rawfilter}",
332 'filter': ${filter},
333 'sortAttachmentsBy': ${sortAttachmentsBy},
334 'versionSummary': $request.versionSummary.equals('true')
335 })
336 $!targetDocument.use($targetDocument.getObject($options.classname, $options.object))##
337 #_attachmentPicker_displayAttachmentGallery($targetDocument, $targetAttachDocument, $options)
338
339 #set ($cancelLinkName = $services.rendering.escape($services.rendering.escape($services.localization.render("${translationPrefix}.cancel"), 'xwiki/2.1'), 'xwiki/2.1'))
340 #set ($cancelLinkTarget = $services.rendering.escape($services.model.serialize($targetDocument), 'xwiki/2.1'))
341 (% class="gallery_buttons buttons" %)(((
342 (% class="buttonwrapper secondary" %)[[$cancelLinkName>>$cancelLinkTarget||class="button secondary" id="attachment-picker-close"]]
343 )))
344 #end
345 {{/velocity}}
France 2030 Logo

This project is being financed by the French State as part of the France 2030 program

Ce projet est financé par l’État Français dans le cadre de France 2030

  • Powered by XWiki 17.10.9-node2. Hosted and managed by XWiki SAS

Get Connected