Wiki source code of Custom configurable sections

Last modified by superadmin on 2022/11/23 11:09

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

Submit feedback regarding this wiki to [email protected]

This wiki is licensed under a Creative Commons 2.0 license
XWiki 14.10.13 - Documentation