diff --git a/.gitignore b/.gitignore index 26ad278..fcf7e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ Tests/Resources/app/cache Tests/Resources/app/logs +bin/ composer.lock -vendor +vendor/ +.idea diff --git a/.travis.yml b/.travis.yml index d0c2202..d334868 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ php: env: - SYMFONY_VERSION=2.5.* - + matrix: allow_failures: - env: SYMFONY_VERSION=dev-master @@ -18,8 +18,6 @@ matrix: env: SYMFONY_VERSION=2.4.* - php: 5.5 env: SYMFONY_VERSION=dev-master - - before_script: - composer self-update diff --git a/Admin/BlogAdmin.php b/Admin/BlogAdmin.php index fb7a11b..89acfb6 100644 --- a/Admin/BlogAdmin.php +++ b/Admin/BlogAdmin.php @@ -14,11 +14,8 @@ use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; -use Sonata\AdminBundle\Validator\ErrorElement; use Sonata\AdminBundle\Form\FormMapper; use Sonata\DoctrinePHPCRAdminBundle\Admin\Admin; -use Symfony\Cmf\Bundle\BlogBundle\Form\PostType; -use Symfony\Cmf\Bundle\BlogBundle\Routing\BlogRouteManager; /** * Blog Admin @@ -30,33 +27,46 @@ class BlogAdmin extends Admin protected $translationDomain = 'CmfBlogBundle'; protected $blogRoot; - protected function configureFormFields(FormMapper $mapper) + /** + * Constructor + * + * @param string $code + * @param string $class + * @param string $baseControllerName + * @param string $blogRoot + */ + public function __construct($code, $class, $baseControllerName, $blogRoot) { - $mapper->add('name', 'text'); - $mapper->add('parent', 'doctrine_phpcr_odm_tree', array( - 'root_node' => $this->blogRoot, - 'choice_list' => array(), - 'select_root_node' => true) - ); - } - - protected function configureDatagridFilters(DatagridMapper $dm) - { - $dm->add('name', 'doctrine_phpcr_string'); + parent::__construct($code, $class, $baseControllerName); + $this->blogRoot = $blogRoot; } - protected function configureListFields(ListMapper $dm) + protected function configureFormFields(FormMapper $formMapper) { - $dm->addIdentifier('name'); + $formMapper + ->with('dashboard.label_blog') + ->add('name', 'text') + ->add('description', 'textarea') + ->add('parentDocument', 'doctrine_phpcr_odm_tree', array( + 'root_node' => $this->blogRoot, + 'choice_list' => array(), + 'select_root_node' => true, + )) + ->end() + ; } - public function setBlogRoot($blogRoot) + protected function configureDatagridFilters(DatagridMapper $filterMapper) { - $this->blogRoot = $blogRoot; + $filterMapper + ->add('name', 'doctrine_phpcr_string') + ; } - public function validate(ErrorElement $ee, $obj) + protected function configureListFields(ListMapper $listMapper) { - $ee->with('name')->assertNotBlank()->end(); + $listMapper + ->addIdentifier('name') + ; } } diff --git a/Admin/PostAdmin.php b/Admin/PostAdmin.php index df1fa90..64c4e26 100644 --- a/Admin/PostAdmin.php +++ b/Admin/PostAdmin.php @@ -14,11 +14,8 @@ use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; -use Sonata\AdminBundle\Validator\ErrorElement; use Sonata\AdminBundle\Form\FormMapper; use Sonata\DoctrinePHPCRAdminBundle\Admin\Admin; -use Symfony\Cmf\Bundle\BlogBundle\Form\PostType; -use Symfony\Cmf\Bundle\BlogBundle\Form\DataTransformer\CsvToArrayTransformer; /** * Post Admin @@ -28,46 +25,52 @@ class PostAdmin extends Admin { protected $translationDomain = 'CmfBlogBundle'; + protected $blogClass; - protected function configureFormFields(FormMapper $mapper) + /** + * Constructor + * + * @param string $code + * @param string $class + * @param string $baseControllerName + * @param string $blogClass + */ + public function __construct($code, $class, $baseControllerName, $blogClass) { - // @todo: I think this would be better as a service, - // but I don't know how integrate the form - // AND have all the Sonata magic from the - // FormMapper->add method. - - // $csvToArrayTransformer = new CsvToArrayTransformer; - - $mapper->add('title'); - $mapper->add('date', 'datetime', array( - 'widget' => 'single_text', - )); - - $mapper->add('body', 'textarea'); - $mapper->add('blog', 'phpcr_document', array( - 'class' => 'Symfony\Cmf\Bundle\BlogBundle\Document\Blog', - )); - - //$tags = $mapper->create('tags', 'text') - // ->addModelTransformer($csvToArrayTransformer); - - // $mapper->add($tags); + parent::__construct($code, $class, $baseControllerName); + $this->blogClass = $blogClass; } - protected function configureDatagridFilters(DatagridMapper $dm) + protected function configureFormFields(FormMapper $formMapper) { - $dm->add('title', 'doctrine_phpcr_string'); + $formMapper + ->with('dashboard.label_post') + ->add('title') + ->add('date', 'datetime', array( + 'widget' => 'single_text', + )) + ->add('bodyPreview', 'textarea') + ->add('body', 'textarea') + ->add('blog', 'phpcr_document', array( + 'class' => $this->blogClass, + )) + ->end() + ; } - protected function configureListFields(ListMapper $dm) + protected function configureDatagridFilters(DatagridMapper $filterMapper) { - $dm->add('blog'); - $dm->add('date', 'datetime'); - $dm->addIdentifier('title'); + $filterMapper + ->add('title', 'doctrine_phpcr_string') + ; } - public function validate(ErrorElement $ee, $obj) + protected function configureListFields(ListMapper $listMapper) { - $ee->with('title')->assertNotBlank()->end(); + $listMapper + ->addIdentifier('title') + ->add('blog') + ->add('date', 'datetime') + ; } } diff --git a/Block/TagCloudBlock.php b/Block/TagCloudBlock.php deleted file mode 100644 index 76607e8..0000000 --- a/Block/TagCloudBlock.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -class TagCloudBlockService extends BaseBlockService -{ - protected $repo; - - public function __construct($name, EngineInterface $templating, TagRepository $repo) - { - $this->repo = $repo; - parent::__construct($name, $templating); - } - - public function getDefaultSettings() - { - return array( - 'path' => 'cmf_blog_post_index' - ); - } - - public function buildEditForm(FormMapper $fm, BlockInterface $block) - { - } - - public function validateBlock(ErrorElement $errorElement, BlockInterface $block) - { - } - - public function execute(BlockInterface $block, Response $response = null) - { - $wTags = $this->repo->getWeightedTags(); - return $this->renderResponse('CmfBlogBundle:Block:tagCloud.html.twig', array( - 'block' => $block, - 'wTags' => $wTags - )); - } -} diff --git a/CmfBlogBundle.php b/CmfBlogBundle.php index 802bdaa..a399649 100644 --- a/CmfBlogBundle.php +++ b/CmfBlogBundle.php @@ -12,8 +12,27 @@ namespace Symfony\Cmf\Bundle\BlogBundle; +use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class CmfBlogBundle extends Bundle { + public function build(ContainerBuilder $container) + { + parent::build($container); + + if (class_exists('Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass')) { + $container->addCompilerPass( + DoctrinePhpcrMappingsPass::createXmlMappingDriver( + array( + realpath(__DIR__ . '/Resources/config/doctrine-phpcr') => 'Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr', + ), + array('cmf_blog.persistence.phpcr.manager_name'), + false, + array('CmfBlogBundle' => 'Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr') + ) + ); + } + } } diff --git a/Controller/BaseController.php b/Controller/BaseController.php new file mode 100644 index 0000000..cebdaa8 --- /dev/null +++ b/Controller/BaseController.php @@ -0,0 +1,85 @@ + + */ +abstract class BaseController +{ + /** + * @var EngineInterface + */ + protected $templating; + + /** + * @var SecurityContextInterface + */ + protected $securityContext; + + /** + * @var ViewHandlerInterface + */ + protected $viewHandler; + + /** + * Constructor + * + * @param EngineInterface $templating + * @param SecurityContextInterface $securityContext + * @param ViewHandlerInterface $viewHandler + */ + public function __construct( + EngineInterface $templating, + SecurityContextInterface $securityContext, + ViewHandlerInterface $viewHandler = null + ) { + $this->templating = $templating; + $this->securityContext = $securityContext; + $this->viewHandler = $viewHandler; + } + + protected function renderResponse($contentTemplate, $params) + { + if ($this->viewHandler) { + $view = new View($params); + $view->setTemplate($contentTemplate); + return $this->viewHandler->handle($view); + } + + return $this->templating->renderResponse($contentTemplate, $params); + } + + protected function getTemplateForResponse(Request $request, $contentTemplate) + { + return str_replace( + array('{_format}', '{_locale}'), + array($request->getRequestFormat(), $request->getLocale()), + $contentTemplate + ); + } +} diff --git a/Controller/BlogController.php b/Controller/BlogController.php index fb77135..c2d9189 100644 --- a/Controller/BlogController.php +++ b/Controller/BlogController.php @@ -12,133 +12,96 @@ namespace Symfony\Cmf\Bundle\BlogBundle\Controller; -use Doctrine\ODM\PHPCR\DocumentManager; -use Symfony\Cmf\Bundle\BlogBundle\Document\Post; -use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker; +use Symfony\Cmf\Bundle\BlogBundle\Model\Blog; +use Symfony\Cmf\Bundle\BlogBundle\Repository\BlogRepository; +use Symfony\Cmf\Bundle\BlogBundle\Repository\PostRepository; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\Templating\EngineInterface; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; use FOS\RestBundle\View\ViewHandlerInterface; -use FOS\RestBundle\View\View; /** * Blog Controller * * @author Daniel Leech */ -class BlogController +class BlogController extends BaseController { /** - * @var EngineInterface + * @var BlogRepository */ - protected $templating; + protected $blogRepository; /** - * @var ViewHandlerInterface + * @var PostRepository */ - protected $viewHandler; + protected $postRepository; /** - * @var DocumentManager + * @var \Knp\Component\Pager\Paginator */ - protected $dm; + protected $paginator; /** - * @var SecurityContextInterface + * @var int */ - protected $securityContext; - - /** - * The permission to check for when doing the publish workflow check. - * - * @var string - */ - private $publishWorkflowPermission = PublishWorkflowChecker::VIEW_ATTRIBUTE; - + protected $postsPerPage; public function __construct( EngineInterface $templating, + SecurityContextInterface $securityContext, ViewHandlerInterface $viewHandler = null, - DocumentManager $dm, - SecurityContextInterface $securityContext + BlogRepository $blogRepository, + PostRepository $postRepository, + $paginator = null, + $postsPerPage = 0 ) { - $this->templating = $templating; - $this->viewHandler = $viewHandler; - $this->dm = $dm; - $this->securityContext = $securityContext; + parent::__construct($templating, $securityContext, $viewHandler); + $this->blogRepository = $blogRepository; + $this->postRepository = $postRepository; + $this->paginator = $paginator; + $this->postsPerPage = $postsPerPage; } /** - * What attribute to use in the publish workflow check. This typically - * is VIEW or VIEW_ANONYMOUS. - * - * @param string $attribute + * List blogs */ - public function setPublishWorkflowPermission($attribute) - { - $this->publishWorkflowPermission = $attribute; - } - - protected function renderResponse($contentTemplate, $params) + public function listAction(Request $request) { - if ($this->viewHandler) { - $view = new View($params); - $view->setTemplate($contentTemplate); - return $this->viewHandler->handle($view); - } - - return $this->templating->renderResponse($contentTemplate, $params); - } - - protected function getPostRepo() - { - return $this->dm->getRepository('Symfony\Cmf\Bundle\BlogBundle\Document\Post'); - } - - public function viewPostAction(Post $contentDocument, $contentTemplate = null) - { - $post = $contentDocument; - - if (true !== $this->securityContext->isGranted($this->publishWorkflowPermission, $post)) { - throw new NotFoundHttpException(sprintf( - 'Post "%s" is not published' - , $post->getTitle())); - } - - $contentTemplate = $contentTemplate ? : 'CmfBlogBundle:Blog:view_post.html.twig'; - - return $this->renderResponse($contentTemplate, array( - 'post' => $post, - )); + return $this->renderResponse( + $this->getTemplateForResponse($request, 'CmfBlogBundle:Blog:list.{_format}.twig'), + array( + 'blogs' => $this->blogRepository->findAll(), + ) + ); } - public function listAction(Request $request, $contentDocument, $contentTemplate = null) + /** + * Blog detail - list posts in a blog, optionally paginated + */ + public function detailAction(Request $request, Blog $contentDocument, $contentTemplate = null) { $blog = $contentDocument; - $tag = $request->get('tag', null); - // @todo: Pagination - $posts = $this->getPostRepo()->search(array( - 'tag' => $tag, - 'blog_id' => $blog->getId(), + $posts = $this->postRepository->search(array( + 'blogId' => $blog->getId(), )); - $contentTemplate = $contentTemplate ?: 'CmfBlogBundle:Blog:list.{_format}.twig'; + $pager = false; + if ($this->postsPerPage) { + $pager = $posts = $this->paginator->paginate( + $posts, + $request->query->get('page', 1), + $this->postsPerPage + ); + } - // @todo: Copy and pasted from ContentBundle::ContentController - // I wonder if we can share some code between content-like - // bundles. - $contentTemplate = str_replace( - array('{_format}', '{_locale}'), - array($request->getRequestFormat(), $request->getLocale()), - $contentTemplate + $templateFilename = $pager ? 'detailPaginated' : 'detail'; + $contentTemplate = $this->getTemplateForResponse( + $request, + $contentTemplate ?: sprintf('CmfBlogBundle:Blog:%s.{_format}.twig', $templateFilename) ); - return $this->renderResponse($contentTemplate, array( - 'blog' => $blog, - 'posts' => $posts, - 'tag' => $tag - )); + return $this->renderResponse($contentTemplate, compact('blog', 'posts', 'pager')); } } diff --git a/DependencyInjection/CmfBlogExtension.php b/DependencyInjection/CmfBlogExtension.php index a5dd1b7..13cac86 100644 --- a/DependencyInjection/CmfBlogExtension.php +++ b/DependencyInjection/CmfBlogExtension.php @@ -35,53 +35,57 @@ public function load(array $configs, ContainerBuilder $container) $config = $this->processConfiguration($configuration, $configs); $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('controllers.xml'); + $loader->load('services.xml'); - if ($config['use_sonata_admin']) { + if (isset($config['persistence']['phpcr'])) { + $this->loadPhpcrPersistence($config, $loader, $container); + } + + if (isset($config['sonata_admin']) && $config['sonata_admin']['enabled']) { $this->loadSonataAdmin($config, $loader, $container); } - if ($config['integrate_menu']['enabled']) { + + if (isset($config['integrate_menu']) && $config['integrate_menu']['enabled']) { $this->loadMenuIntegration($config, $loader, $container); } - $config['class'] = array_merge(array( - 'blog_admin' => 'Symfony\Cmf\Bundle\BlogBundle\Admin\BlogAdmin', - 'post_admin' => 'Symfony\Cmf\Bundle\BlogBundle\Admin\PostAdmin', - 'blog' => 'Symfony\Cmf\Bundle\BlogBundle\Document\Blog', - 'post' => 'Symfony\Cmf\Bundle\BlogBundle\Document\Post', - ), isset($config['class']) ? $config['class'] : array()); + $this->loadPaginationIntegration($config, $container); + } + + protected function loadPhpcrPersistence($config, XmlFileLoader $loader, ContainerBuilder $container) + { + $container->setParameter($this->getAlias().'.blog_basepath', $config['persistence']['phpcr']['blog_basepath']); - foreach ($config['class'] as $type => $classFqn) { + foreach ($config['persistence']['phpcr']['class'] as $type => $classFqn) { $container->setParameter( - $param = sprintf('cmf_blog.%s_class', $type), + $param = sprintf('cmf_blog.phpcr.%s.class', $type), $classFqn ); } + + $loader->load('initializer-phpcr.xml'); + $loader->load('doctrine-phpcr.xml'); } - private function loadSonataAdmin($config, XmlFileLoader $loader, ContainerBuilder $container) + protected function loadSonataAdmin(array $config, XmlFileLoader $loader, ContainerBuilder $container) { $bundles = $container->getParameter('kernel.bundles'); - if ('auto' === $config['use_sonata_admin'] && !isset($bundles['SonataDoctrinePHPCRAdminBundle'])) { + if (!isset($bundles['SonataDoctrinePHPCRAdminBundle'])) { return; } - $loader->load('blog-admin.xml'); - $container->setParameter($this->getAlias() . '.blog_basepath', $config['blog_basepath']); + $loader->load('admin.xml'); } - private function loadMenuIntegration($config, XmlFileLoader $loader, ContainerBuilder $container) + protected function loadMenuIntegration(array $config, XmlFileLoader $loader, ContainerBuilder $container) { $bundles = $container->getParameter('kernel.bundles'); - if ('auto' === $config['integrate_menu']['enabled'] && !isset($bundles['CmfMenuBundle'])) { + if (!isset($bundles['CmfMenuBundle'])) { return; } if (empty($config['integrate_menu']['content_key'])) { if (!class_exists('Symfony\\Cmf\\Bundle\\RoutingBundle\\Routing\\DynamicRouter')) { - if ('auto' === $config['integrate_menu']) { - return; - } throw new \RuntimeException('You need to set the content_key when not using the CmfRoutingBundle DynamicRouter'); } $contentKey = DynamicRouter::CONTENT_KEY; @@ -94,6 +98,18 @@ private function loadMenuIntegration($config, XmlFileLoader $loader, ContainerBu $loader->load('menu.xml'); } + protected function loadPaginationIntegration(array $config, ContainerBuilder $container) + { + if (isset($config['pagination']) && $config['pagination']['enabled']) { + $container->setParameter($this->getAlias().'.pagination.enabled', true); + $container->setParameter($this->getAlias().'.pagination.posts_per_page', $config['pagination']['posts_per_page']); + } else { + // this parameter is used in the cmf_blog.blog_controller service definition, so + // it must be defined until it's a viable option to use the expression language instead + $container->setParameter($this->getAlias().'.pagination.posts_per_page', 0); + } + } + /** * Returns the base path for the XSD files. * @@ -106,6 +122,6 @@ public function getXsdValidationBasePath() public function getNamespace() { - return 'http://cmf.symfony.com/schema/dic/blog'; + return 'http://cmf.symfony.com/schema/dic/cmf_blog'; } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 9c94521..984418a 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -35,45 +35,63 @@ public function getConfigTreeBuilder() $treeBuilder = new TreeBuilder(); $treeBuilder->root('cmf_blog') ->children() - ->enumNode('use_sonata_admin') - ->values(array(true, false, 'auto')) - ->defaultValue('auto') + + // admin + ->arrayNode('sonata_admin') + ->canBeEnabled() ->end() + // menu ->arrayNode('integrate_menu') ->addDefaultsIfNotSet() + ->canBeEnabled() ->children() - ->enumNode('enabled') - ->values(array(true, false, 'auto')) - ->defaultValue('auto') - ->end() ->scalarNode('content_key')->defaultNull()->end() ->end() ->end() - ->scalarNode('blog_basepath') - ->isRequired() - ->end() - ->scalarNode('routing_post_controller') - ->defaultValue('cmf_blog.blog_controller:viewPostAction') - ->end() - ->scalarNode('routing_post_prefix') - ->defaultValue('posts') - ->end() - ->scalarNode('routing_tag_controller') - ->defaultValue('cmf_blog.blog_controller:listAction') - ->end() - ->scalarNode('routing_tag_prefix') - ->defaultValue('tag') + + // pagination + ->arrayNode('pagination') + ->addDefaultsIfNotSet() + ->canBeEnabled() + ->children() + ->scalarNode('posts_per_page')->defaultValue(5)->end() + ->end() ->end() - ->arrayNode('class') + + // persistence + ->arrayNode('persistence') + ->addDefaultsIfNotSet() ->children() - # defaults defined in CmfBlogExtension - ->scalarNode('blog_admin')->end() - ->scalarNode('post_admin')->end() - ->scalarNode('blog')->end() - ->scalarNode('post')->end() + ->arrayNode('phpcr') + ->addDefaultsIfNotSet() + ->canBeEnabled() + ->children() + ->scalarNode('blog_basepath') + ->defaultValue('/cms/blogs')->cannotBeEmpty() + ->end() + ->arrayNode('class') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('blog_admin') + ->defaultValue('Symfony\Cmf\Bundle\BlogBundle\Admin\BlogAdmin') + ->end() + ->scalarNode('post_admin') + ->defaultValue('Symfony\Cmf\Bundle\BlogBundle\Admin\PostAdmin') + ->end() + ->scalarNode('blog') + ->defaultValue('Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr\Blog') + ->end() + ->scalarNode('post') + ->defaultValue('Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr\Post') + ->end() + ->end() + ->end() + ->end() + ->end() ->end() ->end() + ->end() ->end() ; diff --git a/Doctrine/Phpcr/Blog.php b/Doctrine/Phpcr/Blog.php new file mode 100644 index 0000000..f846c79 --- /dev/null +++ b/Doctrine/Phpcr/Blog.php @@ -0,0 +1,115 @@ + + */ +class Blog extends BlogModel implements RouteReferrersReadInterface +{ + /** + * @var string + */ + protected $id; + + /** + * @var string + */ + protected $slug; + + /** + * Parent Document + * + * @var Generic + */ + protected $parentDocument; + + /** + * Routes (mapped from Route::content) + * + * @var \Symfony\Component\Routing\Route[] + */ + protected $routes; + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get slug + * + * @return string + */ + public function getSlug() + { + return $this->slug; + } + + /** + * Get parent document + * + * @return Generic + */ + public function getParentDocument() + { + return $this->parentDocument; + } + + /** + * Set parent document + * + * @param Generic $parentDocument + * @return Blog + */ + public function setParentDocument(Generic $parentDocument) + { + $this->parentDocument = $parentDocument; + + return $this; + } + + /** + * Get routes that point to this content + * + * @return \Symfony\Component\Routing\Route[] + */ + public function getRoutes() + { + return $this->routes; + } + + /** + * {@inheritDoc} + */ + public function setName($name) + { + parent::setName($name); + + $this->slug = PostUtils::slugify($name); + + return $this; + } +} diff --git a/Doctrine/Phpcr/Post.php b/Doctrine/Phpcr/Post.php new file mode 100644 index 0000000..417d232 --- /dev/null +++ b/Doctrine/Phpcr/Post.php @@ -0,0 +1,108 @@ + + */ +class Post extends PostModel implements RouteReferrersReadInterface +{ + /** + * @var string + */ + protected $id; + + /** + * @var string + */ + protected $slug; + + /** + * List of referring routes + * + * @var \Symfony\Component\Routing\Route[] + */ + protected $routes; + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get slug + * + * @return string + */ + public function getSlug() + { + return $this->slug; + } + + /** + * {@inheritDoc} + */ + public function setTitle($title) + { + parent::setTitle($title); + + $this->slug = PostUtils::slugify($title); + + return $this; + } + + /** + * Get parent document + * + * @return BlogModel + */ + public function getParentDocument() + { + return $this->blog; + } + + /** + * Set parent document + * + * @param BlogModel $blog + * @return Post + */ + public function setParentDocument(BlogModel $blog) + { + parent::setBlog($blog); + + return $this; + } + + /** + * Get routes + * + * @return \Symfony\Component\Routing\Route[] + */ + public function getRoutes() + { + return $this->routes; + } +} diff --git a/Document/Blog.php b/Document/Blog.php deleted file mode 100644 index 5d235c1..0000000 --- a/Document/Blog.php +++ /dev/null @@ -1,139 +0,0 @@ - - */ -class Blog implements RouteReferrersReadInterface -{ - /** - * Identifier - */ - protected $id; - - /** - * Node Name / Blog Title - */ - protected $name; - - /** - * Parent Document - */ - protected $parent; - - /** - * Posts (mapped as children) - */ - protected $posts; - - /** - * Routes (mapped from Route::content) - */ - protected $routes; - - - public function getId() - { - return $this->id; - } - - public function getName() - { - return $this->name; - } - - public function setName($name) - { - $this->name = $name; - } - - public function getParent() - { - return $this->parent; - } - - public function setParent($parent) - { - $this->parent = $parent; - } - - public function getPosts() - { - return $this->posts; - } - - public function setPosts($posts) - { - $this->posts = array(); - foreach ($posts as $post) { - $this->addPost($post); - } - } - - public function addPost(Post $post) - { - $this->posts[] = $post; - } - - /** - * @return \Symfony\Component\Routing\Route[] Route instances that point to this content - */ - public function getRoutes() - { - return $this->routes; - } - - public function getPostsRoutes() - { - return $this->getSubRoutes('Symfony\Cmf\Bundle\BlogBundle\Document\PostRoute'); - } - - public function getTagRoutes() - { - return $this->getSubRoutes('Symfony\Cmf\Bundle\BlogBundle\Document\TagRoute'); - } - - public function getSubRoutes($routeClass) - { - $subRoutes = array(); - - foreach ($this->routes as $route) { - foreach ($route->getRouteChildren() as $routeChild) - if ($routeChild instanceof $routeClass) { - $subRoutes[] = $routeChild; - } - } - - if (empty($subRoutes)) { - throw new \Exception(sprintf( - 'Could not find route of class "%s", this special route should be a child '. - 'of the blogs route.', - $routeClass - )); - } - - return $subRoutes; - } - - public function __toString() - { - return (string) $this->name; - } -} diff --git a/Document/TagRoute.php b/Document/TagRoute.php deleted file mode 100644 index 9989e53..0000000 --- a/Document/TagRoute.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ -class TagRoute extends Route -{ -} diff --git a/Form/DataTransformer/CsvToArrayTransformer.php b/Form/DataTransformer/CsvToArrayTransformer.php deleted file mode 100644 index 9860ea6..0000000 --- a/Form/DataTransformer/CsvToArrayTransformer.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ -class CsvToArrayTransformer implements DataTransformerInterface -{ - /** - * Transforms an array of values to a comma - * separated string. - * - * @param array $value - * - * @return string - */ - public function transform($value) - { - if ($value) { - - // trim all the values - $value = array_map('trim', $value); - - // join together - $string = implode(',', $value); - - return $string; - } - } - - /** - * Transforms a CSV string into an array. - * - * @param string $value - * - * @return array - */ - public function reverseTransform($value) - { - if ($value) { - - // string to array - $array = explode(',', $value); - - // trim all the values - $array = array_map('trim', $array); - - return $array; - } - } -} - - diff --git a/Model/Blog.php b/Model/Blog.php new file mode 100644 index 0000000..1e61e7d --- /dev/null +++ b/Model/Blog.php @@ -0,0 +1,160 @@ + + */ +class Blog +{ + /** + * Blog name + * + * @var string + */ + protected $name; + + /** + * Description of the blog + * + * @var string + */ + protected $description; + + /** + * Posts + * + * @var ArrayCollection + */ + protected $posts; + + /** + * Constructor + */ + public function __construct() + { + $this->posts = new ArrayCollection(); + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set name + * + * @param string $name + * @return Blog + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get description + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set description + * + * @param string $description + * @return Blog + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Add post + * + * @param Post $post + * @return Blog + */ + public function addPost(Post $post) + { + if (!$this->posts->contains($post)) { + $this->posts->add($post); + } + + return $this; + } + + /** + * Remove post + * + * @param Post $post + * @return Blog + */ + public function removePost(Post $post) + { + if ($this->posts->contains($post)) { + $this->posts->remove($post); + } + + return $this; + } + + /** + * Get posts + * + * @return ArrayCollection + */ + public function getPosts() + { + return $this->posts; + } + + /** + * Set posts + * + * @param $posts + * @return $this + */ + public function setPosts($posts) + { + $this->posts = new ArrayCollection(); + + foreach ($posts as $post) { + $this->posts->add($post); + } + + return $this; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/Document/Post.php b/Model/Post.php similarity index 55% rename from Document/Post.php rename to Model/Post.php index ed5fa93..cda3a8d 100644 --- a/Document/Post.php +++ b/Model/Post.php @@ -10,209 +10,252 @@ */ -namespace Symfony\Cmf\Bundle\BlogBundle\Document; - -use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR; - -use Symfony\Cmf\Component\Routing\RouteReferrersReadInterface; - -use Symfony\Cmf\Bundle\BlogBundle\Util\PostUtils; +namespace Symfony\Cmf\Bundle\BlogBundle\Model; use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface; use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface; /** - * Object representing a blog post. + * Post Model * * @author Daniel Leech */ -class Post implements RouteReferrersReadInterface, PublishTimePeriodInterface, PublishableInterface +class Post implements PublishTimePeriodInterface, PublishableInterface { /** - * ID / Path to to this object - * @var string + * @var Blog */ - protected $id; + protected $blog; /** - * Node name (same as slug) * @var string */ - protected $name; - - /** - * READ ONLY: Post slug (cannot query directly on name field) - */ - protected $slug; - - /** - * Blog - this is the parent document. - * @var Blog - */ - protected $blog; + protected $title; /** - * Post title (name is generated from this) * @var string */ - protected $title; + protected $bodyPreview; /** - * Post body text * @var string */ protected $body; /** - * Date of publication * @var \DateTime */ protected $date; /** - * List of referring routes - */ - protected $routes; - - /** - * Date to start publishing from * @var \DateTime */ protected $publishStartDate; /** - * Date to stop publishing from * @var \DateTime */ protected $publishEndDate; /** - * If the document should be publishable - * @var Boolean + * @var boolean */ protected $isPublishable = true; + /** + * Constructor + */ public function __construct() { $this->date = new \DateTime(); } - public function getId() + /** + * Get blog + * + * @return Blog + */ + public function getBlog() { - return $this->id; + return $this->blog; } - public function getName() + /** + * Set blog + * + * @param Blog $blog + * @return Post + */ + public function setBlog(Blog $blog) { - return $this->name; - } + $this->blog = $blog; - public function setName($name) - { - $this->name = $name; + return $this; } + /** + * Get title + * + * @return string + */ public function getTitle() { return $this->title; } + /** + * Set title + * + * @param string $title + * @return Post + */ public function setTitle($title) { $this->title = $title; - $this->name = PostUtils::slugify($title); - $this->slug = $this->name; + + return $this; } - public function getSlug() + /** + * Get body preview + * + * @return string + */ + public function getBodyPreview() { - return $this->slug; + return $this->bodyPreview; + } + + /** + * Set body preview + * + * @param string $bodyPreview + * @return Post + */ + public function setBodyPreview($bodyPreview) + { + $this->bodyPreview = $bodyPreview; + + return $this; } + /** + * Get body (the post's content) + * + * @return string + */ public function getBody() { return $this->body; } + /** + * Set body (the post's content) + * + * @param string $body + * @return Post + */ public function setBody($body) { $this->body = $body; + + return $this; } + /** + * Get publication date + * + * @return \DateTime + */ public function getDate() { return $this->date; } + /** + * Set publication date + * + * @param \DateTime $date + * @return Post + */ public function setDate($date) { $this->date = $date; - } - - public function getParent() - { - return $this->getBlog(); - } - - public function getBlog() - { - return $this->blog; - } - - public function setBlog(Blog $blog) - { - $this->blog = $blog; - // The user can create a post from Admin, so - // we let them choose and automatically make - // this Post a child of selected blog. - $this->parent = $blog; - } - - public function getBodyPreview($length = 255) - { - $suffix = strlen($this->body) > $length ? ' ...' : ''; - - return substr($this->body, 0, 255).$suffix; - } - - public function getRoutes() - { - return $this->routes; + return $this; } + /** + * Is publishable + * + * @return bool + */ public function isPublishable() { return $this->isPublishable; } + /** + * Set publishable + * + * @param bool $publishable + * @return Post + */ public function setPublishable($publishable) { $this->isPublishable = $publishable; + + return $this; } + /** + * Get publish start date + * + * @return \DateTime + */ public function getPublishStartDate() { return $this->publishStartDate; } + /** + * Set publish start date + * + * @param \DateTime $publishStartDate + * @return Post + */ public function setPublishStartDate(\DateTime $publishStartDate = null) { $this->publishStartDate = $publishStartDate; + + return $this; } + /** + * Get publish end date + * + * @return \DateTime + */ public function getPublishEndDate() { return $this->publishEndDate; } + /** + * Set publish end date + * + * @param \DateTime $publishEndDate + * @return Post + */ public function setPublishEndDate(\DateTime $publishEndDate = null) { $this->publishEndDate = $publishEndDate; + + return $this; } public function __toString() { - return (string) $this->name; + return (string) $this->title; } } diff --git a/README.md b/README.md index 459126f..356a8c8 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Pending features: ## Requirements -* Symfony 2.2.x +* Symfony 2.3+ * [CoreBundle](https://github.com/symfony-cmf/CoreBundle) * [RoutingAutoBundle](https://github.com/symfony-cmf/RoutingAutoBundle) diff --git a/Repository/BlogRepository.php b/Repository/BlogRepository.php new file mode 100644 index 0000000..e3af5a8 --- /dev/null +++ b/Repository/BlogRepository.php @@ -0,0 +1,108 @@ +resolver = $this->setDefaultOptions(new OptionsResolver()); + } + + public function setBasepath($basepath) + { + $this->basepath = $basepath; + } + + /** + * Search for blogs by an array of options + * + * @param array $options + * @return Blog[] When limit is not provided or is greater than 1 + * @return Blog|null When limit is set to 1 + */ + public function search(array $options) + { + $options = $this->resolver->resolve($options); + + $qb = $this->createQueryBuilderFromOptions($options); + + if (isset($options['limit']) && $options['limit'] === 1) { + return $qb->getQuery()->getOneOrNullResult(); + } + + return $qb->getQuery()->getResult(); + } + + protected function createQueryBuilderFromOptions(array $options) + { + $qb = $this->createQueryBuilder('b'); + + // parent node + $qb->where()->descendant($this->basepath, 'b'); + + // blog name + if (isset($options['name'])) { + $qb->andWhere()->eq()->field('b.name')->literal($options['name']); + } + + // order by + $qb->orderBy()->asc()->field('b.name'); + + // limit + if (isset($options['limit'])) { + $qb->setMaxResults($options['limit']); + } + + return $qb; + } + + protected function setDefaultOptions(OptionsResolver $resolver) + { + $resolver->setOptional(array( + 'limit', + 'name', + )); + + // null types mean don't include / search by the key term + $resolver->setAllowedTypes(array( + 'name' => 'string', + 'limit' => 'int', + )); + + return $resolver; + } + +} diff --git a/Repository/PostRepository.php b/Repository/PostRepository.php index dcfe065..2bb6a5a 100644 --- a/Repository/PostRepository.php +++ b/Repository/PostRepository.php @@ -11,29 +11,127 @@ namespace Symfony\Cmf\Bundle\BlogBundle\Repository; + use Doctrine\ODM\PHPCR\DocumentRepository; -use Symfony\Cmf\Bundle\BlogBundle\Document\Post; +use Doctrine\ODM\PHPCR\Mapping\ClassMetadata; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr\Post; class PostRepository extends DocumentRepository -{ - public function searchQuery($options) +{ + /** + * @var OptionsResolver + */ + protected $resolver; + + /** + * Constructor + * + * @param \Doctrine\ODM\PHPCR\DocumentManager $dm + * @param ClassMetadata $class + */ + public function __construct($dm, ClassMetadata $class) + { + parent::__construct($dm, $class); + $this->resolver = $this->setDefaultOptions(new OptionsResolver()); + } + + /** + * Search for posts by an array of options: + * - blogId: string (required) + * - isPublishable: boolean (optional, default true) // TODO: https://github.com/symfony-cmf/CoreBundle/issues/126 + * - title: string (optional) + * - limit: integer (optional) + * - orderBy: array of arrays('field' => $field, 'order' => 'ASC or DESC') (optional) + * + * @param array $options + * @return Post[] When limit is not provided or is greater than 1 + * @return Post|null When limit is set to 1 + */ + public function search(array $options) { - $options = array_merge(array( - 'blog_uuid' => null, - ), $options); - $qb = $this->createQueryBuilder('a'); + $options = $this->resolver->resolve($options); + + $qb = $this->createQueryBuilderFromOptions($options); - if ($options['blog_id']) { - $qb->where()->descendant($options['blog_id'], 'a'); + if (isset($options['limit']) && $options['limit'] === 1) { + return $qb->getQuery()->getOneOrNullResult(); } - $qb->orderBy()->desc()->field('a.date'); - return $qb->getQuery(); + return $qb->getQuery()->getResult(); } - public function search($options) + /** + * Create query builder from search options + * + * @param array $options + * @return \Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder + */ + protected function createQueryBuilderFromOptions(array $options) { - $q = $this->searchQuery($options); - return $q->execute(); + $qb = $this->createQueryBuilder('p'); + + // parent node (blog id) + $qb->where()->descendant($options['blogId'], 'p'); + + // is published + if (isset($options['isPublishable']) && ($options['isPublishable'] !== null)) { + $qb->andWhere()->eq()->field('p.isPublishable')->literal($options['isPublishable']); + } + + // order by + if (isset($options['orderBy']['field']) && isset($options['orderBy']['order'])) { + + $field = $options['orderBy']['field']; + $sortOrder = strtolower($options['orderBy']['order']); + + $qb->orderBy()->$sortOrder()->field('p.'.$field); + } + + // limit + if (isset($options['limit'])) { + $qb->setMaxResults($options['limit']); + } + + return $qb; + } + + /** + * Set default search options + * + * @param OptionsResolver $resolver + * @return OptionsResolver + */ + protected function setDefaultOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'isPublishable' => true, + 'orderBy' => array( + array( + 'field' => 'date', + 'order' => 'DESC', + ), + ), + )); + + $resolver->setRequired(array( + 'blogId', + )); + + $resolver->setOptional(array( + 'isPublishable', + 'limit', + 'title', + )); + + $resolver->setAllowedTypes(array( + 'isPublishable' => array('null', 'bool'), + 'blogId' => 'string', + 'title' => 'string', + 'limit' => 'int', + 'orderBy' => 'array', + )); + + return $resolver; } } diff --git a/Resources/config/admin.xml b/Resources/config/admin.xml new file mode 100644 index 0000000..b1e79c6 --- /dev/null +++ b/Resources/config/admin.xml @@ -0,0 +1,45 @@ + + + + + + + + + + %cmf_blog.phpcr.blog.class% + SonataAdminBundle:CRUD + %cmf_blog.blog_basepath% + + + + + + + + + + %cmf_blog.phpcr.post.class% + SonataAdminBundle:CRUD + %cmf_blog.phpcr.blog.class% + + + + + + + diff --git a/Resources/config/blog-admin.xml b/Resources/config/blog-admin.xml deleted file mode 100644 index 1e1c518..0000000 --- a/Resources/config/blog-admin.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - %cmf_blog.blog_class% - SonataAdminBundle:CRUD - - - - - - - %cmf_blog.blog_basepath% - - - - - - - %cmf_blog.post_class% - SonataAdminBundle:CRUD - - - - - - - diff --git a/Resources/config/cmf_routing_auto.xml b/Resources/config/cmf_routing_auto.xml new file mode 100644 index 0000000..2908f6e --- /dev/null +++ b/Resources/config/cmf_routing_auto.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/controllers.xml b/Resources/config/controllers.xml deleted file mode 100644 index 846eab2..0000000 --- a/Resources/config/controllers.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - Symfony\Cmf\Bundle\BlogBundle\Controller\BlogController - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine-phpcr.xml b/Resources/config/doctrine-phpcr.xml new file mode 100644 index 0000000..5a4c3ed --- /dev/null +++ b/Resources/config/doctrine-phpcr.xml @@ -0,0 +1,26 @@ + + + + + + %cmf_blog.phpcr.blog.class% + + %cmf_blog.blog_basepath% + + + + + %cmf_blog.phpcr.post.class% + + + + + diff --git a/Resources/config/doctrine-phpcr/Blog.phpcr.xml b/Resources/config/doctrine-phpcr/Blog.phpcr.xml new file mode 100644 index 0000000..c8943f8 --- /dev/null +++ b/Resources/config/doctrine-phpcr/Blog.phpcr.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/doctrine-phpcr/Post.phpcr.xml b/Resources/config/doctrine-phpcr/Post.phpcr.xml new file mode 100644 index 0000000..67d1eb3 --- /dev/null +++ b/Resources/config/doctrine-phpcr/Post.phpcr.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/doctrine/Blog.phpcr.xml b/Resources/config/doctrine/Blog.phpcr.xml deleted file mode 100644 index ddfe29b..0000000 --- a/Resources/config/doctrine/Blog.phpcr.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine/Post.phpcr.xml b/Resources/config/doctrine/Post.phpcr.xml deleted file mode 100644 index daed590..0000000 --- a/Resources/config/doctrine/Post.phpcr.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/config/doctrine/TagRoute.phpcr.xml b/Resources/config/doctrine/TagRoute.phpcr.xml deleted file mode 100644 index 423400e..0000000 --- a/Resources/config/doctrine/TagRoute.phpcr.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/Resources/config/initializer-phpcr.xml b/Resources/config/initializer-phpcr.xml new file mode 100644 index 0000000..a9efb14 --- /dev/null +++ b/Resources/config/initializer-phpcr.xml @@ -0,0 +1,18 @@ + + + + + + + + CmfBlogBundle + + %cmf_blog.blog_basepath% + + + + + + diff --git a/Resources/config/menu.xml b/Resources/config/menu.xml index 02c70dc..5481d24 100644 --- a/Resources/config/menu.xml +++ b/Resources/config/menu.xml @@ -10,12 +10,13 @@ %cmf_blog.content_key% - %cmf_blog.post_class% + %cmf_blog.phpcr.post.class% - + + + - diff --git a/Resources/config/routing/autoroute_default.yml b/Resources/config/routing/autoroute_default.yml deleted file mode 100644 index 688ab72..0000000 --- a/Resources/config/routing/autoroute_default.yml +++ /dev/null @@ -1,30 +0,0 @@ -cmf_routing_auto: - mappings: - Symfony\Cmf\Bundle\BlogBundle\Document\Blog: - content_path: - routing_path: - provider: [specified, { path: cms/routes }] - exists_action: use - not_exists_action: throw_exception - namespace: - provider: [specified, { path: blog }] - exists_action: use - not_exists_action: create - content_name: - provider: [content_method,{ method: getName }] - exists_action: auto_increment - not_exists_action: create - Symfony\Cmf\Bundle\BlogBundle\Document\Post: - content_path: - routing_path: - provider: [content_object, { method: getBlog }] - exists_action: use - not_exists_action: throw_exception - date: - provider: [content_datetime, {method: getDate}] - exists_action: use - not_exists_action: create - content_name: - provider: [content_method, {method: getTitle}] - exists_action: auto_increment - not_exists_action: create diff --git a/Resources/config/routing/blog_controller.xml b/Resources/config/routing/blog_controller.xml new file mode 100644 index 0000000..b57238d --- /dev/null +++ b/Resources/config/routing/blog_controller.xml @@ -0,0 +1,11 @@ + + + + + cmf_blog.blog_controller:listAction + + + diff --git a/Resources/config/schema/blog-1.0.xsd b/Resources/config/schema/blog-1.0.xsd index ecfc8e7..3436d04 100644 --- a/Resources/config/schema/blog-1.0.xsd +++ b/Resources/config/schema/blog-1.0.xsd @@ -1,8 +1,8 @@ - diff --git a/Resources/config/services.xml b/Resources/config/services.xml new file mode 100644 index 0000000..870f8e3 --- /dev/null +++ b/Resources/config/services.xml @@ -0,0 +1,31 @@ + + + + Symfony\Cmf\Bundle\BlogBundle\Controller\BlogController + Symfony\Cmf\Bundle\BlogBundle\Controller\PostController + + + + + + + + + + + + %cmf_blog.pagination.posts_per_page% + + + + + + + + + + + + diff --git a/Resources/config/validation.xml b/Resources/config/validation.xml new file mode 100644 index 0000000..34faa18 --- /dev/null +++ b/Resources/config/validation.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/doc/building-a-blog.rst b/Resources/doc/building-a-blog.rst deleted file mode 100644 index 62f15dc..0000000 --- a/Resources/doc/building-a-blog.rst +++ /dev/null @@ -1,20 +0,0 @@ -Work in progress -================ - -This document will eventually describe the process of implementing -the BlogBundle. - -Routing -------- - -Register the routes in config.yml - -.. code-block:: php - - cmf_routing: - dynamic: - ... - controllers_by_class: - Symfony\Cmf\Bundle\BlogBundle\Document\Blog: cmf_blog.blog_controller:list - Symfony\Cmf\Bundle\BlogBundle\Document\Post: cmf_blog.blog_controller:viewPost - Symfony\Cmf\Bundle\BlogBundle\Document\Tag: cmf_blog.blog_controller:list diff --git a/Resources/translations/CmfBlogBundle.en.xliff b/Resources/translations/CmfBlogBundle.en.xliff index 6a5122e..e11e0fe 100644 --- a/Resources/translations/CmfBlogBundle.en.xliff +++ b/Resources/translations/CmfBlogBundle.en.xliff @@ -87,9 +87,14 @@ Blog + + form.label_body_preview + Body Preview + + form.label_body - Content + Body Content @@ -107,8 +112,8 @@ Name - - form.label_parent + + form.label_parent_document Parent diff --git a/Resources/translations/CmfBlogBundle.fr.xliff b/Resources/translations/CmfBlogBundle.fr.xliff index 5e3437e..3b83108 100644 --- a/Resources/translations/CmfBlogBundle.fr.xliff +++ b/Resources/translations/CmfBlogBundle.fr.xliff @@ -107,8 +107,8 @@ Nom - - form.label_parent + + form.label_parent_document Parent diff --git a/Resources/translations/CmfBlogBundle.pl.xliff b/Resources/translations/CmfBlogBundle.pl.xliff index 8af6ef1..d837f91 100644 --- a/Resources/translations/CmfBlogBundle.pl.xliff +++ b/Resources/translations/CmfBlogBundle.pl.xliff @@ -107,8 +107,8 @@ Nazwa - - form.label_parent + + form.label_parent_document Nadrzędny diff --git a/Resources/translations/CmfBlogBundle.ru.xliff b/Resources/translations/CmfBlogBundle.ru.xliff index 2ea6e83..ba018bd 100644 --- a/Resources/translations/CmfBlogBundle.ru.xliff +++ b/Resources/translations/CmfBlogBundle.ru.xliff @@ -107,8 +107,8 @@ Имя - - form.label_parent + + form.label_parent_document Родитель diff --git a/Resources/translations/CmfBlogBundle.sk.xliff b/Resources/translations/CmfBlogBundle.sk.xliff index a089db6..b5b68a2 100644 --- a/Resources/translations/CmfBlogBundle.sk.xliff +++ b/Resources/translations/CmfBlogBundle.sk.xliff @@ -107,8 +107,8 @@ Názov - - form.label_parent + + form.label_parent_document Rodič diff --git a/Resources/translations/CmfBlogBundle.sl.xliff b/Resources/translations/CmfBlogBundle.sl.xliff index 2a9d863..2be0ffd 100644 --- a/Resources/translations/CmfBlogBundle.sl.xliff +++ b/Resources/translations/CmfBlogBundle.sl.xliff @@ -107,8 +107,8 @@ Ime - - form.label_parent + + form.label_parent_document Vrhnji diff --git a/Resources/views/Admin/list_tags.html.twig b/Resources/views/Admin/list_tags.html.twig deleted file mode 100644 index d96beb0..0000000 --- a/Resources/views/Admin/list_tags.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ object.tags|join(',') }} diff --git a/Resources/views/Blog/detail.html.twig b/Resources/views/Blog/detail.html.twig new file mode 100644 index 0000000..cebf2f4 --- /dev/null +++ b/Resources/views/Blog/detail.html.twig @@ -0,0 +1,37 @@ +{% extends "CmfBlogBundle::layout.html.twig" %} + +{% block content %} +
+ +

{{ blog.name }}

+ +

+ {{ blog.description|nl2br|raw }} +

+ +
+ +

Posts

+ + {% for post in posts %} + +
+

+ {{ post.title }} +

+

+ Posted on {{ post.date|date('F jS, Y') }} at {{ post.date|date('H:i') }} +

+

+ {{ post.bodyPreview }} +

+
+ + {% else %} +

No posts found.

+ {% endfor %} + +
+ +
+{% endblock %} diff --git a/Resources/views/Blog/detailPaginated.html.twig b/Resources/views/Blog/detailPaginated.html.twig new file mode 100644 index 0000000..1768847 --- /dev/null +++ b/Resources/views/Blog/detailPaginated.html.twig @@ -0,0 +1,9 @@ +{% extends "CmfBlogBundle:Blog:detail.html.twig" %} + +{% block content %} + {{ parent() }} + + {% if pager.totalItemCount %} + {{ knp_pagination_render(pager) }} + {% endif %} +{% endblock %} diff --git a/Resources/views/Blog/list.html.twig b/Resources/views/Blog/list.html.twig index 817430a..72e5b80 100644 --- a/Resources/views/Blog/list.html.twig +++ b/Resources/views/Blog/list.html.twig @@ -1,22 +1,26 @@ -{% extends "CmfBlogBundle::default_layout.html.twig" %} +{% extends "CmfBlogBundle::layout.html.twig" %} + {% block content %} -
-
- {% if tag %} -
- {# @todo: Translation #} - Showing posts with tag "{{ tag }}" +
+ +

Blogs

+ + {% for blog in blogs %} + +
+

+ {{ blog.name }} +

+ {% if blog.description %} +

+ {{ blog.description|nl2br|raw }} +

+ {% endif %}
- {% endif %} + + {% else %} +

No blogs found.

+ {% endfor %} +
-
-
-
- {% block posts %} - {% for post in posts %} - {% include "CmfBlogBundle:Blog:list_post.html.twig" %} - {% endfor %} - {% endblock %} -
-
{% endblock %} diff --git a/Resources/views/Blog/list_post.html.twig b/Resources/views/Blog/list_post.html.twig deleted file mode 100644 index c3a5480..0000000 --- a/Resources/views/Blog/list_post.html.twig +++ /dev/null @@ -1,24 +0,0 @@ -
-
-
- {# @todo: Post routes #} - {{ post.title }} -
-
- {{ post.date.format('d-m-Y H:i:s') }} -
-
-
-
- {{ post.bodyPreview }} -
-
-
-
-
-
- {% trans from "CmfSimpleCmsBundle" %}Tagging{% endtrans %}: -
-
-
- diff --git a/Resources/views/Blog/tags_inline.html.twig b/Resources/views/Blog/tags_inline.html.twig deleted file mode 100644 index ed54d31..0000000 --- a/Resources/views/Blog/tags_inline.html.twig +++ /dev/null @@ -1,4 +0,0 @@ -{% for tag in post.tags %} - {# @todo: Tag routes #} - {{ tag }} -{% endfor %} diff --git a/Resources/views/Blog/view_post.html.twig b/Resources/views/Blog/view_post.html.twig deleted file mode 100644 index 6cb552f..0000000 --- a/Resources/views/Blog/view_post.html.twig +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "CmfBlogBundle::default_layout.html.twig" %} -{% block content %} -
-
- {% if cmf_prev(post) %} - << {{ cmf_prev(post).title }} - {% endif %} -
-
- {% if cmf_next(post) %} - {{ cmf_next(post).title }} >> - {% endif %} -
-
-
-
-

{{ post.title }}

-
-
-
-
-

Posted on: {{ post.date.format('Y-m-d H:i:s') }}

-
-
-
-
-
-
- {{ post.body|nl2br|raw }} -
-
-{% endblock %} - diff --git a/Resources/views/Post/detail.html.twig b/Resources/views/Post/detail.html.twig new file mode 100644 index 0000000..696c6d2 --- /dev/null +++ b/Resources/views/Post/detail.html.twig @@ -0,0 +1,38 @@ +{% extends "CmfBlogBundle::layout.html.twig" %} + +{% set post = cmfMainContent %} + +{% block content %} +
+ +

{{ post.title }}

+ +

+ Posted on {{ post.date|date('F jS, Y') }} at {{ post.date|date('H:i') }} +

+ +

+ {{ post.body|nl2br|raw }} +

+ + + + +
+{% endblock %} diff --git a/Resources/views/default_layout.html.twig b/Resources/views/layout.html.twig similarity index 61% rename from Resources/views/default_layout.html.twig rename to Resources/views/layout.html.twig index 31392a5..d278088 100644 --- a/Resources/views/default_layout.html.twig +++ b/Resources/views/layout.html.twig @@ -4,13 +4,19 @@ Default Blog Layout +

Default Blog Layout

This is the default Symfony CMF Blog layout. You probably want to - override this template to integrate the blog bundle system into + override this template to integrate the blog bundle system into your website.

- {% block content %} - {% endblock %} +
+
+ {% block content %} + {% endblock %} +
+
+
diff --git a/Tagging/DoctrineORMStrategy.php b/Tagging/DoctrineORMStrategy.php deleted file mode 100644 index 9c886aa..0000000 --- a/Tagging/DoctrineORMStrategy.php +++ /dev/null @@ -1,36 +0,0 @@ - - */ -class DoctrineORMStrategy implements StrategyInterface -{ - public function getWeightedTags($blogId) - { - // select aggregated tag counts... - // @todo. - } - - public function updateTags(Post $post) - { - foreach ($post->getTags() as $tag) { - // todo - } - } -} diff --git a/Tagging/PHPCRStringStrategy.php b/Tagging/PHPCRStringStrategy.php deleted file mode 100644 index d4bdc84..0000000 --- a/Tagging/PHPCRStringStrategy.php +++ /dev/null @@ -1,59 +0,0 @@ - - */ -class PHPCRStringStrategy implements StrategyInterface -{ - /** - * {@inheritDoc} - */ - public function getWeightedTags($blogId) - { - $qb = $this->postRep->createQueryBuilder('a'); - $qb->select('tags'); - $qb->where()->descendant($blogId, 'a'); // select only children of given blog - $q = $qb->getQuery(); - $res = $q->getPhpcrNodeResult(); - $rows = $res->getRows(); - $weightedTags = array(); - - $max = 0; - foreach ($rows as $row) { - $tag = $row->getValue('tags'); - if (!isset($weightedTags[$tag])) { - $weightedTags[$tag] = array( - 'count' => 0, - ); - } - - $weightedTags[$tag]['count']++; - - if ($weightedTags[$tag]['count'] > $max) { - $max = $weightedTags[$tag]['count']; - } - } - - foreach ($weightedTags as $name => &$tag) { - $tag['weight'] = $tag['count'] / $max; - } - - return $weightedTags; - } -} diff --git a/Tagging/StrategyInterface.php b/Tagging/StrategyInterface.php deleted file mode 100644 index 6297b26..0000000 --- a/Tagging/StrategyInterface.php +++ /dev/null @@ -1,60 +0,0 @@ - - */ -interface StrategyInterface -{ - /** - * Return an associative array of tags as: - * - * array( - * 'symfony' => array( - * 'count' => 123 - * 'weight' => 0.6 - * ), - * 'php' => array( - * 'count' => 100 - * 'weight' => 0.4 - * ), - * // etc. - * ) - * - * The weight is a floating point number between - * 0 and 1 and represents the relative weight of the - * tag within the set. It is calculated as: - * - * (tag count sum) / (highest individual tag count sum) - * - * @param string $blogId - ID of blog, e.g. /content/blogs/my-blog - * - * @return array - */ - public function getWeightedTags($blogId); - - /** - * Give the strategy the chance to update its database - * of tags. - * - * @param Post $post - Blog post - */ - public function updateTags(Post $post); -} diff --git a/Tagging/Tag.php b/Tagging/Tag.php deleted file mode 100644 index f5ebc41..0000000 --- a/Tagging/Tag.php +++ /dev/null @@ -1,39 +0,0 @@ -name = $name; - $this->blog = $blog; - } - - public function getRoutes() - { - return array(); - } - - public function __toString() - { - return (string)$this->name; - } -} - diff --git a/Tests/Functional/Admin/BlogAdminTest.php b/Tests/Functional/Admin/BlogAdminTest.php index f01dc42..e6c4ea7 100644 --- a/Tests/Functional/Admin/BlogAdminTest.php +++ b/Tests/Functional/Admin/BlogAdminTest.php @@ -12,16 +12,68 @@ namespace Symfony\Cmf\Bundle\BlogBundle\Tests\Functional\Admin; -use Symfony\Cmf\Component\Testing\Functional\BaseTestCase; +use Symfony\Cmf\Bundle\BlogBundle\Tests\Functional\BaseTestCase; class BlogAdminTest extends BaseTestCase { - public function testList() + public function testCreate() { - $client = $this->createClient(); + $crawler = $this->requestRoute('GET', 'admin_cmf_blog_blog_create'); - $client->request('GET', '/admin/dashboard'); - $response = $client->getResponse(); - $this->assertEquals(200, $response->getStatusCode()); + $form = $crawler->selectButton('Create')->form(); + $formPrefix = $this->getAdminFormNamePrefix($crawler); + + $form->setValues(array( + $formPrefix.'[name]' => 'Test Blog', + $formPrefix.'[description]' => 'A blog lives here.', + $formPrefix.'[parentDocument]' => '/cms/blogs', + )); + + $this->client->followRedirects(); + $crawler = $this->client->submit($form); + + $this->assertCount(1, $crawler->filter("html:contains('has been successfully created')"), + 'Expected a success flash message, but none was found.' + ); + } + + public function testEdit() + { + $crawler = $this->requestRoute('GET', 'admin_cmf_blog_blog_edit', array( + 'id' => '/cms/blogs/blog-one', + )); + + $form = $crawler->selectButton('Update')->form(); + $formPrefix = $this->getAdminFormNamePrefix($crawler); + + $form->setValues(array( + $formPrefix.'[name]' => 'Edited Blog', + )); + + $this->client->followRedirects(); + $crawler = $this->client->submit($form); + + $this->assertCount(1, $crawler->filter("html:contains('has been successfully updated')"), + 'Expected a success flash message, but none was found.' + ); + + $dm = $this->db('PHPCR')->getOm(); + $this->assertNotNull($blog = $dm->find(null, '/cms/blogs/edited-blog')); + } + + public function testDelete() + { + $crawler = $this->requestRoute('GET', 'admin_cmf_blog_blog_delete', array( + 'id' => '/cms/blogs/blog-one', + )); + + $form = $crawler->selectButton('Yes, delete')->form(); + + $this->client->followRedirects(); + $crawler = $this->client->submit($form); + + $this->assertCount(1, $crawler->filter("html:contains('has been deleted successfully')"), + 'Expected a success flash message, but none was found.' + ); } } diff --git a/Tests/Functional/Admin/PostAdminTest.php b/Tests/Functional/Admin/PostAdminTest.php new file mode 100644 index 0000000..0a84e9e --- /dev/null +++ b/Tests/Functional/Admin/PostAdminTest.php @@ -0,0 +1,40 @@ +requestRoute('GET', 'admin_cmf_blog_post_create'); + + $form = $crawler->selectButton('Create')->form(); + $formPrefix = $this->getAdminFormNamePrefix($crawler); + + $form->setValues(array( + $formPrefix.'[blog]' => '/cms/blogs/blog-one', + $formPrefix.'[title]' => 'A test post.', + $formPrefix.'[body]' => 'the full post content', + $formPrefix.'[bodyPreview]' => 'the post content preview', + )); + + $this->client->followRedirects(); + $crawler = $this->client->submit($form); + + $this->assertCount(1, $crawler->filter("html:contains('has been successfully created')"), + 'Expected a success flash message, but none was found.' + ); + } +} diff --git a/Tests/Functional/BaseTestCase.php b/Tests/Functional/BaseTestCase.php new file mode 100644 index 0000000..60da308 --- /dev/null +++ b/Tests/Functional/BaseTestCase.php @@ -0,0 +1,121 @@ +client = $this->createClient($this->getKernelConfiguration()); + + $this->application = new Application($this->client->getKernel()); + $this->application->setAutoExit(false); + + $this->router = $this->get('router'); + + $this->db('PHPCR')->loadFixtures(array( + 'Symfony\Cmf\Bundle\BlogBundle\Tests\Resources\DataFixtures\PHPCR\LoadBlogData', + )); + } + + /** + * @param string $serviceId + * @return object + */ + protected function get($serviceId) + { + return $this->client->getContainer()->get($serviceId); + } + + /** + * @param string $parameter + * @return mixed + */ + protected function getParameter($parameter) + { + return $this->client->getContainer()->getParameter($parameter); + } + + /** + * @param string $method + * @param string $url + * @return \Symfony\Component\DomCrawler\Crawler + */ + protected function request($method, $url) + { + return $this->client->request($method, $url); + } + + /** + * @param string $method + * @param string $route + * @param array $parameters + * @return \Symfony\Component\DomCrawler\Crawler + */ + protected function requestRoute($method, $route, array $parameters = array()) + { + return $this->request( + $method, + $this->router->generate($route, $parameters) + ); + } + + /** + * Returns the unique ID Sonata Admins use to prefix form fields + * + * @param \Symfony\Component\DomCrawler\Crawler $crawler + * @param string $filter + * @return string + */ + protected function getAdminFormNamePrefix(\Symfony\Component\DomCrawler\Crawler $crawler, $filter = 'input[type=text]') + { + $parts = explode('_', $crawler->filter($filter)->first()->attr('id')); + + return $parts[0]; + } + + /** + * @param string $command + * @param array $options + * @return int + */ + protected function runConsole($command, array $options = array()) + { + $options['-e'] = 'test'; // test environment + $options['-q'] = null; // suppress the command's output + $options['command'] = $command; + + return $this->application->run(new ArrayInput($options)); + } +} diff --git a/Tests/Functional/BlogControllerTest.php b/Tests/Functional/BlogControllerTest.php new file mode 100644 index 0000000..2abd88b --- /dev/null +++ b/Tests/Functional/BlogControllerTest.php @@ -0,0 +1,34 @@ +request('GET', '/blogs'); + + $this->assertCount(3, $crawler->filter('h2')); + } + + /** + * Pagination disabled + */ + public function testDetailAction() + { + $crawler = $this->request('GET', '/blogs/blog-three'); + + $this->assertCount(1, $crawler->filter('h2')); + $this->assertCount(12, $crawler->filter('h3')); + } +} diff --git a/Tests/Functional/PostControllerTest.php b/Tests/Functional/PostControllerTest.php new file mode 100644 index 0000000..376583a --- /dev/null +++ b/Tests/Functional/PostControllerTest.php @@ -0,0 +1,26 @@ +request('GET', '/blogs/blog-one/2014-01-01/first-post'); + + $this->assertCount(2, $crawler->filter('h1')); + $this->assertCount(1, $crawler->filter("html:contains('First Post')"), + 'Expected to find the Post\'s title, but it was not.' + ); + } +} diff --git a/Tests/Resources/DataFixtures/PHPCR/LoadBlogData.php b/Tests/Resources/DataFixtures/PHPCR/LoadBlogData.php new file mode 100644 index 0000000..499a96f --- /dev/null +++ b/Tests/Resources/DataFixtures/PHPCR/LoadBlogData.php @@ -0,0 +1,89 @@ +getPhpcrSession(), '/cms/routes'); + NodeHelper::createPath($manager->getPhpcrSession(), $this->getBasePath()); + $rootNode = $manager->find(null, $this->getBasePath()); + + $numPostsPerBlog = (2 * $this->getPostsPerPage()) + 2; // 3 pages + $blogNames = array( + 'Blog One', + 'Blog Two', + 'Blog Three', + ); + + foreach ($blogNames as $blogName) { + $blog = new Blog(); + $blog->setName($blogName); + $blog->setDescription(implode("\n\n", $faker->paragraphs(3))); + $blog->setParentDocument($rootNode); + + $manager->persist($blog); + + for ($i = 0; $i < $numPostsPerBlog; $i++) { + $paragraphs = $faker->paragraphs(5); + $post = new Post(); + $post->setBlog($blog); + $post->setTitle($faker->sentence(5)); + $post->setBodyPreview($paragraphs[0]); + $post->setBody(implode("\n\n", $paragraphs)); + + if ($i == 0) { + $post->setTitle('First Post'); + $post->setDate(new \DateTime('2014-01-01')); + } + + $manager->persist($post); + } + } + $manager->flush(); + } + + protected function getPostsPerPage() + { + $postsPerPage = $this->container->getParameter('cmf_blog.pagination.posts_per_page'); + + return $postsPerPage != 0 ? $postsPerPage : 5; + } + + protected function getBasePath() + { + return $this->container->getParameter('cmf_blog.blog_basepath'); + } + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/Tests/Resources/app/AppKernel.php b/Tests/Resources/app/AppKernel.php index 1739c6a..8c3a52d 100644 --- a/Tests/Resources/app/AppKernel.php +++ b/Tests/Resources/app/AppKernel.php @@ -15,6 +15,9 @@ public function configure() $this->addBundles(array( new \Symfony\Cmf\Bundle\CoreBundle\CmfCoreBundle(), + new \Symfony\Cmf\Bundle\ContentBundle\CmfContentBundle(), + new \Symfony\Cmf\Bundle\RoutingBundle\CmfRoutingBundle(), + new \Symfony\Cmf\Bundle\RoutingAutoBundle\CmfRoutingAutoBundle(), new \Symfony\Cmf\Bundle\BlogBundle\CmfBlogBundle(), )); } diff --git a/Tests/Resources/app/config/blogbundle.yml b/Tests/Resources/app/config/blogbundle.yml index a5188f8..98b7f7a 100644 --- a/Tests/Resources/app/config/blogbundle.yml +++ b/Tests/Resources/app/config/blogbundle.yml @@ -1,2 +1,42 @@ +parameters: + phpcr_basepath: /cms + +doctrine: + orm: ~ + dbal: + driver: pdo_sqlite + path: %kernel.root_dir%/cache/test_db.sqlite + memory: true + types: + json: Sonata\Doctrine\Types\JsonType + +doctrine_phpcr: + odm: + auto_generate_proxy_classes: true + cmf_blog: - blog_basepath: /cms/content + sonata_admin: ~ + persistence: + phpcr: + blog_basepath: /cms/blogs + +cmf_routing: + chain: + routers_by_id: + router.default: 100 + cmf_routing.dynamic_router: 20 + dynamic: + persistence: + phpcr: + admin_basepath: %phpcr_basepath% + route_basepaths: [%phpcr_basepath%] + content_basepath: %phpcr_basepath% + controllers_by_class: + Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr\Blog: cmf_blog.blog_controller:detailAction + templates_by_class: + Symfony\Cmf\Bundle\BlogBundle\Doctrine\Phpcr\Post: CmfBlogBundle:Post:detail.html.twig + +cmf_routing_auto: + persistence: + phpcr: + route_basepath: /cms/routes diff --git a/Tests/Resources/app/config/config.php b/Tests/Resources/app/config/config.php index 2b8e6cb..a6d3d98 100644 --- a/Tests/Resources/app/config/config.php +++ b/Tests/Resources/app/config/config.php @@ -1,6 +1,6 @@ setParameter('cmf_testing.bundle_fqn', 'Symfony\Cmf\Bundle\BlogBundle'); $loader->import(CMF_TEST_CONFIG_DIR.'/default.php'); $loader->import(CMF_TEST_CONFIG_DIR.'/phpcr_odm.php'); $loader->import(CMF_TEST_CONFIG_DIR.'/sonata_admin.php'); diff --git a/Tests/Resources/app/config/routing.php b/Tests/Resources/app/config/routing.php index 0b064f6..ceb49e8 100644 --- a/Tests/Resources/app/config/routing.php +++ b/Tests/Resources/app/config/routing.php @@ -6,5 +6,7 @@ $collection->addCollection( $loader->import(CMF_TEST_CONFIG_DIR.'/routing/sonata_routing.yml') ); - +$collection->addCollection( + $loader->import(__DIR__.'/routing.yml') +); return $collection; diff --git a/Tests/Resources/app/config/routing.yml b/Tests/Resources/app/config/routing.yml new file mode 100644 index 0000000..6a319d0 --- /dev/null +++ b/Tests/Resources/app/config/routing.yml @@ -0,0 +1,2 @@ +blog_bundle: + resource: '@CmfBlogBundle/Resources/config/routing/blog_controller.xml' diff --git a/Tests/Resources/app/console b/Tests/Resources/app/console new file mode 120000 index 0000000..4134a52 --- /dev/null +++ b/Tests/Resources/app/console @@ -0,0 +1 @@ +../../../vendor/symfony-cmf/testing/bin/console \ No newline at end of file diff --git a/Tests/Unit/Form/DataTransformer/CsvToArrayTransformerTest.php b/Tests/Unit/Form/DataTransformer/CsvToArrayTransformerTest.php deleted file mode 100644 index b16ed94..0000000 --- a/Tests/Unit/Form/DataTransformer/CsvToArrayTransformerTest.php +++ /dev/null @@ -1,40 +0,0 @@ -transformer = new CsvToArrayTransformer; - } - - public function testTransform() - { - $v = array('one', 'two', 'three ', 'four five'); - $res = $this->transformer->transform($v); - - $this->assertEquals('one,two,three,four five', $res); - } - - public function testReverseTransform() - { - $v = ' one, one, two, three , four five '; - $res = $this->transformer->reverseTransform($v); - - $expected = array('one', 'one', 'two', 'three', 'four five'); - $this->assertEquals($expected, $res); - } -} diff --git a/Tests/Unit/Tagging/TagTest.php b/Tests/Unit/Tagging/TagTest.php deleted file mode 100644 index 4e12d4b..0000000 --- a/Tests/Unit/Tagging/TagTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getMock('Symfony\Cmf\Bundle\BlogBundle\Document\Blog'); - $this->tag = new Tag($blog, 'foo'); - } - - public function testTag() - { - $this->assertEquals('foo', (string) $this->tag); - } -} diff --git a/composer.json b/composer.json index 626f69e..5e7c8d1 100644 --- a/composer.json +++ b/composer.json @@ -11,28 +11,38 @@ "homepage": "https://github.com/symfony-cmf/BlogBundle/contributors" } ], - "minimum-stability": "dev", + "minimum-stability": "stable", "require": { "php": ">=5.3.3", - "symfony/framework-bundle": "~2.2", - "symfony-cmf/core-bundle": "1.0.*", + "symfony/framework-bundle": "~2.3", + "symfony-cmf/core-bundle": "1.2.*", + "symfony-cmf/content-bundle": "1.2.*", "symfony-cmf/routing-auto-bundle": "1.0.*", - "doctrine/phpcr-bundle": "1.0.*", - "doctrine/phpcr-odm": "1.0.*" + "sonata-project/doctrine-extensions": "~1.0,>=1.0.2", + "doctrine/phpcr-bundle": "~1.2", + "doctrine/phpcr-odm": "~1.2", + "friendsofsymfony/rest-bundle": "~1.4" }, "require-dev": { - "symfony-cmf/testing": "1.1.*", - "sonata-project/doctrine-phpcr-admin-bundle": "1.0.*", - "symfony-cmf/core-bundle": "1.0.*" + "phpunit/phpunit": "~3.7.28", + "symfony-cmf/testing": "1.2.*", + "sonata-project/doctrine-phpcr-admin-bundle": "1.2.*", + "doctrine/orm": "~2.4.7", + "nelmio/alice": "~1.7,>=1.7.2", + "phpcr/phpcr-shell": "@alpha" }, "suggest": { - "sonata-project/doctrine-phpcr-admin-bundle": "For sonata administration" + "sonata-project/doctrine-phpcr-admin-bundle": "For sonata administration", + "knplabs/knp-paginator-bundle": "To enable pagination of posts" }, "autoload": { "psr-4": { "Symfony\\Cmf\\Bundle\\BlogBundle\\": "" } }, + "config": { + "bin-dir": "bin" + }, "extra": { "branch-alias": { "dev-master": "1.0-dev" diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..277ee13 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,4 @@ +#!/bin/sh +sh vendor/symfony-cmf/testing/bin/travis/doctrine_orm.sh +sh vendor/symfony-cmf/testing/bin/travis/phpcr_odm_doctrine_dbal.sh +bin/phpunit