Config.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • library/
  • P4Cms/
  • Site/
  • Config.php
  • View
  • Commits
  • Open Download .zip Download (12 KB)
<?php
/**
 * A specialized record class for use with site branch objects.
 *
 * Site/Branch config consists of a few rather discrete items
 * including Urls, Active Theme Selection and other General config.
 * We break this data into three seperate records to allow
 * our branching operations to deal with them granularly.
 *
 * The Site Config model adds support for a 'record' property
 * when declaring field definitions so items can choose which
 * location thier data should be stored in. By default fields
 * fall back to the 'ID_GENERAL' record for storage/retrieval.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class P4Cms_Site_Config extends P4Cms_Model
{
    const               ID_GENERAL  = 'config/general';
    const               ID_THEME    = 'config/theme';
    const               ID_URLS     = 'config/urls';

    protected           $_site      = null;
    protected           $_records   = array();
    protected           $_dirty     = array();

    protected static    $_fields    = array(
        'description'   => array(
            'accessor'  => 'getDescription',
            'mutator'   => 'setDescription'
        ),
        'title'          => array(
            'accessor'  => 'getTitle',
            'mutator'   => 'setTitle'
        ),
        'theme'          => array(
            'accessor'  => 'getTheme',
            'mutator'   => 'setTheme',
            'record'    => self::ID_THEME
        ),
        'urls'          => array(
            'accessor'  => 'getUrls',
            'mutator'   => 'setUrls',
            'record'    => self::ID_URLS
        )
    );

    /**
     * Create a new site config instance for the given site/branch.
     *
     * @param   P4Cms_Site  $site   site to represent config for
     */
    public function __construct(P4Cms_Site $site)
    {
        $this->_site = $site;
    }

    /**
     * Get the site branch this configuration is for.
     *
     * @return  P4Cms_Site  the site branch this config model is for.
     */
    public function getSite()
    {
        return $this->_site;
    }

    /**
     * Get the friendly (configurable) title for the associated site branch.
     *
     * @return  string  the site branch's title (defaults to the site id).
     */
    public function getTitle()
    {
        return (string) $this->_getValue('title') ?: $this->getSite()->getId();
    }

    /**
     * Set the friendly (configurable) title for the associated site branch.
     *
     * @param   string  $title      the title for the site branch.
     * @return  P4Cms_Site_Config   provides a fluent interface.
     */
    public function setTitle($title)
    {
        if (!is_string($title)) {
            throw new InvalidArgumentException("The provided title is not a string.");
        }

        return $this->_setValue('title', $title);
    }

    /**
     * Get the urls the associated site branch is configured to respond to.
     *
     * @return  array   the urls the site branch is configured to respond to.
     */
    public function getUrls()
    {
        $urls = $this->_getValue('urls');

        return is_array($urls) ? $urls : array();
    }

    /**
     * Set the urls for the associated site branch as an array of strings, or null.
     *
     * @param   array|string|null   $urls   the urls for the site branch.
     * @return  P4Cms_Site_Config   provides a fluent interface.
     */
    public function setUrls($urls)
    {
        // if provided with a string, convert to an array
        if (is_string($urls)) {
            $urls = array_filter(array_map('trim', preg_split("/\n|,/", $urls)));
        }

        return $this->_setValue('urls', $urls);
    }

    /**
     * Get a single url for this site/branch.
     *
     * If one or more explicit urls have been set for this site/branch
     * (e.g. http://stage.example.com), the first one will be used. If no
     * explicit urls have been configured, we search up through the parent
     * branches until we find one and append the name of the branch as the
     * first path component (e.g. http://example.com/-stage-).
     *
     * @return  string  a url for this site/branch
     */
    public function getUrl()
    {
        $urls = $this->getUrls();

        // if we don't have any urls, we need to grab urls from the
        // closest parent that does and append a branch specifier.
        if (!$urls) {
            $parent = $this->getSite();
            while (!$urls && ($parent = $parent->getParent())) {
                $urls = $parent->getConfig()->getUrls();
            }
        }

        // normalize url to begin with a protocol/scheme.
        $url = array_shift($urls);
        if ($url
            && substr($url, 0, 7) != 'http://'
            && substr($url, 0, 8) != 'https://'
        ) {
            $url = 'http://' . $url;
        }

        // if we got a url from a parent, we need to append
        // a branch specifier to route to this specific branch
        if ($url && isset($parent)) {
            $url = trim($url, "/") . "/-" . $this->getSite()->getBranchBasename() . "-";
        }

        return rtrim($url, "/");
    }

    /**
     * Get the current theme for the associated site branch.
     *
     * @return  string  the name of the theme for the site branch
     *                  returns the default theme if none has been set.
     */
    public function getTheme()
    {
        return $this->_getValue('theme') ?: P4Cms_Theme::DEFAULT_THEME;
    }

    /**
     * Set the theme for the associated site branch.
     *
     * @param   string  $theme          the name of the theme to use.
     * @return  P4Cms_Site_Config       provides a fluent interface.
     * @throws  P4Cms_Site_Exception    if theme does not exist
     */
    public function setTheme($theme)
    {
        if ($theme && !P4Cms_Theme::exists($theme)) {
            throw new P4Cms_Site_Exception("Theme $theme is invalid or does not exist.");
        }

        return $this->_setValue('theme', $theme);
    }

    /**
     * Get the description for the associated site branch.
     *
     * @return  string  the description for the associated site branch
     *                  returns an empty string if none set.
     */
    public function getDescription()
    {
        return (string) $this->_getValue('description');
    }

    /**
     * Set the description for the associated site branch.
     *
     * @param   string  $description    the description for the associated site branch
     * @return  P4Cms_Site_Config       provides a fluent interface.
     */
    public function setDescription($description = null)
    {
        if (!is_null($description) && !is_string($description)) {
            throw new InvalidArgumentException("The provided description is not a string.");
        }

        return $this->_setValue('description', $description);
    }

    /**
     * This record utilizes a general, theme and urls record to store
     * the various data. The parent getFields would have returned the
     * defined fields for all three; we have extended it to also include
     * any custom fields present on the general config.
     *
     * This model always routes unknown fields to the general config.
     * As such, we don't include any custom fields from the theme or
     * url records as they would not be accessible using our models
     * accessors/mutators.
     *
     * @return  array   all fields for this model
     */
    public function getFields()
    {
        $fields = array_merge(
            parent::getFields(),
            $this->_getRecord(static::ID_GENERAL)->getFields()
        );

        // return field names.
        return array_unique($fields);
    }

    /**
     * Our model wraps several records which seperately store the urls,
     * theme and general configuration details. When save is called
     * we start a batch (if one is not already in progress) and save
     * any of the files which have been modified.
     *
     * @param   string              $description    optional - a description of the change.
     * @return  P4Cms_Site_Config   provides a fluent interface
     */
    public function save($description = null)
    {
        // ensure we have a save description.
        $description = $description
            ?: "Saved configuration for '" . $this->getTitle() . "' site.";

        // start the batch
        $adapter = $this->getSite()->getStorageAdapter();
        $batch   = !$adapter->inBatch()
            ? $adapter->beginBatch($description)
            : false;

        // try to save each of the 'dirty' records.
        // note: we reset the adapter in case the record came
        // from cache or otherwise has a bogus adapter.
        try {
            foreach (array_keys($this->_dirty) as $id) {
                $this->_records[$id]->setAdapter($adapter)
                                    ->save();
            }
            $this->_dirty = array();
        } catch (Exception $e) {
            if ($batch) {
                $adapter->revertBatch();
            }
            throw $e;
        }

        // commit the batch.
        if ($batch) {
            $adapter->commitBatch();
        }

        return $this;
    }

    /**
     * The normal _getValue behaviour is to return values present
     * on this model. We have replaced our parent to route the
     * request to one of our underlying records based on the 'record'
     * property of the field. If the field is unknown or has no record
     * property we default to the general record.
     *
     * @param   string  $field  the name of the field to get the value of.
     * @return  mixed   the value of the field.
     */
    protected function _getValue($field)
    {
        $id = $this->_getFieldProperty($field, 'record') ?: static::ID_GENERAL;

        return $this->_getRecord($id)->getValue($field);
    }

    /**
     * The normal _setValue behaviour is to update values present
     * on this model. We have replaced our parent to route the
     * request to one of our underlying records based on the 'record'
     * property of the field. If the field is unknown or has no record
     * property we default to the general record.
     *
     * We also flag the associated record as 'dirty' so any later
     * save operations know to include it.
     *
     * @param   string  $field          the name of the field to set the value of.
     * @param   mixed   $value          the value to set in the field.
     * @return  P4Cms_Site_Config       provides fluent interface.
     * @throws  P4Cms_Model_Exception   if the field does not exist.
     */
    protected function _setValue($field, $value)
    {
        $id = $this->_getFieldProperty($field, 'record') ?: static::ID_GENERAL;

        $this->_getRecord($id)->setValue($field, $value);

        // flag record as dirty so we know to save it later.
        $this->_dirty[$id] = true;

        return $this;
    }

    /**
     * This method will return the specified record using the
     * model's associated storage adapter. If the requested
     * record does not already exist an in memory version will
     * be silently created.
     *
     * The returned records are cached in the protected '_records'
     * array for later re-use.
     *
     * @param   string  $id         The record id to retrieve/create
     * @return  P4Cms_Site_Config   provides fluent interface.
     */
    protected function _getRecord($id)
    {
        if (isset($this->_records[$id])) {
            return $this->_records[$id];
        }

        $adapter = $this->getSite()->getStorageAdapter();
        try {
            $record = P4Cms_Record::fetch($id, null, $adapter);
        } catch (P4Cms_Record_NotFoundException $e) {
            $record = new P4Cms_Record(null, $adapter);
            $record->setId($id);
        }

        $this->_records[$id] = $record;

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