###
### Object editor
###
###
## Macros
##
#*
 * Displays a class name without its space prefix. XML-escapes the output.
 *#
#macro(cleanClassname $classname)$!{escapetool.xml($classname.substring($classname.indexOf('.')).substring(1))}#end
##
#*
 * Display an object
 * NOTE: the $document.display*(something, ...) methods are very difficult to use correctly, because they sometimes generate (unescaped) HTML
 *#
#macro(displayObject $obj $class $props $firstField)
  <div id="xobject_${escapetool.xml($class.name)}_${obj.number}" class="xobject">
  #getFieldSummary($obj $firstField $firstFieldSummary)
  <div id="xobject_${escapetool.xml($class.name)}_${obj.number}_title" class="xobject-title">
    <h3>#cleanClassname(${class.name}) ${obj.number}#if ($firstField): $firstFieldSummary#end
        <a href="$doc.getURL('objectremove', "form_token=$!{services.csrf.getToken()}&amp;classname=${escapetool.url($class.name)}&amp;classid=${obj.number}&amp;xredirect=${escapetool.url($doc.getURL('edit', 'editor=object'))}")" class="xobject-action delete" title="$services.localization.render('core.editors.object.removeObject.tooltip')">$services.localization.render('core.editors.object.removeObject')</a>
        #if (!$hasCustomObject)
          <a href="$doc.getURL('edit', "editor=object&amp;classname=${escapetool.url($class.name)}&amp;object=${obj.number}")" class="xobject-action edit" title="$services.localization.render('core.editors.object.editSingleObject.tooltip')">$services.localization.render('core.editors.object.editSingleObject')</a>
        #end
    </h3>
  </div>
  <div id="xobject_${escapetool.xml($class.name)}_${obj.number}_content" class="xobject-content"><div>
    <dl>
    #if ($hasCustomField)
      #set ($properties = [])
      #foreach ($propName in $customField.split(','))
        #set ($discard = $properties.add($class.get($propName)))
      #end
    #else
      #set ($properties = $class.properties)
    #end
    #foreach ($prop in $properties)
      <dt class="label#if($prop.isDisabled()) disabled#end"><label for="${escapetool.xml($class.name)}_${obj.number}_${escapetool.xml($prop.name)}">${escapetool.xml($prop.translatedPrettyName)}</label></dt>
      <dd#if ($prop.isDisabled()) class="disabled"#end>$doc.display($prop.name, "edit", $obj)</dd>
    #end
    </dl>
    #set ($deprecatedProperties = $class.getDeprecatedObjectProperties($obj))
    #if ($deprecatedProperties.size() > 0)
    <div class="box warningmessage deprecatedProperties">
      <strong>$services.localization.render('core.editors.object.removeDeprecatedProperties.info', [${escapetool.xml($class.name)}])</strong>
      <dl>
      #foreach ($prop in $deprecatedProperties)
        <dt class="label disabled"><label>${escapetool.xml($prop.name)}:</label></dt>
        <dd class="disabled">${escapetool.xml($prop.value)}</dd>
      #end
      </dl>
      <div>
        <a class="syncProperties" href='$doc.getURL("objectsync", "classname=${escapetool.url($class.name)}&amp;object=${obj.number}")'
           title="$services.localization.render('core.editors.object.removeDeprecatedProperties.link.tooltip')">$services.localization.render('core.editors.object.removeDeprecatedProperties.link')</a>
      </div>
    </div>
    #end
  </div></div>## xobject-content
  </div>## xobject
#end
##
#*
 * Determine the title field of the class, the field that should be displayed as a summary of the object.
 *#
#macro(getTitleField $props)
  #set($foundStringProperty = false)
  #set ($foundAlternativeProperty = false)
  ## As a backup, use the first field of the class; will be overwritten later by better values
  #set ($titleField = false)
  #foreach ($prop in $props)
    ## A property containing 'title' or 'name' has precedence
    #if ($prop.name.toLowerCase().indexOf('title') >= 0 || $prop.name.toLowerCase().indexOf('name') >= 0)
      #set ($titleField = $prop.name)
      #break
    ## String properties have precedence over other types
    #elseif (!$foundStringProperty)
      #if ($prop.type == 'StringClass')
        #set ($titleField = $prop.name)
        #set ($foundStringProperty = true)
      #elseif (!$foundAlternativeProperty)
        #set ($titleField = $prop.name)
        #set ($foundAlternativeProperty = true)
      #end
    #end
  #end
#end
##
#*
 * Returns the first 120 characters of the first line of the raw value of the specified object field.
 *#
#macro (getFieldSummary $obj $fieldName $fieldSummary)
  #set ($summary = "$!obj.getProperty($fieldName).value")
  #set ($summary = $summary.trim())
  #set ($multiline = $summary.contains($util.newline))
  #set ($summary = $stringtool.substringBefore($summary, $util.newline))
  #if ($multiline || $summary.length() > 120)
    #set ($summary = "${stringtool.left($summary, 120)}...")
  #end
  #set ($summary = $escapetool.xml($summary))
  #set ($fieldSummary = $NULL)
  #setVariable("$fieldSummary" $summary)
#end
##
#*
 * Display a class
 *#
#macro(displayClass $class)
  #set ($props = $class.getProperties())
  #getTitleField($props)
  <div id="xclass_${escapetool.xml($class.name)}" class="xclass">
    #if ($hasCustomObject)
      #set ($object = $doc.getObject($class.name, $customObject))
      #if ($object)
        <div id="xclass_${escapetool.xml($class.name)}_title" class="xclass-title"><h2>${escapetool.xml($class.name)}</h2></div>
        <div id="xclass_${escapetool.xml($class.name)}_content" class="xclass-content"><div>
        #displayObject($object $class $props $titleField)
        </div></div>
      #else
        #warning($services.localization.render('core.editors.object.noObject'))
      #end
    #else
      #set ($objects = $doc.getObjects($class.name))
      #set ($objectCount = $objects.size())
      <div id="xclass_${escapetool.xml($class.name)}_title" class="xclass-title"><h2>$services.localization.render('core.editors.object.objectsForClass', [${escapetool.xml($class.name)}]) <span id="xclass_${escapetool.xml($class.name)}_xobject_nb" class="xclass_xobject_nb">($objectCount)</span></h2></div>
      <div id="xclass_${escapetool.xml($class.name)}_content" class="xclass-content"><div>
      #foreach ($object in $objects)
        #displayObject($object $class $props $titleField)
      #end
      #displayAddObject($class)
      </div></div>
    #end
  </div>
#end
##
##
#**
 * Display a control for adding an object of a given class
 *#
#macro(displayAddObject $class)
  <div id="add_xobject_${escapetool.xml($class.name)}" class="add_xobject">
    <div id="add_xobject_${escapetool.xml($class.name)}_title" class="add_xobject-title">
      <a href="$doc.getURL('edit', "xpage=editobject&amp;xaction=addObject&amp;className=$escapetool.url(${class.name})&amp;xredirect=$escapetool.url(${doc.getURL('edit', 'editor=object')})")" class="xobject-add-control" title="$services.localization.render('core.editors.object.newObjectForClass.tooltip', ["#cleanClassname(${class.name})"])">$services.localization.render('core.editors.object.newObjectForClass', ["#cleanClassname(${class.name})"])</a>
    </div>
  </div>
#end
##
##
#**
 * Display a control for adding an object from a chosen class
 *#
#macro(displayAddObjectChooseClass)
  <div id="add_xobject" class="add_xobject"> <label for="classname" class="add_xobject-title">$services.localization.render('core.editors.object.add.label'):</label>
  <select id="classname" name="classname" size="1">
    <option value="-">$services.localization.render('core.editors.object.add.selectClass')</option>
    ## Existing classes are enumerated, grouped by space name.
    ## The list is already sorted alphabetically
#set($prevSpace = '')
#foreach ($classname in $xwiki.classList)
  #if ($!xwiki.hasAccessLevel('view', $classname))
    #set ($classdoc = $xwiki.getDocument($classname))
    #if ($classdoc.space != $prevSpace)
      #if ($prevSpace != '')
        </optgroup>
      #end
      <optgroup label="${escapetool.xml($classdoc.space)}">
      #set ($prevSpace = $classdoc.space)
    #end
    <option value="${escapetool.xml($classdoc.fullName)}">${escapetool.xml($classdoc.name)}</option>
  #end
#end
#if($prevSpace != '')
  </optgroup>
#end
</select>
<span class="buttonwrapper"><input type="submit" name="action_objectadd" value="$services.localization.render('core.editors.object.add.submit')" class="button xobject-add-control"/></span>
  </div> ## add_object
#end
##
##
#**
 * Check if there are deleted properties in the classes of the existing objects.
 * If so, provide a control to synchronize property sets of these objects.
 *#
#macro (checkPropertyDeprecation)
  #set ($allClasses = $doc.getxWikiClasses())
  #set ($mustSync = false)
  #foreach ($class in $allClasses)
    #set ($objects = $doc.getObjects($class.name))
    #foreach ($object in $objects)
      #if ($class.getDeprecatedObjectProperties($object).size() > 0)
        #set ($mustSync = true)
        #break
      #end
    #end
    #if ($mustSync) #break #end
  #end
  #if ($mustSync)
    <div class="box warningmessage deprecatedProperties">
      $services.localization.render('core.editors.object.removeDeprecatedProperties.all.info')
      <div><a class="syncProperties syncAllProperties" href="$doc.getURL('objectsync')" title="$services.localization.render('core.editors.object.removeDeprecatedProperties.all.link.tooltip')">$services.localization.render('core.editors.object.removeDeprecatedProperties.all.link')</a></div>
    </div>
  #end
#end ## checkPropertyDeprecation
##
##---------------------------------
## Done with macro definitions, now the actual content
##
##
## Ajax object add:
#if ("$!{request.xaction}" == 'addObject')
  #set ($class = $xwiki.getClass(${request.className}))
  #set ($props = $class.getProperties())
  #if ($props.isEmpty())
    $response.setStatus(404)
    #error($services.localization.render('core.editors.object.add.invalidClassName', [$request.className]))
  #else
    #set ($isNewClass = $doc.getObjects(${request.className}).isEmpty())
    #set ($newobj = $doc.newObject(${request.className}))
    #set ($discard = $doc.save())
    #if ("$!{request.xredirect}" != '')
      $response.sendRedirect($request.xredirect)
    #else
      ## Display
      #if ($isNewClass)
        ## Output the SkinExtension hooks to allow field displayers to pull JavaScript/CSS resources.
        ## The object editor will move these 'includes' (link/script tags) in the head of the HTML page.
        ## Note that we need to pull the JavaScript/CSS only once, for the first object of a given type.
        #skinExtensionHooks
        #displayClass($class)
      #else
        #foreach ($prop in $props)
          #if ($velocityCount==1)
            #set ($firstField = $prop.name)
            #break
          #end
        #end ## foreach prop
        #displayObject($newobj $class $props $firstField)
      #end ## is new class
    #end ## xredirect
  #end ## class exists
##
##
##
##
#elseif ("$!request.xpage" == 'editobject')
  ## Old (pre 1.0) way of using this template
  $response.sendRedirect($doc.getURL('edit', 'editor=object'))
#else
  ## Actual Object editor
  $xwiki.ssfx.use('js/xwiki/editors/dataeditors.css', true)##
  $xwiki.jsfx.use('js/xwiki/editors/dataeditors.js', true)##
  ## Insert JS and CSS needed for the fullscreen edit feature
  $xwiki.jsfx.use('uicomponents/widgets/fullScreen.js', {'forceSkinAction': true, 'language': ${xcontext.language}})##
  $xwiki.ssfx.use('uicomponents/widgets/fullScreen.css', true)##
  ## Some parameters
  #set ($formname = 'update')
  #set ($saveaction = 'save')
  #set ($hasCustomClass = ("$!{request.classname.trim()}" != ''))
  #set ($hasCustomObject = ($hasCustomClass && ("$!{request.nb.trim()}$!{request.classid.trim()}$!{request.object.trim()}" != '')))
  #set ($hasCustomField = ($hasCustomClass && ("$!{request.property.trim()}" != '')))
  #if ($hasCustomClass)
    #set ($classes = [$xwiki.getDocument($request.classname).getxWikiClass()])
  #else
    #set ($classes = $doc.getxWikiClasses())
  #end
  #if ($hasCustomClass || $hasCustomObject || $hasCustomField)
    #set ($fullEditorURL = "$doc.getURL('edit', 'editor=object')")
    <div class="more-actions">
      <a href='$fullEditorURL' class='edit-all' title="$services.localization.render('core.editors.object.editAllObjects')">$services.localization.render('core.editors.object.editAllObjects')</a>
    </div>
  #end
  #set ($redirect = $xwiki.relativeRequestURL)
  <form id="update" method="post" action="$doc.getURL("save")" class="withLock">
  <div id="xwikieditcontent" class="clear">
  
  #checkPropertyDeprecation()

  #set ($q = 'editor=object')
  #if ($hasCustomClass)
    #set ($customClass = "${request.classname}")
    #set ($q = "${q}&amp;classname=$!{escapetool.url(${customClass})}")
    #if ($hasCustomObject)
      #set ($customObject = "${request.object}")
      #if ($customObject == '')
        #set ($customObject = "${request.classid}")
        #if ($customObject == '')
          #set ($customObject = "${request.nb}")
        #end
      #end
      #set ($customObject = $mathtool.toInteger($customObject))
      #set ($q = "${q}&amp;object=${customObject}")
    #end
    #if ($hasCustomField)
      #set ($customField = "$request.property")
      #set ($q = "${q}&amp;property=$!{escapetool.url(${customField})}")
    #end
  #end
    <div class="hidden">
      ## CSRF prevention
      <input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" />
      <input type="hidden" name="xcontinue" value="$doc.getURL($xcontext.action, $q)"/>
      ## This input is used by the full screen widget: it stores the selector of the maximized element, so that the full screen can be restored when coming back from other views.
      <input type="hidden" name="x-maximized" value="$!{escapetool.xml($request.get('x-maximized'))}" />
      ## A submit button that captures "enter" on the form, placed before the "objectadd" submit button,
      ## to prevent from accidentally adding objects when pressing enter.
      #editActionButton('preview', 'preview')
    </div>
    <div id="xwikiobjects">
  #if (!$hasCustomClass)
    #displayAddObjectChooseClass()
  #end
  #foreach ($class in $classes)
    #displayClass($class)
  #end
    </div>## xwikiobjects
  </div>## xwikieditcontent
  <div class="bottombuttons">#template("editactions.vm")</div>
  </form>
#end ## request.xaction
