Version 1.1 par superadmin le 2022/06/20 08:48

Masquer les derniers auteurs
superadmin 1.1 1 {{velocity output="false"}}
2 #template('hierarchy_macros.vm')
3
4 #**
5 * Macro to get the results of a livetable data call.
6 * This page is called from live grids via Ajax with the argument xpage=plain. It returns a
7 * set of results serialized in JSON.
8 *#
9 #macro(gridresult $className $collist)
10 #gridresultwithfilter($className $collist '' '' {})
11 #end
12
13
14 #**
15 * Computes the query used to retrieve the results of a live table data call.
16 * NOTE: This macro is not fully encapsulated because it defines a few Velocity variables that are used in subsequent macros.
17 *#
18 #macro(gridresultwithfilter_buildQuery $className $collist $filterfrom $filterwhere $filterParams)
19 ## Additional columns; should *not* contain raw parameters, all added column names must be filtered
20 #set($fromSql = '')
21 ## Parametrized filter part of the query
22 #set($whereSql = '')
23 ## List of values to use with $whereSql
24 #if (!$filterParams)
25 #set($filterParams = {})
26 #end
27 #if ($filterParams.entrySet())
28 #set($whereParams = {})
29 #else
30 #set($whereParams = [])
31 #end
32 #set($class = $xwiki.getDocument($className).getxWikiClass())
33 ##
34 ## Add the columns needed for the actual data
35 ##
36 #set($tablelist = [])
37 #foreach($colname in $collist)
38 #livetable_addColumnToQuery($colname)
39 #end
40 ##
41 ## Tag filtering
42 ##
43 #if($request.tag)
44 #set($fromSql = "${fromSql} , BaseObject as tobject, DBStringListProperty as tagprop")
45 #set($whereSql = "${whereSql} and tobject.className='XWiki.TagClass' and tobject.name=doc.fullName and tobject.id=tagprop.id.id and tagprop.id.name='tags' and (")
46 #foreach($tag in $request.getParameterValues('tag'))
47 #if($foreach.count > 1) #set($whereSql = "${whereSql} and ") #end
48 ## Tags are case insensitive but they are stored unchanged which means we have to normalize them when performing
49 ## a query. Unfortunately there's no simple way to match multiple tags (AND operator). If we join the list of
50 ## tags in the FROM clause then we match at least one of the tags (OR operator). The only remaining option is to
51 ## check that the list of tags contains each of the desired tags. HQL doesn't help us to lower-case the entire
52 ## list of tags so we use an inner select for this.
53 #if ($whereParams.entrySet())
54 #set($whereSql = "${whereSql} lower(:wikitag${foreach.count}) in (select lower(tag) from tagprop.list tag)")
55 #set($discard = $whereParams.put("wikitag$foreach.count", "${tag}"))
56 #else
57 #set($whereSql = "${whereSql} lower(?) in (select lower(tag) from tagprop.list tag)")
58 #set($discard = $whereParams.add("${tag}"))
59 #end
60 #end
61 #set($whereSql = "${whereSql})")
62 #end
63 ##
64 ##
65 ## Order
66 ##
67 ## if the object for the classname of the order column is not already in the from sql, put it
68 #macro(addObjectClause $objectAlias)
69 #if($fromSql.indexOf($objectAlias) < 0)
70 #set($fromSql = "${fromSql}, BaseObject $objectAlias")
71 #if ($whereParams.entrySet())
72 #set($whereSql = "${whereSql} and ${objectAlias}.name = doc.fullName and ${objectAlias}.className = :${objectAlias}_className")
73 #set($discard = $whereParams.put("${objectAlias}_className", $propClassName))
74 #else
75 #set($whereSql = "${whereSql} and ${objectAlias}.name = doc.fullName and ${objectAlias}.className = ?")
76 #set($discard = $whereParams.add($propClassName))
77 #end
78 #end
79 #end
80 ## Set the order clause for a field. We first ignore the case using the lower function (so that e.g. 'aaa' equals 'AAA')
81 ## but then consider it only for equal values (so that e.g. 'AAA' comes before 'aaa').
82 #macro(setOrderClause $fieldName $direction $useRawValue)
83 #if ($useRawValue)
84 #set($orderSql = " order by ${fieldName} ${direction}")
85 #else
86 #set($orderSql = " order by lower(${fieldName}) ${direction}, ${fieldName} ${direction}")
87 #end
88 #end
89 #set($order = "$!request.sort")
90 #if ($order == 'doc.location')
91 #set ($order = 'doc.fullName')
92 #end
93 #set ($orderSql = '')
94 #if($order != '')
95 #set($orderDirection = "$!{request.get('dir').toLowerCase()}")
96 #if("$!orderDirection" != '' && "$!orderDirection" != 'asc')
97 #set($orderDirection = 'desc')
98 #end
99 #livetable_getTableAlias($order)
100 #if($order.startsWith('doc.'))
101 ## The column is a document field.
102 ##
103 ## These document fields need to be ordered as raw values and not as strings.
104 #set($rawDocumentFields = ['translation', 'date', 'contentUpdateDate', 'creationDate', 'elements', 'minorEdit1', 'hidden'])
105 #set($documentField = $stringtool.removeStart($order, 'doc.'))
106 #setOrderClause(${safe_tableAlias.replace('_','.')}, ${orderDirection}, $rawDocumentFields.contains($documentField))
107 #else
108 ## The column is an object property.
109 ##
110 ## Resolve the property.
111 #livetable_getPropertyClassAndType($order)
112 #set ($multiselect = "$!{propClass.get($order).getProperty('multiSelect').getValue()}")
113 ## We can only handle single values, not multiselect ones.
114 #if ($multiselect != '1')
115 ## Some property types do not need lowercasing since they have unique values by design, so we use the raw values to order.
116 #set($rawPropertyTypes = ['NumberClass', 'BooleanClass', 'DateClass', 'LevelsClass'])
117 ## If the order column is also a filer column, this means that it was already added to the query and all we need to do is to add it to the order clause.
118 #if(!$tablelist.contains($order))
119 ## The order column is not also a filter column, so not yet defined in the query.
120 ## We need to first define it (to the from and where clauses) before we can add it to the order clause.
121 ##
122 ## Resolve the table name of the property to be used in the from clause below.
123 #livetable_getTableName($order)
124 ## If the sort column has a _class specified, join that object in
125 #set($orderObjectAlias = 'obj')
126 #if($propClassName != '' && "$!propClass" != '')
127 ## prepare the alias of the BaseObject table that corresponds to the class of this column
128 #set($orderObjectAlias = "$!{propClassName.replaceAll('[^a-zA-Z0-9_]', '')}_obj")
129 #addObjectClause($orderObjectAlias)
130 #end
131 #set($fromSql = "${fromSql}, ${tableName} ${safe_tableAlias}")
132 ## FIXME: Check if this is indeed a property of the class. Hint: $propType can be used.
133 ## Conditions are put on the object coresponding to the column of the order ($orderObjectAlias), which depends on which is the class of the $order
134 #if ($whereParams.entrySet())
135 #set($whereSql = "${whereSql} and ${orderObjectAlias}.id=${safe_tableAlias}.id.id and ${safe_tableAlias}.name = :${safe_tableAlias}_name")
136 #set($discard = $whereParams.put("${safe_tableAlias}_name", "${order}"))
137 #else
138 #set($whereSql = "${whereSql} and ${orderObjectAlias}.id=${safe_tableAlias}.id.id and ${safe_tableAlias}.name = ?")
139 #set($discard = $whereParams.add("${order}"))
140 #end
141 #end
142 ## Add the column to the order clause.
143 #setOrderClause("${safe_tableAlias}.value", ${orderDirection}, $rawPropertyTypes.contains($propType))
144 #end
145 #end
146 #end
147 ##
148 ##
149 ## Compute the final queries
150 ##
151 #if ($filterParams.entrySet())
152 #set($sqlParams = {})
153 #set($tagsMatchingParams = {})
154 #set($allMatchingParams = {})
155 #else
156 #set($sqlParams = [])
157 #set($tagsMatchingParams = [])
158 #set($allMatchingParams = [])
159 #end
160 #if("$!className" != '')
161 ## Class query
162 #if ($sqlParams.entrySet())
163 #set($sql = ", BaseObject as obj $!fromSql $!filterfrom where obj.name=doc.fullName and obj.className = :className and doc.fullName not in (:classTemplate1, :classTemplate2) $!whereSql $!filterwhere")
164 #set($discard = $sqlParams.put('className', "${className}"))
165 #set($discard = $sqlParams.put('classTemplate1', "${className}Template"))
166 #set($discard = $sqlParams.put('classTemplate2', ${className.replaceAll('Class$', 'Template')}))
167 #set($discard = $sqlParams.putAll($whereParams))
168 #else
169 #set($sql = ", BaseObject as obj $!fromSql $!filterfrom where obj.name=doc.fullName and obj.className = ? and doc.fullName not in (?, ?) $!whereSql $!filterwhere")
170 #set($discard = $sqlParams.addAll(["${className}", "${className}Template", ${className.replaceAll('Class$', 'Template')}]))
171 #set($discard = $sqlParams.addAll($whereParams))
172 #end
173 ##
174 #set($tagsMatchingFiltersFrom = ", BaseObject as obj $!fromSql $!filterfrom")
175 #if ($tagsMatchingParams.entrySet())
176 #set($tagsMatchingFiltersWhere = "obj.name=doc.fullName and obj.className = :className and doc.fullName not in (:classTemplate1, :classTemplate2) $!whereSql $!filterwhere")
177 #set($discard = $tagsMatchingParams.put('className', "${className}"))
178 #set($discard = $tagsMatchingParams.put('classTemplate1', "${className}Template"))
179 #set($discard = $tagsMatchingParams.put('classTemplate2', ${className.replaceAll('Class$', 'Template')}))
180 #set($discard = $tagsMatchingParams.putAll($whereParams))
181 #else
182 #set($tagsMatchingFiltersWhere = "obj.name=doc.fullName and obj.className = ? and doc.fullName not in (?, ?) $!whereSql $!filterwhere")
183 #set($discard = $tagsMatchingParams.addAll(["${className}", "${className}Template", ${className.replaceAll('Class$', 'Template')}]))
184 #set($discard = $tagsMatchingParams.addAll($whereParams))
185 #end
186 ##
187 #set($allMatchingTagsFrom = ", BaseObject as obj $!filterfrom")
188 #if ($allMatchingParams.entrySet())
189 #set($allMatchingTagsWhere = "obj.name=doc.fullName and obj.className = :className and doc.fullName not in (:classTemplate1, :classTemplate2) $!filterwhere")
190 #set($discard = $allMatchingParams.put('className', "${className}"))
191 #set($discard = $allMatchingParams.put('classTemplate1', "${className}Template"))
192 #set($discard = $allMatchingParams.put('classTemplate2', ${className.replaceAll('Class$', 'Template')}))
193 #else
194 #set($allMatchingTagsWhere = "obj.name=doc.fullName and obj.className = ? and doc.fullName not in (?, ?) $!filterwhere")
195 #set($discard = $allMatchingParams.addAll(["${className}", "${className}Template", ${className.replaceAll('Class$', 'Template')}]))
196 #end
197 ##
198 #if($filterParams)
199 #if ($filterParams.entrySet())
200 #set($discard = $sqlParams.putAll($filterParams))
201 #set($discard = $tagsMatchingParams.putAll($filterParams))
202 #set($discard = $allMatchingParams.putAll($filterParams))
203 #else
204 #set($discard = $sqlParams.addAll($filterParams))
205 #set($discard = $tagsMatchingParams.addAll($filterParams))
206 #set($discard = $allMatchingParams.addAll($filterParams))
207 #end
208 #end
209 #else
210 ## Document query
211 #set($sql = "$!fromSql $!filterfrom where 1=1 $!whereSql $!filterwhere")
212 #if ($whereParams.entrySet())
213 #set($discard = $sqlParams.putAll($whereParams))
214 #else
215 #set($discard = $sqlParams.addAll($whereParams))
216 #end
217 ##
218 #set($tagsMatchingFiltersFrom = "$!fromSql $!filterfrom")
219 #set($tagsMatchingFiltersWhere = "1=1 $!whereSql $!filterwhere")
220 #if ($whereParams.entrySet())
221 #set($discard = $tagsMatchingParams.putAll($whereParams))
222 #else
223 #set($discard = $tagsMatchingParams.addAll($whereParams))
224 #end
225 ##
226 #set($allMatchingTagsFrom = "$!filterfrom")
227 #set($allMatchingTagsWhere = "1=1 $!filterwhere")
228 ##
229 #if($filterParams)
230 #if ($filterParams.entrySet())
231 #set($discard = $sqlParams.putAll($filterParams))
232 #set($discard = $tagsMatchingParams.putAll($filterParams))
233 #set($discard = $allMatchingParams.putAll($filterParams))
234 #else
235 #set($discard = $sqlParams.addAll($filterParams))
236 #set($discard = $tagsMatchingParams.addAll($filterParams))
237 #set($discard = $allMatchingParams.addAll($filterParams))
238 #end
239 #end
240 #end
241 #if($orderSql != '')
242 #set($sql = "$sql $!{orderSql}")
243 #end
244 #end
245 #**
246 * Adds TagCloud information to the JSON returned by a live table data call.
247 * NOTE: This macro uses Velocity variables defined by gridresultwithfilter_buildQuery.
248 *
249 * @param $map stores the JSON in memory so that it can be adjusted before serialization
250 *#
251 #macro(gridresult_buildTagCloudJSON $map)
252 ##
253 ## TagCloud matching the current filters
254 ##
255 #set($tagsMatchingFilters = $xwiki.tag.getTagCountForQuery($tagsMatchingFiltersFrom, $tagsMatchingFiltersWhere, $tagsMatchingParams))
256 ## FIXME: We use a map just because the client expects an object, but all we really need is a list..
257 #set($matchingTags = {})
258 #foreach($tag in $tagsMatchingFilters.keySet())
259 ## NOTE: The value doesn't have a special meaning. I've used 1 just because it takes less space when serialized.
260 #set($discard = $matchingTags.put($tag, 1))
261 #end
262 #set($discard = $map.put('matchingtags', $matchingTags))
263 ##
264 ## TagCloud matching all the documents used by the live table
265 ##
266 #set($allMatchingTags = $xwiki.tag.getTagCountForQuery($allMatchingTagsFrom, $allMatchingTagsWhere, $allMatchingParams))
267 ## FIXME: We use a list of maps just because the client expects an array, but we should simply return $allMatchingTags..
268 #set($tags = [])
269 #foreach($tag in $allMatchingTags.keySet())
270 #set($discard = $tags.add({'tag': $tag, 'count': $allMatchingTags.get($tag)}))
271 #end
272 #set($discard = $map.put('tags', $tags))
273 #end
274
275
276 #**
277 * Adds information about each live table row to the JSON returned by a live table data call.
278 * NOTE: This macro uses Velocity variables defined by gridresultwithfilter_buildQuery.
279 *
280 * @param $map stores the JSON in memory so that it can be adjusted before serialization
281 *#
282 #macro(gridresult_buildRowsJSON $map)
283 #set($offset = $numbertool.toNumber($request.get('offset')).intValue())
284 ## Offset starts from 0 in velocity and 1 in javascript
285 #set($offset = $offset - 1)
286 #if(!$offset || $offset < 0)
287 #set($offset = 0)
288 #end
289 #set($limit = $numbertool.toNumber($request.get('limit')).intValue())
290 #if(!$limit)
291 #set ($limit = 15)
292 #end
293 #set($query = $services.query.hql($sql))
294 ## Apply query filters if defined. Otherwise use default.
295 #foreach ($queryFilter in $stringtool.split($!request.queryFilters, ', '))
296 #set ($query = $query.addFilter($queryFilter))
297 #end
298 #set ($query = $query.setLimit($limit).setOffset($offset).bindValues($sqlParams))
299 #set($items = $query.execute())
300 #set($discard = $map.put('totalrows', $query.count()))
301 #set($discard = $map.put('returnedrows', $mathtool.min($items.size(), $limit)))
302 #set($discard = $map.put('offset', $mathtool.add($offset, 1)))
303 #set($rows = [])
304 #foreach($item in $items)
305 #gridresult_buildRowJSON($item $rows)
306 #end
307 #set ($discard = $map.put('rows', $rows))
308 #livetable_filterObfuscated($map)
309 #end
310
311
312 #**
313 * Adds information about the specified live table row to the JSON returned by a live table data call.
314 * NOTE: This macro uses Velocity variables available in gridresult_buildRowsJSON.
315 *
316 * @param $item the name of the document that feeds this live table row
317 * @param $rows stores the JSON in memory so that it can be adjusted before serialization
318 *#
319 #macro(gridresult_buildRowJSON $item $rows)
320 ## Handle both the case where the "language" filter is used and thus languages are returned too and the case where
321 ## only the document name is returned. When more than the document name is returned the $item variable is a list.
322 #if($item.size())
323 ## Extract doc name and doc language from $item
324 #set($docName = $item[0])
325 #set($docLanguage = $item[1])
326 #else
327 #set($docName = $item)
328 #set($docLanguage = '')
329 #end
330 #set ($docReference = $services.model.resolveDocument($docName))
331 #set ($isViewable = $services.security.authorization.hasAccess('view', $docReference))
332 #if ($isViewable)
333 #set ($row = {
334 'doc_viewable': $isViewable,
335 'doc_fullName': $services.model.serialize($docReference, 'local'),
336 'doc_space': $services.model.serialize($docReference.parent, 'local'),
337 'doc_location': "#hierarchy($docReference, {'limit': 5, 'plain': false, 'local': true, 'displayTitle': false})",
338 'doc_url': $xwiki.getURL($docReference),
339 'doc_space_url': $xwiki.getURL($docReference.parent),
340 'doc_wiki': $docReference.wikiReference.name,
341 'doc_wiki_url': $xwiki.getURL($docReference.wikiReference),
342 'doc_hasadmin': $xwiki.hasAdminRights(),
343 'doc_hasedit': $services.security.authorization.hasAccess('edit', $docReference),
344 'doc_hasdelete': $services.security.authorization.hasAccess('delete', $docReference),
345 'doc_edit_url': $xwiki.getURL($docReference, 'edit'),
346 'doc_copy_url': $xwiki.getURL($docReference, 'view', 'xpage=copy'),
347 'doc_delete_url': $xwiki.getURL($docReference, 'delete'),
348 'doc_rename_url': $xwiki.getURL($docReference, 'view', 'xpage=rename&step=1')
349 })
350 #set ($isTranslation = "$!docLanguage" != '' && $xwiki.getLanguagePreference() != $docLanguage)
351 ## Display the language after the document name so that not all translated documents have the same name displayed.
352 #set ($row.doc_name = "$docReference.name#if ($isTranslation) ($docLanguage)#end")
353 #set ($row.doc_hascopy = $row.doc_viewable)
354 #set ($row.doc_hasrename = $row.doc_hasdelete)
355 #set ($row.doc_hasrights = $row.doc_hasedit && $isAdvancedUser)
356 #if ($docReference.name == 'WebHome')
357
358 ## For nested pages, use the page administration.
359 #set ($webPreferencesReference = $services.model.createDocumentReference(
360 'WebPreferences', $docReference.lastSpaceReference))
361 #set ($row.doc_rights_url = $xwiki.getURL($webPreferencesReference, 'admin',
362 'editor=spaceadmin&section=PageRights'))
363 #else
364 ## For terminal pages, use the old rights editor.
365 ## TODO: We should create a page administration for terminal pages too.
366 #set ($row.doc_rights_url = $xwiki.getURL($docReference, 'edit', 'editor=rights'))
367 #end
368 #if ($row.doc_viewable)
369 #set ($itemDoc = $xwiki.getDocument($docReference))
370 ## Handle translations. We need to make sure we display the data associated to the correct document if the returned
371 ## result is a translation.
372 #if ($isTranslation)
373 #set ($translatedDoc = $itemDoc.getTranslatedDocument($docLanguage))
374 #else
375 #set ($translatedDoc = $itemDoc.translatedDocument)
376 #end
377 #set($discard = $itemDoc.use($className))
378 #set($discard = $row.put('doc_objectCount', $itemDoc.getObjectNumbers($className)))
379 #set($discard = $row.put('doc_edit_url', $itemDoc.getURL($itemDoc.defaultEditMode)))
380 #set($discard = $row.put('doc_author_url', $xwiki.getURL($translatedDoc.author)))
381 #set($discard = $row.put('doc_date', $xwiki.formatDate($translatedDoc.date)))
382 #set($discard = $row.put('doc_title', $translatedDoc.plainTitle))
383 #set($rawTitle = $translatedDoc.title)
384 #if($rawTitle != $row['doc_title'])
385 #set($discard = $row.put('doc_title_raw', $rawTitle))
386 #end
387 #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($translatedDoc.authorReference)))
388 #set($discard = $row.put('doc_creationDate', $xwiki.formatDate($translatedDoc.creationDate)))
389 #set($discard = $row.put('doc_creator', $xwiki.getPlainUserName($translatedDoc.creatorReference)))
390 #set($discard = $row.put('doc_hidden', $translatedDoc.isHidden()))
391 #foreach($colname in $collist)
392 #gridresult_buildColumnJSON($colname $row)
393 #end
394 #end
395 #else
396 #set ($row = {
397 'doc_viewable': $isViewable,
398 'doc_fullName': 'obfuscated'
399 })
400 #end
401 #set($discard = $rows.add($row))
402 #end
403
404
405 #**
406 * Adds information about the given column to the JSON returned by a live table data call.
407 * NOTE: This macro uses Velocity variables available in gridresult_buildRowJSON.
408 *
409 * @param $colname the name of the live table column for which to retrieve information
410 * @param $row stores the JSON in memory so that it can be adjusted before serialization
411 *#
412 #macro(gridresult_buildColumnJSON $colname $row)
413 #if($colname.startsWith('doc.'))
414 #elseif($colname == '_action')
415 #set($discard = $row.put($colname, $services.localization.render("${request.transprefix}actiontext")))
416 #elseif($colname == '_attachments')
417 #livetable_getAttachmentsList($translatedDoc)
418 #set($discard = $row.put($colname, $attachlist))
419 #elseif($colname == '_avatar')
420 #livetable_getAvatar($itemDoc)
421 #set($discard = $row.put($colname, $avatar))
422 #elseif($colname == '_images')
423 #livetable_getImagesList($itemDoc)
424 #set($discard = $row.put($colname, $imagesList))
425 ## Output likes if they are available.
426 #elseif($colname == '_likes' && "$!services.like" != "")
427 #set($likes = $services.like.getLikes($docReference))
428 #if ($likes.isPresent())
429 #set($discard = $row.put('_likes', $likes.get()))
430 #end
431 #else
432 #livetable_getPropertyClassAndType($colname)
433 #if(!$propClass.equals($class))
434 #set($discard = $itemDoc.use($propClassName))
435 #end
436 #set($fieldObject = $itemDoc.getFirstObject($colname))
437 #set($fieldProperty = $fieldObject.getProperty($colname))
438 #if ($fieldProperty.getPropertyClass().classType == 'Password')
439 #set($fieldValue = '********')
440 #else
441 #set($fieldValue = "$!fieldProperty.getValue()")
442 #end
443 #set($fieldDisplayValue = "$!itemDoc.display($colname, 'view')")
444 #if($fieldDisplayValue == '')
445 #set($fieldDisplayValue = $services.localization.render("${request.transprefix}emptyvalue"))
446 #end
447 #set($fieldUrl = '')
448 ## Only retrieve an URL for a DBListClass item
449 #if(($propType == 'DBListClass' || $propType == 'PageClass') && $propClass.get($colname).getProperty('multiSelect').value != 1)
450 #set($fieldUrl = $xwiki.getURL($fieldValue))
451 #if($fieldUrl == $xwiki.getURL($services.model.resolveDocument('', 'default', $doc.documentReference.extractReference('WIKI'))))
452 #set($fieldUrl = '')
453 #end
454 #end
455 #set($discard = $row.put($colname, $fieldDisplayValue.replaceFirst($regextool.quote('{{html clean="false" wiki="false"}}'), '').replaceAll("$regextool.quote('{{/html}}')$", '')))
456 #set($discard = $row.put("${colname}_value", $fieldValue))
457 #set($discard = $row.put("${colname}_url", $fieldUrl))
458 ## Reset to the default class
459 #set($discard = $itemDoc.use($className))
460 #end
461 #end
462
463
464 #**
465 * Builds the JSON response to a live table data call.
466 *
467 * @param $map stores the JSON in memory so that it can be adjusted before serialization
468 *#
469 #macro(gridresultwithfilter_buildJSON $className $collist $filterfrom $filterwhere $filterParams $map)
470 #gridresultwithfilter_buildQuery($className $collist $filterfrom $filterwhere $filterParams)
471 #if("$!request.sql" == '1')
472 #set($discard = $map.put('sql', $sql))
473 #set($discard = $map.put('params', $sqlParams))
474 #end
475 #set($discard = $map.put('reqNo', $numbertool.toNumber($request.reqNo).intValue()))
476 #gridresult_buildTagCloudJSON($map)
477 #gridresult_buildRowsJSON($map)
478 #end
479
480
481 #**
482 * Builds the JSON response to a live table data call.
483 *
484 * @param $map stores the JSON in memory so that it can be adjusted before serialization
485 *#
486 #macro(gridresult_buildJSON $className $collist $map)
487 #gridresultwithfilter_buildJSON($className $collist '' '' {} $map)
488 #end
489
490
491 #**
492 * Macro to get the results of a livetable data call.
493 * This page is called from live grids via Ajax with the argument xpage=plain. It returns a
494 * set of results serialized in JSON.
495 *#
496 #macro(gridresultwithfilter $className $collist $filterfrom $filterwhere $filterParams)
497 #if($xcontext.action == 'get' && "$!{request.outputSyntax}" == 'plain')
498 ## Build the JSON in memory (using basic Java data types) so that it can be adjusted before serialization.
499 #set($map = {})
500 #gridresultwithfilter_buildJSON($className $collist $filterfrom $filterwhere $filterParams $map)
501 #jsonResponse($map)
502 #end
503 #end
504
505
506 #**
507 * Get the name of the Property that should be used for a given livetable column.
508 * NOTE the resulting $tableName is safe to use inside SQL queries
509 *#
510 #macro(livetable_getTableName $colname)
511 #livetable_getPropertyClassAndType($colname)
512 #if($propType == 'NumberClass')
513 #set($numberType = $propClass.get($colname).getProperty('numberType').getValue())
514 #if($numberType == 'integer')
515 #set($tableName = 'IntegerProperty')
516 #elseif($numberType == 'float')
517 #set($tableName = 'FloatProperty')
518 #elseif($numberType == 'double')
519 #set($tableName = 'DoubleProperty')
520 #else
521 #set($tableName = 'LongProperty')
522 #end
523 #elseif($propType == 'BooleanClass')
524 #set($tableName = 'IntegerProperty')
525 #elseif($propType == 'DateClass')
526 #set($tableName = 'DateProperty')
527 #elseif($propType == 'TextAreaClass' || $propType == 'UsersClass' || $propType == 'GroupsClass')
528 #set($tableName = 'LargeStringProperty')
529 #elseif($propType == 'StaticListClass' || $propType == 'DBListClass' || $propType == 'DBTreeListClass' || $propType == 'PageClass')
530 #set($multiSelect = $propClass.get($colname).getProperty('multiSelect').getValue())
531 #set($relationalStorage = $propClass.get($colname).getProperty('relationalStorage').getValue())
532 #if($multiSelect == 1)
533 #if($relationalStorage == 1)
534 #set($tableName = 'DBStringListProperty')
535 #else
536 #set($tableName = 'StringListProperty')
537 #end
538 #else
539 #set($tableName = 'StringProperty')
540 #end
541 #else
542 #set($tableName = 'StringProperty')
543 #end
544 #end
545
546 #**
547 * Get the property class and type for a given livetable column.
548 *#
549 #macro(livetable_getPropertyClassAndType $colname)
550 #set($propClassName = "$!request.get(${colname.concat('_class')})")
551 #if($propClassName != '')
552 #set($propClass = $xwiki.getDocument($propClassName).getxWikiClass())
553 #else
554 #set($propClass = $class)
555 #end
556 #set($propType = '')
557 #if($propClass.getPropertyNames().contains($colname))
558 #set($propType = "$!{propClass.get($colname).type}")
559 #end
560 #end
561
562 #**
563 * Old alias of the #livetable_getTableName macro.
564 * @deprecated since 2.2.3, use {@link #livetable_getTableName}
565 *#
566 #macro(grid_gettablename $colname)
567 #livetable_getTableName($colname)
568 #end
569
570
571
572 #**
573 * List attachments for a document, putting the result as HTML markup in the $attachlist variable.
574 *#
575 #macro(livetable_getAttachmentsList $itemDoc)
576 #set($attachlist = '')
577 #foreach($attachment in $itemDoc.attachmentList)
578 #set($attachmentUrl = $itemDoc.getAttachmentURL($attachment.filename))
579 #set($attachlist = "${attachlist}<a href='${attachmentUrl}'>$attachment.filename</a><br/>")
580 #end
581 #end
582
583 #**
584 * Old alias of the #livetable_getAttachmentsList macro.
585 * @deprecated since 2.2.3, use {@link #livetable_getAttachmentsList}
586 *#
587 #macro(grid_attachlist $itemDoc)
588 #livetable_getAttachmentsList($itemDoc)
589 #end
590
591
592
593 #**
594 * List image attachments for a document, putting the result as HTML markup in the $imagesList variable.
595 *#
596 #macro(livetable_getImagesList $itemDoc)
597 #set($imagesList = '')
598 #foreach ($attachment in $itemDoc.attachmentList)
599 #if($attachment.isImage())
600 ## Create a thumbnail by resizing the image on the server side, if needed, to fit inside a 50x50 pixel square.
601 #set($thumbnailURL = $itemDoc.getAttachmentURL($attachment.filename, 'download', "width=50&height=50&keepAspectRatio=true"))
602 #set($imageURL = $itemDoc.getAttachmentURL($attachment.filename))
603 #set($imagesList = "${imagesList}<a href=""$imageURL""><img src=""$thumbnailURL"" alt=""$attachment.filename"" title=""$attachment.filename"" /></a>")
604 #end
605 #end
606 #end
607
608 #**
609 * Old alias of the #livetable_getImagesList macro.
610 * @deprecated since 2.2.3, use {@link #livetable_getImagesList}
611 *#
612 #macro(grid_photolist $itemDoc)
613 #livetable_getImagesList($itemDoc)
614 #end
615
616
617 #**
618 * Generate the HTML code for a user avatar.
619 *#
620 #macro(livetable_getAvatar $itemDoc)
621 #set ($avatar = "#mediumUserAvatar($itemDoc.fullName)")
622 #set ($avatar = $avatar.trim())
623 #end
624
625 #**
626 * Old alias of the #livetable_getAvatar macro.
627 * @deprecated since 2.2.3, use {@link #livetable_getAvatar}
628 *#
629 #macro(grid_avatar $itemDoc)
630 #livetable_getAvatar($itemDoc)
631 #end
632
633
634
635 #**
636 * Macro to extend the query to select the properties for the livetable columns.
637 * NOTE $colName is filtered (all characters but [a-zA-Z0-9_.] are removed) before use
638 *#
639 #macro (livetable_addColumnToQuery $colName)
640 ## Safe because / is not allowed in property names
641 ## The $joinModeMarker is used in #livetable_filterDBStringListProperty.
642 #set ($joinModeMarker = "/join_mode")
643 #if (!$colName.endsWith($joinModeMarker))
644 #set ($filterValue = "$!request.getParameter($colName)")
645 #if ("$!filterValue" != '')
646 #set ($discard = $tablelist.add($colName))
647 ## Some columns may support filtering with multiple constraints (multiple filter values).
648 #set ($filterValues = $request.getParameterValues($colName))
649 #if ($colName.startsWith('doc.'))
650 #if ($colName == 'doc.location')
651 #set ($safeColName = 'doc.fullName')
652 ## Use filterLocation since addLivetableLocationFilter is buggy when called several times (it'll add the
653 ## same HQL binding name every time it's called! See https://jira.xwiki.org/browse/XWIKI-17463).
654 ## Also note that we don't call addLocationFilter since we use a Map for $params.
655 #filterLocation($whereSql, $whereParams, $filterValue, 'locationFilterValue2', true)
656 #elseif ($colName == 'doc.date' || $colName == 'doc.creationDate' || $colName == 'doc.contentUpdateDate')
657 #livetable_getTableAlias($colName)
658 #livetable_filterDateProperty()
659 #else
660 #set ($safeColName = $colName.replaceAll('[^a-zA-Z0-9_.]', '').replace('_', '.'))
661 #if ($whereParams.entrySet())
662 #set ($whereSql = "${whereSql} and upper(str($safeColName)) like upper(:${safeColName.replace('.', '_')}_filter)")
663 #set ($discard = $whereParams.put("${safeColName.replace('.', '_')}_filter", "%$filterValue%"))
664 #else
665 #set ($whereSql = "${whereSql} and upper(str($safeColName)) like upper(?)")
666 #set ($discard = $whereParams.add("%$filterValue%"))
667 #end
668 #end
669 #else
670 #livetable_filterProperty($colName)
671 #end
672 #end
673 #end
674 #end
675
676
677 #**
678 * Determine how the filter values should be matched against the stored values. This macro sets two variables:
679 * <ul>
680 * <li>$matchType: use this when the specified column supports only a single filter value</li>
681 * <li>$matchTypes: use this when the specified column supports multiple filter values.</li>
682 * </ul>
683 *
684 * @param column the column name; each column can have a different match type
685 * @param filterValueCount the number of filter values for which to determine the match type; each filter value can have
686 * a different match type
687 * @param defaultMatchType the default match type to use for the given column when the request doesn't specify one
688 *#
689 #macro (livetable_getMatchTypes $column $filterValueCount $defaultMatchType)
690 #set ($macro.matchTypes = $request.getParameterValues("${column}_match"))
691 #if (!$macro.matchTypes || $macro.matchTypes.isEmpty())
692 ## No match type specified for this column.
693 #set ($matchType = $defaultMatchType)
694 #set ($matchTypes = $stringtool.repeat($matchType, ',', $filterValueCount).split(','))
695 #else
696 ## At least one match type specified for this column.
697 #set ($matchType = $macro.matchTypes.get(0))
698 #set ($matchTypes = [])
699 #set ($discard = $matchTypes.addAll($macro.matchTypes.subList(0, $mathtool.min($macro.matchTypes.size(),
700 $filterValueCount))))
701 #if ($matchTypes.size() < $filterValueCount)
702 ## Add missing match types.
703 #set ($discard = $matchTypes.addAll($stringtool.repeat($matchType, ',', $mathtool.sub($filterValueCount,
704 $matchTypes.size())).split(',')))
705 #end
706 #end
707 #end
708
709
710 #macro (livetable_filterProperty $colname)
711 #livetable_getTableAlias($colname)
712 #livetable_getTableName($colname)
713 #set ($fromSql = "$fromSql, $tableName as $safe_tableAlias")
714 ##
715 ## If the column is not from $class, we need to make sure we join with the proper table.
716 #set ($filterObjectAlias = 'obj')
717 #set ($propClass = $class)
718 #set ($propClassName = $request.getParameter("${colname}_class"))
719 #if ("$!propClassName" != '')
720 #set ($propClass = $xwiki.getDocument($propClassName).getxWikiClass())
721 #if ("$!propClass" != '')
722 ## Prepare the alias of the BaseObject table that corresponds to the class of this column
723 ## Property table is to be joined with its object, determined depending on $propClassName.
724 #set ($filterObjectAlias = "$!{propClassName.replaceAll('[^a-zA-Z0-9_]', '')}_obj")
725 #addObjectClause($filterObjectAlias)
726 #end
727 #end
728 #if ($whereParams.entrySet())
729 #set ($joinObjectTable = "${filterObjectAlias}.id = ${safe_tableAlias}.id.id and ${safe_tableAlias}.id.name = :${safe_tableAlias}_id_name")
730 #set ($discard = $whereParams.put("${safe_tableAlias}_id_name", $colname))
731 #else
732 #set ($joinObjectTable = "${filterObjectAlias}.id = ${safe_tableAlias}.id.id and ${safe_tableAlias}.id.name = ?")
733 #set ($discard = $whereParams.add($colname))
734 #end
735 #set ($whereSql = "$whereSql and $joinObjectTable")
736 ##
737 ## We determine the default match type (when not specified) based on the property meta class (e.g. DateClass).
738 #set ($propMetaClass = $NULL)
739 #if ($propClass && $propClass.getPropertyNames().contains($colname))
740 #set ($propMetaClass = $propClass.get($colname).type)
741 #end
742 ##
743 #set ($numberProperties = ['IntegerProperty', 'LongProperty', 'FloatProperty', 'DoubleProperty'])
744 #if ($numberProperties.contains($tableName))
745 #livetable_filterNumberProperty()
746 #elseif ($tableName == 'DateProperty')
747 #livetable_filterDateProperty()
748 #elseif ($tableName == 'DBStringListProperty')
749 #livetable_filterDBStringListProperty()
750 #elseif ($tableName == 'StringListProperty')
751 #livetable_filterStringListProperty()
752 #else
753 ## StringProperty or LargeStringProperty
754 #livetable_filterStringProperty()
755 #end
756 #end
757
758
759 #**
760 * NOTE: This macro uses variables defined in livetable_filterProperty . It was not meant to be used alone.
761 *#
762 #macro (livetable_filterNumberProperty)
763 #set($numberValue = $numbertool.toNumber($filterValue))
764 #if($tableName == 'IntegerProperty' || $tableName == 'LongProperty')
765 #if($tableName == 'LongProperty')
766 #set($numberValue = $numberValue.longValue())
767 #else
768 ## IntegerProperty
769 #set($numberValue = $numberValue.intValue())
770 #end
771 #if ($whereParams.entrySet())
772 #set($whereSql = "${whereSql} and ${safe_tableAlias}.value = :${safe_tableAlias}_value")
773 #set($discard = $whereParams.put("${safe_tableAlias}_value", $numberValue))
774 #else
775 #set($whereSql = "${whereSql} and ${safe_tableAlias}.value = ?")
776 #set($discard = $whereParams.add($numberValue))
777 #end
778 #else
779 #if($tableName == 'FloatProperty')
780 #set($numberValue = $numberValue.floatValue())
781 #else
782 ## DoubleProperty
783 #set($numberValue = $numberValue.doubleValue())
784 #end
785 #set($precision = 0.000001)
786 #if ($whereParams.entrySet())
787 #set($whereSql = "${whereSql} and abs(:${safe_tableAlias}_value - ${safe_tableAlias}.value) <= ${precision}")
788 #set($discard = $whereParams.put("${safe_tableAlias}_value", $numberValue))
789 #else
790 #set($whereSql = "${whereSql} and abs(? - ${safe_tableAlias}.value) <= ${precision}")
791 #set($discard = $whereParams.add($numberValue))
792 #end
793 #end
794 #end
795
796
797 #**
798 * NOTE: This macro uses variables defined in livetable_filterProperty . It was not meant to be used alone.
799 *#
800 #macro (livetable_filterDateProperty)
801 #if ($safe_tableAlias.startsWith('doc.'))
802 #set ($dateProperty = $safe_tableAlias)
803 #else
804 #set ($dateProperty = "${safe_tableAlias}.value")
805 #end
806 #set ($safeDateProperty = $dateProperty.replace('.', '_'))
807 #set ($dateRange = {})
808 ## Perform partial string matching by default if no match type is specified.
809 ## Note that for the moment we support only one filter value (e.g. one date range) and thus only the first match type
810 ## is taken into account.
811 #livetable_getMatchTypes($colname $filterValues.size() 'partial')
812 #parseDateRange($matchType $filterValue $dateRange)
813 #if ($dateRange.start || $dateRange.end)
814 ## Date range.
815 #if ($dateRange.start)
816 #if ($whereParams.entrySet())
817 #set ($whereSql = "${whereSql} and $dateProperty >= :${safeDateProperty}1")
818 #set ($discard = $whereParams.put("${safeDateProperty}1", $dateRange.start))
819 #else
820 #set ($whereSql = "${whereSql} and $dateProperty >= ?")
821 #set ($discard = $whereParams.add($dateRange.start))
822 #end
823 #end
824 #if ($dateRange.end)
825 #if ($whereParams.entrySet())
826 #set ($whereSql = "${whereSql} and $dateProperty <= :${safeDateProperty}2")
827 #set ($discard = $whereParams.put("${safeDateProperty}2", $dateRange.end))
828 #else
829 #set ($whereSql = "${whereSql} and $dateProperty <= ?")
830 #set ($discard = $whereParams.add($dateRange.end))
831 #end
832 #end
833 #else
834 ## String matching (contains).
835 #if ($whereParams.entrySet())
836 #set ($whereSql = "${whereSql} and upper(str($dateProperty)) like upper(:$safeDateProperty)")
837 #set ($discard = $whereParams.put($safeDateProperty, "%$filterValue%"))
838 #else
839 #set ($whereSql = "${whereSql} and upper(str($dateProperty)) like upper(?)")
840 #set ($discard = $whereParams.add("%$filterValue%"))
841 #end
842 #end
843 #end
844
845
846 #**
847 * NOTE: This macro uses variables defined in livetable_filterProperty . It was not meant to be used alone.
848 *#
849 #macro (livetable_filterDBStringListProperty)
850 ## Perform exact matching by default if no match type is specified.
851 ## Note that for DBStringList properties we take into account only the first match type, even if multiple filter
852 ## values are specified. Basically the first match type is used for all filter values.
853 #livetable_getMatchTypes($colname $filterValues.size() 'exact')
854 #if ($matchType == 'partial' || $matchType == 'prefix')
855 ## We need to join with the list of values in order to be able to use the LIKE operator.
856 #set ($matchTarget = "${safe_tableAlias}_item")
857 #if ($whereParams.entrySet())
858 #set ($paramPrefix = "${safe_tableAlias}_item_")
859 #else
860 #set ($paramPrefix = $NULL)
861 #end
862 #set ($joinPos = $mathtool.add($fromSql.lastIndexOf(" $safe_tableAlias"), $mathtool.add($safe_tableAlias.length(), 1)))
863 #set ($fromSql = "$fromSql.substring(0, $joinPos) join ${safe_tableAlias}.list as $matchTarget $fromSql.substring($joinPos)")
864 #else
865 ## Fall-back on exact matching even if the match type is specified, when its value is not supported.
866 #set ($matchType = 'exact')
867 #set ($matchTarget = "${safe_tableAlias}.list")
868 #if ($whereParams.entrySet())
869 #set ($paramPrefix = "${safe_tableAlias}_list_")
870 #else
871 #set ($paramPrefix = $NULL)
872 #end
873 #end
874 #set ($filterQuery = "#livetable_getFilterQuery($matchTarget $matchType true $filterValues.size() $paramPrefix $NULL)")
875 #set ($whereSql = "$whereSql and ($filterQuery.trim())")
876 #foreach ($filterValue in $filterValues)
877 #livetable_addFilterParam($filterValue $matchType $whereParams "${paramPrefix}${foreach.count}")
878 #end
879 #end
880
881
882 #**
883 * NOTE: This macro uses variables defined in livetable_filterProperty . It was not meant to be used alone.
884 *#
885 #macro (livetable_filterStringListProperty)
886 ## From the user point of view we support only exact matching for StringList properties, due to the way the values of
887 ## these properties are stored (concatenated). But when building the actual query, the match type is in fact partial
888 ## because we have to use the like operator in order to match the concatenated list of values.
889 #livetable_getMatchTypes($colname $filterValues.size() 'exact')
890 #set ($matchTarget = "concat('|', concat(${safe_tableAlias}.textValue, '|'))")
891 #if ($whereParams.entrySet())
892 #set ($paramPrefix = "${safe_tableAlias}_textValue_")
893 #else
894 #set ($paramPrefix = $NULL)
895 #end
896 ## As noted above, we have to use the like operator because the list of values is saved concatenated, so from the
897 ## point of view of the query the match type is always partial.
898 #set ($filterQuery = "#livetable_getFilterQuery($matchTarget 'partial' false $filterValues.size() $paramPrefix $NULL)")
899 #set ($whereSql = "${whereSql} and ($filterQuery.trim())")
900 #foreach ($filterValue in $filterValues)
901 #if ($matchTypes.get($foreach.index) == 'empty')
902 ## The client side cannot pass an empty filter value so it specifies that the value is empty using the match type.
903 #set ($filterValue = '')
904 #end
905 ## As noted above, we can only perform exact matching due to the way the values are stored (concatenated).
906 #livetable_addFilterParam("%|$filterValue|%" 'exact' $whereParams "${paramPrefix}${foreach.count}")
907 #end
908 #end
909
910
911 #**
912 * NOTE: This macro uses variables defined in livetable_filterProperty . It was not meant to be used alone.
913 *#
914 #macro (livetable_filterStringProperty)
915 #if ($propMetaClass.endsWith('ListClass'))
916 ## Perform exact matching by default for StaticListClass, DBListClass and DBTreeListClass
917 ## when they are stored as StringProperty (i.e. single value and no relational storage).
918 #set ($defaultStringMatchType = 'exact')
919 #else
920 ## Perform partial matching by default otherwise.
921 #set ($defaultStringMatchType = 'partial')
922 #end
923 #livetable_getMatchTypes($colname $filterValues.size() $defaultStringMatchType)
924 ## Group the filter values by match type so that we cann optimize the query.
925 #livetable_groupFilterValuesByMatchType($matchTypes $filterValues)
926 #if ($whereParams.entrySet())
927 #set ($paramPrefix = "${safe_tableAlias}_value_")
928 #else
929 #set ($paramPrefix = $NULL)
930 #end
931 ## Note that unlike other property types, the String property supports different match types for different filter
932 ## values. This means we have to call livetable_getFilterQuery for each filter value and then join the constraints
933 ## ourselves.
934 #set ($constraints = [])
935 #set ($paramOffset = 1)
936 #foreach ($entry in $filterValuesByMatchType.entrySet())
937 #set ($matchType = $entry.key)
938 #set ($filterValues = $entry.value)
939 #set ($constraint = "#livetable_getFilterQuery(""${safe_tableAlias}.value"" $matchType false $filterValues.size() $paramPrefix $paramOffset)")
940 #set ($discard = $constraints.add($constraint.trim()))
941 #foreach ($filterValue in $filterValues)
942 #livetable_addFilterParam($filterValue $matchType $whereParams
943 "${paramPrefix}${mathtool.add($paramOffset, $foreach.index)}")
944 #end
945 #set ($paramOffset = $paramOffset + $filterValues.size())
946 #end
947 #set ($whereSql = "${whereSql} and ($stringtool.join($constraints, "" $joinOperator ""))")
948 #end
949
950 #macro (livetable_groupFilterValuesByMatchType $matchTypes $filterValues)
951 #set ($filterValuesByMatchType = {})
952 #foreach ($matchType in $matchTypes)
953 #set ($discard = $filterValuesByMatchType.putIfAbsent($matchType, []))
954 #set ($discard = $filterValuesByMatchType.get($matchType).add($filterValues.get($foreach.index)))
955 #end
956 #end
957
958 #macro (livetable_getJoinOperator $colName)
959 #set ($joinOperator = "$!{request.get(""${colName}${joinModeMarker}"").toUpperCase()}")
960 #if ($joinOperator != 'AND' && $joinOperator != 'OR')
961 #set ($joinOperator = 'AND')
962 #end
963 #end
964
965 #macro (livetable_getFilterQuery $column $matchType $isList $valueCount $paramPrefix $paramOffset)
966 #livetable_getJoinOperator($colname)
967 #if (!$paramOffset)
968 #set ($paramOffset = 1)
969 #end
970 #if ($matchType == 'partial' || $matchType == 'prefix')
971 #livetable_repeatParams("upper($column) like upper(?)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)
972 #elseif($matchType == 'empty')
973 ## Check if the value of the column is like the empty parameter (which is often the empty string), or if the value
974 ## of the column is null (to be compliant with Oracle which stores the empty string as a NULL value).
975 #livetable_repeatParams("($column like ? or $column is null)", " $joinOperator ", $valueCount, $paramPrefix,
976 $paramOffset)
977 #elseif ($isList)
978 #livetable_repeatParams("? in elements($column)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)
979 #elseif ($valueCount > 1 && $joinOperator == 'OR')
980 $column in (#livetable_repeatParams('?', ', ', $valueCount, $paramPrefix, $paramOffset))
981 #else
982 #livetable_repeatParams("$column = ?", ' AND ', $valueCount, $paramPrefix, $paramOffset)
983 #end
984 #end
985
986 #macro (livetable_repeatParams $str $separator $valueCount $paramPrefix $paramOffset)
987 #if ($valueCount > 0)
988 #foreach ($count in [1..$valueCount])
989 #if ($count > 1)
990 $separator##
991 #end
992 #if ($paramPrefix)
993 $str.replace('?', ":${paramPrefix}${mathtool.add($paramOffset, $foreach.index)}")##
994 #else
995 $str##
996 #end
997 #end
998 #end
999 #end
1000
1001 #macro (livetable_addFilterParam $filterValue $matchType $params $paramName)
1002 #if ($matchType == 'partial')
1003 #if ($params.entrySet())
1004 #set ($discard = $params.put($paramName, "%$!filterValue%"))
1005 #else
1006 #set ($discard = $params.add("%$!filterValue%"))
1007 #end
1008 #elseif ($matchType == 'prefix')
1009 #if ($params.entrySet())
1010 #set ($discard = $params.put($paramName, "$!filterValue%"))
1011 #else
1012 #set ($discard = $params.add("$!filterValue%"))
1013 #end
1014 #elseif ($matchType == 'empty')
1015 #if ($params.entrySet())
1016 #set ($discard = $params.put($paramName, ''))
1017 #else
1018 #set ($discard = $params.add(''))
1019 #end
1020 #else
1021 #if ($params.entrySet())
1022 #set ($discard = $params.put($paramName, $filterValue))
1023 #else
1024 #set ($discard = $params.add($filterValue))
1025 #end
1026 #end
1027 #end
1028
1029
1030 #**
1031 * Old alias of the #livetable_addColumnToQuery macro.
1032 * @deprecated since 2.2.3, use {@link #livetable_addColumnToQuery}
1033 *#
1034 #macro(grid_addcolumn $colname)
1035 #livetable_addColumnToQuery($colname)
1036 #end
1037
1038 #**
1039 * Generates a valid SQL table alias for the specified live table column.
1040 *#
1041 #macro (livetable_getTableAlias $columnName)
1042 #set ($prefix = 'doc.')
1043 #if ($columnName.startsWith($prefix))
1044 #set ($suffix = $stringtool.removeStart($columnName, $prefix))
1045 #else
1046 ## Force a prefix to avoid the cases when the column name is a reserved SQL keyword.
1047 #set ($prefix = 'prop_')
1048 #set ($suffix = $columnName)
1049 #end
1050 ## Remove non-word characters.
1051 #set ($safe_tableAlias = "$prefix$suffix.replaceAll('\W', '')")
1052 #end
1053 {{/velocity}}