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 = $actions->getRequest()->getUri(); $uriPrefix = $actions->getRequest()->getUriPrefix(); $uri = substr($uri, strlen($uriPrefix)); if (preg_match("/^\\/[^\\/]+\\.php(.*)\$/", $uri, $matches)) { $uri = $matches[1]; } // 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::getMatchingEnginePage($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; }
/** * You too can do this in a plugin dependent on a, see the provided stylesheet * for how to correctly specify an icon to go with your button. See the * apostrophePluginConfiguration class for the registration of the event listener. */ public static function getGlobalButtons() { // Only if we have suitable credentials if (aMediaTools::userHasUploadPrivilege()) { aTools::addGlobalButtons(array(new aGlobalButton('media', 'Media', 'aMedia/index', 'a-media', '/admin/media', 'aMedia'))); } }
public static function getGlobalButtons() { $user = sfContext::getInstance()->getUser(); if ($user->hasCredential('admin')) { aTools::addGlobalButtons(array(new aGlobalButton('polls', 'Polls', '@a_poll_poll_admin', 'a-poll'))); } }
public static function getGlobalButtons() { $user = sfContext::getInstance()->getUser(); if ($user->hasCredential('blog_author') || $user->hasCredential('blog_admin')) { aTools::addGlobalButtons(array(new aGlobalButton('blog', 'Blog', '@a_blog_admin', 'a-blog-btn'), new aGlobalButton('events', 'Events', '@a_event_admin', 'a-events day-' . date('j')))); } }
/** * DOCUMENT ME * @param mixed $root * @param mixed $active * @param mixed $options */ public function __construct($root, $active, $options = array()) { $this->user = sfContext::getInstance()->getUser(); $this->livingOnly = !(aTools::isPotentialEditor() && sfContext::getInstance()->getUser()->getAttribute('show-archived', true, 'apostrophe')); $this->root = $root; $this->active = $active; $this->options = $options; $this->buildNavigation(); }
public static function getGlobalButtons() { $mediaEnginePage = aPageTable::retrieveBySlug('/admin/media'); // Only if we have suitable credentials $user = sfContext::getInstance()->getUser(); if ($user->hasCredential('media_admin') || $user->hasCredential('media_upload')) { aTools::addGlobalButtons(array(new aGlobalButton('media', 'Media', 'aMedia/index', 'a-media', $mediaEnginePage))); } }
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'); }
public static function retrieveOrCreateByPageIdAndName($pageId, $name) { $culture = aTools::getUserCulture(); $area = Doctrine_Query::create()->from('aArea a')->where('a.page_id = ? AND a.name = ? AND a.culture = ?', array($pageId, $name, $culture))->fetchOne(); if ($area) { return $area; } $area = new aArea(); $area->page_id = $pageId; $area->name = $name; $area->culture = $culture; return $area; }
/** * Given an array of blogItems this function will populate its virtual page * areas with the current slot versions. * @param aBlogItem $blogItems */ public static function populatePages($blogItems) { $pageIds = array(); foreach ($blogItems as $aBlogItem) { $pageIds[] = $aBlogItem['page_id']; } if (count($pageIds)) { $q = aPageTable::queryWithSlots(); $q->whereIn('id', $pageIds); $pages = $q->execute(); aTools::cacheVirtualPages($pages); } }
/** * 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); } }
/** * @see sfValidatorBase */ protected function doClean($value) { $clean = (string) parent::doClean($value); $clean = aTools::strtolower($clean); $slugified = aTools::slugify($clean, $this->getOption('allow_slashes')); if ($this->getOption('strict')) { if ($slugified !== $clean) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } } else { $clean = $slugified; } return $clean; }
public function addCategory($name, $blog_id, $type = 'posts') { $category = current($this->sql->query("SELECT * FROM a_category where name = :name", array('name' => $name))); if ($category) { $category_id = $category['id']; } else { $s = "INSERT INTO a_category (name, created_at, updated_at, slug) "; $s .= "VALUES (:name, :created_at, :updated_at, :slug)"; $params = array('name' => $name, 'created_at' => aDate::mysql(), 'updated_at' => aDate::mysql(), 'slug' => aTools::slugify($name)); $this->sql->query($s, $params); $category_id = $this->sql->lastInsertId(); } $s = 'INSERT INTO a_blog_item_to_category (blog_item_id, category_id) VALUES(:blog_item_id, :category_id) ON DUPLICATE KEY UPDATE blog_item_id=blog_item_id'; $parms = array('blog_item_id' => $blog_id, 'category_id' => $category_id); $this->sql->query($s, $parms); return $category_id; }
public static function listenToRoutingLoadConfigurationEvent(sfEvent $event) { $r = $event->getSubject(); if (aMediaTools::getOption("routes_register") && in_array('aMedia', sfConfig::get('sf_enabled_modules'))) { // Since the media plugin is now an engine, we need our own // catch-all rule for administrative URLs in the media area. // Prepending it first means it matches last $r->prependRoute('a_media_other', new aRoute('/:action', array('module' => 'aMedia'))); $r->prependRoute('a_media_image_show', new aRoute('/view/:slug', array('module' => 'aMedia', 'action' => 'show'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$'))); // Allow permalinks for PDF originals $r->prependRoute('a_media_image_original', new sfRoute(sfConfig::get('app_a_routes_media_image_original', '/uploads/media_items/:slug.original.:format'), array('module' => 'aMediaBackend', 'action' => 'original'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$', 'format' => '^(\\w+)$'))); $route = new sfRoute(sfConfig::get('app_a_routes_media_image', '/uploads/media_items/:slug.:width.:height.:resizeType.:format'), array('module' => 'aMediaBackend', 'action' => 'image'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$', 'width' => '^\\d+$', 'height' => '^\\d+$', 'resizeType' => '^\\w$', 'format' => '^(jpg|png|gif)$')); $r->prependRoute('a_media_image', $route); $route = new sfRoute(sfConfig::get('app_a_routes_media_image_cropped', '/uploads/media_items/:slug.:cropLeft.:cropTop.:cropWidth.:cropHeight.:width.:height.:resizeType.:format'), array('module' => 'aMediaBackend', 'action' => 'image'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$', 'width' => '^\\d+$', 'height' => '^\\d+$', 'cropLeft' => '^\\d+$', 'cropTop' => '^\\d+$', 'cropWidth' => '^\\d+$', 'cropHeight' => '^\\d+$', 'resizeType' => '^\\w$', 'format' => '^(jpg|png|gif)$')); $r->prependRoute('a_media_image_cropped', $route); // What we want: // /media <-- everything // /image <-- media of type image // /video <-- media of type video // /tag/tagname <-- media with this tag // /image/tag/tagname <-- images with this tag // /video/tag/tagname <-- video with this tag // /media?search=blah blah blah <-- searches are full of // dirty URL-unfriendly characters and // are traditionally query strings. $r->prependRoute('a_media_index', new aRoute('/', array('module' => 'aMedia', 'action' => 'index'))); $r->prependRoute('a_media_index_type', new aRoute('/:type', array('module' => 'aMedia', 'action' => 'index'), array('type' => '(image|video)'))); $r->prependRoute('a_media_index_category', new aRoute('/category/:category', array('module' => 'aMedia', 'action' => 'index'), array('category' => '.*'))); // Support for existing pretty-URL tag links so they don't 404. We don't recommend // this for the generation of new links because it doesn't handle various // punctuation well with a wide variety of webserver configurations $r->prependRoute('a_media_index_deprecated_tag', new aRoute('/tag/:tag', array('module' => 'aMedia', 'action' => 'index'), array('tag' => '.*'))); // We removed :tag because we allow tags with dots and such and pretty URLs // work great until your rewrite rules decide they are supposed to be static files $r->prependRoute('a_media_index_tag', new aRoute('/tag', array('module' => 'aMedia', 'action' => 'index'), array('tag' => '.*'))); $r->prependRoute('a_media_select', new aRoute('/select', array('class' => 'aRoute', 'module' => 'aMedia', 'action' => 'select'))); $r->prependRoute('a_media_info', new sfRoute('/info', array('module' => 'aMediaBackend', 'action' => 'info'))); $r->prependRoute('a_media_tags', new sfRoute('/tags', array('module' => 'aMediaBackend', 'action' => 'tags'))); $r->prependRoute('a_media_upload', new aRoute('/upload', array('module' => 'aMedia', 'action' => 'upload'))); $r->prependRoute('a_media_edit_multiple', new aRoute('/editMultiple', array('module' => 'aMedia', 'action' => 'editMultiple'))); $r->prependRoute('a_media_edit', new aRoute('/edit', array('module' => 'aMedia', 'action' => 'edit'))); $r->prependRoute('a_media_new_video', new aRoute('/newVideo', array('module' => 'aMedia', 'action' => 'newVideo'))); $r->prependRoute('a_media_edit_video', new aRoute('/editVideo', array('module' => 'aMedia', 'action' => 'editVideo'))); } }
public function executeShow(sfWebRequest $request) { $this->buildParams(); $this->dateRange = ''; $this->aEvent = $this->getRoute()->getObject(); $this->categories = aCategoryTable::getCategoriesForPage($this->page); $this->forward404Unless($this->aEvent); $this->forward404Unless($this->aEvent['status'] == 'published' || $this->getUser()->isAuthenticated()); $this->preview = $this->getRequestParameter('preview'); aBlogItemTable::populatePages(array($this->aEvent)); // Thanks to Giles Smith for catching that we had no titles on our blog post permalink pages! // Too much Chrome will do that to you (: // Title is pre-escaped as valid HTML $prefix = aTools::getOptionI18n('title_prefix'); $suffix = aTools::getOptionI18n('title_suffix'); $this->getResponse()->setTitle($prefix . $this->aEvent->Page->getTitle() . $suffix, false); $this->calendar = $this->buildCalendar($request); return $this->pageTemplate; }
public function getEffectiveVariant($options) { $variants = aTools::getVariantsForSlotType($this->type, $options); if (!isset($variants)) { // No variants, no class return ''; } // Treat null and an empty string the same $variant = $this->variant . ''; // If the variant is not defined (and the empty string will not be), // and there is at least one variant, return the first one as the default. // If there are no variants return an empty string if (!isset($variants[$variant])) { if (count($variants)) { // Return the first variant for the type, if any, when the variant is bogus $keys = array_keys($variants); return $keys[0]; } return ''; } // If the variant is valid, return it as the CSS class return $this->variant; }
public static function listenToRoutingLoadConfigurationEvent(sfEvent $event) { $r = $event->getSubject(); if (aMediaTools::getOption("routes_register") && in_array('aMedia', sfConfig::get('sf_enabled_modules'))) { // Since the media plugin is now an engine, we need our own // catch-all rule for administrative URLs in the media area. // Prepending it first means it matches last $r->prependRoute('a_media_other', new aRoute('/:action', array('module' => 'aMedia'))); $r->prependRoute('a_media_image_show', new aRoute('/view/:slug', array('module' => 'aMedia', 'action' => 'show'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$'))); // Allow permalinks for PDF originals $r->prependRoute('a_media_image_original', new sfRoute('/uploads/media_items/:slug.original.:format', array('module' => 'aMediaBackend', 'action' => 'original'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$', 'format' => '^(jpg|png|gif|pdf)$'))); $route = new sfRoute('/uploads/media_items/:slug.:width.:height.:resizeType.:format', array('module' => 'aMediaBackend', 'action' => 'image'), array('slug' => '^' . aTools::getSlugRegexpFragment() . '$', 'width' => '^\\d+$', 'height' => '^\\d+$', 'resizeType' => '^\\w$', 'format' => '^(jpg|png|gif)$')); $r->prependRoute('a_media_image', $route); // What we want: // /media <-- everything // /image <-- media of type image // /video <-- media of type video // /tag/tagname <-- media with this tag // /image/tag/tagname <-- images with this tag // /video/tag/tagname <-- video with this tag // /media?search=blah blah blah <-- searches are full of // dirty URL-unfriendly characters and // are traditionally query strings. $r->prependRoute('a_media_index', new aRoute('/', array('module' => 'aMedia', 'action' => 'index'))); $r->prependRoute('a_media_index_type', new aRoute('/:type', array('module' => 'aMedia', 'action' => 'index'), array('type' => '(image|video)'))); $r->prependRoute('a_media_index_category', new aRoute('/category/:category', array('module' => 'aMedia', 'action' => 'index'), array('category' => '.*'))); $r->prependRoute('a_media_index_tag', new aRoute('/tag/:tag', array('module' => 'aMedia', 'action' => 'index'), array('tag' => '.*'))); $r->prependRoute('a_media_select', new aRoute('/select', array('class' => 'aRoute', 'module' => 'aMedia', 'action' => 'select'))); $r->prependRoute('a_media_info', new sfRoute('/info', array('module' => 'aMediaBackend', 'action' => 'info'))); $r->prependRoute('a_media_tags', new sfRoute('/tags', array('module' => 'aMediaBackend', 'action' => 'tags'))); $r->prependRoute('a_media_upload_images', new aRoute('/uploadImages', array('module' => 'aMedia', 'action' => 'uploadImages'))); $r->prependRoute('a_media_edit_images', new aRoute('/editImages', array('module' => 'aMedia', 'action' => 'editImages'))); $r->prependRoute('a_media_new_video', new aRoute('/newVideo', array('module' => 'aMedia', 'action' => 'newVideo'))); $r->prependRoute('a_media_edit_video', new aRoute('/editVideo', array('module' => 'aMedia', 'action' => 'editVideo'))); } }
<?php use_helper('I18N'); $options = $sf_user->getAttribute("slot-options-{$pageid}-{$name}-{$permid}", null, 'apostrophe'); $variants = aTools::getVariantsForSlotType($slot->type, $options); if (count($variants) > 1) { ?> <?php // You can't switch variants until you've saved something for architectural reasons, however ?> <?php // we do need this menu waiting in the wings so that we can turn it on on the first save of an edit view ?> <li class="a-controls-item variant" style="<?php echo $slot->isNew() ? "display:none" : ""; ?> " id="a-<?php echo "{$pageid}-{$name}-{$permid}-variant"; ?> "> <?php echo jq_link_to_function(__('Options', null, 'apostrophe'), '$("#a-' . $pageid . '-' . $name . '-' . $permid . '-variant").toggleClass("open").children("ul.a-variant-options").toggle()', array('class' => 'a-variant-options-toggle a-btn icon a-settings', 'id' => 'a-' . $pageid . '-' . $name . '-' . $permid . '-variant-options-toggle')); ?> <ul class="a-options a-variant-options dropshadow"> <?php foreach ($variants as $variant => $settings) { ?> <?php // These classes and ids are carefully set up so that _ajaxUpdateSlot can ?> <?php
<?php if (isset($filterFieldConfig[$name])) { ?> <?php //This field needs dropdown filters to be applied ?> <ul class="a-multi-title"> <li><a href="#" class="a-btn a-sort-label">[?php echo __('<?php echo $field->getConfig('label'); ?> ', array(), '<?php echo $this->getI18nCatalogue(); ?> ') ?]</a> <div class="filternav <?php echo aTools::slugify($name); ?> "> <hr/> <?php if ($filterFieldConfig[$name]->isComponent()) { ?> [?php include_component('<?php echo $this->getModuleName(); ?> ', 'list_th_<?php echo $name; ?> _dropdown', array('filters' => $filters, 'name' => '<?php echo $name; ?>
-<?php echo $item['id']; ?> "> <?php if (isset($item['external']) && $item['external']) { ?> <?php echo link_to($item['title'], $item['slug']); ?> <?php } else { ?> <?php echo link_to($item['title'], aTools::urlForPage($item['slug'], array('absolute' => true))); ?> <?php } ?> <?php if (isset($item['children']) && count($item['children']) && $nest < $maxDepth) { ?> <?php include_partial('aNavigation/accordion', array('nav' => $item['children'], 'draggable' => $draggable, 'maxDepth' => $maxDepth - 1, 'name' => $name, 'nest' => $nest + 1, 'dragIcon' => $dragIcon, 'class' => $class, 'active' => $active)); ?> <?php } ?>
/** * It's OK to call this if there is no lock. * Eases its use in calls like flunkUnless */ protected function unlockTree() { aTools::unlock(); }
function a_navaccordion() { $page = aTools::getCurrentPage(); $children = $page->getAccordionInfo(true); return a_navtree_body($children); }
$areaOptions = isset($areaOptions) ? $sf_data->getRaw('areaOptions') : null; $page = isset($page) ? $sf_data->getRaw('page') : null; $pageid = isset($pageid) ? $sf_data->getRaw('pageid') : null; $permid = isset($permid) ? $sf_data->getRaw('permid') : null; $slot = isset($slot) ? $sf_data->getRaw('slot') : null; $slug = isset($slug) ? $sf_data->getRaw('slug') : null; use_helper('a'); ?> <?php if ($editable) { ?> <?php slot("a-slot-controls-{$pageid}-{$name}-{$permid}"); ?> <?php include_partial('a/simpleEditWithVariants', array('pageid' => $pageid, 'name' => $name, 'permid' => $permid, 'slot' => $slot, 'page' => $page, 'controlsSlot' => false, 'label' => a_get_option($options, 'editLabel', a_('Edit')))); ?> <?php end_slot(); } ?> <div class="a-inset-area-slot <?php echo aTools::slugify($options['insetTemplate']); ?> "> <?php include_partial('aInsetAreaSlot/' . $options['insetTemplate'] . 'Template', array('editable' => $editable, 'name' => $name, 'options' => $options, 'areaOptions' => $areaOptions, 'page' => $page, 'pageid' => $pageid, 'permid' => $permid, 'slot' => $slot, 'slug' => $slug)); ?> </div>
protected function setup() { if (!isset($this->options)) { // Prevents numerous warnings and problems if there are no slot options present $this->options = array(); } $this->page = aTools::getCurrentPage(); $this->slug = $this->page->slug; // TODO: remove this workaround in 1.5. All uses of actual_slug and real-slug need to go away // in favor of actual_url, we just don't want to break any old overrides in client projects. $this->realSlug = aTools::getRealPage() ? aTools::getRealPage()->getSlug() : 'global'; $this->slot = $this->page->getSlot($this->name, $this->permid); if (!$this->slot || $this->slot->type !== $this->type) { $this->slot = $this->page->createSlot($this->type); } if ($this->getOption('edit')) { $this->editable = true; } else { if (aTools::getAllowSlotEditing()) { $this->editable = $this->page->userHasPrivilege('edit'); } else { $this->editable = false; } } if ($this->getOption('preview')) { $this->editable = false; } if ($this->editable) { $user = $this->getUser(); $id = $this->page->getId(); $name = $this->name; $permid = $this->permid; // Make sure the options passed to a_slot // can be found again at save time if (!$this->updating) { // Slot options can be influenced by variant switching, and that's fine, and the editor might // need to know about it to do the right thing, so it's appropriate to reset the slot // options in the attribute. However, we also need to know what the original, pristine // options from a_slot or a_area were in order to allow variants to be switched without // having side effects on each other's option sets $user->setAttribute("slot-original-options-{$id}-{$name}-{$permid}", $this->options, 'apostrophe'); // Refactored to get rid of duplicate logic $allowedVariants = array_keys(aTools::getVariantsForSlotType($this->type, $this->options)); $user->setAttribute("slot-allowed-variants-{$id}-{$name}-{$permid}", $allowedVariants, 'apostrophe'); } $user->setAttribute("slot-options-{$id}-{$name}-{$permid}", $this->options, 'apostrophe'); } // Calling getEffectiveVariant ensures we default to the behavior of the first one // defined, or the first one allowed if there is an allowed_variants option $variant = $this->slot->getEffectiveVariant($this->options); if ($variant) { // Allow slot variants to adjust slot options. This shouldn't be used to radically // change the slot, just as an adjunct to CSS, styling things in ways CSS can't $variants = aTools::getVariantsForSlotType($this->slot->type, $this->options); if (isset($variants[$variant]['options'])) { $options = $variants[$variant]['options']; $this->options = array_merge($this->options, $options); } } $this->pageid = $this->page->id; $this->id = $this->pageid . '-' . $this->name . '-' . $this->permid; // The basic slot types, and some custom slot types, are // simplified by having this field ready to go $this->value = $this->slot->value; // Not everyone wants the default 'double click the outline to // start editing' behavior $this->outlineEditable = $this->editable && $this->getOption('outline_editable', $this->slot->isOutlineEditable()); // Useful if you're reimplementing that via a button etc $id = $this->id; $this->showEditorJS = "\$('#content-{$id}').hide(); \$('#form-{$id}').fadeIn();"; if (isset($this->validationData['form'])) { // Make Symfony 1.2 form validation extra-convenient $this->form = $this->validationData['form']; } }
/** * Accepts array('info' => [page info array], 'where' => [where clause]) * * Returns results the current user is permitted to see. You can override this if you specify the * following options (must specify all or none): * 'user_id', 'has_view_locked_permission', 'group_ids', 'has_cms_admin_permission' * * You can override the user's culture by specifying 'culture' * @param mixed $options * @return mixed */ public static function getPagesInfo($options) { $whereClauses = array(); $ignorePermissions = false; if (isset($options['ignore_permissions'])) { // getAncestorsInfo has to return everything in some contexts to work properly $ignorePermissions = $options['ignore_permissions']; } if (!isset($options['culture'])) { $options['culture'] = aTools::getUserCulture(); } // In the absence of a bc option for page visibility, we can make a better // determination based on the user's access rights (1.5) $joins = ''; if (isset($options['user_id'])) { // If you pass this in you have to pass all of it in. But if you are just // interested in the current user you needn't bother (see the else clause) $user_id = $options['user_id']; $group_ids = $options['group_ids']; if (!count($group_ids)) { // Should never be empty due to IN's limitations $group_ids = array(0); } $hasViewLockedPermission = $options['has_view_locked_permission']; $hasCmsAdmin = $options['has_cms_admin_permission']; } else { // Get it automatically for the current user $user = sfContext::getInstance()->getUser(); $user_id = 0; $hasViewLockedPermission = false; $group_ids = array(0); $hasCmsAdmin = false; if ($user->isAuthenticated()) { $user_id = $user->getGuardUser()->id; // In 1.5 this one is a little bit of a misnomer because of the new provision for locking // to individuals or groups rather than "Editors & Guests". In the new use case it is // merely a prerequisite $credentials = sfConfig::get('app_a_view_locked_sufficient_credentials', 'view_locked'); $hasViewLockedPermission = $user->hasCredential($credentials); $group_ids = aArray::getIds($user->getGroups()); // Careful: empty IN clauses do not work if (!count($group_ids)) { $group_ids = array(0); } $hasCmsAdmin = $user->hasCredential('cms_admin'); } } $joins .= 'LEFT JOIN a_access aa ON aa.page_id = p.id AND aa.user_id = ' . $user_id . ' '; if (!count($group_ids)) { // A group that can never be $group_ids = array(0); } $joins .= 'LEFT JOIN a_group_access ga ON ga.page_id = p.id AND ga.group_id IN (' . implode(',', $group_ids) . ') '; $viewLockedClause = ''; if ($hasViewLockedPermission) { $viewLockedClause = 'OR p.view_guest IS TRUE '; } // CMS admin can always view if (!$hasCmsAdmin && !$ignorePermissions) { // YOU CAN VIEW IF // * view_admin_lock is NOT set, AND // * You can edit // OR // p.archived is false AND p.published_at is in the past AND p.view_is_secure is false // OR // p.archived is false AND p.published_at is in the past AND p.view_is_secure is true AND (p.view_guest is true OR you have view_locked OR you have an explicit view privilege // However note that if you have a group privilege you don't need to have hasViewLockedPermission (all groups are candidates) $whereClauses[] = '(p.view_admin_lock IS FALSE AND (((aa.privilege = "edit") || (ga.privilege = "edit")) OR ' . '((p.archived IS FALSE OR p.archived IS NULL) AND p.published_at < NOW() AND ' . '((p.view_is_secure IS FALSE OR p.view_is_secure IS NULL) OR ' . '(p.view_is_secure IS TRUE AND ' . '(ga.privilege = "view_custom" OR ' . ($hasViewLockedPermission ? '(p.view_guest IS TRUE OR aa.privilege = "view_custom")' : '(0 <> 0)') . '))))))'; } if (!isset($options['admin'])) { $options['admin'] = false; } if (!isset($options['where'])) { throw new sfException("You must specify a where clause when calling getPagesInfo"); } $culture = $options['culture']; $admin = $options['admin']; $where = $options['where']; // Raw PDO for performance $connection = Doctrine_Manager::connection(); $pdo = $connection->getDbh(); // When we look for the current culture, we need to do it in the ON clause, not // in the WHERE clause. Otherwise we don't get any information at all about pages // not i18n'd yet $escCulture = $connection->quote($culture); $query = "SELECT p.id, p.slug, p.view_is_secure, p.view_guest, p.view_admin_lock, p.edit_admin_lock, p.archived, p.lft, p.rgt, p.level, p.engine, p.template, s.value AS title FROM a_page p\n LEFT JOIN a_area a ON a.page_id = p.id AND a.name = 'title' AND a.culture = {$escCulture}\n LEFT JOIN a_area_version v ON v.area_id = a.id AND a.latest_version = v.version \n LEFT JOIN a_area_version_slot avs ON avs.area_version_id = v.id\n LEFT JOIN a_slot s ON s.id = avs.slot_id {$joins}"; // admin pages are almost never visible in navigation if (!$admin) { $whereClauses[] = '(p.admin IS FALSE OR p.admin IS NULL)'; } // Virtual pages are never appropriate for getPagesInfo. Note that privileges for virtual pages // are by definition always the responsibility of the code that brought them into being and never // based on "normal" page permissions, so getPagesInfo is entirely the wrong API for them $whereClauses[] = '(substr(p.slug, 1, 1) = "/")'; $whereClauses[] = $where; $query .= "WHERE " . implode(' AND ', $whereClauses); $query .= " ORDER BY p.lft"; $resultSet = $pdo->query($query); // Turn it into an actual array rather than some iterable almost-array thing $results = array(); $seenId = array(); foreach ($resultSet as $result) { // Careful: with the new LEFT JOINs on access rights we have extra rows. // Get only one for each page if (isset($seenId[$result['id']])) { continue; } $seenId[$result['id']] = true; // If there is no title yet, supply one to help the translator limp along if (!strlen($result['title'])) { if ($result['slug'] === '/') { $result['title'] = 'home'; } else { if (preg_match('|([^/]+)$|', $result['slug'], $matches)) { $result['title'] = $matches[1]; } } } $results[] = $result; } return $results; }
function _a_navcolumn_body($page) { $sortHandle = ""; $sf_user = sfContext::getInstance()->getUser(); $admin = $page->userHasPrivilege('edit'); if ($admin) { $sortHandle = "<div class='a-btn icon a-drag a-controls'></div>"; } $result = ""; // Inclusion of archived pages should be a bit generous to allow for tricky situations // in which those who can edit a subpage might not be able to find it otherwise. // We don't want the performance hit of checking for the right to edit each archived // subpage, so just allow those with potential-editor privs to see that archived pages // exist, whether or not they are allowed to actually edit them if (aTools::isPotentialEditor() && $sf_user->getAttribute('show-archived', true, 'apostrophe')) { $livingOnly = false; } else { $livingOnly = true; } $result = '<ul id="a-navcolumn" class="a-navcolumn">'; $childrenInfo = $page->getChildrenInfo($livingOnly); if (!count($childrenInfo)) { $childrenInfo = $page->getPeerInfo($livingOnly); } $n = 1; foreach ($childrenInfo as $childInfo) { $class = "peer_item"; if ($childInfo['id'] == $page->id) { $class = "self_item"; } if ($n == 1) { $class .= ' first'; } if ($n == count($childrenInfo)) { $class .= ' last'; } // Specific format to please jQuery.sortable $result .= "<li id=\"a-navcolumn-item-" . $childInfo['id'] . "\" class=\"a-navcolumn-item {$class}\">\n"; $title = $childInfo['title']; if ($childInfo['archived']) { $title = '<span class="a-archived-page" title=""' . $title . '" is Unpublished">' . $title . '</span>'; } $result .= $sortHandle . link_to($title, aTools::urlForPage($childInfo['slug'])); $result .= "</li>\n"; $n++; } $result .= "</ul>\n"; if ($admin) { $result .= jq_sortable_element('#a-navcolumn', array('url' => 'a/sort?page=' . $page->getId())); } return $result; }
<?php use_helper('jQuery', 'Url', 'I18N'); ?> <?php $page = aTools::getCurrentPage(); ?> <?php if ($edit) { ?> <form method="POST" action="<?php echo url_for('a/rename') . '?' . http_build_query(array('id' => $page->id)); ?> " id="a-breadcrumb-rename-form" class="epc-form a-breadcrumb-form rename"> <?php $form = new aRenameForm($page); ?> <?php echo $form->renderHiddenFields(); ?> <?php echo $form['title']->render(array('id' => 'a-breadcrumb-rename-title')); ?> <ul id="a-breadcrumb-rename-controls" class="a-form-controls a-breadcrumb-controls rename" style="display:none;"> <li>
public function executeReorganize(sfWebRequest $request) { // Reorganizing the tree = escaping your page-specific security limitations. // So only full CMS admins can do it. $this->flunkUnless($this->getUser()->hasCredential('cms_admin')); $root = aPageTable::retrieveBySlug('/'); $this->forward404Unless($root); $this->treeData = $root->getTreeJSONReady(false); // setTitle takes care of escaping things $this->getResponse()->setTitle(aTools::getOptionI18n('title_prefix') . 'Reorganize' . aTools::getOptionI18n('title_suffix')); }
public function go() { $dir_iterator = new RecursiveDirectoryIterator($this->dir); $iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST); $count = 0; foreach ($iterator as $sfile) { if ($sfile->isFile()) { $file = $sfile->getPathname(); if (preg_match('/(^|\\/)\\./', $file)) { # Silently ignore all dot folders to avoid trouble with svn and friends $this->giveFeedback("info", "Ignoring dotfile", $file); continue; } $pathinfo = pathinfo($file); if ($pathinfo['filename'] === 'Thumbs.db') { continue; } $info = aImageConverter::getInfo($file); if ($info === false) { $this->giveFeedback("warning", "Not supported or corrupt", $file); continue; } $item = new aMediaItem(); if ($info['format'] === 'pdf') { $item->type = 'pdf'; } else { $item->type = 'image'; } // Split it up to make tags out of the portion of the path that isn't dir (i.e. the folder structure they used) $dir = $this->dir; $dir = preg_replace('/\\/$/', '', $dir) . '/'; $relevant = preg_replace('/^' . preg_quote($dir, '/') . '/', '', $file); // TODO: not Microsoft-friendly, might matter in some setting $components = preg_split('/\\//', $relevant); $tags = array_slice($components, 0, count($components) - 1); foreach ($tags as &$tag) { // We don't strictly need to be this harsh, but it's safe and definitely // takes care of some things we definitely can't allow, like periods // (which cause mod_rewrite problems with pretty Symfony URLs). // TODO: clean it up in a nicer way without being UTF8-clueless // (aTools::slugify is UTF8-safe) $tag = aTools::slugify($tag); } $item->title = aTools::slugify($pathinfo['filename']); $item->setTags($tags); if (!strlen($item->title)) { $this->giveFeedback("error", "Files must have a basename", $file); continue; } // The preSaveImage / save / saveImage dance is necessary because // the sluggable behavior doesn't kick in until save and the image file // needs a slug based filename. if (!$item->preSaveImage($file)) { $this->giveFeedback("error", "Save failed", $file); continue; } $item->save(); if (!$item->saveImage($file)) { $this->giveFeedback("error", "Save failed", $file); $item->delete(); continue; } unlink($file); $count++; $this->giveFeedback("completed", $count, $file); } } $this->giveFeedback("total", $count); }
" class="a-nav a-nav-breadcrumb a-breadcrumb-<?php echo $name ? $name : 'component'; ?> breadcrumb clearfix"> <?php foreach ($nav as $pos => $item) { ?> <?php if (!$item['archived'] || $draggable) { ?> <li class="<?php echo $class; if ($item['slug'] == $active) { echo ' a-current-page'; } ?> "><?php echo link_to($item['title'], aTools::urlForPage($item['slug'])); if ($pos + 1 < count($nav)) { echo '<span class="a-breadcrumb-separator">' . $separator . '</span>'; } ?> </li> <?php } ?> <?php } ?> </ul>
<?php // Compatible with sf_escaping_strategy: true $a_event = isset($a_event) ? $sf_data->getRaw('a_event') : null; ?> <?php use_helper("a"); ?> <?php $catClass = ""; foreach ($a_event->getCategories() as $category) { $catClass .= " category-" . aTools::slugify($category); } ?> <div class="a-blog-item event <?php echo $a_event->getTemplate(); echo $catClass != '' ? $catClass : ''; ?> "> <?php if ($a_event->userHasPrivilege('edit')) { ?> <ul class="a-ui a-controls a-blog-post-controls"> <li> <?php echo a_button(a_('Edit'), url_for('a_event_admin_edit', $a_event), array('a-btn', 'icon', 'a-edit', 'lite', 'alt', 'no-label')); ?> </li>