Version 1.1 von admin am 2025/02/23 10:10

Verstecke letzte Bearbeiter
admin 1.1 1 {{velocity output="false"}}
2 ## Constants:
3 #set($redirectParameter = 'xredirect')
4 #set($nameOfThisDocument = 'XWiki.ConfigurableClass')
5
6
7 #*
8 * Try to determine whether a document was edited by a user who has edit right on this page. This is tricky because
9 * documents are imported with the name XWiki.XWikiGuest who has no access to anything after import.
10 *
11 * @param theDoc - Document who's editor should be checked for edit access on this document.
12 *###
13 #macro(checkDocumentSavedByAuthorizedUser, $docToCheck, $currentDoc, $hasAccess)
14 ## The system is started and the only user is XWikiGuest who has admin right but gives it up when he imports the default
15 ## documents, we are checking to see if this looks like the guest imported the document with the first import.
16 #if($docToCheck.getWiki() == $xcontext.getMainWikiName()
17 && $docToCheck.getVersion() == '1.1'
18 && $docToCheck.getCreator() != $docToCheck.getContentAuthor()
19 && $docToCheck.getContentAuthor() == 'XWiki.XWikiGuest')
20 ##
21 #set($userToCheck = $docToCheck.getCreator())
22 #else
23 #set($userToCheck = $docToCheck.getAuthor())
24 #end
25 #set ($hasAccess = $NULL)
26 #setVariable ("$hasAccess" $xwiki.hasAccessLevel('edit', $userToCheck, $currentDoc))
27 #end
28
29
30 #*
31 * Find names of documents which contain objects of the class 'XWiki.ConfigurableClass'
32 *
33 * @param $section - String - Look for apps which specify that they should be configured in this section,
34 * if null or "" then returns them for all sections.
35 *
36 * @param $globaladmin - boolean - If true then we will look for applications which should be configured globally.
37 *
38 * @param $space - String - If not looking for apps which are configured globally, then this is the space where we
39 * will look for apps in. If null or "" or if $globaladmin is true, then all spaces will be
40 * searched.
41 *
42 * @param $outputList - List - The returns from this macro will be put in this list, passing the list as a parameter
43 * a safety measure because macros can't return values.
44 *###
45 #macro(findNamesOfAppsToConfigure, $section, $globaladmin, $space, $outputList)
46 ## We keep looking in the old configureGlobally property since we do not provide a migration for it:
47 ## this choice has been made to avoid any problem during the upgrade of old wikis.
48 ## And we are doing this look in a separate query for performance reason: adding a or statement is very bad
49 ## in terms of performance. This all should be improved in the future with proper caching.
50 ##
51 #set ($fromClauseWithoutGlobal = [
52 'BaseObject as obj',
53 'StringProperty as category',
54 'IntegerProperty as sectionOrder'
55 ])
56 ##
57 #set ($fromClause = [])
58 #set ($discard = $fromClause.addAll($fromClauseWithoutGlobal))
59 #set ($discard = $fromClause.add('StringProperty as global'))
60 ##
61 #set ($fromClauseDeprecated = [])
62 #set ($discard = $fromClauseDeprecated.addAll($fromClauseWithoutGlobal))
63 #set ($discard = $fromClauseDeprecated.add('IntegerProperty as deprecatedGlobal'))
64 ##
65 #set ($whereClauseWithoutGlobal = [
66 "obj.name = doc.fullName and obj.className = :className",
67 "category.id = obj.id and category.name = 'displayInCategory'",
68 "sectionOrder.id = obj.id and sectionOrder.name = 'sectionOrder'"
69 ])
70 ##
71 #set ($whereClause = [])
72 #set ($discard = $whereClause.addAll($whereClauseWithoutGlobal))
73 #set ($discard = $whereClause.add("global.id = obj.id and global.name = 'scope' and global.value in (:global)"))
74 ##
75 #set ($whereClauseDeprecated = [])
76 #set ($discard = $whereClauseDeprecated.addAll($whereClauseWithoutGlobal))
77 #set ($discard = $whereClauseDeprecated.add("deprecatedGlobal.id = obj.id and deprecatedGlobal.name = 'configureGlobally' and deprecatedGlobal.value = :deprecatedGlobal"))
78 ##
79 #set ($params = {'className' : $nameOfThisDocument})
80 ## Order by category and section priority, leaving empty/null category at the end, because we want to read the
81 ## category meta data from the first (most important) section in each category.
82 #set ($orderByClause = [
83 "case when category.value is null or category.value = '' then 1 else 0 end",
84 'category.value',
85 'sectionOrder.value'
86 ])
87 #if ($globaladmin == true)
88 #set ($discard = $params.put('deprecatedGlobal', 1))
89 #set ($discard = $params.put('global', ['WIKI','WIKI+ALL_SPACES']))
90 #else
91 #set ($discard = $params.put('deprecatedGlobal', 0))
92 #if ("$!space" != '')
93 #set ($discard = $params.put('global', ['SPACE']))
94 #set ($discard = $whereClause.add('doc.space = :space'))
95 #set ($discard = $params.put('space', $space))
96 #else
97 #set ($discard = $params.put('global', ['ALL_SPACES','WIKI+ALL_SPACES']))
98 #end
99 #end
100 #if ("$!section" != '')
101 #set ($discard = $fromClause.add('StringProperty as section'))
102 #set ($discard = $fromClauseDeprecated.add('StringProperty as section'))
103 #set ($discard = $whereClause.add("section.id = obj.id and section.name = 'displayInSection' and section.value = :section"))
104 #set ($discard = $whereClauseDeprecated.add("section.id = obj.id and section.name = 'displayInSection' and section.value = :section"))
105 #set ($discard = $params.put('section', $section))
106 #end
107 #set ($statement = ', ' + $stringtool.join($fromClause, ', ') +
108 ' where ' + $stringtool.join($whereClause, ' and ') +
109 ' order by ' + $stringtool.join($orderByClause, ', '))
110 #set ($statementDeprecated = ', ' + $stringtool.join($fromClauseDeprecated, ', ') +
111 ' where ' + $stringtool.join($whereClauseDeprecated, ' and ') +
112 ' order by ' + $stringtool.join($orderByClause, ', '))
113 ##
114 #set ($deprecatedParams = {})
115 #set ($discard = $deprecatedParams.putAll($params))
116 #set ($discard = $deprecatedParams.remove('global'))
117 #set ($discard = $deprecatedParams.remove('space'))
118 #set ($discard = $params.remove('deprecatedGlobal'))
119 ##
120 ## We can't remove duplicates using the unique filter because the select clause will be extended with the information
121 ## needed by the order by clause. Thus we remove the duplicates after we get the results.
122 #set ($orderedSetOfAppNames = $collectiontool.orderedSet)
123 #set ($discard = $orderedSetOfAppNames.addAll($services.query.hql($statement).bindValues($params).execute()))
124 #set ($discard = $orderedSetOfAppNames.addAll($services.query.hql($statementDeprecated).bindValues($deprecatedParams).execute()))
125 #set ($discard = $outputList.addAll($orderedSetOfAppNames))
126 #if ($globaladmin == false && "$!space" != '')
127 ## If we are looking for the apps of a specific space, we should also get the one configured for all spaces.
128 ## Note that we're doing that call at the end to avoid polluting the different velocity variables during the
129 ## execution.
130 #findNamesOfAppsToConfigure($section, false, '', $outputList)
131 #end
132 #end
133
134 #*
135 * Augment the $adminMenu variable with all $nameOfThisDocument (i.e. XWiki.ConfigurableClass) xobjects found on this wiki.
136 *###
137 #macro(findCustomSectionsToConfigure $adminMenu)
138 #set ($outputList = [])
139 #set ($global = ($editor == 'globaladmin'))
140 #findNamesOfAppsToConfigure('', $global, $currentSpace, $outputList)
141 #set ($sectionsByName = {})
142 #set ($categoriesByName = {})
143 #foreach ($category in $adminMenu)
144 #set ($discard = $categoriesByName.put($category.id, $category))
145 #foreach ($section in $category.children)
146 #set ($discard = $sectionsByName.put($section.id, $section))
147 #end
148 #end
149 ##
150 #set ($query = "editor=$escapetool.url(${editor})")
151 #if ($editor != 'globaladmin')
152 #set ($query = $query + "&space=$escapetool.url(${currentSpace})")
153 #end
154 #foreach ($appName in $outputList)
155 ##
156 ## Get the configurable application
157 #set ($app = $xwiki.getDocument($appName))
158 ##
159 ## If getDocument returns null, then warn the user that they don't have view access to that application.
160 #if (!$app)
161 #set ($discard = $appsUserCannotView.add($appName))
162 #end
163 ##
164 #foreach ($configurableObject in $app.getObjects($nameOfThisDocument))
165 #set ($displayInCategory = $app.getValue('displayInCategory', $configurableObject))
166 ## It's OK to not specify a category, in which case the app will be added to a default one (i.e. "other").
167 #if ("$!displayInCategory" != '')
168 #if ($categoriesByName.containsKey($displayInCategory))
169 ## Add a new section in this category.
170 #set ($appCategory = $categoriesByName.get($displayInCategory))
171 #else
172 ## Add a new category.
173 #set ($key = "admin.$displayInCategory.toLowerCase()")
174 #set ($name = $displayInCategory)
175 #if ($services.localization.get($key))
176 #set ($name = $services.localization.render($key))
177 #end
178 #set ($appCategory= {
179 'id': $displayInCategory,
180 'name': $name,
181 'children': []
182 })
183 #set ($discard = $categoriesByName.put($displayInCategory, $appCategory))
184 ## Insert the category at the end for now. We'll sort the categories after we add all of them.
185 #set ($discard = $adminMenu.add($appCategory))
186 #end
187 #set ($categoryIcon = $app.getValue('categoryIcon', $configurableObject))
188 #if ("$!categoryIcon" != '')
189 #set ($appCategory.icon = $categoryIcon)
190 #end
191 #set ($displayBeforeCategory = $app.getValue('displayBeforeCategory', $configurableObject))
192 #if ("$!displayBeforeCategory" != '')
193 #set ($appCategory.displayBeforeCategory = $displayBeforeCategory)
194 #end
195 #else
196 #set ($appCategory = $categoriesByName.get('other'))
197 #end
198 ##
199 #set ($displayInSection = $app.getValue('displayInSection', $configurableObject))
200 ##
201 ## If there is no section specified in the object, just skip it
202 #if ("$!displayInSection" == '')
203 ## Skip, bad object
204 ## If there is no section for this configurable or if the section cannot be edited, then check if the
205 ## application can be edited by the current user, if so then we display the icon from the current app and
206 ## don't display any message to tell the user they can't edit that section.
207 #elseif ($sectionsByName.containsKey($displayInSection))
208 #set ($appSection = $sectionsByName.get($displayInSection))
209 #set ($appSection.configurable = true)
210 #set ($newSection = false)
211 #else
212 ##
213 ## If there is no section for this configurable, then we will have to add one.
214 #set ($key = "admin.$displayInSection.toLowerCase()")
215 #set ($name = $displayInSection)
216 #if ($services.localization.get($key))
217 #set ($name = $services.localization.render($key))
218 #end
219 #set ($appSection = {
220 'id': $displayInSection,
221 'name': $name,
222 'url': $xwiki.getURL($currentDoc, $adminAction, "${query}&section=$escapetool.url($displayInSection)"),
223 'configurable': true,
224 'order': $configurableObject.getValue('sectionOrder')
225 })
226 #if ($app.getValue('configureGlobally', $configurableObject) != 1)
227 #set ($appSection.perSpace = true)
228 #end
229 #set ($key = "admin.${displayInSection.toLowerCase()}.description")
230 #if ($services.localization.get($key))
231 #set ($appSection.description = $services.localization.render($key))
232 #end
233 #set ($discard = $sectionsByName.put($displayInSection, $appSection))
234 #set ($discard = $appCategory.children.add($appSection))
235 #set ($newSection = true)
236 #end
237 ##
238 ## If an attachment by the filename iconAttachment exists and is an image
239 #set ($attachment = $app.getAttachment("$!app.getValue('iconAttachment', $configurableObject)"))
240 #if ($attachment && $attachment.isImage())
241 ## Set the icon for this section as the attachment URL.
242 #set ($appSection.iconReference = "${appName}@$attachment.getFilename()")
243 #elseif (!$appSection.iconReference)
244 #set ($appSection.iconReference = 'XWiki.ConfigurableClass@DefaultAdminSectionIcon.png')
245 #end
246 ##
247 ## If the user doesn't have edit access to the application, we want to show a message on the icon
248 #if (!$xcontext.hasAccessLevel('edit', $app.getFullName()) && $newSection)
249 #set ($appSection.readOnly = true)
250 #elseif ($xcontext.hasAccessLevel('edit', $app.getFullName()) && $appSection.readOnly)
251 #set ($appSection.readOnly = false)
252 #end
253 #end## Foreach configurable object in this app.
254 #end## Foreach application which is configurable.
255 ##
256 ## Sort the categories
257 ##
258 #foreach ($category in $adminMenu)
259 #set ($category.score = 0)
260 #end
261 #foreach ($round in [1..$adminMenu.size()])
262 #set ($scoreChanged = false)
263 #foreach ($category in $adminMenu)
264 #if ($category.displayBeforeCategory)
265 #set ($newScore = $categoriesByName.get($category.displayBeforeCategory).score + 1)
266 #if ($newScore && $newScore > $category.score)
267 #set ($category.score = $newScore)
268 #set ($scoreChanged = true)
269 #end
270 #end
271 #end
272 #if (!$scoreChanged)
273 #break
274 #end
275 #end
276 #set ($adminMenu = $collectiontool.sort($adminMenu, 'score:desc'))
277 #end
278
279
280 #*
281 * Show the heading for configuration for a given application.
282 *
283 * $appName (String) Name of the application to show configuration heading for.
284 *
285 * $headingAlreadyShowing (boolean) If true then we don't make another heading. Otherwise it is set to true.
286 *###
287 #macro(showHeading, $appName, $headingAlreadyShowing)
288 #if(!$headingAlreadyShowing)
289 #set($headingAlreadyShowing = true)
290 #set($escapedAppName = $services.rendering.escape($appName, 'xwiki/2.1'))
291 #set($doubleEscapedAppName = $services.rendering.escape($escapedAppName, 'xwiki/2.1'))
292
293 == {{translation key="admin.customize"/}} [[$doubleEscapedAppName>>$escapedAppName]]: ==
294 #end
295 #end
296
297 #define($formHtml)
298 #if ($objClass.getPropertyNames().size() > 0)
299 <dl>
300 #foreach($propName in $objClass.getPropertyNames())
301 #if($propertiesToShow.size() > 0 && !$propertiesToShow.contains($propName))
302 ## Silently skip over this property.
303 #else
304 #set($hintKey = "${obj.xWikiClass.name}_${propName}.hint")
305 #set($hint = $services.localization.render($hintKey))
306 #if($hint == $hintKey)
307 #set($hint = $NULL)
308 #end
309 ##
310 ## Further processing of the field display HTML is needed.
311 ## Step 1: Strip <pre> tags which $obj.display inserts, this won't affect content because it's escaped.
312 #set($out = $obj.display($propName, 'edit').replaceAll('<[/]?+pre>', ''))
313 ## Step 2: Select only content between first < and last > because $obj.display inserts html macros.
314 ## Careful not to remove html macros from the content because they are not escaped!
315 #set ($out = $out.substring($out.indexOf('<'), $mathtool.add(1, $out.lastIndexOf('>'))))
316 ## Step 3: Prepend app name to all ID and FOR attributes to prevent id collision with multiple apps on one page.
317 #set ($oldId = "$objClass.getName()_$obj.getNumber()_$propName")
318 #set ($newId = "${escapedAppName}_$oldId")
319 #set ($out = $out.replaceAll(" (id|for)=('|"")$regextool.quote($oldId)",
320 " ${escapetool.d}1=${escapetool.d}2$regextool.quoteReplacement($newId)"))
321 ## App Name is prepended to for= to make label work with id which is modified to prevent collisions.
322 <dt><label#if ($out.matches("(?s).*id=['""]${newId}['""].*")) for="${newId}"#end>##
323 #if ($out.indexOf('type=''checkbox''') != -1 && $out.indexOf('class="xwiki-form-listclass"') == -1)
324 $out ##
325 #set ($out = '')
326 #end
327 $escapetool.xml($app.displayPrettyName($propName, $obj))
328 </label>
329 #if($linkPrefix != '')
330 <a href="$escapetool.xml($linkPrefix + $propName)" class="xHelp" title="$services.localization.render('admin.documentation')">$services.localization.render('admin.documentation')</a>
331 #end
332 #if ($hint)
333 <span class="xHint">$hint</span>
334 #end
335 </dt>
336 #if ($out != '')
337 <dd>$out</dd>
338 #else
339 ## We always display a dd element to avoid having a last dt element alone, which would lead to an invalid html.
340 <dd class="hidden">$out</dd>
341 #end
342 #end## If property is in propertiesToShow
343 #end## Foreach property in this class
344 </dl>
345 #end
346 #end## define $formHtml
347 {{/velocity}}