FormNestedCheckbox.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • library/
  • P4Cms/
  • View/
  • Helper/
  • FormNestedCheckbox.php
  • View
  • Commits
  • Open Download .zip Download (10 KB)
<?php
/**
 * Derivative of zend form multi checkbox view helper.
 *
 * This version leverages the multi checkbox to do the actual input rendering but adds
 * on nesting via UL's.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class P4Cms_View_Helper_FormNestedCheckbox extends Zend_View_Helper_FormMultiCheckbox
{
    /**
     * Generates a set of nested checkbox button elements.
     *
     * @param string|array $name If a string, the element name.  If an array,
     *                           all other parameters are ignored, and the
     *                           array elements are extracted in place of
     *                           added parameters.
     * @param mixed $values      The checkbox value(s) to mark as 'checked'.
     * @param array $attribs     Optional HTML attributes for checkbox
     * @param array $options     A multidimensional array of key-value pairs
     *                           where the array key is the checkbox value,
     *                           and the array value is the checkbox text. For
     *                           nesting entries, the key is ignored and the
     *                           value is an array of further entries and/or
     *                           nesting levels.
     * @param int   $depth       optional - how far we've recursed.
     * @return string The checkbox XHTML.
     */
    public function formNestedCheckbox(
        $name,
        $values  = null,
        $attribs = null,
        $options = null,
        $depth   = 0
    )
    {
        // if set (and it appears to happen) attribs listsep will over-ride our passed
        // value. Eliminate the possibility.
        unset($attribs['listsep']);

        $classes    = !$depth ? "nested-checkbox" : "";
        $classes   .= !$depth && empty($options) ? " empty-checkbox" : "";
        $html       = "<ul" . ($classes ? " class=\"" . $classes . "\"" : "") . ">\n";
        $entries    = array();

        foreach ($options ?: array() as $checkboxValue => $value) {
            // recurse if the entries value is itself an array
            if (is_array($value)) {
                // first output any entries we have collected at the current level and clear cache
                if (!empty($entries)) {
                   $html .= $this->formCheckbox($name, $values, $attribs, $entries);
                }
                $entries = array();

                // handle the new nesting level
                $html .= $this->formNestedCheckbox($name, $values, $attribs, $value, $depth + 1);

                continue;
            }

            // cache out this entry, it will be output pre-recursion or at the end
            $entries[$checkboxValue] = $value;
        }

        // output any left-over entries that weren't caught by recursion
        if (!empty($entries)) {
            $html .= $this->formCheckbox($name, $values, $attribs, $entries);
        }

        // if there are no entries, output empty text if set.
        if (!$depth && empty($options) && isset($attribs['emptyText'])) {
            $html .= "<li>" . $this->view->escape($attribs['emptyText']) . "</li>";
        }

        $html .= "</ul>";

        return $html;
    }

    /**
     * Modified version of parent's formRadio to support:
     *  - read-only checkboxes
     *  - supports element specific 'class' options
     *  - id is an md5 of the value for values over 32 characters
     *
     * @param   string|array    $name       If a string, the element name.
     *                                      If an array, all other parameters are
     *                                      ignored, and the array elements are
     *                                      extracted in place of added parameters.
     * @param   mixed           $value      The checkbox value to mark as 'checked'.
     * @param   array|string    $attribs    Attributes added to each checkbox.
     * @param   array           $options    An array of key-value pairs where the array
     *                                      key is the checkbox value, and the array value
     *                                      is the checkbox text.
     * @return  string          The checkbox XHTML.
     */
    public function formCheckbox($name, $value = null, $attribs = null, $options = null)
    {

        $info = $this->_getInfo($name, $value, $attribs, $options);
        extract($info); // name, value, attribs, options, disable

        // also pull out css class details
        $class = isset($attribs['class']) ? $attribs['class'] : null;

        // retrieve attributes for labels (prefixed with 'label_' or 'label')
        $labelAttribs = array();
        foreach ($attribs as $key => $val) {
            $tmp    = false;
            $keyLen = strlen($key);
            if ((6 < $keyLen) && (substr($key, 0, 6) == 'label_')) {
                $tmp = substr($key, 6);
            } elseif ((5 < $keyLen) && (substr($key, 0, 5) == 'label')) {
                $tmp = substr($key, 5);
            }

            if ($tmp) {
                // make sure first char is lowercase
                $tmp[0] = strtolower($tmp[0]);
                $labelAttribs[$tmp] = $val;
                unset($attribs[$key]);
            }
        }

        $labelPlacement = 'append';
        foreach ($labelAttribs as $key => $val) {
            switch (strtolower($key)) {
                case 'placement':
                    unset($labelAttribs[$key]);
                    $val = strtolower($val);
                    if (in_array($val, array('prepend', 'append'))) {
                        $labelPlacement = $val;
                    }
                    break;
            }
        }

        // the checkbox values and labels
        $options = (array) $options;

        // build the element
        $xhtml = '';
        $list  = array();

        // should the name affect an array collection?
        $name = $this->view->escape($name);
        if ($this->_isArray && ('[]' != substr($name, -2))) {
            $name .= '[]';
        }

        // ensure value is an array to allow matching multiple times
        $value = (array) $value;

        // XHTML or HTML end tag?
        $endTag = ' />';
        if (($this->view instanceof Zend_View_Abstract) && !$this->view->doctype()->isXhtml()) {
            $endTag= '>';
        }

        // Set up the filter - Alnum + hyphen + underscore
        require_once 'Zend/Filter/PregReplace.php';
        $pattern = @preg_match('/\pL/u', 'a')
            ? '/[^\p{L}\p{N}\-\_]/u'    // Unicode
            : '/[^a-zA-Z0-9\-\_]/';     // No Unicode
        $filter = new Zend_Filter_PregReplace($pattern, "");

        // normalize read-only option to an array.
        $readOnly = isset($attribs['readOnly']) && is_array($attribs['readOnly'])
            ? $attribs['readOnly']
            : array();
        unset($attribs['readOnly']);

        // add checkboxes to the list.
        foreach ($options as $optValue => $optLabel) {

            // Should the label be escaped?
            if ($escape) {
                $optLabel = $this->view->escape($optLabel);
            }

            // if class value is an array pull out our elements entry
            $optClass = $class;
            if (is_array($class)) {
                $optClass = isset($class[$optValue]) ? $class[$optValue] : null;
            }
            $labelAttribs['class'] = $attribs['class'] = $optClass;

            // is it disabled?
            $disabled = '';
            if (true === $disable) {
                $disabled = ' disabled="disabled"';
            } elseif (is_array($disable) && in_array($optValue, $disable)) {
                $disabled = ' disabled="disabled"';
            }

            // is it checked?
            $checked = '';
            if (in_array($optValue, $value)) {
                $checked = ' checked="checked"';
            }

            // is it read-only?
            // if so, render a hidden input so the value gets submitted,
            // but set the main input to be disabled so it looks right.
            $hiddenInput = '';
            if (in_array($optValue, $readOnly)) {
                $disabled    = ' disabled="disabled"';
                $hiddenInput = '<input type="' . $this->_inputType . '"'
                             . ' style="display: none;"'
                             . ' name="' . $name . '"'
                             . ' value="' . $this->view->escape($optValue) . '"'
                             . $checked
                             . $endTag;

                // clear the value so it is only output once.
                $optValue   = null;
            }

            // generate ID
            $optId = $id . '-' . (strlen($optValue) <= 32
                ? $filter->filter($optValue)
                : md5($optValue));

            // Wrap the checkboxes in labels
            $checkbox = '<label'
                      . $this->_htmlAttribs($labelAttribs) . ' for="' . $optId . '">'
                      . $hiddenInput
                      . (('prepend' == $labelPlacement) ? $optLabel : '')
                      . '<input type="' . $this->_inputType . '"'
                      . ' name="' . $name . '"'
                      . ' id="' . $optId . '"'
                      . ' value="' . $this->view->escape($optValue) . '"'
                      . $checked
                      . $disabled
                      . $this->_htmlAttribs($attribs)
                      . $endTag
                      . (('append' == $labelPlacement) ? $optLabel : '')
                      . '</label>';

            // Wrap the checkbox in an li
            $checkbox = '<li' . $this->_htmlAttribs(array('class' => $optClass)) . '>'
                      . $checkbox
                      . '</li>';

            // add to the array of checkboxes
            $list[] = $checkbox;
        }

        // done!
        $xhtml .= implode("\n", $list);

        return $xhtml;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/library/P4Cms/View/Helper/FormNestedCheckbox.php
#1 8972 Matt Attaway Initial add of the Chronicle source code