MenuItem.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • menu/
  • forms/
  • MenuItem.php
  • View
  • Commits
  • Open Download .zip Download (14 KB)
<?php
/**
 * This is the menu item form.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Menu_Form_MenuItem extends P4Cms_Form
{
    /**
     * Defines the elements that make up the menu item form.
     * Called automatically when the form object is created.
     */
    public function init()
    {
        // form should use p4cms-ui styles.
        $this->setAttrib('class', 'p4cms-ui menu-item-form');

        // set the method for the form to POST
        $this->setMethod('post');

        $this->addElement('hidden', 'uuid',   array('ignore' => true));
        $this->addElement('hidden', 'menuId', array('ignore' => true));

        $this->addElement(
            'text',
            'label',
            array(
                'label'         => 'Label',
                'order'         => -40,
                'required'      => true,
                'filters'       => array('StringTrim')
            )
        );
        $this->getElement('label')
             ->getDecorator('htmlTag')
             ->setOption('class', 'menu-item-label');

        $this->addElement(
            'select',
            'position',
            array(
                'label'         => 'Position',
                'order'         => -30,
                'required'      => true,
                'ignore'        => true,
                'multiOptions'  => array(
                    'before'    => 'Before',
                    'after'     => 'After',
                    'under'     => 'Under'
                )
            )
        );
        $this->getElement('position')
             ->getDecorator('htmlTag')
             ->setOption('class', 'menu-item-position');

        $this->addElement(
            'select',
            'location',
            array(
                'label'         => 'Location',
                'order'         => -20,
                'required'      => true,
                'ignore'        => true
            )
        );
        $this->getElement('location')
             ->getDecorator('label')
             ->setOption('tagClass', 'menu-item-location');
        $this->_updateLocationOptions();

        $this->addElement(
            'select',
            'type',
            array(
                'label'         => 'Type',
                'order'         => -10,
                'required'      => true,
                'value'         => 'Zend_Navigation_Page_Uri',
                'multiOptions'  => static::getTypeOptions()
            )
        );

        $this->addElement(
            'select',
            'target',
            array(
                'label'         => 'Target',
                'order'         => 100,
                'required'      => false,
                'multiOptions'  => array(
                    '_self'     => 'Current Window',
                    '_blank'    => 'New Window',
                    '_top'      => 'Top Window',
                    '_parent'   => 'Parent Window'
                )
            )
        );

        $this->addElement(
            'text',
            'class',
            array(
                'label'         => 'CSS Class',
                'order'         => 200,
                'required'      => false,
                'filters'       => array('StringTrim')
            )
        );

        $this->addElement(
            'textarea',
            'onClick',
            array(
                'label'         => 'Click Event',
                'order'         => 210,
                'required'      => false,
                'rows'          => 3,
                'cols'          => 60,
                'description'   => 'Optional JavaScript code for the onclick event.'
            )
        );

        $this->addElement(
            'SubmitButton',
            'save',
            array(
                'label'     => 'Save',
                'required'  => false,
                'class'     => 'preferred',
                'ignore'    => true
            )
        );

        // put the button in a fieldset.
        $this->addDisplayGroup(
            array('save'),
            'buttons',
            array(
                'order' => 300,
                'class' => 'buttons'
            )
        );
    }

    /**
     * Extend parent to deal with the combined type and
     * (virtual) handler fields for dynamic menu items.
     *
     * @param   string  $field  The field to retreive value of
     * @return  mixed   the requested fields value
     */
    public function getValue($field)
    {
        if ($field != 'type' && $field != 'handler') {
            return parent::getValue($field);
        }

        $values = $this->getValues();
        return isset($values[$field]) ? $values[$field] : null;
    }

    /**
     * Extend parent to split out the dynamic menu handler id
     * from the menu item type when present.
     *
     * @param  bool $suppressArrayNotation see parent
     * @return array
     */
    public function getValues($suppressArrayNotation = false)
    {
        $values = parent::getValues($suppressArrayNotation);

        if (isset($values['type'])) {
            $values = $this->splitType($values['type']) + $values;
        }

        return $values;
    }

    /**
     * Extends parent to combine handler field with type
     * when dealing with dynamic menu items.
     *
     * @param   P4Cms_Record|array  $defaults   the default values to set on elements
     * @return  Zend_Form           provides fluent interface
     */
    public function setDefaults($defaults)
    {
        parent::setDefaults($this->combineType($defaults));

        $this->_updateLocationOptions();

        return $this;
    }

    /**
     * Extends parent to combine handler field with type
     * when dealing with dynamic menu items.
     *
     * @param  array    $data   see parent
     * @return boolean
     */
    public function isValid($data)
    {
        return parent::isValid($this->combineType($data));
    }

    /**
     * For dynamic menu items this form adds the dynamic handler id
     * to the end of the type value (separated by a slash). This method
     * exists to aid in splitting these values apart again.
     *
     * @param   string  $type   the combined type/dynamic-handler id
     *                          (safe to call on type only values).
     * @return  array   an array containing the menu item type and optionally
     *                  the dynamic handler id (for dynamic menu items).
     */
    public function splitType($type)
    {
        $values = array('type' => $type);
        if (strpos($type, 'P4Cms_Navigation_Page_Dynamic/') === 0) {
            list($values['type'], $values['handler']) = explode('/', $type, 2);
        }

        return $values;
    }

    /**
     * For dynamic menu items we append the dynamic handler id
     * to the end of the type value (separated by a slash).
     *
     * @param   array   $values     form values array where the type and
     *                              dynamic handler are potentially separate.
     * @return  array   values with the dynamic handler id appended to the
     *                  type value and separated by a slash.
     */
    public function combineType($values)
    {
        if (isset($values['type'], $values['handler'])
            && $values['type'] == 'P4Cms_Navigation_Page_Dynamic'
        ) {
            $values['type'] .= '/' . $values['handler'];
            unset($values['handler']);
        }

        return $values;
    }

    /**
     * Generate the page type multi-options for use with the type field.
     * Static so it can be used elsewhere (e.g. menu grid filters).
     *
     * @param   bool    $checklist      optional - prepare options for a nested checklist
     *                                  instead of a select field (false by default).
     * @param   bool    $includeMenu    optional - include a 'Menu' entry in the options
     *                                  (false by default).
     * @return  array   list of type multi-options.
     */
    public static function getTypeOptions($checklist = false, $includeMenu = false)
    {
        // start with standard navigation page types (excluding dynamic types).
        $types = P4Cms_Navigation_PageTypeHandler::fetchAll();
        $types = $types->filter(
            'id',
            'P4Cms_Navigation_Page_Dynamic',
            P4Cms_Model_Iterator::FILTER_INVERSE
        );
        $types = array_combine(
            $types->invoke('getId'),
            $types->invoke('getLabel')
        );

        if ($includeMenu) {
            $types['P4Cms_Menu'] = 'Menu';
        }

        // sort the entries by label
        natsort($types);

        // if preparing for a nested checklist, include a group label.
        if ($checklist) {
            $types['P4Cms_Navigation_Page_Dynamic'] = 'Dynamic';
        }

        // put dynamic types in an opt-group.
        $group         = 'Dynamic';
        $types[$group] = array();
        $dynamicTypes  = P4Cms_Navigation_DynamicHandler::fetchAll();
        foreach ($dynamicTypes as $dynamicType) {
            $label = $dynamicType->getLabel();
            $value = 'P4Cms_Navigation_Page_Dynamic/' . $dynamicType->getId();

            $types[$group][$value] = $label;
        }
        natcasesort($types[$group]);

        // include a blank entry (except for checklists)
        if (!$checklist) {
            $types = array("" => "") + $types;
        }

        return $types;
    }

    /**
     * Fills in the multi-options for the location drop-down.
     * The entry being edited and its children are excluded from this
     * list to avoid recursive selection.
     *
     * If no location has been set but we do have a menu id this
     * method will also set the location and position appropriately.
     */
    protected function _updateLocationOptions()
    {
        $uuid      = $this->getValue('uuid');
        $menuId    = $this->getValue('menuId');
        $location  = $this->getValue('location');
        $locations = $this->_getLocations();

        // clear our location if it is not valid
        if ($location) {
            $items = $locations->filter('id', $location, P4Cms_Model_Iterator::FILTER_COPY);
            if (!count($items)) {
                $location = null;
            }
        }

        // Handle editing and adding cases:
        // - If we have an item UUID we are editing an existing item and need
        //   to deal with removing it from the list and setting location.
        // - Otherwise, if we have a menuId and no location we are adding
        //   and need to set the location to the end of the current list.
        if ($uuid) {
            $items = $locations->filter('menuItemId', $uuid, P4Cms_Model_Iterator::FILTER_COPY);

            // do the limiting and location setting if item could be found
            if ($items->count()) {
                $item = $items->first();

                // if we don't have an explicitly set location; determine one
                if (!$location) {
                    $previous = $item->getPreviousMenuItem();
                    $next     = $item->getNextMenuItem();
                    $parentId = $item->getParentId();

                    // run through the three possible positions in order of preference
                    if ($previous) {
                        $this->getElement('location')->setValue($previous->getId());
                        $this->getElement('position')->setValue('after');
                    } else if ($next) {
                        $this->getElement('location')->setValue($next->getId());
                        $this->getElement('position')->setValue('before');
                    } else if ($parentId) {
                        $this->getElement('location')->setValue($parentId);
                        $this->getElement('position')->setValue('under');
                    }
                }

                // remove our item and any children to avoid the user
                // making a recursive selection for location.
                $removalItems = new RecursiveIteratorIterator(
                    $item->getMenuItem(),
                    RecursiveIteratorIterator::SELF_FIRST
                );

                $removalIds = array($uuid);
                foreach ($removalItems as $removalItem) {
                    $removalIds[] = $removalItem->uuid;
                }

                $locations->filter('menuItemId', $removalIds, P4Cms_Model_Iterator::FILTER_INVERSE);
            }
        } else if ($menuId && !$location) {
            $items = $locations->filter('menuId', $menuId, P4Cms_Model_Iterator::FILTER_COPY);
            $items->filter('depth', 1);

            // if the menu has children, set our position to be the last child
            // otherwise set our position as under the menu.
            if ($items->count()) {
                $this->getElement('location')->setValue(end($items)->getId());
                $this->getElement('position')->setValue('after');
            } else {
                $this->getElement('location')->setValue($menuId);
                $this->getElement('position')->setValue('under');
            }
        }

        // any modifications to the locations list are complete
        // at this point simply glue together the IDs and labels
        // (indented for depth) so we can set the multi-options
        $options = array('' => '');
        foreach ($locations as $location) {
            $prefix = str_repeat(static::UTF8_NBSP, $location->getDepth() * 2);
            $options[$location->getId()] = $prefix . $location->getLabel();
        }

        $this->getElement('location')->setMultiOptions($options);
    }

    /**
     * Get the possible locations for this menu item to be positioned relative to.
     * Retrieves all menus and all menu items in a single flat list.
     * (@see P4Cms_Menu::fetchMixed)
     *
     * @return  P4Cms_Model_Iterator    menus and menu items in a single flat list.
     */
    protected function _getLocations()
    {
        return P4Cms_Menu::fetchMixed();
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/menu/forms/MenuItem.php
#1 8972 Matt Attaway Initial add of the Chronicle source code