Creating Custom Lists for Newscoop Templates

In Newscoop templates you can use Smarty3 plugin blocks to display Newscoop content as a list:

{{ list_articles }}{{ /list_articles }}

{{ list_users }}{{ /list_users }}

To create custom lists with different Newscoop content, look at the following four files:

List content
NewscoopExampleBundle/TemplateList/ExampleContentList.php
List criteria
NewscoopExampleBundle/TemplateList/ExampleContentCriteria.php
Smarty block
NewscoopExampleBundle/Resources/smartyPlugins/block.list_example_content.php
Listener
NewscoopExampleBundle/EventListener/ListObjectsListener.php

List Content

The List Class delivers the content to the template. It must be registered in the CampContext class with ListObjectsListener.php.

<?php

namespace Newscoop\ExamplePluginBundle\TemplateList;

use Newscoop\ListResult;
use Newscoop\TemplateList\PaginatedBaseList;

/**
 * ExampleContent List
 */
class ExampleContentList extends PaginatedBaseList // we extend PaginatedBaseList to use build-in support for paginator
{
    protected function prepareList($criteria, $parameters)
    {
        // get query builder (or Query), use passed $criteria object to build query
        $target = $this->getListByCriteria($criteria);
        // paginate query builder, pagenumber is injected to paginatorService in list block, use max results from criteria.
        // get ListResults with paginated data

        // if you don't have records in database then you can uncomment this code (it will create dummy criteria objects):
        // $target = array();
        // for ($i=0; $i < 20 ; $i++) {
        //     $target[$i] = new \Newscoop\ExamplePluginBundle\Entity\Example();
        //     $target[$i]->setName('Name for '.$i.' example');
        //     $target[$i]->setDescription('Description for '.$i.' example');
        //     $target[$i]->getCreatedAt(new \DateTime());
        // }


        $list = $this->paginateList($target, null, $criteria->maxResults);

        return $list;
    }

    /**
     * Get list for given criteria
     *
     * You can place this method also in Entity Repository.
     *
     * @param Newscoop\ExamplePluginBundle\TemplateList\ExampleContentCriteria $criteria
     *
     * @return Newscoop\ListResult
     */
    private function getListByCriteria(ExampleContentCriteria $criteria)
    {
        $em = \Zend_Registry::get('container')->get('em');
        $qb = $em->getRepository('Newscoop\ExamplePluginBundle\Entity\Example')
            ->createQueryBuilder('e');

        // use processed by list constraints from list block (template)
        foreach ($criteria->perametersOperators as $key => $operator) {
            $qb->andWhere('e.'.$key.' = :'.$key)
                ->setParameter($key, $criteria->$key);
        }

        // use processed by list order definitions from list block (template)
        $metadata = $em->getClassMetadata('Newscoop\ExamplePluginBundle\Entity\Example');
        foreach ($criteria->orderBy as $key => $order) {
            if (array_key_exists($key, $metadata->columnNames)) {
                $key = 'e.' . $key;
            }

            $qb->orderBy($key, $order);
        }

        return $qb;
    }
}

List Criteria

The Criteria class defines the list properties, constraints, sorting order and other parameters. A custom list for example content with an object with an id, name, description and created_by_date should allow sorting and filtering by id, name and created_by_date.

<?php

namespace Newscoop\ExamplePluginBundle\TemplateList;

use Newscoop\Criteria;

class ExampleContentCriteria extends Criteria
{
    /**
     * @var int
     */
    public $id;

    /**
     * @var string
     */
    public $name;

    /**
     * @var \DateTime
     */
    public $created_by_date;
}

Smarty Block

The smarty block is the implementation of the list, template tags and paginator.

<?php
/**
 * list_example_content block plugin
 *
 * Type:     block
 * Name:     list_example_content
 *
 * @param array $params
 * @param mixed $content
 * @param object $smarty
 * @param bool $repeat
 * @return string
 */
function smarty_block_list_example_content($params, $content, &$smarty, &$repeat)
{
    $context = $smarty->getTemplateVars('gimme');
    // get paginator service
    $paginatorService = \Zend_Registry::get('container')->get('newscoop.listpaginator.service');
    $cacheService = \Zend_Registry::get('container')->get('newscoop.cache');

    if (!isset($content)) { // init
        $start = $context->next_list_start('\Newscoop\ExamplePluginBundle\TemplateList\ExampleContentList');
        // initiate list object, pass new criteria object and paginatorService
        $list = new \Newscoop\ExamplePluginBundle\TemplateList\ExampleContentList(
            new \Newscoop\ExamplePluginBundle\TemplateList\ExampleContentCriteria(),
            $paginatorService,
            $cacheService
        );

        // inject page parameter name to paginatorService, every list have own name used for pagination
        $list->setPageParameterName($context->next_list_id($context->getListName($list)));
        // inject requested page number (get from request value of list page parameter name)
        $list->setPageNumber(\Zend_Registry::get('container')->get('request')->get($list->getPageParameterName(), 1));

        // get list
        $list->getList($start, $params);
        if ($list->isEmpty()) {
            $context->setCurrentList($list, array());
            $context->resetCurrentList();
            $repeat = false;

            return null;
        }

        // set current list and connect used in list properties
        $context->setCurrentList($list, array('content', 'pagination'));
        // assign current list element to context
        // how we get current_example_content_list name? Our list class have name "ExampleContentList"
        // so we add "current_" and replace all big letters to "_"
        $context->content = $context->current_example_content_list->current;
        $repeat = true;
    } else { // next
        $context->current_example_content_list->defaultIterator()->next();
        if (!is_null($context->current_example_content_list->current)) {
            // assign current list element to context
            $context->content = $context->current_example_content_list->current;
            $repeat = true;
        } else {
            $context->resetCurrentList();
            $repeat = false;
        }
    }

    return $content;
}

Listener

Register the List object in the Newscoop listener class.

<?php

namespace Newscoop\ExamplePluginBundle\EventListener;

use Newscoop\EventDispatcher\Events\CollectObjectsDataEvent;

class ListObjectsListener
{
    /**
     * Register plugin list objects in Newscoop
     *
     * @param  CollectObjectsDataEvent $event
     */
    public function registerObjects(CollectObjectsDataEvent $event)
    {
        $event->registerListObject('newscoop\examplepluginbundle\templatelist\examplecontent', array(
            // for newscoop convention we need remove "List" from "ExampleContentList" class name.
            'class' => 'Newscoop\ExamplePluginBundle\TemplateList\ExampleContent',
            // list name without "list_" - another Newscoop convention
            'list' => 'example_content',
            'url_id' => 'cnt',
        ));

        $event->registerObjectTypes('content', array(
            'class' => '\Newscoop\ExamplePluginBundle\Entity\Example'
        ));
    }
}

And register the listener in the Newscoop configuration.

# Resources/config/services.yml
newscoop_example_plugin.list_objects.listener:
    class: Newscoop\ExamplePluginBundle\EventListener\ListObjectsListener
    tags:
      - { name: kernel.event_listener, event: newscoop.listobjects.register, method: registerObjects }

Using the Custom Block in a Template

<ul>
{{ list_example_content length="2" }}
    <li>
    {{ $gimme->content->getName() }}
    </li>

{{if $gimme->current_list->at_end}}
</ul>
{{ /if }}

    {{ listpagination }}
{{ /list_example_content }}