public function configure() { parent::configure(); // We must explicitly limit the fields because otherwise tables with foreign key relationships // to the pages table will extend the form whether it's appropriate or not. If you want to do // those things on behalf of an engine used in some pages, define a form class called // enginemodulenameEngineForm. It will automatically be instantiated with the engine page // as an argument to the constructor, and rendered beneath the main page settings form. // On submit, it will be bound to the parameter name that begins its name format and, if valid, // saved consecutively after the main page settings form. The form will be rendered via // the _renderPageSettingsForm partial in your engine module, which must exist, although it // can be as simple as echo $form. (Your form is passed to the partial as $form.) // // We would use embedded forms if we could. Unfortunately Symfony has unresolved bugs relating // to one-to-many relations in embedded forms. $this->useFields(array('slug', 'template', 'engine', 'archived', 'view_is_secure')); unset($this['author_id'], $this['deleter_id'], $this['Accesses'], $this['created_at'], $this['updated_at'], $this['view_credentials'], $this['edit_credentials'], $this['lft'], $this['rgt'], $this['level']); $this->setWidget('template', new sfWidgetFormSelect(array('choices' => aTools::getTemplates()))); $this->setWidget('engine', new sfWidgetFormSelect(array('choices' => aTools::getEngines()))); // On vs. off makes more sense to end users, but when we first // designed this feature we had an 'archived vs. unarchived' // approach in mind $this->setWidget('archived', new sfWidgetFormChoice(array('expanded' => true, 'choices' => array(false => "Published", true => "Unpublished"), 'default' => false))); if ($this->getObject()->hasChildren()) { $this->setWidget('cascade_archived', new sfWidgetFormInputCheckbox()); $this->setValidator('cascade_archived', new sfValidatorBoolean(array('true_values' => array('true', 't', 'on', '1'), 'false_values' => array('false', 'f', 'off', '0', ' ', '')))); $this->setWidget('cascade_view_is_secure', new sfWidgetFormInputCheckbox()); $this->setValidator('cascade_view_is_secure', new sfValidatorBoolean(array('true_values' => array('true', 't', 'on', '1'), 'false_values' => array('false', 'f', 'off', '0', ' ', '')))); } $this->setWidget('view_is_secure', new sfWidgetFormChoice(array('expanded' => true, 'choices' => array(false => "Public", true => "Login Required"), 'default' => false))); $this->addPrivilegeWidget('edit', 'editors'); $this->addPrivilegeWidget('manage', 'managers'); $this->setValidator('slug', new aValidatorSlug(array('required' => true, 'allow_slashes' => true), array('required' => 'The slug cannot be empty.', 'invalid' => 'The slug must contain only slashes, letters, digits, dashes and underscores. Also, you cannot change a slug to conflict with an existing slug.'))); $this->setValidator('template', new sfValidatorChoice(array('required' => true, 'choices' => array_keys(aTools::getTemplates())))); // Making the empty string one of the choices doesn't seem to be good enough // unless we expressly clear 'required' $this->setValidator('engine', new sfValidatorChoice(array('required' => false, 'choices' => array_keys(aTools::getEngines())))); // The slug of the home page cannot change (chicken and egg problems) if ($this->getObject()->getSlug() === '/') { unset($this['slug']); } else { $this->validatorSchema->setPostValidator(new sfValidatorDoctrineUnique(array('model' => 'aPage', 'column' => 'slug'), array('invalid' => 'There is already a page with that slug.'))); } $this->widgetSchema->setIdFormat('a_settings_%s'); $this->widgetSchema->setNameFormat('settings[%s]'); $this->widgetSchema->setFormFormatterName('list'); $user = sfContext::getInstance()->getUser(); if (!$user->hasCredential('cms_admin')) { unset($this['editors']); unset($this['managers']); unset($this['slug']); } // We changed the form formatter name, so we have to reset the translation catalogue too $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('apostrophe'); }
/** * Poor man's multiple inheritance. This allows us to subclass an existing * actions class in order to create an engine version of it. See aEngineActions * for the call to add to your own preExecute method * @param mixed $actions */ public static function preExecute($actions) { $request = $actions->getRequest(); // Figure out where we are all over again, because there seems to be no clean way // to get the same controller-free URL that the routing engine gets. TODO: // ask Fabien how we can do that. $uri = urldecode($actions->getRequest()->getUri()); $rr = preg_quote(sfContext::getInstance()->getRequest()->getRelativeUrlRoot(), '/'); if (preg_match("/^(?:https?:\\/\\/[^\\/]+)?{$rr}(?:\\/[^\\/]+\\.php)?(.*)\$/", $uri, $matches)) { $uri = $matches[1]; } else { throw new sfException("Unable to parse engine URL {$uri}"); } // This will quickly fetch a result that was already cached when we // ran through the routing table (unless we hit the routing table cache, // in which case we're looking it up for the first time, also OK) $page = aPageTable::getMatchingEnginePageInfo($uri, $remainder); if (!$page) { throw new sfException('Attempt to access engine action without a page'); } $page = aPageTable::retrieveByIdWithSlots($page['id']); // We want to do these things the same way executeShow would aTools::validatePageAccess($actions, $page); aTools::setPageEnvironment($actions, $page); // Convenient access to the current page for the subclass $actions->page = $page; // If your engine supports allowing the user to choose from several page types // to distinguish different ways of using your engine, then you'll need to // return the template name from your show and index actions (and perhaps // others as appropriate). You can pull that information straight from // $this->page->template, or you can take advantage of $this->pageTemplate which // is ready to return as the result of an action (default has been changed // to Success, other values have their first letter capitalized) $templates = aTools::getTemplates(); // originalTemplate is what's in the template field of the page, except that // nulls and empty strings from pre-1.5 Apostrophe have been converted to 'default' // for consistency $actions->originalTemplate = $page->template; if (!strlen($actions->originalTemplate)) { // Compatibility with 1.4 templates and reasonable Symfony expectations $actions->originalTemplate = 'default'; } // pageTemplate is suitable to return from an action. 'default' becomes 'Success' // (the Symfony standard for a "normal" template's suffix) and other values have // their first letter capitalized if ($actions->originalTemplate === 'default') { $actions->pageTemplate = 'Success'; } else { $actions->pageTemplate = ucfirst($actions->originalTemplate); } }
/** * Flat name => label array for use in select elements * @return mixed */ public static function getTemplateChoices() { $templates = aTools::getTemplates(); $choices = array(); foreach ($templates as $engine => $etemplates) { foreach ($etemplates as $name => $label) { $choices["{$engine}:{$name}"] = $label; } } return $choices; }