Hide last authors
Fabio Scubla 1.1 1 {{include reference="AppWithinMinutes.VelocityMacros" /}}
2
3 {{groovy}}
4 import com.xpn.xwiki.XWikiContext;
5 import com.xpn.xwiki.api.Context;
6 import com.xpn.xwiki.api.Object;
7 import com.xpn.xwiki.api.PropertyClass;
8 import com.xpn.xwiki.doc.XWikiDocument;
9 import com.xpn.xwiki.objects.BaseObject;
10
11 /**
12 * Used to preview class fields that have a custom display associated, before they are actually added/saved to the
13 * class. For instance, when the user drags a Date field from the palette to the field canvas the class editor needs to
14 * display that Date field as if the user would be editing an object with this Date field in "Inline form" edit mode.
15 * This means that if the Date field has a custom display, the custom display should be used (e.g. using a Date picker).
16 */
17 class PropertyCustomDisplayer
18 {
19 private XWikiContext context;
20
21 public PropertyCustomDisplayer(Context context)
22 {
23 this.context = context.getContext();
24 }
25
26 public String display(PropertyClass property, String prefix, com.xpn.xwiki.api.Object object)
27 {
28 HashMap<String, Object> backup = new HashMap<String, Object>();
29 try {
30 XWikiDocument.backupContext(backup, this.context);
31 return this.displayInternal(property.getPropertyClass(), prefix, object.getXWikiObject());
32 } finally {
33 XWikiDocument.restoreContext(backup, this.context);
34 }
35 }
36
37 private String displayInternal(com.xpn.xwiki.objects.classes.PropertyClass property, String prefix, BaseObject object)
38 {
39 StringBuffer result = new StringBuffer();
40 property.displayCustom(result, property.getName(), prefix, "edit", object, this.context);
41 return result.toString();
42 }
43 }
44 xcontext.put('propertyCustomDisplayer', new PropertyCustomDisplayer(xcontext))
45 {{/groovy}}
46
47 {{velocity output="false"}}
48 #**
49 * Constants
50 *#
51 ## Magic date used to mark in AWM that the date field is not set for the current entry. See https://jira.xwiki.org/browse/XWIKI-10296
52 #set($MAGIC_DATE = $datetool.toDate('yyyy-MM-dd', '9999-12-31'))
53
54 #**
55 * Displays the field palette.
56 *#
57 #macro (displayFieldPalette)
58 <div id="palette">
59 <p><strong>$services.localization.render('platform.appwithinminutes.classEditorPaletteTitle')</strong></p>
60 <p class="xHint">$services.localization.render('platform.appwithinminutes.classEditorPaletteHint')</p>
61 ## List all form field types, grouped by category.
62 #set ($formFieldDocs = [])
63 #set ($formFieldClassName = 'AppWithinMinutes.FormFieldClass')
64 #set ($categoryListStatement = 'from doc.object(AppWithinMinutes.FormFieldCategoryClass) as category order by category.priority')
65 <ul>
66 #foreach ($category in $services.query.xwql($categoryListStatement).execute())
67 #set ($categoryDoc = $xwiki.getDocument($category))
68 <li>
69 <div class="category">$categoryDoc.plainTitle</div>
70 #set ($formFieldsForCategoryStatement = "from doc.object($formFieldClassName) as field where field.category = :category order by field.priority")
71 #set ($formFieldsForCategoryQuery = $services.query.xwql($formFieldsForCategoryStatement).bindValue('category', $category))
72 <ul>
73 #foreach ($formField in $formFieldsForCategoryQuery.execute())
74 #set ($formFieldDoc = $xwiki.getDocument($formField))
75 #set ($discard = $formFieldDocs.add($formFieldDoc))
76 #set ($formFieldIcon = $formFieldDoc.getObject($formFieldClassName).getProperty('icon').value)
77 #set ($formFieldIconRendered = $services.icon.renderHTML($formFieldIcon))
78 #if ("$!formFieldIconRendered" == "")
79 #if ($formFieldIcon.contains('/'))
80 #set ($formFieldIconURL = $xwiki.getSkinFile($formFieldIcon))
81 #else
82 #set ($formFieldIconURL = $formFieldDoc.getAttachmentURL($formFieldIcon))
83 #end
84 #set ($formFieldIconRendered = "<img src='$formFieldIconURL' alt='$escapetool.xml($formFieldDoc.plainTitle)' class='icon' />")
85 #end
86 <li class="field">
87 $formFieldIconRendered
88 $escapetool.xml($formFieldDoc.plainTitle)
89 ## FIXME: We should use the 'get' action instead to prevent the stats module from recording this AJAX request.
90 ## The 'edit' action is a temporary solution until the sheet module is modified to allow a sheet to be enforced through
91 ## the query string even if it doesn't match the action (e.g. the 'get' action).
92 ## The sheet parameter is required when editing a new class because the request will be made to a document that doesn't exist.
93 ## FIXME2: In the future don't force the text editor type and instead use the default editor. This means
94 ## that if the WYSIWYG editor is used, we'll need to convert the HTML into the target syntax so that the
95 ## Template in #updateAndSaveTemplate is saved with target syntax and not HTML.
96 ## See https://jira.xwiki.org/browse/XWIKI-13789
97 #set ($fieldURL = $doc.getURL('edit', $escapetool.url({
98 'xpage': 'plain',
99 'sheet': 'AppWithinMinutes.ClassEditSheet',
100 'template': 'AppWithinMinutes.ClassTemplate',
101 'field': $formFieldDoc.fullName,
102 'xeditmode': 'text'
103 })))
104 <input type="hidden" value="$fieldURL" class="data"/>
105 </li>
106 #end
107 </ul>
108 </li>
109 #end
110 </ul>
111 </div>
112 #end
113
114 #**
115 * Displays the field canvas.
116 *#
117 #macro (displayFieldCanvas)
118 #set ($propertyType2FormField = {})
119 #foreach ($formFieldDoc in $formFieldDocs)
120 ## Use the type of the field template.
121 #set ($type = $formFieldDoc.getxWikiClass().properties.get(0).classType)
122 #set ($discard = $propertyType2FormField.put($type, $formFieldDoc))
123 #end
124 <div id="canvas">
125 <p class="hint">
126 $services.localization.render('platform.appwithinminutes.classEditorCanvasHint')
127 </p>
128 <ul>
129 #set ($unknownFields = [])
130 #foreach ($field in $doc.getxWikiClass().properties)
131 #set ($formFieldDoc = $propertyType2FormField.get($field.classType))
132 #if ($formFieldDoc)
133 <li>#displayField($field $formFieldDoc)</li>
134 #else
135 #set($discard = $unknownFields.add($field))
136 #end
137 #end
138 </ul>
139 <div class="hidden">
140 ## Output the field meta data even if the field is not supported to preserve it when the class is saved.
141 #foreach ($field in $unknownFields)
142 #displayFieldMetaData($field)
143 #end
144 </div>
145 </div>
146 #end
147
148 #**
149 * Display the options to create/update the class template, the class sheet and the class translation bundle.
150 *#
151 #macro (displayClassOptions)
152 #set ($className = $stringtool.removeEnd($doc.fullName, 'Class'))
153 #set ($templateReference = $services.model.resolveDocument("${className}Template"))
154 #set ($translationsReference = $services.model.resolveDocument("${className}Translations"))
155 #set ($classSheets = $services.sheet.getClassSheets($doc))
156 #set ($sheetReference = $null)
157 #if ($classSheets.isEmpty())
158 #set ($sheetReference = $services.model.resolveDocument("${className}Sheet"))
159 #elseif ($classSheets.size() == 1)
160 #set ($sheetReference = $classSheets.get(0))
161 #end
162 ## Hide the options if neither the sheet nor the template nor the translation bundle exists. They don't have to be
163 ## updated, they have to be created.
164 <dl id="options" #if (!$xwiki.exists($sheetReference) && !$xwiki.exists($templateReference)
165 && !$xwiki.exists($translationsReference))class="hidden"#end>
166 <dt>
167 <label for="updateClassTemplate">
168 <input type="checkbox" id="updateClassTemplate" name="updateClassTemplate" checked="checked" />
169 $services.localization.render('platform.appwithinminutes.classEditorUpdateTemplateLabel')
170 </label>
171 </dt>
172 <dd>
173 <span class="xHint">
174 $services.localization.render('platform.appwithinminutes.classEditorUpdateTemplateHint',
175 ["#pageLink($templateReference)"])
176 </span>
177 </dd>
178 <dt>
179 <label for="updateClassSheet">
180 <input type="checkbox" id="updateClassSheet" name="updateClassSheet"
181 #if ($sheetReference)checked="checked" #{else}disabled="disabled" #end/>
182 $services.localization.render('platform.appwithinminutes.classEditorUpdateSheetLabel')
183 </label>
184 </dt>
185 <dd>
186 #if ($sheetReference)
187 <span class="xHint">
188 $services.localization.render('platform.appwithinminutes.classEditorUpdateSheetHint',
189 ["#pageLink($sheetReference)"])
190 </span>
191 #else
192 <span class="warningmessage">
193 $services.localization.render('platform.appwithinminutes.classEditorMultipleSheetsWarning')
194 </span>
195 #end
196 </dd>
197 <dt>
198 <label for="updateClassTranslations">
199 <input type="checkbox" id="updateClassTranslations" name="updateClassTranslations" checked="checked" />
200 $services.localization.render('platform.appwithinminutes.classEditorUpdateTranslationsLabel')
201 </label>
202 </dt>
203 <dd>
204 <span class="xHint">
205 $services.localization.render('platform.appwithinminutes.classEditorUpdateTranslationsHint',
206 ["#pageLink($translationsReference)"])
207 </span>
208 </dd>
209 </dl>
210 #end
211
212 #macro (pageLink $reference)
213 #set ($class = 'wikilink')
214 #set ($action = 'view')
215 #set ($params = {})
216 #if (!$xwiki.exists($reference))
217 #set ($class = 'wikicreatelink')
218 #set ($action = 'create')
219 #set ($discard = $params.put('parent', $doc.fullName))
220 #end
221 <span class="$class"><a href="$escapetool.xml($xwiki.getURL($reference, $action, $escapetool.url($params)))"
222 >$escapetool.xml($reference.name)</a></span>##
223 #end
224
225 #**
226 * Display a form field.
227 *#
228 #macro (displayField $field $formFieldDoc)
229 #if ($formFieldDoc.getObject('XWiki.StyleSheetExtension'))
230 #set ($discard = $xwiki.ssx.use($formFieldDoc.fullName))
231 #end
232 #if ($formFieldDoc.getObject('XWiki.JavaScriptExtension'))
233 #set ($discard = $xwiki.jsx.use($formFieldDoc.fullName))
234 #end
235 <div class="hidden">
236 #displayFieldMetaData($field)
237 ## We need this information to avoid querying and loading all FormField documents twice.
238 ## NOTE: We use a different ID format to avoid collisions with the field meta properties.
239 <input type="hidden" id="template-$field.name" name="template-$field.name"
240 value="$escapetool.xml($formFieldDoc.fullName)"
241 data-propertyName="$escapetool.xml($formFieldDoc.getxWikiClass().propertyNames[0])" />
242 </div>
243 #set ($className = $stringtool.removeEnd($doc.fullName, 'Class'))
244 #set ($templateRef = $services.model.resolveDocument("${className}Template"))
245 #set ($templateDoc = $xwiki.getDocument($templateRef))
246 ## Simulate the editing of the class instance from the template document.
247 ## Note that we can't simply call display on the template document because $field could be a new field that hasn't
248 ## been added to the class yet (so the object from the template doesn't have this field yet).
249 <dl class="field-viewer">
250 #displayFieldProperty($field "${doc.fullName}_0_" $templateDoc.getObject($doc.fullName, true))
251 </dl>
252 #set ($propertyNames = ['name', 'prettyName', 'number', 'required', 'hint'])
253 #set ($formFieldObj = $formFieldDoc.getObject('AppWithinMinutes.FormFieldClass'))
254 #set ($customPropertyNames = $formFieldObj.getProperty('properties').value.split('\s+'))
255 #set ($discard = $customPropertyNames.removeAll($propertyNames))
256 #set ($discard = $propertyNames.addAll($customPropertyNames.subList(0, $customPropertyNames.size())))
257 <dl class="field-config">
258 #foreach ($propertyName in $propertyNames)
259 #set ($propertyDefinition = $field.xWikiClass.get($propertyName))
260 #if ($propertyDefinition)
261 #displayFieldProperty($propertyDefinition "field-${field.name}_" $field)
262 #end
263 #end
264 </dl>
265 #end
266
267 #**
268 * Display the field meta data. This is needed to preserve the field when its type is not supported by the editor.
269 *#
270 #macro (displayFieldMetaData $field)
271 <input type="hidden" id="type-$field.name" name="type-$field.name" value="$field.classType" />
272 #end
273
274 #**
275 * Displays a configuration property of a class field. This macro can also be used to display a property of an object.
276 *#
277 #macro (displayFieldProperty $property $prefix $field)
278 #set ($displayFormType = $property.getProperty('displayFormType'))
279 #if ($property.classType == 'Boolean' && (!$displayFormType || $displayFormType.value == 'checkbox'))
280 <dt>
281 <label for="$!{prefix}$property.name">
282 #displayPropertyEditInput($property, $prefix, $field)$escapetool.xml($property.prettyName)
283 </label>
284 </dt>
285 <dd></dd>
286 #else
287 <dt><label for="${prefix}$property.name">$escapetool.xml($property.prettyName)</label></dt>
288 <dd>#displayPropertyEditInput($property, $prefix, $field)</dd>
289 #end
290 #end
291
292 #**
293 * Displays the input used to edit the specified property of the given object. The given object can be either an
294 * instance of an XWiki class or a class field. In the first case the property represents an object field and in the
295 * second case the property represents a field meta property. We currently don't use custom display for metaproperty,
296 * so in that case we fallback on displayEdit.
297 *#
298 #macro (displayPropertyEditInput $property $prefix $object)
299 #set ($wrappedProperty = $property.propertyClass)
300 #if ($wrappedProperty.isCustomDisplayed($xcontext.context))
301 #set ($customDisplayer = $!xcontext.get('propertyCustomDisplayer').display($property, $prefix, $object))
302 #if ((! $customDisplayer) && ("$!customDisplayer" == ""))
303 $doc.displayEdit($property, $prefix, $object)
304 #else
305 $customDisplayer
306 #end
307 #else
308 $doc.displayEdit($property, $prefix, $object)
309 #end
310 #end
311
312 #**
313 * Called when a new form field is added via AJAX.
314 *#
315 #macro (displayNewField)
316 ## Output the SkinExtension hooks to allow field displayers to pull JavaScript/CSS resources.
317 ## Output also the LinkExtension hook because $xwiki.linkx.use() is used to load CSS files from WebJars.
318 ## The class editor moves this resource includes in the HTML page head.
319 <!-- com.xpn.xwiki.plugin.skinx.LinkExtensionPlugin -->
320 #skinExtensionHooks
321 #set ($formFieldDoc = $xwiki.getDocument($request.field))
322 #set ($formFieldDocClassFields = $formFieldDoc.getxWikiClass().getXWikiClass().properties)
323 #if ($formFieldDocClassFields.size() > 0)
324 ## Clone the field template.
325 #set ($field = $formFieldDocClassFields.get(0).clone())
326 #if ("$!field.prettyName" == '')
327 #set ($discard = $field.setPrettyName($formFieldDoc.title))
328 #end
329 #set ($xclass = $doc.getxWikiClass().getXWikiClass())
330 #set ($discard = $xclass.addField($field.name, $field))
331 #set ($discard = $field.setObject($xclass))
332 #displayField($doc.getxWikiClass().get($field.name) $formFieldDoc)
333 #else
334 Unsupported form field.
335 #end
336 #end
337
338 #**
339 * Preview a class field (requires Programming Right).
340 *#
341 #macro (previewField)
342 ## Find the request parameter that specifies the field template.
343 #foreach ($paramName in $request.getParameterMap().keySet())
344 #if ($paramName.startsWith('template-'))
345 #set ($fieldName = $paramName.substring(9))
346 #set ($fieldTemplateDoc = $xwiki.getDocument($request.getParameter($paramName)))
347 #break
348 #end
349 #end
350 ##
351 ## Clone the field template.
352 #set ($field = $fieldTemplateDoc.getxWikiClass().getXWikiClass().properties.get(0).clone())
353 ##
354 ## Update the field meta properties based on the submitted data.
355 #set ($valuesFromRequest = $xcontext.context.getForm().getObject("field-$fieldName"))
356 #set ($discard = $field.getxWikiClass().fromMap($valuesFromRequest, $field))
357 ##
358 ## Don't rename the field (ignore the submitted name).
359 #set ($discard = $field.setName($fieldName))
360 ##
361 ## We have to add the field to the class before setting its value.
362 ## (otherwise the field value from the request is ignored).
363 #set ($xclass = $doc.getxWikiClass().getXWikiClass())
364 #set ($discard = $xclass.addField($fieldName, $field))
365 #set ($discard = $field.setObject($xclass))
366 ##
367 ## Create an object that has this field and set its value from request.
368 #set ($object = $fieldTemplateDoc.getObject($doc.fullName, true))
369 ##
370 ## Filter empty values from the request, otherwise the update method could try to select an invalid value.
371 #set ($values = [])
372 #foreach ($value in $request.getParameterValues("${doc.fullName}_0_$fieldName"))
373 #if ($value != '')
374 #set ($discard = $values.add($value))
375 #end
376 #end
377 #if ($values.size() > 0)
378 #set ($stringArray = $request.getParameterValues("template-$fieldName"))
379 #set ($discard = $xclass.fromMap({$fieldName: $values.toArray($stringArray)}, $object.getXWikiObject()))
380 #end
381 ##
382 ## Display the field.
383 #set ($field = $doc.getxWikiClass().get($fieldName))
384 #displayPropertyEditInput($field, "${doc.fullName}_0_", $object)
385 #end
386
387 #**
388 * Display the edit class form.
389 *#
390 #macro (displayEditForm)
391 #set ($discard = $xwiki.jsfx.use('js/scriptaculous/effects.js'))
392 #set ($discard = $xwiki.jsfx.use('js/scriptaculous/dragdrop.js'))
393 #set ($discard = $xwiki.jsx.use('AppWithinMinutes.ClassEditSheet'))
394 #set ($discard = $xwiki.ssx.use('AppWithinMinutes.ClassEditSheet'))
395 #set ($discard = $xwiki.ssx.use('AppWithinMinutes.ClassSheetGenerator'))
396 #if ("$!request.wizard" == 'true')
397 #appWizardHeader('structure')
398 #end
399 #displayFieldPalette()
400 #displayFieldCanvas()
401 #displayClassOptions()
402 #if("$!request.wizard" == 'true')
403 #appWizardFooter('structure')
404 #end
405 <div class="clearfloats"></div>
406 #end
407
408 #**
409 * Displays either the edit class form or a new form field. The later is used when adding a new form field via AJAX.
410 *#
411 #macro (doEdit)
412 #if ("$!request.field" != '')
413 #displayNewField()
414 #elseif ("$!request.preview" == 'true')
415 #previewField()
416 #else
417 ## Make sure that only the sheet content is rendered when the class is saved using AJAX.
418 <div class="hidden">
419 <input type="hidden" name="xpage" value="plain" />
420 #if ($request.wizard == 'true')
421 ## Preserve the wizard mode.
422 <input type="hidden" name="wizard" value="true" />
423 #end
424 ## Compute the application title to be used as the wizard step title.
425 #getAppTitle
426 </div>
427 #displayEditForm()
428 #end
429 #end
430
431 #**
432 * Create the home page of the application code space, if it doesn't exist already.
433 *#
434 #macro (maybeCreateCodeSpace)
435 #set ($codeHomePageReference = $services.model.resolveDocument('', 'default', $doc.documentReference.parent))
436 #if (!$xwiki.exists($codeHomePageReference))
437 #set ($codeSpaceTemplate = $services.model.resolveDocument('AppWithinMinutes.CodeSpaceTemplate'))
438 #set ($copyAsJob = $services.refactoring.copyAs($codeSpaceTemplate, $codeHomePageReference))
439 #try()
440 #set ($discard = $copyAsJob.join())
441 #set ($copyAsJobStatus = $services.job.getJobStatus($copyAsJob.request.id))
442 #set ($errorMessage = $copyAsJobStatus.logTail.getFirstLogEvent('ERROR').toString())
443 #end
444 #end
445 #end
446
447 #**
448 * Updates and saves the class definition based on the submitted data.
449 *#
450 #macro(updateAndSaveClass)
451 #set($class = $doc.xWikiClass)
452 #set($xclass = $class.getXWikiClass().clone())
453 #set($xdoc = $doc.document)
454 ##
455 ## Handle new fields and field type changes.
456 ##
457 #set($fieldNames = [])
458 #foreach($paramName in $request.getParameterMap().keySet())
459 #if($paramName.startsWith('type-'))
460 #set($fieldName = $paramName.substring(5))
461 #set($fieldType = $request.getParameter($paramName))
462 #set($field = $class.get($fieldName))
463 #if(!$field || $field.classType != $fieldType)
464 #if($field)
465 ## The field type has changed. Remove the field and add a new one with the proper type.
466 #set($discard = $xclass.removeField($fieldName))
467 #end
468 ## Add a new class field with the specified type.
469 #set($fieldTemplateRef = $request.getParameter("template-$fieldName"))
470 #if("$!fieldTemplateRef" != '')
471 #set($fieldTemplateDoc = $xwiki.getDocument($fieldTemplateRef))
472 #set($field = $fieldTemplateDoc.getxWikiClass().getXWikiClass().properties.get(0).clone())
473 #set($discard = $field.setObject($xclass))
474 #set($discard = $xclass.addField($fieldName, $field))
475 #set($discard = $fieldNames.add($fieldName))
476 #set($discard = $xdoc.setMetaDataDirty(true))
477 #end
478 #else
479 #set($discard = $fieldNames.add($fieldName))
480 #end
481 #end
482 #end
483 ##
484 ## Handle deleted fields.
485 ##
486 #foreach($field in $class.properties)
487 #if(!$fieldNames.contains($field.name))
488 #set($discard = $xclass.removeField($field.name))
489 #end
490 #end
491 ##
492 ## Handle field updates.
493 ##
494 #set($fieldsToRename = {})
495 #foreach($fieldName in $xclass.propertyNames)
496 #set($field = $xclass.get($fieldName))
497 #set($valuesFromRequest = $xcontext.context.getForm().getObject("field-$fieldName"))
498 #set($discard = $field.getxWikiClass().fromMap($valuesFromRequest, $field))
499 #if($field.name.matches('^[a-zA-Z_][\w:\-\.]*$'))
500 #if($fieldName != $field.name)
501 ## The field name has changed.
502 #if($xclass.get($field.name))
503 ## There is already a field with the same name.
504 #set($errorMessage = $services.localization.render('platform.appwithinminutes.classEditorDuplicateFieldNameError', [$field.name]))
505 #break
506 #else
507 #set($discard = $xclass.removeField($fieldName))
508 #set($discard = $xclass.addField($field.name, $field))
509 #set($originalField = $class.get($fieldName))
510 #if($originalField)
511 ## This is not a new field.
512 #set($discard = $fieldsToRename.put($fieldName, $field.name))
513 #set($discard = $xclass.addPropertyForRemoval($originalField.propertyClass))
514 #end
515 #end
516 #end
517 #else
518 #set($errorMessage = $services.localization.render('propertynamenotcorrect'))
519 #break
520 #end
521 #end
522 ##
523 ## Save
524 ##
525 #if(!$errorMessage)
526 #set($discard = $xdoc.setXClass($xclass))
527 #set($discard = $xdoc.renameProperties($doc.documentReference, $fieldsToRename))
528 #set($discard = $xdoc.setHidden(true))
529 #set($discard = $xdoc.setMetaDataDirty(true))
530 #set($discard = $doc.save($services.localization.render('core.comment.updateClassProperty'), $minorEdit))
531 #end
532 ##
533 ## Handle field renames.
534 ##
535 #if(!$errorMessage && !$fieldsToRename.isEmpty())
536 ## We need to load all documents (except the class and template, which we handle below) that have objects of this class and rename their properties.
537 ## If we don`t skip the template, we can not control the behaviour of emptyIsToday for date fields, which we want to handle in #updateAndSaveTemplate only once.
538 ##
539 ## FIXME: even if it is not a good practice to have an object in the class document, it is still possible. We should handle field renames for the class document
540 ## as well. Note that there is a possibility that objects in the class' document are automatically updated. Needs checking.
541 ##
542 ## We use HQL because XWQL doesn't allow us to escape the special characters from the class name.
543 #set($instancesStatement = ', BaseObject as obj where doc.fullName = obj.name and obj.className = :className'
544 + ' and doc.fullName not in (:className, :templateName)')
545 #set($className = $stringtool.removeEnd($doc.fullName, 'Class'))
546 #set($instancesQuery = $services.query.hql($instancesStatement).bindValue('className', $doc.fullName).bindValue(
547 'templateName', "${className}Template"))
548 #foreach($instanceDocName in $instancesQuery.execute())
549 #set($instanceDoc = $xwiki.getDocument($instanceDocName))
550 #set($discard = $instanceDoc.document.renameProperties($doc.documentReference, $fieldsToRename))
551 #set($discard = $instanceDoc.save($services.localization.render('core.comment.updateClassPropertyName'), true))
552 #end
553 #end
554 #end
555
556 #**
557 * Handle Date fields that have the "Empty is today" option checked in the class edit form.
558 * See https://jira.xwiki.org/browse/XWIKI-10296
559 **#
560 #macro(handleEmptyIsTodayDateFields $templateDoc)
561 #foreach($property in $doc.xWikiClass.properties)
562 ## We check directly on the request if the user provided an empty date. We can not check from the template
563 ## document's object that we've just parsed from the request using the updateObjectFromRequest method because it
564 ## already applies the emtpyIsToday mechanism and that would not be good for us.
565 #set($newValueRequestParameterName = "${doc.fullName}_0_${property.name}")
566 #set($newDateStringValue = "$!{request.getParameter($newValueRequestParameterName)}")
567 #if($property.classType == 'Date' && $property.getValue('emptyIsToday') == 1 && $newDateStringValue == '')
568 #set($discard = $templateDoc.set($property.name, $MAGIC_DATE))
569 #end
570 #end
571 #end
572
573 #**
574 * Updates and saves the class template based on the submitted data.
575 *#
576 #macro(updateAndSaveTemplate)
577 #if(!$errorMessage && $request.updateClassTemplate)
578 #set($className = $stringtool.removeEnd($doc.fullName, 'Class'))
579 #set($templateRef = $services.model.resolveDocument("${className}Template"))
580 #set($templateDoc = $xwiki.getDocument($templateRef))
581 #set($discard = $templateDoc.setParent($doc.documentReference.name))
582 #if ($request.templateTitle)
583 #set($discard = $templateDoc.setTitle($request.templateTitle))
584 #end
585 #if ($request.templateContent)
586 #set($discard = $templateDoc.setContent($request.templateContent))
587 #end
588 ## Rename the properties of the template's object, if applicable.
589 #set($discard = $templateDoc.document.renameProperties($doc.documentReference, $fieldsToRename))
590 ## Fill the template's object with the default values from the class editor's form.
591 #set($discard = $templateDoc.updateObjectFromRequest($doc.fullName))
592 ##
593 #handleEmptyIsTodayDateFields($templateDoc)
594 #set($discard = $templateDoc.setHidden(true))
595 #set($discard = $templateDoc.save(
596 $services.localization.render('platform.appwithinminutes.classEditorTemplateSaveComment'),
597 $minorEdit))
598 #end
599 #end
600
601 #**
602 * Updates and saves the class sheet based on the submitted data.
603 *#
604 #macro(updateAndSaveSheet)
605 #if(!$errorMessage && $request.updateClassSheet)
606 #set($classSheets = $services.sheet.getClassSheets($doc))
607 #if($classSheets.isEmpty())
608 #set($className = $stringtool.removeEnd($doc.fullName, 'Class'))
609 #set($sheetReference = $services.model.resolveDocument("${className}Sheet"))
610 #set($discard = $services.sheet.bindClassSheet($doc, $sheetReference))
611 #set($discard = $doc.save($services.localization.render('platform.appwithinminutes.classEditorBindSheetSaveComment'),
612 $minorEdit))
613 #elseif($classSheets.size() == 1)
614 #set($sheetReference = $classSheets.get(0))
615 #end
616 #if($sheetReference)
617 #set($sheetDoc = $xwiki.getDocument($sheetReference))
618 #set($sheetGeneratorDoc = $xwiki.getDocument('AppWithinMinutes.ClassSheetGenerator'))
619 #set($discard = $sheetDoc.setParent($doc.documentReference.name))
620 #set($discard = $sheetDoc.setContent($doc.getRenderedContent($sheetGeneratorDoc.content,
621 $sheetGeneratorDoc.syntax.toIdString(), 'plain/1.0')))
622 ## We assume for now that the output produced by the sheet generator uses the same syntax as the code of the sheet
623 ## generator. We have to set the syntax because the default wiki syntax (used when creating new wiki pages) could
624 ## be different than the one used by the sheet generator.
625 #set($discard = $sheetDoc.setSyntax($sheetGeneratorDoc.syntax))
626 #set($discard = $sheetDoc.setHidden(true))
627 #set($discard = $sheetDoc.save($services.localization.render('platform.appwithinminutes.classEditorSheetSaveComment'),
628 $minorEdit))
629 #end
630 #end
631 #end
632
633 #**
634 * Updates and saves the class translation bundle based on the submitted data.
635 *#
636 #macro(updateAndSaveTranslations)
637 #if(!$errorMessage && $request.updateClassTranslations)
638 #set($className = $stringtool.removeEnd($doc.fullName, 'Class'))
639 #set($translationsRef = $services.model.resolveDocument("${className}Translations"))
640 #set($translationsDoc = $xwiki.getDocument($translationsRef))
641 #set($translationsObj = $translationsDoc.getObject('XWiki.TranslationDocumentClass', true))
642 #set ($scope = 'USER')
643 #if ($services.security.authorization.hasAccess('admin', $doc.documentReference.wikiReference))
644 #set ($scope = 'WIKI')
645 #end
646 #set($discard = $translationsObj.set('scope', $scope))
647 #set($discard = $translationsDoc.setParent($doc.documentReference.name))
648 #set($translationsGeneratorDoc = $xwiki.getDocument('AppWithinMinutes.ClassTranslationsGenerator'))
649 #set($discard = $translationsDoc.setContent($doc.getRenderedContent($translationsGeneratorDoc.content,
650 $translationsGeneratorDoc.syntax.toIdString(), 'plain/1.0')))
651 #set($discard = $translationsDoc.setSyntaxId('plain/1.0'))
652 #set($discard = $translationsDoc.setHidden(true))
653 #set($discard = $translationsDoc.save(
654 $services.localization.render('platform.appwithinminutes.classEditorTranslationsSaveComment'),
655 $minorEdit))
656 #end
657 #end
658
659 #**
660 * Updates and saves the class definition, the class sheet and the class template.
661 *#
662 #macro (doSave)
663 #set ($minorEdit = "$!request.minorEdit" != '')
664 #maybeCreateCodeSpace
665 #updateAndSaveClass
666 #updateAndSaveTemplate
667 #updateAndSaveSheet
668 #updateAndSaveTranslations
669 #if ($action == 'save')
670 #if ($errorMessage)
671 <div class="box errormessage">$errorMessage</div>
672 #elseif ("$!request.wizard" == 'true')
673 ## Redirect to next wizard step.
674 #set ($className = $stringtool.removeEnd($doc.fullName, 'Class'))
675 #set ($templateProviderReference = $services.model.resolveDocument("${className}TemplateProvider"))
676 #set ($queryString = {
677 'wizard': true,
678 'sheet': 'AppWithinMinutes.TemplateProviderEditSheet'
679 })
680 #if (!$xwiki.exists($templateProviderReference))
681 #set ($discard = $queryString.putAll({
682 'template': 'XWiki.TemplateProviderTemplate',
683 'parent': $doc.fullName
684 }))
685 #end
686 $response.sendRedirect($xwiki.getURL($templateProviderReference, 'edit', $escapetool.url($queryString)))
687 #else
688 ## Redirect to view mode.
689 $response.sendRedirect($doc.getURL())
690 #end
691 #else
692 #if ($errorMessage)
693 $response.sendError(400, $errorMessage)
694 #else
695 $response.setStatus(204)
696 #end
697 #end
698 #end
699 {{/velocity}}
700
701 {{velocity}}
702 #if("$!request.wizard" == 'true')
703 {{include reference="AppWithinMinutes.WizardStep" /}}
704 #end
705 {{/velocity}}
706
707 {{velocity}}
708 {{html clean="false"}}
709 ## Determine the action button that triggered the request
710 #set ($action = 'edit')
711 #foreach ($paramName in $request.getParameterMap().keySet())
712 #if ($paramName.startsWith('xaction_'))
713 #set ($action = $paramName.substring(8))
714 #break
715 #end
716 #end
717 #if ($action == 'edit')
718 #doEdit()
719 #elseif ($action == 'save' || $action == 'saveandcontinue')
720 #if ($services.csrf.isTokenValid($request.form_token))
721 #doSave()
722 #else
723 $response.sendRedirect($services.csrf.getResubmissionURL())
724 #end
725 #end
726 {{/html}}
727 {{/velocity}}

Tips

You can click on the arrows next to the breadcrumb elements to quickly navigate to sibling and children pages.

Need help?

If you need help with XWiki you can contact: