Wiki source code of XWiki.ConfigurableClass

Last modified by Admin on 2010/02/19 11:21

Show last authors
1 {{velocity}}
2 #*
3 * This part takes the configuration from any documents containing XWiki.ConfigurableClass objects and creates a form
4 * for each. To includeForm this document, you may specify:
5 *
6 * $section - String - The section which we are administrating eg: "Registration", "Users", or "Import".
7 * If none is specified then it checks for a request parameter called "section" and uses that,
8 * if no parameter, then this code assumes that it is part of the admin icons sheet and adds icons
9 * for any section which is not in $sections, in that event, this code assumes it is being run
10 * inside of a <ul> block.
11 *
12 * $sections - List<String> - If section is not specified, any sections on this list will not have icons made for them
13 * the assumption being that the icons are already there. If section is specified then this
14 * is not taken into account and may safely be undefined.
15 *
16 * $currentDoc - String (document.fullName) - The administration document, users who don't have permission to edit
17 * it will not be able to include applications (possibly injecting
18 * arbitrary code.) if none specified then $doc.getFullName() is used.
19 *
20 * $globaladmin - boolean - If set true then we will assume we are administrationg the entire wiki.
21 * If not set then we look for a request parameter called "editor" if that exists and equals
22 * "globaladmin" then $globaladmin is true, if it doesn't exist then we check to see if
23 * $currentDoc.getFullName() equals "XWiki.XWikiPreferences".
24 *
25 * $doNotUnlockConfigurableDocuments - boolean - If true then this code will not make any attempt to unlock configurable
26 * documents. By default it does because it locks any document in the
27 * section which is being configured which would lead to a lot of stray
28 * locks if they weren't all cancelled. Only recommended if this page is
29 * being included twice in the same page.
30 *###
31 ## Constants:
32 #set($redirectParameter = 'xredirect')
33 #set($nameOfThisDocument = 'XWiki.ConfigurableClass')
34 ##
35 ## Form submission depends on this.
36 $xwiki.jsfx.use('js/xwiki/actionbuttons/actionButtons.js', true)
37 ##
38 #if(!$section)
39 #set($section = $request.getParameter('section'))
40 #end
41 #if(!$currentDoc)
42 #set($currentDoc = $doc.getFullName())
43 #end
44 ## Get value of $globaladmin if not specified.
45 #if("$!globaladmin" == '')
46 #if($editor != 'globaladmin'
47 && $request.getParameter('editor') != 'globaladmin'
48 && $currentDoc != "XWiki.XWikiPreferences")
49 ##
50 #set($globaladmin = false)
51 #else
52 #set($globaladmin = true)
53 #end
54 #end
55 #set($currentSpace = $xwiki.getDocument("$currentDoc").getSpace())
56 ##
57 ## This application should not run with programming rights because it evaluates code which may not be trustworthy.
58 ## Removing the next line will open a security hole.
59 #sandboxDocument()
60 ##
61 ## This application locks every document in a section while that section is being edited so we should
62 ## check for locks held by the current user on any of the applications configured here and remove them.
63 #if(!$doNotUnlockConfigurableDocuments)
64 #set($outputList = [])
65 #findNamesOfAppsToConfigure("", $globaladmin, $currentSpace, $outputList)##
66 ## We don't want to generate javascript which unlocks the current document just after we got finished locking it.
67 #set($discard = $outputList.remove($currentDoc))
68 #unlockDocuments($outputList)
69 #end
70 ##
71 ##------------------------------------------------------------------------------------------------------------
72 ## If $section exists then we are viewing the admin page for a perticular section.
73 ## eg: 'Registration', 'Presentation', 'Import' etc.
74 ##------------------------------------------------------------------------------------------------------------
75 ##
76 #if($section && $section != '')
77 ##
78 ## This is for keeping track of whether we have shown the heading yet or not.
79 ## If the heading doesn't need to be shown, but an error occurs in processing, then we show the heading
80 ## so that the user knows what the error relates to.
81 #set($headingShowing = false)
82 ##
83 ## Searches the database for names of apps to be configured
84 #set($outputList = [])
85 #findNamesOfAppsToConfigure($section, $globaladmin, $xwiki.getDocument("$currentDoc").getSpace(), $outputList)
86 ##
87 #if($outputList.size() > 0)
88 ## Merge save buttons, remove headings from subsections, and make information links into popups.
89 $xwiki.jsx.use($nameOfThisDocument)
90 #end
91 ##
92 #foreach($appName in $outputList)
93 ##
94 ## Make sure the current user has permission to edit the configurable application.
95 #set($userHasAccessToDocument = $xcontext.hasAccessLevel('edit', $appName))
96 ##
97 ## If the document was not last saved by a user with edit privilage on this page
98 ## then we can't safely display the page but we should warn the viewer.
99 #if($userHasAccessToDocument)
100 ## Get the configurable application
101 #set($app = $xwiki.getDocument($appName))
102 ##
103 #set($documentSavedByAuthorizedUser = false)
104 #checkDocumentSavedByAuthorizedUser($app, $currentDoc, $documentSavedByAuthorizedUser)
105 #end
106 ## Will we have to display an error message?
107 #set($displayError = !$userHasAccessToDocument || !$documentSavedByAuthorizedUser)
108 ##
109 ## There is no need to display a heading unless:
110 ## 1. There was already a section before this document.
111 ## 2. This is not the first document in this section.
112 ##
113 ## If we are displaying the heading and there is an error to be shown Javascript will not strip the heading.
114 #if(!$appName.equals($outputList.get(0)) || $sections.contains($section))
115 ## Create a document heading.
116 #showHeading($appName, $displayError, $headingShowing)
117 #end
118 ##
119 #if(!$userHasAccessToDocument)
120 #showHeading($appName, true, $headingShowing)
121
122 {{error}}$msg.get('xe.admin.configurable.noPermissionThisApplication'){{/error}}
123
124 #else
125 ##
126 #if(!$documentSavedByAuthorizedUser)
127 #showHeading($appName, true, $headingShowing)
128
129 {{error}}$msg.get('xe.admin.configurable.applicationAuthorNoAdmin', [$app.Author]){{/error}}
130
131 #else
132 ##
133 ## Locking document
134 ##------------------------------------------------------------------------------------------------------------
135 #if($app.getLocked())
136 #set($locked = true)
137 #end
138 ## If the document is locked and not by the current user and forceEdit is not set true,
139 #if($locked && $app.getLockingUser() != $xcontext.getUser() && !$request.getParameter('forceEdit'))
140 #set($requestURL = "$request.getRequestURL()")
141 #if($requestURL.indexOf("?") == -1)
142 #set($requestURL = "${requestURL}?")
143 #end
144 #showHeading($appName, true, $headingShowing)
145
146 {{error}}$msg.get("doclockedby") $app.getLockingUser() [[$msg.get("forcelock")>>${requestURL}&forceEdit=1]]{{/error}}
147
148 #else
149 ## If the document is not already locked, attempt to aquire the lock.
150 #if(!$locked)
151 ## Try to use an ajax call to lock the document.
152 {{html clean=false wiki=true}}</p>
153 <noscript>
154
155 {{warning}}$msg.get('xe.admin.configurable.cannotLockNoJavascript'){{/warning}}
156
157 </noscript>
158 <script type="text/javascript">
159 document.observe("dom:loaded", function() {
160 new Ajax.Request("$xwiki.getURL($app.getFullName(), 'lock', 'ajax=1')");
161 });
162 </script>
163 <p>{{/html}}
164 #set($discard = $lockedDocumentNames.add($app.getFullName()))
165 #end
166 ##------------------------------------------------------------------------------------------------------------
167 ## Done Locking.
168 ##
169 ## Get all objects of the "ConfigurableClass" from this document.
170 #set($allConfigurableObjs = $app.getObjects($nameOfThisDocument))
171 ## Seperate out the objects which are for this section.
172 #set($configurableObjs = [])
173 #foreach($configurableObj in $allConfigurableObjs)
174 #if($app.getValue('displayInSection', $configurableObj) == $section)
175 ## If this is space admin, then don't display global, if global don't display space.
176 #if($globaladmin == ($app.getValue('configureGlobally', $configurableObj) == 1))
177 #set($discard = $configurableObjs.add($configurableObj))
178 #end
179 #end
180 #end
181 #if($configurableObjs.size() == 0)
182 ## Internal error, not translated.
183 #showHeading($appName, true, $headingShowing)
184
185 {{error}}Internal error: All objects were filtered out for application: $appName.{{/error}}
186
187 #else
188 #set($formAction = "$xwiki.getURL($app.getFullName(), 'save')")
189 #set($formId = "$section.toLowerCase()_$app.getFullName()")
190 #set($escapedAppName = $escapetool.html($app.getFullName()))
191 #foreach($configurableObj in $configurableObjs)
192 ## Display the header if one exists.
193 #set($heading = $app.getValue('heading', $configurableObj))
194 #if($heading && $heading != '')
195 == #evaluate($heading) ==
196 #end
197 ## If the class specifies custom code to evaluate,
198 ## then close the html macro, evaluate it and reopen the macro.
199 #set($codeToExecute = "$!app.getValue('codeToExecute', $configurableObj)")
200 #if($codeToExecute != '')
201 #evaluate($codeToExecute)
202 #end
203 ##
204 ## If propertiesToShow is set, then we will only show the properties contained therein.
205 #set($propertiesToShow = $app.getValue('propertiesToShow', $configurableObj))
206 #if(!$propertiesToShow || $propertiesToShow.getClass().getName().indexOf('List') == -1)
207 #set($propertiesToShow = [])
208 #end
209 ##
210 ## If linkPrefix is set, then we will make each property label a link which starts with that prefix.
211 #set($linkPrefix = "$!app.getValue('linkPrefix', $configurableObj)")
212 ##
213 ## If the Configurable object specifies a configuration class, use it,
214 ## otherwise assume custom forms are used instead.
215 #set($configClassName = "$!app.getValue('configurationClass', $configurableObj)")
216 #if($configClassName != '')
217 #set($objClass = $xwiki.getDocument("$configClassName").getxWikiClass())
218 #if(!$objClass || $objClass.getClass().getName().indexOf('.Class') == -1)
219 #showHeading($appName, true, $headingShowing)
220
221 {{error}}$msg.get('xe.admin.configurable.configurationClassNonexistant'){{/error}}
222
223 #else
224 ## Use the first object from the document which is of the configuration class.
225 #set($obj = $app.getObject($objClass.getName()))
226 ##
227 #if(!$obj || $obj.getClass().getName().indexOf('.Object') == -1)
228 #showHeading($appName, true, $headingShowing)
229
230 {{error}}
231 $msg.get('xe.admin.configurable.noObjectOfConfigurationClassFound',
232 [$objClass.getName(), $app.getFullName()])
233 {{/error}}
234
235 #else
236 #define($formHtml)
237 ## We don't begin the form until we have content for it so that a configurable can specify a
238 ## custom form in codeToExecute and if that configurable object is the first of it's kind in that
239 ## document, the custom form will not be put inside of our form.
240 #if(!$insideForm)
241 <form id="$formId" method="post" action="$formAction" onsubmit="cancelCancelEdit()">
242 <fieldset>
243 #set($insideForm = true)
244 #end
245 #foreach($propName in $objClass.getPropertyNames())
246 #if($propertiesToShow.size() > 0 && !$propertiesToShow.contains($propName))
247 ## Silently skip over this property.
248 #else
249 <p>
250 #set($prettyName = "#evaluate($app.displayPrettyName($propName, $obj))")
251 ## App Name is prepended to for= to make label work with id which is modified to prevent collisions.
252 <label for="${escapedAppName}_$objClass.getName()_0_$propName">
253 #if($linkPrefix != '')
254 #set($linkScript = "$linkPrefix$propName")
255 <a href="$escapetool.html("#evaluate($linkScript)")">$escapetool.html($prettyName)</a>
256 #else
257 $escapetool.html($prettyName)
258 #end
259 </label>
260 $obj.display($propName, "edit")
261 </p>
262 #end## If property is in propertiesToShow
263 #end## Foreach property in this class
264 #end## define $formHtml
265 ## Strip pre tags and html macro invocations which $obj.display inserts.
266 ## then prepend application name to ids to prevent id collissions.
267
268 {{html clean="false" wiki="false"}}</p>
269 $formHtml.toString().replaceAll('\{\{[/]?html[^}]*\}\}|<[/]?pre>', '').replaceAll(" id='", " id='${escapedAppName}_")
270 <p>{{/html}}##
271
272 #end## If object exists
273 #end## If class exists
274 #end## If class name is specified.
275 #end## Foreach configurable object found in this document
276 ## If a form was started then we end it.
277 #if($insideForm)
278
279 {{html clean="false" wiki="false"}}</p>
280 ## We add in a redirect field to prevent the user from being carried away when they save
281 ## if they don't have javascript.
282 #set($thisURL = $request.getRequestURL())
283 #if($request.getQueryString() && $request.getQueryString().length() > 0)
284 #set($thisURL = "${thisURL}?$request.getQueryString()")
285 #end
286 <input type="hidden" id="${escapedAppName}_redirect" name="$redirectParameter" value="$escapetool.html($thisURL)" />
287 </fieldset>
288 <div class="bottombuttons">
289 <p class="admin-buttons">
290 <span class="buttonwrapper">
291 ## Text to display on the button. If there is a heading then this button should be labeled
292 ## that it is for saving this section. Otherwise it should be a generic "save" button.
293 #if($headingShowing)
294 #set($buttonText = "$msg.get('admin.save') $escapedAppName")
295 #else
296 #set($buttonText = "$msg.get('admin.save')")
297 #end
298 <input class="button" type="submit" name="action_saveandcontinue" value="$buttonText" />
299 </span>
300 </p>
301 </div> ## bottombuttons
302 </form>
303 #set($insideForm = false)
304 <p>{{/html}}
305
306 #end
307 #end## If there are configurable objects
308 #end## If document is not locked or forceEdit is enabled
309 #end## If app author has permission to edit admin page
310 #end## If the current user has permission to edit the configurable application.
311 #end## Foreach document name in names to configure
312
313 {{html clean=false wiki=false}}
314 <script type="text/javascript">
315 /* <![CDATA[ */
316 ## Alt+Shift+S presses the first saveAndContinue button it finds, not what we want so we will disable edit shortcuts.
317 XWiki.actionButtons.EditActions = Object.extend(XWiki.actionButtons.EditActions, {addShortcuts : function() { }});
318 ##
319 ## TODO: cancel and "submit all" buttons.
320 //]]>
321 </script>
322 {{/html}}
323
324 ##
325 #else
326 ##
327 ##------------------------------------------------------------------------------------------------------------
328 ## If section is not set then we are viewing the main administration page.
329 ##------------------------------------------------------------------------------------------------------------
330 ##
331 ## If there is no list called sections then we set sections to an empty list.
332 #if(!$sections || $sections.getClass().getName().indexOf("List") == -1)
333 #set($sections = [])
334 #end
335 ##
336 ## We have to create a list of documents which the current user doesn't have permission to view.
337 ## So we can add an error messsage to the bottom of the page if there are any.
338 #set($appsUserCannotView = [])
339 ##
340 ## A list of sections (to be added) which the user is not allowed to edit, icons will be displayed with a message
341 #set($sectionsUserCannotEdit = [])
342 ## List of sections to be added, in order by creationDate of oldest contained application.
343 #set($sectionsToAdd = [])
344 ## Map of URL of icon to use by the name of the section to use that icon on.
345 #set($iconBySection = {})
346 ##
347 #set($outputList = [])
348 #findNamesOfAppsToConfigure("", $globaladmin, $currentSpace, $outputList)
349 ##
350 #foreach($appName in $outputList)
351 ##
352 ## Get the configurable application
353 #set($app = $xwiki.getDocument($appName))
354 ##
355 ## If getDocument returns null, then warn the user that they don't have view access to that application.
356 #if(!$app)
357 #set($discard = $appsUserCannotView.add($appName))
358 #end
359 ##
360 #set($configurableObjects = $app.getObjects($nameOfThisDocument))
361 #foreach($configurableObject in $configurableObjects)
362 #set($displayInSection = $app.getValue('displayInSection', $configurableObject))
363 ##
364 ## If there is no section for this configurable or if the section cannot be edited, then check if the
365 ## application can be edited by the current user, if so then we display the icon from the current app and
366 ## don't display any message to tell the user they can't edit that section.
367 #if(!$sections.contains($displayInSection) || $sectionsUserCannotEdit.contains($displayInSection))
368 ##
369 ## If there is no section for this configurable, then we will have to add one.
370 #if(!$sections.contains($displayInSection) && !$sectionsToAdd.contains($displayInSection))
371 #set($discard = $sectionsToAdd.add($displayInSection))
372 #end
373 ##
374 ## If an attachment by the filename iconAttachment exists and is an image
375 #set($attachment = $app.getAttachment("$app.getValue('iconAttachment', $configurableObject)"))
376 #if($attachment && $attachment.isImage())
377 ## Set the icon for this section as the attachment URL.
378 #set($discard = $iconBySection.put($displayInSection, $app.getAttachmentURL($attachment.getFilename())))
379 #end
380 ##
381 ## If the user doesn't have edit access to the application, we want to should show a message on the icon
382 #if(!$xcontext.hasAccessLevel("edit", $app.getFullName()))
383 #if(!$sectionsUserCannotEdit.contains($displayInSection))
384 #set($discard = $sectionsUserCannotEdit.add($displayInSection))
385 #end
386 #elseif($sectionsUserCannotEdit.contains($displayInSection))
387 ## If the user didn't have access to the section before but does have access to _this_ app which is
388 ## configured in the section, then the section becomes accessable.
389 #set($discard = $sectionsUserCannotEdit.remove($displayInSection))
390 #end
391 #end## If section doesn't exist or user doesn't have access.
392 #end## Foreach configurable object in this app.
393 #end## Foreach application which is configurable.
394 ##
395 ## Now we go through sectionsToAdd and generate icons for them
396 #set($defaultIcon = $xwiki.getAttachmentURL($nameOfThisDocument, 'DefaultAdminSectionIcon.png'))
397 #if($globaladmin)
398 #set($queryString = "editor=globaladmin&amp;section=")
399 #else
400 #set($queryString = "space=${currentSpace}&amp;section=")
401 #if($request.getParameter('editor'))
402 #set($queryString = "editor=$escapetool.url($request.getParameter('editor'))&amp;$queryString")
403 #end
404 #end
405
406 {{html clean=false wiki=false}}
407 #foreach($sectionToAdd in $sectionsToAdd)
408 #set($icon = $iconBySection.get($sectionToAdd))
409 #if(!$icon)
410 #set($icon = $defaultIcon)
411 #end
412 <li class="$escapetool.html($sectionToAdd).replaceAll(' ', '_')">
413 #set($hasAccess = !$sectionsUserCannotEdit.contains($sectionToAdd))
414 #if($hasAccess)
415 <a href="$xwiki.getURL($currentDoc, $xcontext.getAction(), "$queryString$escapetool.url($sectionToAdd)")">
416 #else
417 <a title="$msg.get('xe.admin.configurable.sectionIconNoAccessTooltip')">
418 #end
419 <img src="$icon" alt="$escapetool.html($sectionToAdd) icon"/>
420 $escapetool.html($sectionToAdd)
421 #if(!$hasAccess)
422 <br/><span class="errormessage">$msg.get('xe.admin.configurable.sectionIconNoAccess')</span>
423 #end
424 </a>
425 </li>
426 #end
427 {{/html}}
428
429 ## Finally we display an error message if there are any applications which we were unable to view.
430 #if($appsUserCannotView.size() > 0)
431
432 {{error}}$msg.get('xe.admin.configurable.noViewAccessSomeApplications', [$appsUserCannotView]){{/error}}
433
434 #end
435 #end## If we should be looking at the main administration page.
436 ##
437 ##------------------------------------------------------------------------------------------------------------
438 ## The Macros, nothing below this point is run directly.
439 ##------------------------------------------------------------------------------------------------------------
440 ##
441 #*
442 *
443 * Any documents which are on the provided list ($documentNames) which are locked by the current user will be unlocked.
444 * If this macro has programming rights, then they are unlocked programmatically, otherwise a javascript tag is
445 * generated with ajax calls to cancel for all of the documents. If there are documents on this list which are not
446 * locked by the current user, then they are ignored.
447 *
448 * @param $documentNames - List<String> - fullNames of documents which should be unlocked if they are locked by the
449 * current user.
450 *###
451 #macro(unlockDocuments $documentNames)
452 #if($documentNames.size() > 0)
453 #set($sql = "doc.fullName=")
454 #foreach($documentName in $documentNames)
455 #set($sql = "${sql}'$documentName' or doc.fullName=")
456 #end
457 ## Trim the dangling ' or doc.fullName=?'
458 #set($sql = $sql.substring(0, $sql.lastIndexOf(' or doc.fullName=')))
459 #set($sql = ", XWikiLock lock where lock.docId=doc.id and lock.userName='$xcontext.getUser()' and (${sql})")
460 #set($namesOfdocumentsToUnlock = $xwiki.searchDocuments($sql))
461 ## Use ajax and hope the user runs javascript.
462 {{html clean=false wiki=false}}
463 <script type="text/javascript">
464 document.observe("dom:loaded", function() {
465 #foreach($nameOflockedDocument in $namesOfdocumentsToUnlock)
466 new Ajax.Request("$xwiki.getURL($nameOflockedDocument, 'cancel', 'ajax=1')");
467 #end
468 });
469 </script>
470 {{/html}}##
471 #end## If output list size > 0
472 #end## Macro
473 ##
474 #*
475 * Find names of documents which contain objects of the class 'XWiki.ConfigurableClass'
476 *
477 * @param $section - String - Look for apps which specify that they should be configured in this section,
478 * if null or "" then returns them for all sections.
479 *
480 * @param $globaladmin - boolean - If true then we will look for applications which should be configured globally.
481 *
482 * @param $space - String - If not looking for apps which are configured globally, then this is the space where we
483 * will look for apps in. If null or "" or if $globaladmin is true, then all spaces will be
484 * searched.
485 *
486 * @param $outputList - List - The returns from this macro will be put in this list, passing the list as a parameter
487 * a safety measure because macros can't return values.
488 *###
489 #macro(findNamesOfAppsToConfigure, $section, $globaladmin, $space, $outputList)
490 ## Use a parameterized sql query to prevent injection.
491 #set($params = [])
492 #if($section && $section != '')
493 #set($discard = $params.add("$section"))
494 #set($sqlA = ' StringProperty as section,')
495 #set($sqlB = " and section.id=obj.id and section.name='displayInSection' and section.value=?")
496 #else
497 ## Make sure they are "" in case they were set prior to calling the macro.
498 #set($sqlA = '')
499 #set($sqlB = '')
500 #end
501 ## Set up query based on value of $globaladmin
502 #if($globaladmin == true)
503 #set($sqlC = '1')
504 #else
505 #if($space && $space != '')
506 #set($sqlC = '0 and doc.space = ?')
507 #set($discard = $params.add($space))
508 #else
509 #set($sqlC = '0')
510 #end
511 #end
512 #set($sql = ", BaseObject as obj,$sqlA IntegerProperty as global where "
513 + "doc.fullName=obj.name and obj.className='" + $nameOfThisDocument + "'$sqlB "
514 + "and global.id=obj.id and global.name='configureGlobally' and global.value=$sqlC "
515 + "order by doc.creationDate")
516 ##
517 ## Run the search
518 #set($outputList = $xwiki.searchDocuments($sql, 0, 0, $params))
519 ##
520 #end
521 ##
522 #*
523 * If this document is saved with programming access or is includeForm'd into a document with programming, we have to
524 * drop programming rights in order for it to run safely because it evaluates potentially untrustworthy code.
525 *###
526 #macro(sandboxDocument)
527 #if($xcontext.hasProgrammingRights())
528 $xcontext.getContext().getDoc().setContentAuthor('XWiki.XWikiGuest')##
529 #end
530 #end
531 ##
532 #*
533 * Try to determine whether a document was edited by a user who has edit right on this page. This is trickey because
534 * documents are imported with the name XWiki.XWikiGuest who has no access to anything after import.
535 *
536 * @param theDoc - Document who's editor should be checked for edit access on this document.
537 *###
538 #macro(checkDocumentSavedByAuthorizedUser, $docToCheck, $currentDoc, $hasAccess)
539 ## The system is started and the only user is XWikiGuest who has admin right but gives it up when he imports the default
540 ## documents, we are checking to see if this looks like the guest imported the document with the first import.
541 #if($docToCheck.getWiki() == $xcontext.getMainWikiName()
542 && $docToCheck.getVersion() == '1.1'
543 && $docToCheck.getCreator() != $docToCheck.getContentAuthor()
544 && $docToCheck.getContentAuthor() == 'XWiki.XWikiGuest')
545 ##
546 #set($userToCheck = $docToCheck.getCreator())
547 #else
548 #set($userToCheck = $docToCheck.getAuthor())
549 #end
550 #set($hasAccess = $xwiki.hasAccessLevel("edit", $userToCheck, $currentDoc))
551 #end
552 ##
553 #*
554 * Show the heading for configuration for a given application.
555 *
556 * $appName (String) Name of the application to show configuration heading for.
557 *
558 * $displayError (boolean) If true, then the heading will be class="applicationHeading errorMessage" and javascript
559 * will know not to remove it when it is merging forms.
560 *
561 * $headingAlreadyShowing (boolean) If true then we don't make another heading. Otherwise it is set to true.
562 *###
563 #macro(showHeading, $appName, $displayError, $headingAlreadyShowing)
564 #if(!$headingAlreadyShowing)
565 #set($headingAlreadyShowing = true)
566 ## If we are to display an error message, the class should be changed so that javascript doesn't remove the
567 ## heading from over the error message.
568 #*
569 #if($displayError)
570 (%class="applicationHeading errorMessage"%)
571 #else
572 (%class="applicationHeading"%)
573 #end*###
574
575 = $msg.get("admin.customize") __[[$appName>>$appName]]__: =
576 ## (%%)
577 #end
578 #end
579 {{/velocity}}