/ */ 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.
" . "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; } }