ContentEntry.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • content/
  • views/
  • helpers/
  • ContentEntry.php
  • View
  • Commits
  • Open Download .zip Download (20 KB)
<?php
/**
 * View helper that displays a content entry.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Content_View_Helper_ContentEntry extends Zend_View_Helper_HtmlElement
{
    const               OPT_FIELDS          = 'fields';
    const               OPT_FIELD_OPTIONS   = 'fieldOptions';
    const               OPT_PRELOAD_FORM    = 'preloadForm';
    const               OPT_TAG_NAME        = 'tagName';
    const               OPT_DISPLAY         = 'display';

    const               DATA_INCLUDE_FIELDS = 'fields';
    const               DATA_INCLUDE_CHANGE = 'change';
    const               DATA_INCLUDE_STATUS = 'status';
    const               DATA_INCLUDE_OPENED = 'opened';

    protected           $_elementDojoType   = 'p4cms.content.Element';
    protected           $_elementDefaultTag = 'div';

    protected           $_entryDojoType     = 'p4cms.content.Entry';
    protected           $_entryElementType  = 'div';

    protected           $_entry             = null;
    protected           $_options           = null;

    protected static    $_defaultEntry      = null;
    protected static    $_defaultOptions    = null;

    /**
     * The default method called for this helper. If no params are passed
     * the default entry/options will be active. If an entry is passed the
     * passed options are always used with it (even if null). If non null
     * options are passed they will always be used.
     *
     * @param   P4Cms_Content|string|null   $entry      The entry to use or null for default
     * @param   array                       $options    The options to use or null for default
     *                                                  when using the default entry
     * @return  Content_View_Helper_ContentEntry    A helper instance with the correct options/entry
     */
    public function contentEntry($entry = null, array $options = null)
    {
        // if there is no entry and no options
        // return the active helper.
        if (is_null($entry) && is_null($options)) {
            return $this;
        }

        // if we made it here we must have a new entry and/or options.
        // create an instance and set the passed entry/options; if either
        // is invalid the new entry will fall back to the active entry
        // options which are stored statically
        $helper = new static;

        // normalize entry to an object for the case of a passed id
        if (!is_null($entry) && !$entry instanceof P4Cms_Content) {
            $entry = P4Cms_Content::fetch($entry);
        }

        $helper->view     = $this->view;
        $helper->_entry   = $entry;
        $helper->_options = $options;

        return $helper;
    }

    /**
     * Set a default entry and options to be used if this helper
     * is called without pasing arguments. This is normaly used
     * by the controller to set the active entry and recommended
     * options.
     *
     * @param P4Cms_Content|null    $entry      entry to use or null for default
     * @param array|null            $options    options to use or null for default
     */
    public function setDefaults(P4Cms_Content $entry = null, array $options = null)
    {
        // normalize entry to an object for the case of a passed id
        if (!is_null($entry) && !$entry instanceof P4Cms_Content) {
            $entry = P4Cms_Content::fetch($entry);
        }

        static::$_defaultEntry   = $entry;
        static::$_defaultOptions = $options;
    }

    /**
     * Returns the default entry or null.
     *
     * @return  P4Cms_Content|null  The default entry if we have one.
     */
    public function getDefaultEntry()
    {
       return static::$_defaultEntry;
    }

    /**
     * Returns the default options array or null.
     *
     * @return array|null   The default options array if we have one.
     */
    public function getDefaultOptions()
    {
        return static::$_defaultOptions;
    }

    /**
     * Return the entry being used for this helper instance. Will return
     * the entry passed to contentEntry() if valid falling back to the
     * default entry.
     *
     * @return P4Cms_Content|null   The active content entry or null
     */
    public function getEntry()
    {
        if ($this->_entry instanceof P4Cms_Content) {
            return $this->_entry;
        }

        if (!static::$_defaultEntry instanceof P4Cms_Content) {
            throw new P4Cms_Content_Exception(
                "No entry has been set on the Content Entry helper"
            );
        }

        return static::$_defaultEntry;
    }

    /**
     * Return the options being used for this helper instance. The default
     * options are returned if we are on the default entry and null options
     * were passed to contentEntry(). If we are using a non-default content
     * entry the passed options are always returned even when null.
     *
     * We only ever use the default options with the default entry as settings
     * such as include form are likely to be set here and are not generally
     * advisable when rendering other content entries.
     *
     * @return P4Cms_Content|null   The active content entry or null
     */
    public function getOptions()
    {
        if ($this->_options != null || $this->_entry instanceof P4Cms_Content) {
            return $this->_options;
        }

        return static::$_defaultOptions;
    }

    /**
     * A magic method which calls through to render; see render for details.
     *
     * @return  string  The output of our render method.
     */
    public function __toString()
    {
        try {
            return $this->render();
        } catch (Exception $e) {
            return "";
        }
    }

    /**
     * Output the appropriate markup to display a content entry and
     * all of its associated fields.
     *
     * @return  string                  the markup for a content entry.
     *
     * @publishes   p4cms.content.render
     *              Return the passed HTML after applying any modifications. Allows customization of
     *              the markup for a content entry.
     *              string          $html       The HTML markup for a content entry.
     *              P4Cms_Content   $content    The content entry being rendered.
     *              array           $options    An array of options for the view helper to influence
     *                                          rendering.
     */
    public function render()
    {
        // start the content dijit.
        $html = $this->open();

        // show the rendered fields of the content entry.
        foreach ($this->getRenderedFields() as $field) {
            $html .= $this->element($field);
        }

        // close the content entry.
        $html .= $this->close();

        // allow third-parties to influence rendering.
        $html = P4Cms_PubSub::filter('p4cms.content.render', $html, $this->getEntry(), $this->getOptions());

        return $html;
    }

    /**
     * Get all rendered fields of the content entry.
     *
     * @return  array   list of all fields of the content entry
     */
    public function getRenderedFields()
    {
        $entry   = $this->getEntry();
        $options = $this->getOptions();

        // if no fields specified, display all fields.
        $type   = $entry->getContentType();
        $fields = isset($options[static::OPT_FIELDS]) && is_array($options[static::OPT_FIELDS])
            ? $options[static::OPT_FIELDS]
            : null;
        if ($fields === null) {
            $fields = $type->getElementNames();
        }

        $rendered = array();
        foreach ($fields as $field) {
            if (!$type->hasElement($field)) {
                continue;
            }

            // if rendering this field is disabled, skip it.
            $fieldDefinition = $type->getElement($field);
            if (isset($fieldDefinition['display']['render'])
                && !$fieldDefinition['display']['render']
            ) {
                continue;
            }

            $rendered[] = $field;
        }

        return $rendered;
    }

    /**
     * Output the appropriate markup to open the active entry.
     * Produce dijit markup if user can edit this content entry or plain html otherwise.
     *
     * @return  string  the opening markup for a content entry.
     */
    public function open()
    {
        $entry   = $this->getEntry();
        $options = $this->getOptions();

        // pull out the version and headVersion if possible; defaults to blank
        $version     = '';
        $headVersion = '';
        if ($entry->getId()) {
            $file = $entry->toP4File();
            if ($file->hasStatusField('headRev')) {
                $version     = $file->getStatus('headRev');
                $headVersion = P4_File::fetch(
                    P4_File::stripRevspec($file->getFilespec())
                )->getStatus('headRev');
            }
        }

        // prepare html attributes.
        $attribs = array(
            "id"                => "content-entry-" . $entry->getId(),
            "class"             => "content-entry content-entry-type-". $entry->getContentTypeId(),
            "dojoType"          => $this->_entryDojoType,
            "contentType"       => $entry->getContentTypeId(),
            "contentId"         => $entry->getId(),
            "contentTitle"      => $entry->getTitle(),
            "contentVersion"    => $version,
            "headVersion"       => $headVersion,
            "deleted"           => Zend_Json::encode($entry->isDeleted())
        );

        // add all privileges for which user has access to 'content/entryID' resource.
        $user       = P4Cms_User::fetchActive();
        $resource   = 'content/' . $entry->getId();
        $privileges = $user->getAllowedPrivileges($resource);

        // add list with privileges to the dijit attributes.
        $attribs["allowedPrivileges"] = implode(", ", $privileges);

        // declare entry markup (_htmlAttrib() does escaping).
        $html = "<" . $this->_entryElementType . $this->_htmlAttribs($attribs) . ">";

        // optionally preload the form.
        if ($this->_shouldPreloadForm($entry, $privileges, $options)) {
            $formOptions = array('entry' => $entry);

            // if request specifies an id prefix, add it to options.
            $request = Zend_Controller_Front::getInstance()->getRequest();
            $formOptions['idPrefix'] = $request->getParam('formIdPrefix');

            $form = new Content_Form_Content($formOptions);
            $form->populate();

            $iconUrl = $this->view->url(
                array(
                    'module'        => 'content',
                    'controller'    => 'type',
                    'action'        => 'icon',
                    'id'            => $entry->getContentType()->getId()
                )
            );

            // render form into markup.
            $html .= "<div class='p4cms-ui content-form-container' "
                  .  "dojoType='dojox.layout.ContentPane' "
                  .  "style='display: none'>"
                  .  "<div class=\"content-type-heading\">"
                  .  "<img src=\"$iconUrl\" width=\"64\" height=\"64\"/>"
                  .  "<span>" . $this->view->escape($entry->getContentType()->getLabel()) ."</span>"
                  .  "</div>\n"
                  .  $form->render()
                  .  "</div>";
        }

        return $html;
    }

    /**
     * Indicate whether an element exists in the content entry.
     *
     * @param   string  $name       the name of the element/field to display.
     * @return  bool    true if the element exists in the entry, false otherwise
     */
    public function hasElement($name)
    {
        return $this->getEntry()->hasField($name) ? true : false;
    }

    /**
     * Output the appropriate markup to display a content element.
     *
     * @param   string                  $name       the name of the element/field to display.
     * @param   null|array              $options    options to influence entry markup.
     * @return  string|bool             the markup for the content element or false
     */
    public function element($name, array $options = null)
    {
        // utilize the default options if none were passed
        $defaults   = $this->getOptions();
        $optionsKey = static::OPT_FIELD_OPTIONS;
        $options    = is_null($options) && isset($defaults[$optionsKey], $defaults[$optionsKey][$name])
            ? $defaults[$optionsKey][$name]
            : $options;

        // get the entry and, defensive of null, the type
        $entry = $this->getEntry();
        $type  = $entry ? $entry->getContentType() : null;

        // return false if entry or element aren't known
        // to avoid throwing an exception
        if (!$entry || !$type->hasElement($name)) {
            return false;
        }

        // determine display options
        $displayOptions = isset($options[static::OPT_DISPLAY])
            ? $options[static::OPT_DISPLAY]
            : array();

        $element    = $type->getFormElement($name);
        $definition = $type->getElement($name);
        $label      = $element->getLabel() ? : $name;
        $value      = $entry->getDisplayValue($name, $displayOptions);

        // prepare html attributes.
        $attribs = array(
            "class"         => "content-element content-element-type-" . $definition['type']
                            .  " content-element-" . $name,
            "dojoType"      => $this->_elementDojoType,
            "elementName"   => $name,
            "elementLabel"  => $label,
            "contentId"     => $entry->getId(),
            "required"      => $element->isRequired() ? 'true' : 'false'
        );

        // allow caller to specify an element id.
        if (isset($options['id'])) {
            $attribs['id'] = $options['id'];
        }

        $tagName = $this->_getTagName($definition, $options);

        // _htmlAttribs() does HTML attribute escaping;
        // $value is escaped in P4Cms_Content::getDisplayValue() as long as
        // proper display filters are defined in element's definition
        return "<" . $tagName . $this->_htmlAttribs($attribs) . ">"
            . $value . "</" . $tagName . ">";
    }

    /**
     * Output the closing tag for a content entry dijit.
     *
     * @return  string  the closing tag for a content entry.
     *
     * @publishes   p4cms.content.render.close
     *              Return the passed HTML after applying any modifications. Allows customization of
     *              the closing HTML markup for a content entry.
     *              string                              $html       The closing HTML markup for the
     *                                                              content entry.
     *              Content_View_Helper_ContentEntry    $helper     The view helper rendering the
     *                                                              content entry.
     */
    public function close()
    {
        $html = "</" . $this->_entryElementType . ">";

        // allow third-parties to influence rendering.
        $html = P4Cms_PubSub::filter(
            'p4cms.content.render.close',
            $html,
            $this
        );

        return $html;
    }

    /**
     * Return an array with data about the content entry.
     *
     * Supports the options:
     *  DATA_INCLUDE_FIELDS => true/false/array of field ids (true is default)
     *  DATA_INCLUDE_CHANGE => true/false                    (false is default)
     *  DATA_INCLUDE_STATUS => true/false                    (false is default)
     *  DATA_INCLUDE_OPENED => true/false                    (false is default)
     *
     * @param   array|null  $options    Any custom options to use
     * @return  array       The requested entry details
     */
    public function data(array $options = null)
    {
        // normalize options to array and set defaults
        $options = ((array) $options) + array(
            static::DATA_INCLUDE_FIELDS => true,
            static::DATA_INCLUDE_CHANGE => false,
            static::DATA_INCLUDE_STATUS => false,
            static::DATA_INCLUDE_OPENED => false
        );

        // the data array will be our final return
        $data = array();

        // deal with the fields option
        $fields = $options[static::DATA_INCLUDE_FIELDS];
        if (!empty($fields) && (bool)$fields) {
            $data['fields'] = $this->getEntry()->getValues();

            // if we have an array of allowed fields enforce it
            if (is_array($fields)) {
                foreach ($data['fields'] as $key => $value) {
                    if (!in_array($key, $fields)) {
                        unset($data['fields'][$key]);
                    }
                }
            }
        }

        // deal with the opened option
        if ($options[static::DATA_INCLUDE_OPENED] && $this->getEntry()->getId()) {
            try {
                $data['opened'] = P4Cms_Content_Opened::fetch(
                    $this->getEntry()->getId(),
                    $this->getEntry()->getAdapter()
                )->getUsers();
            } catch (P4Cms_Record_NotFoundException $e) {
                // no data to add no-one has it open
            }
        }

        $file = $this->getEntry()->toP4File();

        // include the change details if requested
        if ((bool) $options[static::DATA_INCLUDE_CHANGE]) {
            $data['change'] = $file->getChange()->getValues();
        }

        // include the status if requested
        if ((bool) $options[static::DATA_INCLUDE_STATUS]) {
            $data['status'] = array(
                'Version'   => $file->getStatus('headRev'),
                'Action'    => $file->getStatus('headAction')
            );
        }

        return $data;
    }

    /**
     * Get the view object set on this helper.
     *
     * @return  Zend_View_Interface     the view object set on this helper.
     * @throws  Content_Exception       if no view object set.
     */
    public function getView()
    {
        if (!$this->view instanceof Zend_View_Interface) {
            throw new Content_Exception(
                "Cannot get view object. No valid view object has been set."
            );
        }

        return $this->view;
    }

    /**
     * Get the html tag name to use for this element.
     *
     * @param   array       $definition     the element definition.
     * @param   null|array  $options        options to influence entry markup.
     * @return  string  the html tag name for this element.
     */
    protected function _getTagName($definition, array $options = null)
    {
        if (is_array($options) && isset($options[static::OPT_TAG_NAME])) {
            return $options[static::OPT_TAG_NAME];
        }

        if (isset($definition['display'][static::OPT_TAG_NAME])) {
            return $definition['display'][static::OPT_TAG_NAME];
        }

        return $this->_elementDefaultTag;
    }

    /**
     * Determine if the form should be preloaded. Form will only be
     * preloaded if preload option is set and user has permission.
     *
     * @param   string|P4Cms_Content    $entry      a content entry id or instance.
     * @param   array                   $privileges privileges current user has for this entry.
     * @param   null|array              $options    options to influence entry markup.
     * @return  string                  the opening markup for a content entry.
     */
    protected function _shouldPreloadForm($entry, array $privileges, array $options = null)
    {
        // if user has no add/edit permission for this entry, return false.
        $privilege = $entry->hasId() ? 'edit' : 'add';
        if (!in_array($privilege, $privileges)) {
            return false;
        }

        $preloadOption = Content_View_Helper_ContentEntry::OPT_PRELOAD_FORM;
        if (isset($options[$preloadOption])) {
            return $options[$preloadOption];
        }

        return false;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/content/views/helpers/ContentEntry.php
#1 8972 Matt Attaway Initial add of the Chronicle source code