From 4e384817b76fb8726e84dd2092c9e57ca7a14995 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Mon, 26 May 2014 08:59:07 -0500
Subject: [PATCH] forms: Add concept of inline forms

In-line forms are forms that are rendered as one field. The data of the
inline form is also saved in the data for one field. Currently, the data is
rendered to JSON and stashed in the database.

The data in the field is also accessible via the variable replacement
system, so something line %{ticket.field.subfield} can be handled by the
inline form field.
---
 include/class.forms.php                       | 123 +++++++++++++++++-
 include/client/templates/inline-form.tmpl.php |  24 ++++
 include/staff/templates/inline-form.tmpl.php  |  26 ++++
 3 files changed, 172 insertions(+), 1 deletion(-)
 create mode 100644 include/client/templates/inline-form.tmpl.php
 create mode 100644 include/staff/templates/inline-form.tmpl.php

diff --git a/include/class.forms.php b/include/class.forms.php
index b0dd1913a..782df4c82 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -153,7 +153,7 @@ class FormField {
     static $widget = false;
 
     var $ht = array(
-        'label' => 'Unlabeled',
+        'label' => false,
         'required' => false,
         'default' => false,
         'configuration' => array(),
@@ -1760,6 +1760,125 @@ class FileUploadField extends FormField {
     }
 }
 
+class InlineFormData extends ArrayObject {
+    var $_form;
+
+    function __construct($form, array $data=array()) {
+        parent::__construct($data);
+        $this->_form = $form;
+    }
+
+    function getVar($tag) {
+        foreach ($this->_form->getFields() as $f) {
+            if ($f->get('name') == $tag)
+                return $this[$f->get('id')];
+        }
+    }
+}
+
+
+class InlineFormField extends FormField {
+    static $widget = 'InlineFormWidget';
+
+    var $_iform = null;
+
+    function validateEntry($value) {
+        if (!$this->getInlineForm()->isValid()) {
+            $this->_errors[] = 'Correct errors in the inline form';
+        }
+    }
+
+    function parse($value) {
+        // The InlineFieldWidget returns an array of cleaned data
+        return $value;
+    }
+
+    function to_database($value) {
+        return JsonDataEncoder::encode($value);
+    }
+
+    function to_php($value) {
+        $data = JsonDataParser::decode($value);
+        // The InlineFormData helps with the variable replacer API
+        return new InlineFormData($this->getInlineForm(), $data);
+    }
+
+    function display($data) {
+        $form = $this->getInlineForm();
+        ob_start(); ?>
+        <div><?php
+        foreach ($form->getFields() as $field) { ?>
+            <span style="display:inline-block;padding:0 5px;vertical-align:top">
+                <strong><?php echo Format::htmlchars($field->get('label')); ?></strong>
+                <div><?php
+                    $value = $data[$field->get('id')];
+                    echo $field->display($value); ?></div>
+            </span><?php
+        } ?>
+        </div><?php
+        return ob_get_clean();
+    }
+
+    function getInlineForm() {
+        $form = $this->get('form');
+        if (is_array($form)) {
+            $form = new Form($form);
+        }
+        return $form;
+    }
+}
+
+class InlineDynamicFormField extends FormField {
+    function getInlineForm($data=false) {
+        if (!isset($this->_iform) || $data) {
+            $config = $this->getConfiguration();
+            $this->_iform = DynamicForm::lookup($config['form']);
+            if ($data)
+                $this->_iform = $this->_iform->getForm($data);
+        }
+        return $this->_iform;
+    }
+
+    function getConfigurationOptions() {
+        $forms = DynamicForm::objects()->filter(array('type'=>'G'))
+            ->values_flat('id', 'title');
+        $choices = array();
+        foreach ($forms as $row) {
+            list($id, $title) = $row;
+            $choices[$id] = $title;
+        }
+        return array(
+            'form' => new ChoiceField(array(
+                'id'=>2, 'label'=>'Inline Form', 'required'=>true,
+                'default'=>'', 'choices'=>$choices
+            )),
+        );
+    }
+}
+
+class InlineFormWidget extends Widget {
+    function render($mode=false) {
+        $form = $this->field->getInlineForm();
+        if (!$form)
+            return;
+        // Handle first-step edits -- load data from $this->value
+        if ($form instanceof DynamicForm && !$form->getSource())
+            $form = $form->getForm($this->value);
+        $inc = ($mode == 'client') ? CLIENTINC_DIR : STAFFINC_DIR;
+        include $inc . 'templates/inline-form.tmpl.php';
+    }
+
+    function getValue() {
+        $data = $this->field->getSource();
+        if (!$data)
+            return null;
+        $form = $this->field->getInlineForm($data);
+        if (!$form)
+            return null;
+        return $form->getClean();
+    }
+}
+
 class Widget {
     static $media = null;
 
@@ -1784,6 +1903,8 @@ class Widget {
             return $data[$this->name];
         elseif (isset($data[$this->field->get('name')]))
             return $data[$this->field->get('name')];
+        elseif (isset($data[$this->field->get('id')]))
+            return $data[$this->field->get('id')];
         return null;
     }
 
diff --git a/include/client/templates/inline-form.tmpl.php b/include/client/templates/inline-form.tmpl.php
new file mode 100644
index 000000000..696e1d7cf
--- /dev/null
+++ b/include/client/templates/inline-form.tmpl.php
@@ -0,0 +1,24 @@
+<div><?php
+foreach ($form->getFields() as $field) { ?>
+    <span style="display:inline-block;padding-right:5px;vertical-align:top">
+        <span class="<?php if ($field->get('required')) echo 'required'; ?>">
+            <?php echo Format::htmlchars($field->get('label')); ?></span>
+        <div><?php
+        $field->render(); ?>
+        <?php if ($field->get('required')) { ?>
+            <span class="error">*</span>
+        <?php
+        }
+        if ($field->get('hint') && !$field->isBlockLevel()) { ?>
+            <br/><em style="color:gray;display:inline-block"><?php
+                echo Format::htmlchars($field->get('hint')); ?></em>
+        <?php
+        }
+        foreach ($field->errors() as $e) { ?>
+            <br />
+            <span class="error"><?php echo Format::htmlchars($e); ?></span>
+        <?php } ?>
+        </div>
+    </span><?php
+} ?>
+</div>
diff --git a/include/staff/templates/inline-form.tmpl.php b/include/staff/templates/inline-form.tmpl.php
new file mode 100644
index 000000000..5de3a283a
--- /dev/null
+++ b/include/staff/templates/inline-form.tmpl.php
@@ -0,0 +1,26 @@
+<div><?php
+foreach ($form->getFields() as $field) { ?>
+    <span style="display:inline-block;padding-right:5px;vertical-align:middle">
+<?php   if (!$field->isBlockLevel()) { ?>
+        <span class="<?php if ($field->get('required')) echo 'required'; ?>">
+            <?php echo Format::htmlchars($field->get('label')); ?></span>
+<?php   } ?>
+        <div><?php
+        $field->render(); ?>
+        <?php if ($field->get('required')) { ?>
+            <span class="error">*</span>
+        <?php
+        }
+        if ($field->get('hint') && !$field->isBlockLevel()) { ?>
+            <br/><em style="color:gray;display:inline-block"><?php
+                echo Format::htmlchars($field->get('hint')); ?></em>
+        <?php
+        }
+        foreach ($field->errors() as $e) { ?>
+            <br />
+            <span class="error"><?php echo Format::htmlchars($e); ?></span>
+        <?php } ?>
+        </div>
+    </span><?php
+} ?>
+</div>
-- 
GitLab