public function testShiftLevel() { $section1 = $this->repository->find('section1'); $section2 = $this->repository->find('section2'); $page1 = $this->repository->find('page1'); $page2 = $this->repository->find('page2'); $page3 = $this->repository->find('page3'); $this->assertEquals($this->repository, $this->invokeMethod($this->repository, 'shiftLevel', array($page3, 1, true))); self::$em->refresh($page3); $this->assertEquals(1, $page3->getLevel()); $this->assertEquals($this->repository, $this->invokeMethod($this->repository, 'shiftLevel', array($page3, 1, false))); self::$em->refresh($page3); $this->assertEquals(2, $page3->getLevel()); $this->invokeMethod($this->repository, 'shiftLevel', array($this->root, 2)); foreach ($this->repository->findAll() as $node) { self::$em->refresh($node); } $this->assertEquals(2, $this->root->getLevel()); $this->assertEquals(3, $section1->getLevel()); $this->assertEquals(3, $section2->getLevel()); $this->assertEquals(4, $page1->getLevel()); $this->assertEquals(4, $page2->getLevel()); $this->assertEquals(4, $page3->getLevel()); $this->invokeMethod($this->repository, 'shiftLevel', array($this->root, -2, true)); foreach ($this->repository->findAll() as $node) { self::$em->refresh($node); } $this->assertEquals(2, $this->root->getLevel()); $this->assertEquals(1, $section1->getLevel()); $this->assertEquals(1, $section2->getLevel()); $this->assertEquals(2, $page1->getLevel()); $this->assertEquals(2, $page2->getLevel()); $this->assertEquals(2, $page3->getLevel()); }
/** * @covers \BackBee\NestedNode\Builder\PageBuilder::getPage * @covers \BackBee\NestedNode\Builder\PageBuilder::doPersistIfValid */ public function testGetPagePersisted() { $chid1 = self::$builder->setUid('first-child')->setTitle('title')->setSite($this->site)->setLayout($this->layout)->setParent($this->root)->setPersistMode(PageBuilder::PERSIST_AS_FIRST_CHILD)->getPage(); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForInsert($chid1)); self::$em->flush(); self::$em->refresh($this->root); $this->assertFalse($chid1->isRoot()); $this->assertFalse($chid1->hasMainSection()); $this->assertEquals(1, $this->root->getLeftnode()); $this->assertEquals(2, $this->root->getRightnode()); $this->assertEquals(1, $chid1->getPosition()); $this->assertEquals(1, $chid1->getLevel()); $child2 = self::$builder->setUid('second-child')->setTitle('title')->setSite($this->site)->setLayout($this->layout)->setParent($this->root)->setPersistMode(PageBuilder::PERSIST_AS_LAST_CHILD)->getPage(); self::$em->flush(); $this->assertEquals(2, $child2->getPosition()); $this->assertEquals(1, $child2->getLevel()); $section = self::$builder->setUid('section')->setTitle('title')->setSite($this->site)->setLayout($this->layout)->setParent($this->root)->isSection(true)->setPersistMode(PageBuilder::PERSIST_AS_FIRST_CHILD)->getPage(); self::$em->flush(); self::$em->refresh($this->root); $this->assertTrue($section->hasMainSection()); $this->assertEquals(0, $section->getPosition()); $this->assertEquals(1, $section->getLevel()); $this->assertEquals(2, $section->getLeftnode()); $this->assertEquals(3, $section->getRightnode()); $this->assertEquals(1, $this->root->getLeftnode()); $this->assertEquals(4, $this->root->getRightnode()); }
/** * Updates the associated definition of the set of metadata. * * @param array $definitions * @param \BackBee\NestedNode\Page $page */ public function update(array $definitions = null, Page $page = null) { $content = null === $page ? null : $page->getContentSet(); if (null !== $definitions) { foreach ($definitions as $name => $definition) { if (false === is_array($definition)) { continue; } if (null === ($metadata = $this->get($name))) { $metadata = new MetaData($name); $this->add($metadata); } foreach ($definition as $attrname => $attrvalue) { if (false === is_array($attrvalue)) { $attrvalue = '' === $metadata->getAttribute($attrname) ? $attrvalue : $metadata->getAttribute($attrname); $metadata->setAttribute($attrname, $attrvalue, $content); continue; } if (true === $metadata->hasAttribute($attrname)) { if (null !== $page && true === array_key_exists('layout', $attrvalue)) { $layout_uid = $page->getLayout()->getUid(); if (true === array_key_exists($layout_uid, $attrvalue['layout'])) { $scheme = is_array($attrvalue['layout'][$layout_uid]) ? reset($attrvalue['layout'][$layout_uid]) : $attrvalue['layout'][$layout_uid]; $metadata->updateAttributeScheme($attrname, $scheme, $content); } } continue; } if (true === array_key_exists('default', $attrvalue)) { $value = is_array($attrvalue['default']) ? reset($attrvalue['default']) : $attrvalue['default']; $metadata->setAttribute($attrname, $value, $content); } if (null !== $page && true === array_key_exists('layout', $attrvalue)) { $layout_uid = $page->getLayout()->getUid(); if (true === array_key_exists($layout_uid, $attrvalue['layout'])) { $value = is_array($attrvalue['layout'][$layout_uid]) ? reset($attrvalue['layout'][$layout_uid]) : $attrvalue['layout'][$layout_uid]; $metadata->setAttribute($attrname, $value, $content); } } } } } }
public function rssAction($uri = null) { if (null === $this->application) { throw new FrontControllerException('A valid BackBee application is required.', FrontControllerException::INTERNAL_ERROR); } if (false === $this->application->getContainer()->has('site')) { throw new FrontControllerException('A BackBee\\Site instance is required.', FrontControllerException::INTERNAL_ERROR); } $site = $this->application->getContainer()->get('site'); if (false !== ($ext = strrpos($uri, '.'))) { $uri = substr($uri, 0, $ext); } if ('_root_' == $uri) { $page = $this->application->getEntityManager()->getRepository('BackBee\\NestedNode\\Page')->getRoot($site); } else { $page = $this->application->getEntityManager()->getRepository('BackBee\\NestedNode\\Page')->findOneBy(array('_site' => $site, '_url' => '/' . $uri, '_state' => Page::getUndeletedStates())); } try { $this->application->info(sprintf('Handling URL request `rss%s`.', $uri)); $response = new Response($this->application->getRenderer()->render($page, 'rss', null, 'rss.phtml', false)); $response->headers->set('Content-Type', 'text/xml'); $response->setClientTtl(15); $response->setTtl(15); $this->send($response); } catch (\Exception $e) { $this->defaultAction('/rss/' . $uri); } }
/** * Sets the associated page for this section. * * @param Page $page * * @return Section */ public function setPage(Page $page) { $this->_page = $page; $page->setMainSection($this); return $this; }
public function createRootPage() { $page = new Page('test', array('title' => 'title', 'url' => 'url')); $layout = new Layout(); $page->setLayout($layout->setDataObject($this->getDefaultLayoutZones())); return $page; }
public function createRootPage($uid = null) { $page = new Page($uid, ['title' => 'title', 'url' => 'url']); $page->setLayout($this->createLayout('test')); return $page; }
/** * Returns the maximum position of children of $page. * * @param Page $page * * @return integer */ private function getMaxPosition(Page $page) { if (false === $page->hasMainSection()) { return 0; } $query = $this->createQueryBuilder('p'); $max = $query->select($query->expr()->max('p._position'))->andParentIs($page)->getQuery()->getResult(\Doctrine\ORM\Query::HYDRATE_SINGLE_SCALAR); return null === $max ? 0 : $max; }
public function testResolvePageConst() { $page = new Page(); $page->setUrl('/page-url'); $bag = $this->resolver->resolve($page); $expected = ['title' => ['content' => 'default title'], 'description' => ['content' => 'default description'], 'robots' => ['content' => 'follow'], 'og-url' => ['content' => '/page-url']]; $this->assertInstanceOf('BackBee\\MetaData\\MetaDataBag', $bag); $this->assertEquals($expected, $bag->jsonSerialize()); }
/** * Replace root contentset for a page and its descendants. * * @param \BackBee\NestedNode\Page $page * @param \BackBee\ClassContent\ContentSet $oldContentSet * @param \BackBee\ClassContent\ContentSet $newContentSet * @param \BackBee\Security\Token\BBUserToken $userToken */ public function updateRootContentSetByPage(Page $page, ContentSet $oldContentSet, ContentSet $newContentSet, BBUserToken $userToken) { $em = $this->_em; $q = $this->createQueryBuilder('c'); $results = $q->leftJoin('c._pages', 'p')->leftJoin('p._section', 'sp')->leftJoin('c._subcontent', 'subcontent')->where('subcontent = :contentToReplace')->andWhere('sp._root = :cpageRoot')->andWhere('sp._leftnode > :cpageLeftnode')->andWhere('sp._rightnode < :cpageRightnode')->setParameters(['contentToReplace' => $oldContentSet, 'cpageRoot' => $page->getSection()->getRoot(), 'cpageLeftnode' => $page->getLeftnode(), 'cpageRightnode' => $page->getRightnode()])->getQuery()->getResult(); if ($results) { foreach ($results as $parentContentSet) { /* create draft for the main container */ $draft = $em->getRepository('BackBee\\ClassContent\\Revision')->getDraft($parentContentSet, $userToken, true); if (null !== $draft) { $parentContentSet->setDraft($draft); } /* Replace the old ContentSet by the new one */ $parentContentSet->replaceChildBy($oldContentSet, $newContentSet); $em->persist($parentContentSet); } } }
/** * Set a page to filter the query on a nested portion. * * @param BackBee\NestedNode\Page $page */ public function addPageFilter(Page $page) { if ($page && !$page->isRoot()) { $this->leftJoin('cc._mainnode', 'p')->leftJoin('p._section', 'sp')->andWhere('sp._root = :selectedPageRoot')->andWhere('sp._leftnode >= :selectedPageLeftnode')->andWhere('sp._rightnode <= :selectedPageRightnode')->setParameters(['selectedPageRoot' => $page->getSection()->getRoot(), 'selectedPageLeftnode' => $page->getLeftnode(), 'selectedPageRightnode' => $page->getRightnode()]); } }
/** * Resolves constant value. * * @param string $const * @param Page|NULL $page * * @return string */ protected function resolveConst($const, Page $page = null) { $value = ''; switch (strtolower($const)) { case 'url': $value = null !== $page ? $page->getUrl() : ''; default: break; } return $value; }
/** * @covers \BackBee\NestedNode\Repository\PageQueryBuilder::andIsSiblingsOf */ public function testAndIsSiblingOf() { $root = new Page('root'); $child = new Page('child'); $child->setParent($root); $q = $this->repository->createQueryBuilder('p')->andIsSiblingsOf($root); $this->assertInstanceOf('BackBee\\NestedNode\\Repository\\PageQueryBuilder', $q); $this->assertEquals('SELECT p FROM BackBee\\NestedNode\\Page p INNER JOIN p._section p_s WHERE p_s._parent IS NULL AND p_s = p', $q->getDql()); $q->resetDQLPart('where')->andIsSiblingsOf($child); $this->assertEquals('SELECT p FROM BackBee\\NestedNode\\Page p INNER JOIN p._section p_s WHERE p_s._root = :root0 AND (p_s._leftnode BETWEEN 1 AND 2) AND p._level <= :level0', $q->getDql()); $this->assertEquals($child->getSection()->getRoot(), $q->getParameter('root0')->getValue()); $this->assertEquals($child->getLevel(), $q->getParameter('level0')->getValue()); $q->resetDQLPart('where')->setParameters(array())->andIsSiblingsOf($child, true); $this->assertEquals('SELECT p FROM BackBee\\NestedNode\\Page p INNER JOIN p._section p_s WHERE p_s._root = :root0 AND (p_s._leftnode BETWEEN 1 AND 2) AND p._level <= :level0 AND p != :page2', $q->getDql()); $this->assertEquals($child, $q->getParameter('page2')->getValue()); $q->resetDQLPart('where')->setParameters(array())->andIsSiblingsOf($child, false, array('p._position' => 'ASC')); $this->assertEquals('SELECT p FROM BackBee\\NestedNode\\Page p INNER JOIN p._section p_s WHERE p_s._root = :root0 AND (p_s._leftnode BETWEEN 1 AND 2) AND p._level <= :level0 ORDER BY p._position ASC', $q->getDql()); $q->andIsSiblingsOf($child, false, null, 10); $this->assertEquals(10, $q->getMaxResults()); $this->assertEquals(0, $q->getFirstResult()); $q->andIsSiblingsOf($child, false, null, 10, 1); $this->assertEquals(10, $q->getMaxResults()); $this->assertEquals(1, $q->getFirstResult()); }
public function testGetAction() { // create pages $now = new \DateTime(); $homePage = new Page(); $homePage->setTitle('Home Page')->setState(Page::STATE_ONLINE)->setSite($this->site)->setModified($now); $this->em->persist($homePage); $this->em->flush($homePage); $this->getAclManager()->insertOrUpdateObjectAce($homePage, new UserSecurityIdentity($this->group_id, 'BackBee\\Security\\Group'), MaskBuilder::MASK_VIEW); // no filters - should return current site root page $response = $this->sendRequest(self::requestGet('/rest/1/page/' . $homePage->getUid())); $this->assertTrue($response->isOk(), sprintf('HTTP 200 expected, HTTP %s returned.', $response->getStatusCode())); $pageProperties = json_decode($response->getContent(), true); $this->assertInternalType('array', $pageProperties); $this->assertCount(24, $pageProperties); $this->assertEquals($homePage->getUid(), $pageProperties['uid']); $this->assertEquals($now->getTimestamp(), $pageProperties['modified']); }
private function generatePage($title = 'backbee', $url = null, $doPersist = false) { $page = new Page(); $page->setRoot($this->root); $page->setParent($this->root); $page->setTitle($title); $page->setUrl($url); if ($doPersist) { self::$em->persist($page); self::$em->flush($page); } return $page; }
/** * Persists the page is need and valid * * @param Page $page The page to be built. */ private function doPersistIfValid(Page $page) { if (null === $page->getParent() && !empty($this->persist)) { // If root, only persist $this->em->persist($page); return; } $method = ''; if (self::PERSIST_AS_FIRST_CHILD === $this->persist) { $method = 'insertNodeAsFirstChildOf'; } elseif (self::PERSIST_AS_LAST_CHILD === $this->persist) { $method = 'insertNodeAsLastChildOf'; } if (!empty($method)) { $this->em->getRepository('BackBee\\NestedNode\\Page')->{$method}($page, $page->getParent(), $this->isSection); $this->em->persist($page); } }
/** * Add query part to select descendants of $page. * * @param Page $page The page to look for descendants * @param boolean $strict Optional, if false (by default) $node is include to the selection * @param integer|null $depth Optional, filter ancestors by their level * @param string[]|null $order Optional, ordering spec ( [$field => $sort] ) * @param integer|null $limit Optional, max number of results, if null no limit * @param integer $start Optional, first result index (0 by default) * @param boolean $limitToSection Optional, if true limits to descendants being section * * @return PageQueryBuilder */ public function andIsDescendantOf(Page $page, $strict = false, $depth = null, array $order = null, $limit = null, $start = 0, $limitToSection = false) { $suffix = $this->getSuffix(); $this->andWhere($this->getSectionAlias() . '._root = :root' . $suffix)->andWhere($this->expr()->between($this->getSectionAlias() . '._leftnode', $page->getSection()->getLeftnode(), $page->getSection()->getRightnode()))->setParameter('root' . $suffix, $page->getSection()->getRoot()); if (true === $strict) { $this->andWhere($this->getAlias() . ' != :page' . $suffix)->setParameter('page' . $suffix, $page); } if (null !== $depth) { $this->andWhere($this->getAlias() . '._level <= :level' . $suffix)->setParameter('level' . $suffix, $page->getLevel() + $depth); } if (null !== $order) { $this->addMultipleOrderBy($order); } if (null !== $limit) { $this->setMaxResults($limit)->setFirstResult($start); } if (true === $limitToSection) { $this->andIsSection(); } return $this; }
/** * Remove stored page-content and site-content indexes from a page. * * @param Page $page * * @return IndexationRepository */ public function removeIdxPage(Page $page) { $query = 'DELETE FROM idx_page_content WHERE page_uid = :page'; $params = array('page' => $page->getUid()); return $this->removeIdxSite($page)->_executeQuery($query, $params); }
/** * Test cascade Doctrine annotations for entity */ public function testDoctrineCascades() { self::$kernel->resetDatabase(); $site = new Site('site-test', ['label' => 'site-test']); $layout = self::$kernel->createLayout('layout-test', 'layout-test'); self::$em->persist($site); self::$em->persist($layout); self::$em->flush(); $root = new Page('root', ['title' => 'root']); $root->setSite($site)->setLayout($layout); // Persist cascade on Page::_mainsection and Page::_contentset self::$em->persist($root); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForInsert($root)); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForInsert($root->getMainSection())); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForInsert($root->getContentSet())); self::$em->flush($root); // Remove cascade on Page::_mainsection and Page::_contentset self::$em->remove($root); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForDelete($root)); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForDelete($root->getContentSet())); $this->assertTrue(self::$em->getUnitOfWork()->isScheduledForDelete($root->getMainSection())); self::$em->flush(); }
/** * Sets the parent node. * * @param Page $parent * * @return Page */ public function setParent(Page $parent) { if (!$parent->hasMainSection()) { throw new InvalidArgumentException('A parent page must be a section'); } if (!$this->hasMainSection() || $this->isRoot()) { $this->setSection($parent->getSection()); } else { $this->getSection()->setParent($parent->getSection()); } return $this; }
/** * Computes the URL of a page according to a scheme. * * @param array $scheme The scheme to apply * @param Page $page The page * @param AbstractClassContent $content The optionnal main content of the page * @return string The generated URL */ private function doGenerate($scheme, Page $page, AbstractClassContent $content = null) { $replacement = ['$parent' => $page->isRoot() ? '' : $page->getParent()->getUrl(false), '$title' => StringUtils::urlize($page->getTitle()), '$datetime' => $page->getCreated()->format('ymdHis'), '$date' => $page->getCreated()->format('ymd'), '$time' => $page->getCreated()->format('His')]; $matches = []; if (preg_match_all('/(\\$content->[a-z]+)/i', $scheme, $matches)) { foreach ($matches[1] as $pattern) { $property = explode('->', $pattern); $property = array_pop($property); try { $replacement[$pattern] = StringUtils::urlize($content->{$property}); } catch (\Exception $e) { $replacement[$pattern] = ''; } } } $matches = []; if (preg_match_all('/(\\$ancestor\\[([0-9]+)\\])/i', $scheme, $matches)) { foreach ($matches[2] as $level) { $ancestor = $this->application->getEntityManager()->getRepository('BackBee\\NestedNode\\Page')->getAncestor($page, $level); if (null !== $ancestor && $page->getLevel() > $level) { $replacement['$ancestor[' . $level . ']'] = $ancestor->getUrl(false); } else { $replacement['$ancestor[' . $level . ']'] = ''; } } } $url = preg_replace('/\\/+/', '/', str_replace(array_keys($replacement), array_values($replacement), $scheme)); return $this->getUniqueness($page, $url); }
/** * Custom patch process for Page's sibling or parent node. * * @param Page $page * @param array $operations passed by reference */ private function patchSiblingAndParentOperation(Page $page, array &$operations) { $sibling_operation = null; $parent_operation = null; foreach ($operations as $key => $operation) { $op = array('key' => $key, 'op' => $operation); if ('/sibling_uid' === $operation['path']) { $sibling_operation = $op; } elseif ('/parent_uid' === $operation['path']) { $parent_operation = $op; } } if (null !== $sibling_operation || null !== $parent_operation) { if ($page->isRoot()) { throw new BadRequestHttpException('Cannot move root node of a site.'); } if ($page->isOnline(true)) { $this->granted('PUBLISH', $page); // user must have publish permission on the page } } try { if (null !== $sibling_operation) { unset($operations[$sibling_operation['key']]); $sibling = $this->getPageByUid($sibling_operation['op']['value']); $this->granted('EDIT', $sibling->getParent()); $this->getPageRepository()->moveAsPrevSiblingOf($page, $sibling); } elseif (null !== $parent_operation) { unset($operations[$parent_operation['key']]); $parent = $this->getPageByUid($parent_operation['op']['value']); if ($this->isFinal($parent)) { throw new BadRequestHttpException('Can\'t create children of ' . $parent->getLayout()->getLabel() . ' layout'); } $this->moveAsFirstChildOf($page, $parent); } } catch (InvalidArgumentException $e) { throw new BadRequestHttpException('Invalid node move action: ' . $e->getMessage()); } }
/** * Compute values of attributes according to the AbstractClassContent provided. * * @param \BackBee\ClassContent\AbstractClassContent $content * * @return \BackBee\MetaData\MetaData */ public function computeAttributes(AbstractClassContent $content, Page $page = null) { foreach ($this->attributes as $attribute => $value) { if (true === $this->is_computed[$attribute] && true === array_key_exists($attribute, $this->scheme)) { try { $functions = explode('||', $value); $matches = array(); if (false !== preg_match_all('/(\\$([a-z_\\/\\\\]+)(\\[([0-9]+)\\]){0,1}(->){0,1})+/i', $this->scheme[$attribute], $matches, PREG_PATTERN_ORDER)) { $this->attributes[$attribute] = $this->scheme[$attribute]; $initial_content = $content; $count = count($matches[0]); for ($i = 0; $i < $count; $i++) { $content = $initial_content; foreach (explode('->', $matches[0][$i]) as $scheme) { $draft = null; if (true === is_object($content)) { if (null !== ($draft = $content->getDraft())) { $content->releaseDraft(); } } $newcontent = $content; $m = array(); if (preg_match('/\\$([a-z\\/\\\\]+)(\\[([0-9]+)\\]){0,1}/i', $scheme, $m)) { if (3 < count($m) && $content instanceof ContentSet && 'ContentSet' === $m[1]) { $newcontent = $content->item($m[3]); } elseif (3 < count($m) && $content instanceof ContentSet) { $index = intval($m[3]); $classname = 'BackBee\\ClassContent\\' . str_replace('/', NAMESPACE_SEPARATOR, $m[1]); foreach ($content as $subcontent) { if (get_class($subcontent) == $classname) { if (0 === $index) { $newcontent = $subcontent; } else { $index--; } } } } elseif (true === is_object($content) && 1 < count($m)) { $property = $m[1]; try { $newcontent = $content->{$property}; } catch (\Exception $e) { $newcontent = new Text(); } } } if (null !== $draft) { $content->setDraft($draft); } $content = $newcontent; } if ($content instanceof AbstractClassContent && $content->isElementContent()) { if (null !== ($draft = $content->getDraft())) { $content->releaseDraft(); } if ($content instanceof File) { $new_value = $content->path; } else { $new_value = trim(str_replace(array("\n", "\r"), '', strip_tags('' . $content))); } $this->attributes[$attribute] = str_replace($matches[0][$i], $new_value, $this->attributes[$attribute]); if (null !== $draft) { $content->setDraft($draft); } } elseif (true === is_array($content)) { $v = array(); foreach ($content as $c) { if ($c instanceof Keyword) { } $v[] = trim(str_replace(array("\n", "\r"), '', strip_tags('' . $c))); } $this->attributes[$attribute] = str_replace($matches[0][$i], implode(',', $v), $this->attributes[$attribute]); } else { $new_value = trim(str_replace(array("\n", "\r"), '', strip_tags($content))); $this->attributes[$attribute] = str_replace($matches[0][$i], $new_value, $this->attributes[$attribute]); } } } array_shift($functions); if (0 < count($functions)) { foreach ($functions as $fct) { $parts = explode(':', $fct); $functionName = array_shift($parts); array_unshift($parts, $this->attributes[$attribute]); $this->attributes[$attribute]; $this->attributes[$attribute] = call_user_func_array($functionName, $parts); } } } catch (\Exception $e) { } } elseif (preg_match('/^\\#([a-z]+)$/i', $value, $matches)) { switch (strtolower($matches[1])) { case 'url': if (null !== $page) { $this->attributes[$attribute] = $page->getUrl(); } default: break; } } } return $this; }