Skip to content
Snippets Groups Projects
class.forms.php 93.5 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 if (!$ext) {
                        continue;
                    }
                    elseif (strpos($ext, '/')) {
    
                        $mimetypes[$ext] = true;
    
                    }
                    else {
                        if ($ext[0] != '.')
                            $ext = '.' . $ext;
    
    
                        // Ensure that the extension is lower-cased for comparison latr
                        $ext = strtolower($ext);
    
    
                        // Add this to the MIME types list so it can be exported to
                        // the @accept attribute
                        if (!isset($extensions[$ext]))
                            $mimetypes[$ext] = true;
    
                        $extensions[$ext] = true;
                    }
    
                $config['__extensions'] = array_keys($extensions);
            }
            elseif (is_array($config['extensions'])) {
                $config['__extensions'] = $config['extensions'];
    
            }
    
            // 'mimetypes' is the array represented from the user interface,
            // '__mimetypes' is a complete list of supported MIME types.
            $config['__mimetypes'] = array_keys($mimetypes);
            return $config;
        }
    
    
        // When the field is saved to database, encode the ID listing as a json
        // array. Then, inspect the difference between the files actually
        // attached to this field
        function to_database($value) {
            $this->getFiles();
            if (isset($this->attachments)) {
                $ids = array();
                // Handle deletes
                foreach ($this->attachments->getAll() as $f) {
                    if (!in_array($f['id'], $value))
                        $this->attachments->delete($f['id']);
                    else
                        $ids[] = $f['id'];
                }
                // Handle new files
                foreach ($value as $id) {
                    if (!in_array($id, $ids))
                        $this->attachments->upload($id);
                }
            }
            return JsonDataEncoder::encode($value);
        }
    
        function parse($value) {
            // Values in the database should be integer file-ids
            return array_map(function($e) { return (int) $e; },
                $value ?: array());
        }
    
        function to_php($value) {
            return JsonDataParser::decode($value);
        }
    
        function display($value) {
            $links = array();
            foreach ($this->getFiles() as $f) {
    
                $links[] = sprintf('<a class="no-pjax" href="%s">%s</a>',
                    Format::htmlchars($f['download_url']), Format::htmlchars($f['name']));
    
            }
            return implode('<br/>', $links);
        }
    
    
        function toString($value) {
            $files = array();
            foreach ($this->getFiles() as $f) {
                $files[] = $f['name'];
            }
            return implode(', ', $files);
        }
    
    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($data=false) {
    
            $form = $this->get('form');
            if (is_array($form)) {
    
                $form = new Form($form, $data ?: $this->value ?: $this->getSource());
    
            }
            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();
        }
    }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
    class Widget {
    
        static $media = null;
    
    Jared Hancock's avatar
    Jared Hancock committed
    
        function __construct($field) {
            $this->field = $field;
            $this->name = $field->getFormName();
    
            $this->id = '_' . $this->name;
    
        }
    
        function parseValue() {
    
            $this->value = $this->getValue();
    
            if (!isset($this->value) && is_object($this->field->getAnswer()))
                $this->value = $this->field->getAnswer()->getValue();
    
            if (!isset($this->value) && isset($this->field->value))
    
                $this->value = $this->field->value;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function getValue() {
    
            $data = $this->field->getSource();
    
            // Search for HTML form name first
            if (isset($data[$this->name]))
                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;
    
    
        /**
         * getJsValueGetter
         *
         * Used with the dependent fields feature, this function should return a
         * single javascript expression which can be used in a larger expression
         * (<> == true, where <> is the result of this function). The %s token
         * will be replaced with a jQuery variable representing this widget.
         */
        function getJsValueGetter() {
            return '%s.val()';
        }
    
    Jared Hancock's avatar
    Jared Hancock committed
    }
    
    class TextboxWidget extends Widget {
    
    Jared Hancock's avatar
    Jared Hancock committed
        static $input_type = 'text';
    
    
        function render($options=array()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->field->getConfiguration();
            if (isset($config['size']))
                $size = "size=\"{$config['size']}\"";
    
            if (isset($config['length']) && $config['length'])
    
    Jared Hancock's avatar
    Jared Hancock committed
                $maxlength = "maxlength=\"{$config['length']}\"";
    
            if (isset($config['classes']))
    
                $classes = 'class="'.$config['classes'].'"';
    
            if (isset($config['autocomplete']))
                $autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"';
    
            if (isset($config['disabled']))
                $disabled = 'disabled="disabled"';
    
            if (isset($config['translatable']) && $config['translatable'])
                $translatable = 'data-translate-tag="'.$config['translatable'].'"';
            $placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder',
                $config['placeholder']));
    
    Jared Hancock's avatar
    Jared Hancock committed
            <input type="<?php echo static::$input_type; ?>"
    
                id="<?php echo $this->id; ?>"
    
                <?php echo implode(' ', array_filter(array(
    
                    $size, $maxlength, $classes, $autocomplete, $disabled,
                    $translatable, $placeholder))); ?>
    
    Jared Hancock's avatar
    Jared Hancock committed
                name="<?php echo $this->name; ?>"
                value="<?php echo Format::htmlchars($this->value); ?>"/>
            <?php
        }
    }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
    class PasswordWidget extends TextboxWidget {
        static $input_type = 'password';
    
        function parseValue() {
            // Show empty box unless failed POST
            if ($_SERVER['REQUEST_METHOD'] == 'POST'
                    && $this->field->getForm()->isValid())
                parent::parseValue();
            else
                $this->value = '';
        }
    }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
    class TextareaWidget extends Widget {
    
        function render($options=array()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->field->getConfiguration();
    
            $class = $cols = $rows = $maxlength = "";
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (isset($config['rows']))
                $rows = "rows=\"{$config['rows']}\"";
            if (isset($config['cols']))
                $cols = "cols=\"{$config['cols']}\"";
    
            if (isset($config['length']) && $config['length'])
    
    Jared Hancock's avatar
    Jared Hancock committed
                $maxlength = "maxlength=\"{$config['length']}\"";
    
            if (isset($config['html']) && $config['html']) {
    
                $class = array('richtext', 'no-bar');
                $class[] = @$config['size'] ?: 'small';
                $class = sprintf('class="%s"', implode(' ', $class));
    
                $this->value = Format::viewableImages($this->value);
            }
    
            <span style="display:inline-block;width:100%">
    
            <textarea <?php echo $rows." ".$cols." ".$maxlength." ".$class
                    .' placeholder="'.$config['placeholder'].'"'; ?>
    
                id="<?php echo $this->id; ?>"
    
    Jared Hancock's avatar
    Jared Hancock committed
                name="<?php echo $this->name; ?>"><?php
                    echo Format::htmlchars($this->value);
                ?></textarea>
            </span>
            <?php
        }
    }
    
    class PhoneNumberWidget extends Widget {
    
        function render($options=array()) {
    
            $config = $this->field->getConfiguration();
    
    Jared Hancock's avatar
    Jared Hancock committed
            list($phone, $ext) = explode("X", $this->value);
            ?>
    
            <input id="<?php echo $this->id; ?>" type="text" name="<?php echo $this->name; ?>" value="<?php
    
            echo Format::htmlchars($phone); ?>"/><?php
    
            // Allow display of extension field even if disabled if the phone
            // number being edited has an extension
    
            if ($ext || $config['ext']) { ?> <?php echo __('Ext'); ?>:
    
                <input type="text" name="<?php
    
                echo $this->name; ?>-ext" value="<?php echo Format::htmlchars($ext);
                    ?>" size="5"/>
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function getValue() {
    
            $data = $this->field->getSource();
            $base = parent::getValue();
            if ($base === null)
                return $base;
            $ext = $data["{$this->name}-ext"];
    
            // NOTE: 'X' is significant. Don't change it
    
    Jared Hancock's avatar
    Jared Hancock committed
            if ($ext) $ext = 'X'.$ext;
    
            return $base . $ext;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    }
    
    class ChoicesWidget extends Widget {
    
        function render($options=array()) {
    
            $mode = isset($options['mode']) ? $options['mode'] : null;
    
                if (!($val = (string) $this->field))
    
                    $val = sprintf('<span class="faded">%s</span>', __('None'));
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->field->getConfiguration();
    
            if ($mode == 'search') {
                $config['multiselect'] = true;
            }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            // Determine the value for the default (the one listed if nothing is
            // selected)
    
    Peter Rotich's avatar
    Peter Rotich committed
            $choices = $this->field->getChoices(true);
    
            $prompt = ($config['prompt'])
                ? $this->field->getLocal('prompt', $config['prompt'])
                : __('Select'
                /* Used as a default prompt for a custom drop-down list */);
    
            $have_def = false;
    
            // We don't consider the 'default' when rendering in 'search' mode
            if (!strcasecmp($mode, 'search')) {
                $def_val = $prompt;
            } else {
    
                $def_key = $this->field->get('default');
                if (!$def_key && $config['default'])
                    $def_key = $config['default'];
    
                if (is_array($def_key))
                    $def_key = key($def_key);
    
                $have_def = isset($choices[$def_key]);
    
                $def_val = $have_def ? $choices[$def_key] : $prompt;
    
            $values = $this->value;
    
            if (!is_array($values) && isset($values)) {
    
                $values = array($values => $this->field->getChoice($values));
            }
    
            if (!is_array($values))
    
    Peter Rotich's avatar
    Peter Rotich committed
                $values = $have_def ? array($def_key => $choices[$def_key]) : array();
    
    
            ?>
            <select name="<?php echo $this->name; ?>[]"
    
                id="<?php echo $this->id; ?>"
    
                data-placeholder="<?php echo $prompt; ?>"
    
                <?php if ($config['multiselect'])
    
                    echo ' multiple="multiple" class="chosen-select"'; ?>>
    
                <?php if (!$have_def && !$config['multiselect']) { ?>
    
    Jared Hancock's avatar
    Jared Hancock committed
                <option value="<?php echo $def_key; ?>">&mdash; <?php
                    echo $def_val; ?> &mdash;</option>
                <?php }
    
    Peter Rotich's avatar
    Peter Rotich committed
                foreach ($choices as $key => $name) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                    if (!$have_def && $key == $def_key)
                        continue; ?>
    
                    <option value="<?php echo $key; ?>" <?php
    
    Peter Rotich's avatar
    Peter Rotich committed
                        if (isset($values[$key])) echo 'selected="selected"';
    
    Jared Hancock's avatar
    Jared Hancock committed
                    ?>><?php echo $name; ?></option>
                <?php } ?>
            </select>
            <?php
    
            if ($config['multiselect']) {
             ?>
            <script type="text/javascript">
            $(function() {
    
                $("#<?php echo $this->id; ?>")
    
                .chosen({'disable_search_threshold':10, 'width': '250px'});
    
    
        function getValue() {
            $value = parent::getValue();
    
            if (!$value) return null;
    
            // Assume multiselect
            $values = array();
            $choices = $this->field->getChoices();
            if (is_array($value)) {
                foreach($value as $k => $v) {
                    if (isset($choices[$v]))
                        $values[$v] = $choices[$v];
                }
            }
            return $values;
        }
    
    
        function getJsValueGetter() {
            return '%s.find(":selected").val()';
        }
    
    Jared Hancock's avatar
    Jared Hancock committed
    }
    
    class CheckboxWidget extends Widget {
        function __construct($field) {
            parent::__construct($field);
            $this->name = '_field-checkboxes';
        }
    
    
        function render($options=array()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->field->getConfiguration();
    
            if (!isset($this->value))
                $this->value = $this->field->get('default');
    
            <input id="<?php echo $this->id; ?>" style="vertical-align:top;"
    
                type="checkbox" name="<?php echo $this->name; ?>[]" <?php
    
    Jared Hancock's avatar
    Jared Hancock committed
                if ($this->value) echo 'checked="checked"'; ?> value="<?php
                echo $this->field->get('id'); ?>"/>
            <?php
            if ($config['desc']) { ?>
                <em style="display:inline-block"><?php
    
                echo Format::viewableImages($config['desc']); ?></em>
    
    Jared Hancock's avatar
    Jared Hancock committed
            <?php }
        }
    
        function getValue() {
    
            $data = $this->field->getSource();
            if (count($data))
                return @in_array($this->field->get('id'), $data[$this->name]);
    
    Jared Hancock's avatar
    Jared Hancock committed
            return parent::getValue();
        }
    
    
        function getJsValueGetter() {
    
            return '%s.is(":checked")';
    
    Jared Hancock's avatar
    Jared Hancock committed
    }
    
    class DatetimePickerWidget extends Widget {
    
        function render($options=array()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->field->getConfiguration();
            if ($this->value) {
    
                $this->value = is_int($this->value) ? $this->value :
                    strtotime($this->value);
    
                if ($config['gmt']) {
                    // Convert to GMT time
                    $tz = new DateTimeZone($cfg->getTimezone());
                    $D = DateTime::createFromFormat('U', $this->value);
                    $this->value += $tz->getOffset($D);
                }
    
    Jared Hancock's avatar
    Jared Hancock committed
                list($hr, $min) = explode(':', date('H:i', $this->value));
    
                $this->value = Format::date($this->value, false, false, 'UTC');
    
    Jared Hancock's avatar
    Jared Hancock committed
            }
            ?>
            <input type="text" name="<?php echo $this->name; ?>"
    
                id="<?php echo $this->id; ?>"
    
    Jared Hancock's avatar
    Jared Hancock committed
                value="<?php echo Format::htmlchars($this->value); ?>" size="12"
    
                autocomplete="off" class="dp" />
    
    Jared Hancock's avatar
    Jared Hancock committed
            <script type="text/javascript">
                $(function() {
                    $('input[name="<?php echo $this->name; ?>"]').datepicker({
                        <?php
                        if ($config['min'])
                            echo "minDate: new Date({$config['min']}000),";
                        if ($config['max'])
                            echo "maxDate: new Date({$config['max']}000),";
                        elseif (!$config['future'])
                            echo "maxDate: new Date().getTime(),";
                        ?>
                        numberOfMonths: 2,
                        showButtonPanel: true,
                        buttonImage: './images/cal.png',
    
                        dateFormat: $.translate_format('<?php echo $cfg->getDateFormat(true); ?>')
    
    Jared Hancock's avatar
    Jared Hancock committed
                    });
                });
            </script>
            <?php
            if ($config['time'])
                // TODO: Add time picker -- requires time picker or selection with
                //       Misc::timeDropdown
                echo '&nbsp;' . Misc::timeDropdown($hr, $min, $this->name . ':time');
        }
    
        /**
         * Function: getValue
         * Combines the datepicker date value and the time dropdown selected
         * time value into a single date and time string value.
         */
        function getValue() {
    
            $data = $this->field->getSource();
    
            $config = $this->field->getConfiguration();
            if ($datetime = parent::getValue()) {
    
                $datetime = is_int($datetime) ? $datetime :
                    strtotime($datetime);
    
                if ($datetime && isset($data[$this->name . ':time'])) {
    
                    list($hr, $min) = explode(':', $data[$this->name . ':time']);
                    $datetime += $hr * 3600 + $min * 60;
                }
    
                if ($datetime && $config['gmt']) {
                    // Convert to GMT time
                    $tz = new DateTimeZone($cfg->getTimezone());
                    $D = DateTime::createFromFormat('U', $datetime);
                    $datetime -= $tz->getOffset($D);
                }
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $datetime;
        }
    }
    
    
    class SectionBreakWidget extends Widget {
    
        function render($options=array()) {
    
            ?><div class="form-header section-break"><h3><?php
    
            echo Format::htmlchars($this->field->getLocal('label'));
            ?></h3><em><?php echo Format::htmlchars($this->field->getLocal('hint'));
    
            ?></em></div>
            <?php
        }
    }
    
    class ThreadEntryWidget extends Widget {
    
        function render($options=array()) {
    
            if ($options['client']) {
                $namespace = $options['draft-namespace']
                    ?: 'ticket.client';
                 $object_id = substr(session_id(), -12);
            } else {
                $namespace = $options['draft-namespace'] ?: 'ticket.staff';
    
            list($draft, $attrs) = Draft::getDraftAndDataAttrs($namespace, $object_id, $this->value);
    
            <span class="required"><?php
                echo Format::htmlchars($this->field->getLocal('label'));
            ?>: <span class="error">*</span></span><br/>
    
            <textarea style="width:100%;" name="<?php echo $this->field->get('name'); ?>"
    
                placeholder="<?php echo Format::htmlchars($this->field->get('hint')); ?>"
    
                class="<?php if ($cfg->isHtmlThreadEnabled()) echo 'richtext';
                    ?> draft draft-delete" <?php echo $attrs; ?>
    
                cols="21" rows="8" style="width:80%;"><?php echo
    
                $draft ?: Format::htmlchars($this->value); ?></textarea>
    
            $config = $this->field->getConfiguration();
            if (!$config['attachments'])
                return;
    
            $attachments = $this->getAttachments($config);
    
            print $attachments->render($options);
    
            foreach ($attachments->getMedia() as $type=>$urls) {
                foreach ($urls as $url)
                    Form::emitMedia($url, $type);
    
    
        function getAttachments($config=false) {
            if (!$config)
                $config = $this->field->getConfiguration();
    
    
            $field = new FileUploadField(array(
    
                'name'=>'attach:' . $this->field->get('id'),
    
            $field->setForm($this->field->getForm());
            return $field;
    
    class FileUploadWidget extends Widget {
        static $media = array(
            'css' => array(
                '/css/filedrop.css',
            ),
        );
    
    
        function render($options) {
    
            $config = $this->field->getConfiguration();
            $name = $this->field->getFormName();
    
            $id = substr(md5(spl_object_hash($this)), 10);
    
            $attachments = $this->field->getFiles();
    
            $mimetypes = array_filter($config['__mimetypes'],
                function($t) { return strpos($t, '/') !== false; }
            );
    
            $files = array();
    
            foreach ($this->value ?: array() as $fid) {
    
                $found = false;
                foreach ($attachments as $f) {
    
                        $files[] = $f;
                        $found = true;
                        break;
                    }
                }
    
                if (!$found && ($file = AttachmentFile::lookup($fid))) {
    
                    $files[] = array(
                        'id' => $file->getId(),
                        'name' => $file->getName(),
                        'type' => $file->getType(),
                        'size' => $file->getSize(),
    
                        'download_url' => $file->getDownloadUrl(),
    
                ?>" class="filedrop"><div class="files"></div>
                <div class="dropzone"><i class="icon-upload"></i>
    
    Jared Hancock's avatar
    Jared Hancock committed
                <?php echo sprintf(
                    __('Drop files here or %s choose them %s'),
                    '<a href="#" class="manual">', '</a>'); ?>
    
            <input type="file" multiple="multiple"
                id="file-<?php echo $id; ?>" style="display:none;"
    
                accept="<?php echo implode(',', $config['__mimetypes']); ?>"/>
    
            <script type="text/javascript">
    
            $(function(){$('#<?php echo $id; ?> .dropzone').filedropbox({
    
              url: 'ajax.php/form/upload/<?php echo $this->field->get('id') ?>',
    
              link: $('#<?php echo $id; ?>').find('a.manual'),
    
              paramname: 'upload[]',
    
              fallback_id: 'file-<?php echo $id; ?>',
    
              allowedfileextensions: <?php echo JsonDataEncoder::encode(
    
                $config['__extensions'] ?: array()); ?>,
    
              allowedfiletypes: <?php echo JsonDataEncoder::encode(
    
              maxfiles: <?php echo $config['max'] ?: 20; ?>,
    
              maxfilesize: <?php echo ($config['size'] ?: 1048576) / 1048576; ?>,
    
              name: '<?php echo $name; ?>[]',
              files: <?php echo JsonDataEncoder::encode($files); ?>
            });});
            </script>
    <?php
        }
    
        function getValue() {
            $data = $this->field->getSource();
    
            $ids = array();
            // Handle manual uploads (IE<10)
            if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES[$this->name])) {
                foreach (AttachmentFile::format($_FILES[$this->name]) as $file) {
    
                        $F = $this->field->uploadFile($file);
                        $ids[] = $F->getId();
    
                    }
                    catch (FileUploadError $ex) {}
    
                }
                return array_merge($ids, parent::getValue() ?: array());
            }
    
            // If no value was sent, assume an empty list
    
            elseif ($data && is_array($data) && !isset($data[$this->name]))
    
                return array();
    
            return parent::getValue();
        }
    }
    
    
    class FileUploadError extends Exception {}
    
    
    class FreeTextField extends FormField {
        static $widget = 'FreeTextWidget';
    
        function getConfigurationOptions() {
            return array(
                'content' => new TextareaField(array(
    
                    'configuration' => array('html' => true, 'size'=>'large'),
    
                    'label'=>__('Content'), 'required'=>true, 'default'=>'',
                    'hint'=>__('Free text shown in the form, such as a disclaimer'),
                )),
            );
        }
    
        function hasData() {
            return false;
        }
    
        function isBlockLevel() {
            return true;
        }
    }
    
    class FreeTextWidget extends Widget {
    
        function render($options=array()) {
    
            $config = $this->field->getConfiguration();
    
            ?><div class=""><?php
            if ($label = $this->field->getLocal('label')) { ?>
                <h3><?php
                echo Format::htmlchars($label);
            ?></h3><?php
            }
            if ($hint = $this->field->getLocal('hint')) { ?>
            <em><?php
                echo Format::htmlchars($hint);
            ?></em><?php
            } ?>
            <div><?php
    
                echo Format::viewableImages($config['content']); ?></div>
            </div>
            <?php
        }
    }
    
    
    class VisibilityConstraint {
    
        const HIDDEN =      0x0001;
        const VISIBLE =     0x0002;
    
        var $initial;
        var $constraint;
    
        function __construct($constraint, $initial=self::VISIBLE) {
            $this->constraint = $constraint;
            $this->initial = $initial;
        }
    
        function emitJavascript($field) {
            $func = 'recheck';
            $form = $field->getForm();
    ?>
        <script type="text/javascript">
          !(function() {
            var <?php echo $func; ?> = function() {
    
              var target = $('#field<?php echo $field->getWidget()->id; ?>');
    
    
    <?php   $fields = $this->getAllFields($this->constraint);
            foreach ($fields as $f) {
                $field = $form->getField($f);
                echo sprintf('var %1$s = $("#%1$s");',
    
                    $field->getWidget()->id);
    
            }
            $expression = $this->compileQ($this->constraint, $form);
    ?>
    
              if (<?php echo $expression; ?>)
    
    Peter Rotich's avatar
    Peter Rotich committed
                target.slideDown('fast', function (){
    
                    $(this).trigger('show');
                    });
              else
                target.slideUp('fast', function (){
                    $(this).trigger('hide');
                    });
    
            };
    
    <?php   foreach ($fields as $f) {
                $w = $form->getField($f)->getWidget();
    ?>
    
            $('#<?php echo $w->id; ?>').on('change', <?php echo $func; ?>);
    
            $('#field<?php echo $w->id; ?>').on('show hide', <?php
                    echo $func; ?>);
    
    <?php   } ?>
          })();
        </script><?php
        }
    
        /**
         * Determines if the field was visible when the form was submitted
         */
        function isVisible($field) {
            return $this->compileQPhp($this->constraint, $field);
        }
    
        function compileQPhp(Q $Q, $field) {
    
            if (!($form = $field->getForm())) {
                return $this->initial == self::VISIBLE;
            }
    
            $expr = array();
            foreach ($Q->constraints as $c=>$value) {
                if ($value instanceof Q) {
                    $expr[] = $this->compileQPhp($value, $field);
                }
                else {
                    @list($f, $op) = explode('__', $c, 2);
                    $field = $form->getField($f);
                    $wval = $field->getClean();
                    switch ($op) {
                    case 'eq':
                    case null:
    
                        $expr[] = ($wval == $value && $field->isVisible());
    
                    }
                }
            }
            $glue = $Q->isOred()
                ? function($a, $b) { return $a || $b; }
                : function($a, $b) { return $a && $b; };
            $initial = !$Q->isOred();
            $expression = array_reduce($expr, $glue, $initial);
            if ($Q->isNegated)
                $expression = !$expression;
            return $expression;
        }
    
        function getAllFields(Q $Q, &$fields=array()) {
            foreach ($Q->constraints as $c=>$value) {
                if ($c instanceof Q) {
                    $this->getAllFields($c, $fields);
                }
                else {
                    list($f, $op) = explode('__', $c, 2);
                    $fields[$f] = true;
                }
            }
            return array_keys($fields);
        }
    
        function compileQ($Q, $form) {
            $expr = array();
            foreach ($Q->constraints as $c=>$value) {
                if ($value instanceof Q) {
                    $expr[] = $this->compileQ($value, $form);
                }
                else {
                    list($f, $op) = explode('__', $c, 2);
    
                    $widget = $form->getField($f)->getWidget();
                    $id = $widget->id;
    
                    switch ($op) {
                    case 'eq':
    
                        $expr[] = sprintf('(%s.is(":visible") && %s)',
                                $id,
                                sprintf('%s == %s',
                                    sprintf($widget->getJsValueGetter(), $id),
                                    JsonDataEncoder::encode($value))
                                );
    
                    }
                }
            }
            $glue = $Q->isOred() ? ' || ' : ' && ';
            $expression = implode($glue, $expr);
            if (count($expr) > 1)
                $expression = '('.$expression.')';
            if ($Q->isNegated)
                $expression = '!'.$expression;
            return $expression;
        }
    }