{{velocity}} {{html clean="false"}} {{/html}} {{/velocity}}

Wiki source code of Custom configurable sections

Last modified by superadmin on 2025/05/07 16:12

Show last authors
1 {{include reference="XWiki.ConfigurableClassMacros" /}}
2
3 {{velocity}}
4 #*
5 * This part takes the configuration from any documents containing XWiki.ConfigurableClass objects and creates a form
6 * for each. To includeForm this document, you may specify:
7 *
8 * $section - String - The section which we are administrating eg: "Registration", "Users", or "Import".
9 * If none is specified then it checks for a request parameter called "section" and uses that,
10 * if no parameter, then this code assumes that it is part of the admin icons sheet and adds icons
11 * for any section which is not in $sections, in that event, this code assumes it is being run
12 * inside of a <ul> block.
13 *
14 * $sections - List<String> - If section is not specified, any sections on this list will not have icons made for them,
15 * the assumption being that the icons are already there. If section is specified then this
16 * is not taken into account and may safely be undefined.
17 *
18 * $currentDoc - String (document.fullName) - The administration document, users who don't have permission to edit
19 * it will not be able to include applications (possibly injecting
20 * arbitrary code.) if none specified then $doc.getFullName() is used.
21 *
22 * $globaladmin - boolean - If set true then we will assume we are administrationg the entire wiki.
23 * If not set then we look for a request parameter called "editor" if that exists and equals
24 * "globaladmin" then $globaladmin is true, if it doesn't exist then we check to see if
25 * $currentDoc.getFullName() equals "XWiki.XWikiPreferences".
26 *###
27 ##
28 ## Form submission depends on this.
29 $xwiki.jsfx.use('js/xwiki/actionbuttons/actionButtons.js', true)
30 ## In case of conflict issue we want to display the diff properly
31 #set ($discard = $xwiki.ssfx.use('uicomponents/viewers/diff.css', true))
32 #set ($discard = $xwiki.jsfx.use('uicomponents/viewers/diff.js'))
33 ##
34 #if(!$section)
35 #set($section = $request.getParameter('section'))
36 #end
37 #if(!$currentDoc)
38 #set($currentDoc = $doc.getFullName())
39 #end
40 ## Get value of $globaladmin if not specified.
41 #if("$!globaladmin" == '')
42 #if($editor != 'globaladmin'
43 && $request.getParameter('editor') != 'globaladmin'
44 && $currentDoc != 'XWiki.XWikiPreferences')
45 ##
46 #set($globaladmin = false)
47 #else
48 #set($globaladmin = true)
49 #end
50 #end
51 #set($currentSpace = $xwiki.getDocument($currentDoc).getSpace())
52 ##
53 ##------------------------------------------------------------------------------------------------------------
54 ## If $section exists then we are viewing the admin page for a particular section.
55 ## eg: 'Registration', 'Presentation', 'Import' etc.
56 ##------------------------------------------------------------------------------------------------------------
57 ##
58 #if($section && $section != '')
59 ##
60 ## This is for keeping track of whether we have shown the heading yet or not.
61 ## If the heading doesn't need to be shown, but an error occurs in processing, then we show the heading
62 ## so that the user knows what the error relates to.
63 #set($headingShowing = false)
64 ##
65 ## Searches the database for names of apps to be configured
66 #set($outputList = [])
67 #findNamesOfAppsToConfigure($section, $globaladmin, $xwiki.getDocument($currentDoc).getSpace(), $outputList)
68 ##
69 #foreach($appName in $outputList)
70 ##
71 ## Make sure the current user has permission to edit the configurable application.
72 ## Unless we are in the page administration which is never about modifying the application configuration page
73 #set($userHasAccessToDocument = $level == '.page' || $xcontext.hasAccessLevel('edit', $appName))
74 ##
75 ## If the document was not last saved by a user with edit privilege on this page
76 ## then we can't safely display the page but we should warn the viewer.
77 #if($userHasAccessToDocument)
78 ## Get the configurable application
79 #set($app = $xwiki.getDocument($appName))
80 ##
81 #set($documentSavedByAuthorizedUser = false)
82 #checkDocumentSavedByAuthorizedUser($app, $currentDoc, $documentSavedByAuthorizedUser)
83 #end
84 ##
85 ## There is no need to display a heading unless:
86 ## 1. There was already a section before this document.
87 ## 2. This is not the first document in this section.
88 ##
89 ## If we are displaying the heading and there is an error to be shown Javascript will not strip the heading.
90 #if(!$appName.equals($outputList.get(0)) || $sections.contains($section))
91 ## Create a document heading.
92 #showHeading($appName, $headingShowing)
93 #end
94 ##
95 #if(!$userHasAccessToDocument)
96 #showHeading($appName, $headingShowing)
97
98 {{error}}{{translation key="xe.admin.configurable.noPermissionThisApplication"/}}{{/error}}
99
100 #elseif(!$documentSavedByAuthorizedUser)
101 #showHeading($appName, $headingShowing)
102
103 {{error}}{{translation key="xe.admin.configurable.applicationAuthorNoAdmin" parameters="$app.Author"/}}{{/error}}
104
105 ##
106 ##------------------------------------------------------------------------------------------------------------
107 ## If the document is locked and not by the current user and forceEdit is not set true,
108 #elseif($app.getLocked() && $app.getLockingUser() != $xcontext.getUser() && !$request.getParameter('forceEdit'))
109 #set($requestURL = "$request.getRequestURL()")
110 #if($requestURL.indexOf('?') == -1)
111 #set($requestURL = "${requestURL}?$request.queryString")
112 #end
113 #showHeading($appName, $headingShowing)
114
115 {{error}}{{translation key="doclockedby"/}} $app.getLockingUser() [[{{translation key="forcelock"/}}>>${requestURL}&forceEdit=1]]{{/error}}
116
117 #else
118 ## If the document is not already locked, attempt to acquire the lock.
119 #if(!$app.getLocked())
120
121 {{html wiki=true}}
122 <noscript>
123
124 {{warning}}{{translation key="xe.admin.configurable.cannotLockNoJavascript"/}}{{/warning}}
125
126 </noscript>
127 {{/html}}
128
129 {{html clean=false}}
130 <script>
131 document.observe("xwiki:dom:loaded", function() {
132 XWiki.DocumentLock && new XWiki.DocumentLock('$escapetool.javascript($app.prefixedFullName)').lock();
133 });
134 </script>
135 {{/html}}
136 #end
137 ##------------------------------------------------------------------------------------------------------------
138 ## Done Locking.
139 ##
140 ## Get all objects of the "ConfigurableClass" from this document.
141 #set($allConfigurableObjs = $app.getObjects($nameOfThisDocument))
142 ## Separate out the objects which are for this section.
143 #set($configurableObjs = [])
144 #foreach($configurableObj in $allConfigurableObjs)
145 #if($app.getValue('displayInSection', $configurableObj) == $section)
146 #set($discard = $configurableObjs.add($configurableObj))
147 #end
148 #end
149 #if($configurableObjs.size() == 0)
150 ## Internal error, not translated.
151 #showHeading($appName, $headingShowing)
152
153 {{error}}Internal error: All objects were filtered out for application:
154 $services.rendering.escape($appName, 'xwiki/2.1').{{/error}}
155
156 #else
157 #set($formAction = $xwiki.getURL($app.getFullName(), 'save'))
158 #set($formId = "${section.toLowerCase()}_${app.getFullName()}")
159 #set($escapedAppName = $escapetool.xml($app.getFullName()))
160 #foreach($configurableObj in $configurableObjs)
161 #set ($heading = $app.getValue('heading', $configurableObj))
162 #set ($codeToExecute = "$!app.getValue('codeToExecute', $configurableObj)")
163 ## If linkPrefix is set, then we will make each property label a link which starts with that prefix.
164 #set ($linkPrefix = "$!app.getValue('linkPrefix', $configurableObj)")
165 #if (!$app.restricted)
166 #set ($evaluatedConfigurableObj = $configurableObj.evaluate())
167 #set ($heading = $evaluatedConfigurableObj.heading)
168 #set ($linkPrefix = $evaluatedConfigurableObj.linkPrefix)
169 #end
170 ## Display the header if one exists.
171 #if($heading && $heading != '')
172 == $services.rendering.escape($heading, 'xwiki/2.1') ==
173 #end
174 ## Display code to execute
175 #if ($codeToExecute != '')
176 (%class="codeToExecute"%)(((##
177 $configurableObj.display('codeToExecute', 'view', false)
178 )))
179 #end
180 ##
181 ## If propertiesToShow is set, then we will only show the properties contained therein.
182 #set($propertiesToShow = $app.getValue('propertiesToShow', $configurableObj))
183 #if(!$propertiesToShow || $propertiesToShow.getClass().getName().indexOf('List') == -1)
184 #set($propertiesToShow = [])
185 #end
186 ##
187 ## If the Configurable object specifies a configuration class, use it,
188 ## otherwise assume custom forms are used instead.
189 #set($configClassName = "$!app.getValue('configurationClass', $configurableObj)")
190 #if($configClassName != '')
191 #set($objClass = $xwiki.getDocument($configClassName).getxWikiClass())
192 #if(!$objClass || $objClass.getClass().getName().indexOf('.Class') == -1)
193 #showHeading($appName, $headingShowing)
194
195 {{error}}{{translation key="xe.admin.configurable.configurationClassNonexistant"/}}{{/error}}
196
197 #else
198 ## Use the first object from the document which is of the configuration class.
199 #set($obj = $app.getObject($objClass.getName()))
200 ##
201 #if(!$obj || $obj.getClass().getName().indexOf('.Object') == -1)
202 #showHeading($appName, $headingShowing)
203
204 {{error}}
205 #set($escapedObjClassName =
206 $services.rendering.escape($escapetool.java($objClass.getName()), 'xwiki/2.1'))
207 #set($translationEscapedAppName =
208 $services.rendering.escape($escapetool.java($app.getFullName()), 'xwiki/2.1'))
209 {{translation key="xe.admin.configurable.noObjectOfConfigurationClassFound"
210 parameters="~"$escapedObjClassName~", ~"$translationEscapedAppName~""/}}
211 {{/error}}
212
213 #else
214 ##
215 ## Merge save buttons, remove headings from subsections, and make information links into popups.
216 ## This is not done if there is only a custom defined form.
217 $xwiki.jsx.use($nameOfThisDocument)##
218 ##
219 ## We don't begin the form until we have content for it so that a configurable can specify a
220 ## custom form in codeToExecute and if that configurable object is the first of it's kind in that
221 ## document, the custom form will not be put inside of our form.
222 #if(!$insideForm)
223 ## We are opening a form and fieldset without closing it, thus we cannot clean this html.
224
225 {{html clean=false}}
226 <form id="$formId" method="post" action="$formAction" class="xform">
227 <fieldset>
228 {{/html}}
229 #set($insideForm = true)
230 #end
231
232 {{html}}
233 $formHtml.toString()
234 {{/html}}
235 #end## If object exists
236 #end## If class exists
237 #end## If class name is specified.
238 #end## Foreach configurable object found in this document
239 ## If a form was started then we end it.
240 #if($insideForm)
241
242 ## This is closing an open form which was opened above, we cannot clean this html.
243 {{html clean=false}}
244 ## We add in a redirect field to prevent the user from being carried away when they save
245 ## if they don't have javascript.
246 #set($thisURL = $request.getRequestURL())
247 #if($request.getQueryString() && $request.getQueryString().length() > 0)
248 #set($thisURL = "${thisURL}?$request.getQueryString()")
249 #end
250 <input type="hidden" id="${escapedAppName}_redirect" name="$redirectParameter" value="$escapetool.xml($thisURL)" />
251 <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
252 </fieldset>
253 <div class="bottombuttons">
254 <p class="admin-buttons">
255 <span class="buttonwrapper">
256 ## Text to display on the button. If there is a heading then this button should be labeled
257 ## that it is for saving this section. Otherwise it should be a generic "save" button.
258 #if($headingShowing)
259 #set($buttonText = "$services.localization.render('admin.save') $escapedAppName")
260 #else
261 #set($buttonText = "$services.localization.render('admin.save')")
262 #end
263 <input class="button" type="submit" name="action_saveandcontinue" value="$buttonText" />
264 </span>
265 </p>
266 </div> ## bottombuttons
267 </form>
268 #set($insideForm = false)
269 {{/html}}
270 #end
271 #end## If there are configurable objects
272 #end## If document is not locked or forceEdit is enabled
273 #end## Foreach document name in names to configure
274
275 {{html}}
276 <script>
277 /* <![CDATA[ */
278 ## Alt+Shift+S presses the first saveAndContinue button it finds, not what we want so we will disable edit shortcuts.
279 document.observe('xwiki:dom:loaded', function() {
280 XWiki.actionButtons.EditActions = Object.extend(XWiki.actionButtons.EditActions, {addShortcuts : function() { }});
281 });
282 //]]>
283 </script>
284 {{/html}}##
285 ##
286 #elseif ($currentDoc != 'XWiki.ConfigurableClass')
287 ##
288 ##------------------------------------------------------------------------------------------------------------
289 ## If section is not set then we are viewing the main administration page.
290 ##------------------------------------------------------------------------------------------------------------
291 ##
292 ## If there is no list called sections then we set sections to an empty list.
293 #if(!$sections || $sections.getClass().getName().indexOf('List') == -1)
294 #set($sections = [])
295 #end
296 ##
297 ## We have to create a list of documents which the current user doesn't have permission to view.
298 ## So we can add an error message to the bottom of the page if there are any.
299 #set($appsUserCannotView = [])
300 ##
301 ## A list of sections (to be added) which the user is not allowed to edit, icons will be displayed with a message
302 #set($sectionsUserCannotEdit = [])
303 ## List of sections to be added, in order by creationDate of oldest contained application.
304 #set($sectionsToAdd = [])
305 ## Map of URL of icon to use by the name of the section to use that icon on.
306 #set($iconBySection = {})
307 ##
308 #set($outputList = [])
309 #findNamesOfAppsToConfigure("", $globaladmin, $currentSpace, $outputList)
310 ##
311 #foreach($appName in $outputList)
312 ##
313 ## Get the configurable application
314 #set($app = $xwiki.getDocument($appName))
315 ##
316 ## If getDocument returns null, then warn the user that they don't have view access to that application.
317 #if(!$app)
318 #set($discard = $appsUserCannotView.add($appName))
319 #end
320 ##
321 #set($configurableObjects = $app.getObjects($nameOfThisDocument))
322 #foreach($configurableObject in $configurableObjects)
323 #set($displayInSection = $app.getValue('displayInSection', $configurableObject))
324 ##
325 ## If there is no section for this configurable or if the section cannot be edited, then check if the
326 ## application can be edited by the current user, if so then we display the icon from the current app and
327 ## don't display any message to tell the user they can't edit that section.
328 #if(!$sections.contains($displayInSection) || $sectionsUserCannotEdit.contains($displayInSection))
329 ##
330 ## If there is no section for this configurable, then we will have to add one.
331 #if(!$sections.contains($displayInSection) && !$sectionsToAdd.contains($displayInSection))
332 #set($discard = $sectionsToAdd.add($displayInSection))
333 #end
334 ##
335 ## If an attachment by the filename iconAttachment exists and is an image
336 #set($attachment = $app.getAttachment("$app.getValue('iconAttachment', $configurableObject)"))
337 #if($attachment && $attachment.isImage())
338 ## Set the icon for this section as the attachment URL.
339 #set($discard = $iconBySection.put($displayInSection, $app.getAttachmentURL($attachment.getFilename())))
340 #end
341 ##
342 ## If the user doesn't have edit access to the application, we want to show a message on the icon
343 #if(!$xcontext.hasAccessLevel("edit", $app.getFullName()))
344 #if(!$sectionsUserCannotEdit.contains($displayInSection))
345 #set($discard = $sectionsUserCannotEdit.add($displayInSection))
346 #end
347 #elseif($sectionsUserCannotEdit.contains($displayInSection))
348 ## If the user didn't have access to the section before but does have access to _this_ app which is
349 ## configured in the section, then the section becomes accessible.
350 #set($discard = $sectionsUserCannotEdit.remove($displayInSection))
351 #end
352 #end## If section doesn't exist or user doesn't have access.
353 #end## Foreach configurable object in this app.
354 #end## Foreach application which is configurable.
355 ##
356 ## Now we go through sectionsToAdd and generate icons for them
357 #set($defaultIcon = $xwiki.getAttachmentURL($nameOfThisDocument, 'DefaultAdminSectionIcon.png'))
358 #if($globaladmin)
359 #set($queryString = "editor=globaladmin&amp;section=")
360 #else
361 #set($queryString = "space=$escapetool.url($currentSpace)&amp;section=")
362 #if($request.getParameter('editor'))
363 #set($queryString = "editor=$escapetool.url($request.getParameter('editor'))&amp;$queryString")
364 #end
365 #end
366
367 ## This is an html fragment and thus cannot be cleaned
368 {{html clean=false}}
369 #foreach($sectionToAdd in $sectionsToAdd)
370 #set($icon = $iconBySection.get($sectionToAdd))
371 #if(!$icon)
372 #set($icon = $defaultIcon)
373 #end
374 <li class="$escapetool.xml($sectionToAdd).replaceAll(' ', '_')">
375 #set($hasAccess = !$sectionsUserCannotEdit.contains($sectionToAdd))
376 #if($hasAccess)
377 <a href="$xwiki.getURL($currentDoc, $xcontext.getAction(), "$queryString$escapetool.url($sectionToAdd)")">
378 #else
379 <a title="$services.localization.render('xe.admin.configurable.sectionIconNoAccessTooltip')">
380 #end
381 <span>
382 <img src="$icon" alt="$escapetool.xml($sectionToAdd) icon"/>
383 ## Try to translate the names of the sections, build the key by adding an "admin." in front.
384 ## Not the best way to translate, but very inline with the way the translations are done in XWiki.AdminSheet for individual administration page titles.
385 ## If there is no translation (translated message is equals to key), don't display the message key, but the section name instead.
386 #if($services.localization.get("admin.${sectionToAdd.toLowerCase()}"))
387 #set($sectionDisplayName = $services.localization.render("admin.${sectionToAdd.toLowerCase()}"))
388 #else
389 #set($sectionDisplayName = $sectionToAdd)
390 #end
391 $escapetool.xml($sectionDisplayName)
392 </span>
393 #if(!$hasAccess)
394 <br/>#inlineError($services.localization.render('xe.admin.configurable.sectionIconNoAccess'))
395 #end
396 </a>
397 </li>
398 #end
399 {{/html}}
400
401 ## Finally we display an error message if there are any applications which we were unable to view.
402 #if($appsUserCannotView.size() > 0)
403 {{error}}$services.localization.render('xe.admin.configurable.noViewAccessSomeApplications',
404 'xwiki/2.1', [$appsUserCannotView]){{/error}}
405
406 #end
407 #end## If we should be looking at the main administration page.
408 {{/velocity}}