Branch.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • site/
  • forms/
  • Branch.php
  • View
  • Commits
  • Open Download .zip Download (9 KB)
<?php
/**
 * Form to add site branches.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Site_Form_Branch extends P4Cms_Form
{
    /**
     * Setup form to collect branch information.
     */
    public function init()
    {
        // set the method for the form to POST
        $this->setMethod('post');

        // set id prefix to avoid collisions with other in-page forms.
        $this->setIdPrefix('branch');

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

        $this->addElement(
            'text',
            'name',
            array(
                'label'         => 'Name',
                'required'      => true,
                'filters'       => array('StringTrim')
            )
        );

        // generate list of possible source sites
        $siteOptions  = array();
        $fetchOptions = array(P4Cms_Site::FETCH_BY_ACL => array('branch', 'pull-from'));
        foreach (P4Cms_Site::fetchAll($fetchOptions) as $site) {
            if (!array_key_exists($site->getSiteId(), $siteOptions)) {
                $siteOptions[$site->getSiteId()] = $site->getConfig()->getTitle();
            }
        }
        $this->addElement(
            'select',
            'site',
            array(
                'label'         => 'Site',
                'required'      => true,
                'multiOptions'  => $siteOptions
            )
        );

        $this->addElement(
            'select',
            'parent',
            array(
                'label'         => 'Branch From',
                'required'      => true
            )
        );
        $this->_updateParentOptions();

        // add a field to collect the site description.
        $this->addElement(
            'textarea',
            'description',
            array(
                'label'         => 'Description',
                'rows'          => 3,
                'cols'          => 56,
                'required'      => false,
                'filters'       => array('StringTrim')
            )
        );

        // add a field to collect the branch's urls.
        $this->addElement(
            'textarea',
            'urls',
            array(
                'label'         => 'Branch Address',
                'rows'          => 3,
                'cols'          => 56,
                'description'   => "Optionally provide a list of urls for which this branch will be served.<br/>"
                                .  "For example: dev.domain.com, stage.domain.com"
            )
        );
        $this->getElement('urls')
             ->getDecorator('Description')
             ->setEscape(false);

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

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

    /**
     * Validate the form, ensure id isn't empty (id is based on
     * a filtered version of the name).
     *
     * @param  array    $data   the data to validate.
     * @return boolean
     */
    public function isValid($data)
    {
        $isValid = parent::isValid($data);

        $name    = isset($data['name']) ? $data['name'] : '';
        $filter  = new P4Cms_Filter_TitleToId;
        if ($name && !$filter->filter($name)) {
            $this->getElement('name')->addError(
                "Name must contain at least one letter or number."
            );
            $isValid = false;
        }

        // ensure branch urls are unique within all branches
        return $isValid && !$this->_isBranchAddressTaken();
    }

    /**
     * Override parent to update 'site' and 'parent' elements options when form is populated.
     *
     * @param   P4Cms_Record|array  $defaults   the default values to set on elements
     * @return  Site_Form_Branch    provides fluent interface
     */
    public function setDefaults($defaults)
    {
        parent::setDefaults($defaults);
        $this->_updateParentOptions();

        return $this;
    }

    /**
     * Set options for 'parent' element.
     */
    protected function _updateParentOptions()
    {
        // deal with edit and add cases separately
        //   add: determine site from form's site value, fall back to first site
        //  edit: determine site from existing branch, remove site element and,
        //        if editing a mainline, remove parent element (mainline can't have parent)
        $parentId = null;
        $branchId = $this->getValue('id');
        if (!$branchId) {
            $siteElement = $this->getElement('site');
            $options     = $siteElement->getMultiOptions();
            reset($options);    // we need this reset as the cursor isn't on the first option
            $siteId      = $siteElement->getValue() ?: key($options);
        } else {
            $site        = P4Cms_Site::fetch($branchId);
            $siteId      = $site->getSiteId();
            $stream      = $site->getStream();
            $parentId    = $stream->getParent();

            // we are editing, its impossible to change the site, so we remove the element
            $this->removeElement('site');

            // if we are editing the mainline, no need for parent at all
            // and therefore nothing more to do in this method (return)
            if ($stream->getType() === 'mainline') {
                $this->removeElement('parent');
                return;
            }
        }

        // generate parent options (filter for branches on selected site)
        $options  = array();
        $disabled = array();
        $exclude  = array();
        $user     = P4Cms_User::fetchActive();
        $branches = P4Cms_Site::fetchAll(array(P4Cms_Site::FETCH_BY_SITE => $siteId));
        foreach ($branches as $branch) {
            // during edit, exclude the branch we are editing and all of its children
            // (prevent user from trying to move branch under itself)
            $stream = $branch->getStream();
            $id     = $branch->getId();
            if ($id === $branchId || in_array($stream->getParent(), $exclude)) {
                if (!in_array($id, $exclude)) {
                    $exclude[] = $id;
                }
                continue;
            }

            // disable branches that we are not allowed to pull-from.
            // unless we are editing and that branch is already our parent
            if ($id !== $parentId && !$user->isAllowed('branch', 'pull-from', $branch->getAcl())) {
                $disabled[] = $id;
            }

            // indent option label according to branch depth
            $prefix       = str_repeat(static::UTF8_NBSP, $stream->getDepth() * 2);
            $options[$id] = $prefix . $stream->getName();
        }

        $this->getElement('parent')
             ->setMultiOptions($options)
             ->setAttrib('disable', $disabled);
    }

    /**
     * Helper function to ensure that branch urls are not taken.
     *
     * @param   boolean     $setError   optional - whether to set error on urls
     *                      element if url was already taken (true by default)
     * @return  boolean     true if any of the urls present in 'urls' field value
     *                      has already been taken by some other branch
     */
    protected function _isBranchAddressTaken($setError = true)
    {
        // prepare callback function to return given url with no schema
        $normalizeUrlCallback = function($url)
        {
            $url = stripos($url, 'http://')  === 0 ? substr($url, 7) : $url;
            $url = stripos($url, 'https://') === 0 ? substr($url, 8) : $url;

            return $url;
        };

        // compose list of taken branch urls
        $branchId = $this->getValue('id');
        $taken    = array();
        foreach (P4Cms_Site::fetchAll() as $branch) {
            if ($branch->getId() !== $branchId) {
                $taken = array_merge(
                    $taken,
                    array_map($normalizeUrlCallback, $branch->getConfig()->getUrls())
                );
            }
        }

        // check if any url passed in urls element is already taken
        $urls    = $this->getValue('urls');
        $urls    = array_filter(array_map('trim', preg_split("/\n|,/", $urls)));
        $isTaken = false;
        foreach ($urls as $url) {
            $url = $normalizeUrlCallback(trim($url));
            if (in_array($url, $taken)) {
                $isTaken = true;
                if ($setError) {
                    $this->getElement('urls')->addError(
                        "Url '$url' is already taken by other branch."
                    );
                }
            }
        }

        return $isTaken;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/site/forms/Branch.php
#1 8972 Matt Attaway Initial add of the Chronicle source code