first step for tag list and add Pagerfanta for paginator
authorcavaliet
Fri, 21 Oct 2011 17:10:54 +0200
changeset 15 99ad73ef7385
parent 14 fc78844c8a76
child 16 89a003e5b108
child 18 c85b9d1ddf19
first step for tag list and add Pagerfanta for paginator
.hgsubstate
app/autoload.php
vendor/bundles/Pagerfanta/Adapter/AdapterInterface.php
vendor/bundles/Pagerfanta/Adapter/ArrayAdapter.php
vendor/bundles/Pagerfanta/Adapter/DoctrineCollectionAdapter.php
vendor/bundles/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php
vendor/bundles/Pagerfanta/Adapter/DoctrineORM/CountWalker.php
vendor/bundles/Pagerfanta/Adapter/DoctrineORM/LimitSubqueryWalker.php
vendor/bundles/Pagerfanta/Adapter/DoctrineORM/WhereInWalker.php
vendor/bundles/Pagerfanta/Adapter/DoctrineORMAdapter.php
vendor/bundles/Pagerfanta/Adapter/MandangoAdapter.php
vendor/bundles/Pagerfanta/Adapter/NullAdapter.php
vendor/bundles/Pagerfanta/Adapter/PropelAdapter.php
vendor/bundles/Pagerfanta/Exception/Exception.php
vendor/bundles/Pagerfanta/Exception/InvalidArgumentException.php
vendor/bundles/Pagerfanta/Exception/LessThan1CurrentPageException.php
vendor/bundles/Pagerfanta/Exception/LessThan1MaxPerPageException.php
vendor/bundles/Pagerfanta/Exception/LogicException.php
vendor/bundles/Pagerfanta/Exception/NotIntegerCurrentPageException.php
vendor/bundles/Pagerfanta/Exception/NotIntegerMaxPerPageException.php
vendor/bundles/Pagerfanta/Exception/NotValidCurrentPageException.php
vendor/bundles/Pagerfanta/Exception/NotValidMaxPerPageException.php
vendor/bundles/Pagerfanta/Exception/OutOfRangeCurrentPageException.php
vendor/bundles/Pagerfanta/Pagerfanta.php
vendor/bundles/Pagerfanta/PagerfantaInterface.php
vendor/bundles/Pagerfanta/View/DefaultView.php
vendor/bundles/Pagerfanta/View/OptionableView.php
vendor/bundles/Pagerfanta/View/TwitterBootstrapView.php
vendor/bundles/Pagerfanta/View/ViewFactory.php
vendor/bundles/Pagerfanta/View/ViewFactoryInterface.php
vendor/bundles/Pagerfanta/View/ViewInterface.php
--- a/.hgsubstate	Thu Oct 20 18:35:34 2011 +0200
+++ b/.hgsubstate	Fri Oct 21 17:10:54 2011 +0200
@@ -1,1 +1,1 @@
-7d2fb5d7c9fff6d099ab7d154e7b520bec6773e3 vendor/bundles/IRI/Bundle/WikiTagBundle
+cc32af72517618a30f426fe5fba0572ad0a50035 vendor/bundles/IRI/Bundle/WikiTagBundle
--- a/app/autoload.php	Thu Oct 20 18:35:34 2011 +0200
+++ b/app/autoload.php	Fri Oct 21 17:10:54 2011 +0200
@@ -15,6 +15,7 @@
     'Assetic'                  => __DIR__.'/../vendor/assetic/src',
     'Metadata'                 => __DIR__.'/../vendor/metadata/src',
     'FOS'                      => __DIR__.'/../vendor/bundles',
+	'Pagerfanta'  			   => __DIR__.'/../vendor/bundles',
 	'IRI\Bundle\WikiTagBundle' => __DIR__.'/../vendor/bundles',
 ));
 $loader->registerPrefixes(array(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/AdapterInterface.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+/**
+ * AdapterInterface.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+interface AdapterInterface
+{
+    /**
+     * Returns the number of results.
+     *
+     * @return integer The number of results.
+     *
+     * @api
+     */
+    function getNbResults();
+
+    /**
+     * Returns an slice of the results.
+     *
+     * @param integer $offset The offset.
+     * @param integer $length The length.
+     *
+     * @return array The slice.
+     *
+     * @api
+     */
+    function getSlice($offset, $length);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/ArrayAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+/**
+ * ArrayAdapter.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class ArrayAdapter implements AdapterInterface
+{
+    private $array;
+
+    /**
+     * Constructor.
+     *
+     * @param array $array The array.
+     *
+     * @api
+     */
+    public function __construct(array $array)
+    {
+        $this->array = $array;
+    }
+
+    /**
+     * Returns the array.
+     *
+     * @return array The array.
+     *
+     * @api
+     */
+    public function getArray()
+    {
+        return $this->array;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        return count($this->array);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        return array_slice($this->array, $offset, $length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineCollectionAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+use Doctrine\Common\Collections\Collection;
+
+/**
+ * DoctrineCollectionAdapter.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class DoctrineCollectionAdapter implements AdapterInterface
+{
+    private $collection;
+
+    /**
+     * Constructor.
+     *
+     * @param Collection $collection A Doctrine collection.
+     *
+     * @api
+     */
+    public function __construct(Collection $collection)
+    {
+        $this->collection = $collection;
+    }
+
+    /**
+     * Returns the collection.
+     *
+     * @return Collection The collection.
+     *
+     * @api
+     */
+    public function getCollection()
+    {
+        return $this->collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        return $this->collection->count();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        return $this->collection->slice($offset, $length);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+use Doctrine\ODM\MongoDB\Query\Builder;
+
+/**
+ * DoctrineODMMongoDBAdapter.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class DoctrineODMMongoDBAdapter implements AdapterInterface
+{
+    private $queryBuilder;
+
+    /**
+     * Constructor.
+     *
+     * @param Builder $queryBuilder A DoctrineMongo query builder.
+     *
+     * @api
+     */
+    public function __construct(Builder $queryBuilder)
+    {
+        $this->queryBuilder = $queryBuilder;
+    }
+
+    /**
+     * Returns the query builder.
+     *
+     * @return Builder The query builder.
+     *
+     * @api
+     */
+    public function getQueryBuilder()
+    {
+        return $this->queryBuilder;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        return $this->queryBuilder->getQuery()->count();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        return iterator_to_array($this->queryBuilder->limit($length)->skip($offset)->getQuery()->execute());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineORM/CountWalker.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter\DoctrineORM;
+
+use Doctrine\ORM\Query\TreeWalkerAdapter,
+    Doctrine\ORM\Query\AST\SelectStatement,
+    Doctrine\ORM\Query\AST\SelectExpression,
+    Doctrine\ORM\Query\AST\PathExpression,
+    Doctrine\ORM\Query\AST\AggregateExpression;
+
+class CountWalker extends TreeWalkerAdapter
+{
+
+    /**
+     * Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
+     *
+     * @param SelectStatement $AST
+     * @return void
+     */
+    public function walkSelectStatement(SelectStatement $AST)
+    {
+        $parent = null;
+        $parentName = null;
+
+
+
+        foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
+
+            // skip mixed data in query
+            if (isset($qComp['resultVariable'])) {
+                continue;
+            }
+
+            if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
+                $parent = $qComp;
+                $parentName = $dqlAlias;
+                break;
+            }
+        }
+
+
+        $pathExpression = new PathExpression(
+                        PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
+                        $parent['metadata']->getSingleIdentifierFieldName()
+        );
+        $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
+
+        $AST->selectClause->selectExpressions = array(
+            new SelectExpression(
+                    new AggregateExpression('count', $pathExpression, true), null
+            )
+        );
+
+        // ORDER BY is not needed, only increases query execution through unnecessary sorting.
+        $AST->orderByClause = null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineORM/LimitSubqueryWalker.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter\DoctrineORM;
+
+use Doctrine\ORM\Query\TreeWalkerAdapter,
+    Doctrine\ORM\Query\AST\SelectStatement,
+    Doctrine\ORM\Query\AST\SimpleSelectExpression,
+    Doctrine\ORM\Query\AST\PathExpression,
+    Doctrine\ORM\Query\AST\AggregateExpression;
+
+/**
+ * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
+ *
+ * @category    DoctrineExtensions
+ * @package     DoctrineExtensions\Paginate
+ * @author      David Abdemoulaie <dave@hobodave.com>
+ * @copyright   Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
+ * @license     http://hobodave.com/license.txt New BSD License
+ */
+class LimitSubqueryWalker extends TreeWalkerAdapter
+{
+
+    /**
+     * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
+     * of the root Entity
+     *
+     * @param SelectStatement $AST
+     * @return void
+     */
+    public function walkSelectStatement(SelectStatement $AST)
+    {
+        $parent = null;
+        $parentName = null;
+        foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
+
+            // skip mixed data in query
+            if (isset($qComp['resultVariable'])) {
+                continue;
+            }
+
+            if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
+                $parent = $qComp;
+                $parentName = $dqlAlias;
+                break;
+            }
+        }
+
+        $pathExpression = new PathExpression(
+                        PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
+                        $parent['metadata']->getSingleIdentifierFieldName()
+        );
+        $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
+
+        $AST->selectClause->selectExpressions = array(
+            new SimpleSelectExpression($pathExpression)
+        );
+
+        if (isset($AST->orderByClause)) {
+            foreach ($AST->orderByClause->orderByItems as $item) {
+                $pathExpression = new PathExpression(
+                                PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
+                                $item->expression->identificationVariable,
+                                $item->expression->field
+                );
+                $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
+                $AST->selectClause->selectExpressions[] = new SimpleSelectExpression($pathExpression);
+            }
+        }
+
+        $AST->selectClause->isDistinct = true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineORM/WhereInWalker.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter\DoctrineORM;
+
+use Doctrine\ORM\Query\TreeWalkerAdapter,
+    Doctrine\ORM\Query\AST\SelectStatement,
+    Doctrine\ORM\Query\AST\PathExpression,
+    Doctrine\ORM\Query\AST\InExpression,
+    Doctrine\ORM\Query\AST\InputParameter,
+    Doctrine\ORM\Query\AST\ConditionalPrimary,
+    Doctrine\ORM\Query\AST\ConditionalTerm,
+    Doctrine\ORM\Query\AST\ConditionalExpression,
+    Doctrine\ORM\Query\AST\ConditionalFactor,
+    Doctrine\ORM\Query\AST\WhereClause;
+
+/**
+ * Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent
+ *
+ * @category    DoctrineExtensions
+ * @package     DoctrineExtensions\Paginate
+ * @author      David Abdemoulaie <dave@hobodave.com>
+ * @copyright   Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
+ * @license     http://hobodave.com/license.txt New BSD License
+ */
+class WhereInWalker extends TreeWalkerAdapter
+{
+
+    /**
+     * Replaces the whereClause in the AST
+     *
+     * Generates a clause equivalent to WHERE IN (:pgid_1, :pgid_2, ...)
+     *
+     * The parameter namespace (pgid) is retrieved from the pg.ns query hint
+     * The total number of parameters is retrieved from the id.count query hint
+     *
+     * @param  SelectStatement $AST
+     * @return void
+     */
+    public function walkSelectStatement(SelectStatement $AST)
+    {
+        $parent = null;
+        $parentName = null;
+        foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
+
+            // skip mixed data in query
+            if (isset($qComp['resultVariable'])) {
+                continue;
+            }
+
+            if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
+                $parent = $qComp;
+                $parentName = $dqlAlias;
+                break;
+            }
+        }
+
+        $pathExpression = new PathExpression(
+                        PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName()
+        );
+        $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
+        $inExpression = new InExpression($pathExpression);
+        $ns = $this->_getQuery()->getHint('pg.ns');
+        $count = $this->_getQuery()->getHint('id.count');
+        for ($i = 1; $i <= $count; $i++) {
+            $inExpression->literals[] = new InputParameter(":{$ns}_$i");
+        }
+        $conditionalPrimary = new ConditionalPrimary;
+        $conditionalPrimary->simpleConditionalExpression = $inExpression;
+
+        // if no existing whereClause
+        if ($AST->whereClause === null) {
+            $AST->whereClause = new WhereClause(
+                            new ConditionalExpression(array(
+                                new ConditionalTerm(array(
+                                    new ConditionalFactor($conditionalPrimary)
+                                ))
+                            ))
+            );
+        } else { // add to the existing using AND
+            // existing AND clause
+            if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
+                $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
+            }
+            // single clause where
+            elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
+                $AST->whereClause->conditionalExpression = new ConditionalExpression(
+                                array(
+                                    new ConditionalTerm(
+                                            array(
+                                                $AST->whereClause->conditionalExpression,
+                                                $conditionalPrimary
+                                            )
+                                    )
+                                )
+                );
+            }
+            // an OR clause
+            elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
+                $tmpPrimary = new ConditionalPrimary;
+                $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
+                $AST->whereClause->conditionalExpression = new ConditionalTerm(
+                                array(
+                                    $tmpPrimary,
+                                    $conditionalPrimary,
+                                )
+                );
+            } else {
+                // error check to provide a more verbose error on failure
+                throw \Exception("Unknown conditionalExpression in WhereInWalker");
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/DoctrineORMAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+use Doctrine\ORM\QueryBuilder;
+use Doctrine\ORM\Query;
+use Doctrine\ORM\NoResultException;
+
+/**
+ * DoctrineORMAdapter.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ *
+ * @api
+ */
+class DoctrineORMAdapter implements AdapterInterface
+{
+    /**
+     * @var Query
+     */
+    private $query;
+
+    private $fetchJoinCollection;
+
+    /**
+     * Constructor.
+     *
+     * @param Query|QueryBuilder $query               A Doctrine ORM query or query builder.
+     * @param Boolean            $fetchJoinCollection Whether the query joins a collection (false by default).
+     *
+     * @api
+     */
+    public function __construct($query, $fetchJoinCollection = false)
+    {
+        if ($query instanceof QueryBuilder) {
+            $query = $query->getQuery();
+        }
+
+        $this->query = $query;
+        $this->fetchJoinCollection = (Boolean) $fetchJoinCollection;
+    }
+
+    /**
+     * Returns the query
+     *
+     * @return Query
+     *
+     * @api
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Returns whether the query joins a collection.
+     *
+     * @return Boolean Whether the query joins a collection.
+     */
+    public function getFetchJoinCollection()
+    {
+        return $this->fetchJoinCollection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        /* @var $countQuery Query */
+        $countQuery = $this->cloneQuery($this->query);
+
+        $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Pagerfanta\Adapter\DoctrineORM\CountWalker'));
+        $countQuery->setFirstResult(null)->setMaxResults(null);
+
+        try {
+            $data =  $countQuery->getScalarResult();
+            $data = array_map('current', $data);
+            return array_sum($data);
+        } catch(NoResultException $e) {
+            return 0;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        if ($this->fetchJoinCollection) {
+            $subQuery = $this->cloneQuery($this->query);
+            $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Pagerfanta\Adapter\DoctrineORM\LimitSubqueryWalker'))
+                ->setFirstResult($offset)
+                ->setMaxResults($length);
+
+            $ids = array_map('current', $subQuery->getScalarResult());
+
+            $whereInQuery = $this->cloneQuery($this->query);
+            // don't do this for an empty id array
+            if (count($ids) > 0) {
+                $namespace = 'pg_';
+
+                $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Pagerfanta\Adapter\DoctrineORM\WhereInWalker'));
+                $whereInQuery->setHint('id.count', count($ids));
+                $whereInQuery->setHint('pg.ns', $namespace);
+                $whereInQuery->setFirstResult(null)->setMaxResults(null);
+                foreach ($ids as $i => $id) {
+                    $i++;
+                    $whereInQuery->setParameter("{$namespace}_{$i}", $id);
+                }
+            }
+
+            return $whereInQuery->getResult($this->query->getHydrationMode());
+        }
+
+        return $this->cloneQuery($this->query)
+            ->setMaxResults($length)
+            ->setFirstResult($offset)
+            ->getResult($this->query->getHydrationMode())
+        ;
+    }
+
+    /**
+     * Clones a query.
+     *
+     * @param Query $query The query.
+     *
+     * @return Query The cloned query.
+     */
+    private function cloneQuery(Query $query)
+    {
+        /* @var $cloneQuery Query */
+        $cloneQuery = clone $query;
+        $cloneQuery->setParameters($query->getParameters());
+        foreach ($query->getHints() as $name => $value) {
+            $cloneQuery->setHint($name, $value);
+        }
+
+        return $cloneQuery;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/MandangoAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+use Mandango\Query;
+
+/**
+ * MandangoAdapter.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class MandangoAdapter implements AdapterInterface
+{
+    private $query;
+
+    /**
+     * Constructor.
+     *
+     * @param Query $query The query.
+     *
+     * @api
+     */
+    public function __construct(Query $query)
+    {
+        $this->query = $query;
+    }
+
+    /**
+     * Returns the query.
+     *
+     * @return Query The query.
+     *
+     * @api
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        return $this->query->count();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        return $this->query->limit($length)->skip($offset)->all();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/NullAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+/**
+ * NullAdapter.
+ *
+ * @author Benjamin Dulau <benjamin.dulau@anonymation.com>
+ */
+class NullAdapter implements AdapterInterface
+{
+    private $nbResults;
+
+    /**
+     * Constructor.
+     *
+     * @param integer $nbResults Total item count.
+     *
+     * @api
+     */
+    public function __construct($nbResults = 0)
+    {
+        $this->nbResults = (int) $nbResults;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        return $this->nbResults;
+    }
+
+    /**
+     * The following methods are derived from code of the Zend Framework
+     * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+     *
+     * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+     *
+     * {@inheritdoc}
+     *
+     */
+    public function getSlice($offset, $length)
+    {
+        if ($offset >= $this->getNbResults()) {
+            return array();
+        }
+
+        $remainItemCount  = $this->getNbResults() - $offset;
+        $currentItemCount = $remainItemCount > $length ? $length : $remainItemCount;
+
+        return array_fill(0, $currentItemCount, null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Adapter/PropelAdapter.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Adapter;
+
+/**
+ * PropelAdapter.
+ *
+ * @author William DURAND <william.durand1@gmail.com>
+ */
+class PropelAdapter implements AdapterInterface
+{
+    private $query;
+
+    /**
+     * Constructor.
+     */
+    public function __construct($query)
+    {
+        $this->query = $query;
+    }
+
+    /**
+     * Returns the query.
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        $q = clone $this->getQuery();
+
+        return $q->limit(0)->offset(0)->count();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlice($offset, $length)
+    {
+        $q = clone $this->getQuery();
+
+        return $q->limit($length)->offset($offset)->find();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/Exception.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * Exception.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+interface Exception
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/InvalidArgumentException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * InvalidArgumentException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements Exception
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/LessThan1CurrentPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * LessThan1CurrentPageException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class LessThan1CurrentPageException extends NotValidCurrentPageException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/LessThan1MaxPerPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * LessThan1MaxPerPageException
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class LessThan1MaxPerPageException extends NotValidMaxPerPageException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/LogicException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * LogicException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class LogicException extends \LogicException implements Exception
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/NotIntegerCurrentPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * NotIntegerCurrentPageException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class NotIntegerCurrentPageException extends NotValidCurrentPageException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/NotIntegerMaxPerPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * NotIntegerMaxPerPageException
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class NotIntegerMaxPerPageException extends NotValidMaxPerPageException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/NotValidCurrentPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * NotValidCurrentPageException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class NotValidCurrentPageException extends InvalidArgumentException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/NotValidMaxPerPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * NotValidMaxPerPageException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class NotValidMaxPerPageException extends InvalidArgumentException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Exception/OutOfRangeCurrentPageException.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\Exception;
+
+/**
+ * OutOfRangeCurrentPageException.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ */
+class OutOfRangeCurrentPageException extends NotValidCurrentPageException
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/Pagerfanta.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,244 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta;
+
+use Pagerfanta\Adapter\AdapterInterface;
+use Pagerfanta\Exception\LogicException;
+use Pagerfanta\Exception\NotIntegerMaxPerPageException;
+use Pagerfanta\Exception\LessThan1MaxPerPageException;
+use Pagerfanta\Exception\NotIntegerCurrentPageException;
+use Pagerfanta\Exception\LessThan1CurrentPageException;
+use Pagerfanta\Exception\OutOfRangeCurrentPageException;
+
+/**
+ * Pagerfanta.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class Pagerfanta implements PagerfantaInterface, \IteratorAggregate
+{
+    private $adapter;
+    private $maxPerPage;
+    private $currentPage;
+    private $currentPageResults;
+    private $nbResults;
+    private $nbPages;
+
+    /**
+     * Constructor.
+     *
+     * @param AdapterInterface $adapter    An adapter.
+     * @param integer          $maxPerPage The maximum of results per page.
+     *
+     * @api
+     */
+    public function __construct(AdapterInterface $adapter)
+    {
+        $this->adapter = $adapter;
+        $this->maxPerPage = 10;
+        $this->currentPage = 1;
+    }
+
+    /**
+     * Returns the adapter.
+     *
+     * @return AdapterInterface The adapter.
+     *
+     * @api
+     */
+    public function getAdapter()
+    {
+        return $this->adapter;
+    }
+
+    /**
+     * This method implements a fluent interface.
+     *
+     * {@inheritdoc}
+     */
+    public function setMaxPerPage($maxPerPage)
+    {
+        // tries to normalize from string to integer
+        if (is_string($maxPerPage) && (int) $maxPerPage == $maxPerPage) {
+            $maxPerPage = (int) $maxPerPage;
+        }
+
+        if (!is_int($maxPerPage)) {
+            throw new NotIntegerMaxPerPageException();
+        }
+
+        if ($maxPerPage < 1) {
+            throw new LessThan1MaxPerPageException();
+        }
+
+        $this->currentPageResults = null;
+        $this->nbPages = null;
+        $this->maxPerPage = $maxPerPage;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMaxPerPage()
+    {
+        return $this->maxPerPage;
+    }
+
+    /**
+     * This method implements a fluent interface.
+     *
+     * {@inheritdoc}
+     */
+    public function setCurrentPage($currentPage, $allowOutOfRangePages = false, $normalizeOutOfRangePages = false)
+    {
+        // tries to normalize from string to integer
+        if ((is_string($currentPage) || is_float($currentPage)) && (int) $currentPage == $currentPage) {
+            $currentPage = (int) $currentPage;
+        }
+
+        // integer?
+        if (!is_int($currentPage)) {
+            throw new NotIntegerCurrentPageException();
+        }
+
+        // less than 1?
+        if ($currentPage < 1) {
+            $currentPage = 1;
+        }
+
+        // out of range pages
+        if (!$allowOutOfRangePages && $currentPage > 1) {
+            if ($currentPage > $this->getNbPages()) {
+                if (!$normalizeOutOfRangePages) {
+                    throw new OutOfRangeCurrentPageException();
+                }
+
+                $currentPage = $this->getNbPages();
+            }
+        }
+
+        $this->currentPageResults = null;
+        $this->currentPage = $currentPage;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCurrentPage()
+    {
+        return $this->currentPage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCurrentPageResults()
+    {
+        if (null === $this->currentPageResults) {
+            $offset = ($this->getCurrentPage() - 1) * $this->getMaxPerPage();
+            $length = $this->getMaxPerPage();
+            $this->currentPageResults = $this->adapter->getSlice($offset, $length);
+        }
+
+        return $this->currentPageResults;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbResults()
+    {
+        if (null === $this->nbResults) {
+            $this->nbResults = $this->getAdapter()->getNbResults();
+        }
+
+        return $this->nbResults;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNbPages()
+    {
+        if (null === $this->nbPages) {
+            $this->nbPages = (int) ceil($this->getNbResults() / $this->getMaxPerPage());
+        }
+
+        return $this->nbPages;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function haveToPaginate()
+    {
+        return $this->getNbResults() > $this->maxPerPage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasPreviousPage()
+    {
+        return $this->currentPage > 1;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPreviousPage()
+    {
+        if (!$this->hasPreviousPage()) {
+            throw new LogicException('There is not previous page.');
+        }
+
+        return $this->currentPage - 1;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasNextPage()
+    {
+        return $this->currentPage < $this->getNbPages();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNextPage()
+    {
+        if (!$this->hasNextPage()) {
+            throw new LogicException('There is not next page.');
+        }
+
+        return $this->currentPage + 1;
+    }
+
+    /**
+     * Implements the \IteratorAggregate interface.
+     *
+     * Returns an \ArrayIterator instance with the current results.
+     *
+     * @api
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->getCurrentPageResults());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/PagerfantaInterface.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,145 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta;
+
+/**
+ * PagerfantaInterface.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+interface PagerfantaInterface
+{
+    /**
+     * Sets the max per page.
+     *
+     * @param integer $maxPerPage The max per page.
+     *
+     * @api
+     */
+    function setMaxPerPage($maxPerPage);
+
+    /**
+     * Returns the max per page.
+     *
+     * Tries to normalize from string to integer.
+     *
+     * @return integer The max per page.
+     *
+     * @throws NotIntegerMaxPerPageException If the max per page is not an integer even normalizing.
+     * @throws LessThan1MaxPerPageException  If the max per page is less than 1.
+     *
+     * @api
+     */
+    function getMaxPerPage();
+
+    /**
+     * Sets the current page.
+     *
+     * @param integer $currentPage              The current page.
+     * @param Boolean $allowOutOfRangePages     Whether to allow out of range pages or not (false by default).
+     * @param Boolean $normalizeOutOfRangePages Whether to show the last page instead (false by default).
+     *
+     * @throws NotIntegerCurrentPageException If the current page is not an integer even normalizing.
+     * @throws LessThan1CurrentPageException  If the current page is less than 1.
+     * @throws OutOfRangeCurrentPageException If It is not allowed out of range pages and they are not normalized.
+     *
+     * @api
+     */
+    function setCurrentPage($currentPage);
+
+    /**
+     * Returns the current page.
+     *
+     * @return integer The current page.
+     *
+     * @api
+     */
+    function getCurrentPage();
+
+    /**
+     * Returns the results for the current page.
+     *
+     * @return array The results.
+     *
+     * @api
+     */
+    function getCurrentPageResults();
+
+    /**
+     * Returns the number of results.
+     *
+     * @return integer The number of results.
+     *
+     * @api
+     */
+    function getNbResults();
+
+    /**
+     * Returns the number of pages.
+     *
+     * @return integer The number of pages.
+     *
+     * @api
+     */
+    function getNbPages();
+
+    /**
+     * Returns whether have to paginate or not.
+     *
+     * This is true if the number of results is higher than the max per page.
+     *
+     * @return Boolean Whether have to paginate or not.
+     */
+    function haveToPaginate();
+
+    /**
+     * Returns whether there is previous page or not.
+     *
+     * @return Boolean Whether there is previous page or not.
+     *
+     * @api
+     */
+    function hasPreviousPage();
+
+    /**
+     * Returns the previous page.
+     *
+     * @return integer The previous page.
+     *
+     * @throws PagerfantaException If there is not previous page.
+     *
+     * @api
+     */
+    function getPreviousPage();
+
+    /**
+     * Returns whether there is next page or not.
+     *
+     * @return Boolean Whether there is previous page or not.
+     *
+     * @api
+     */
+    function hasNextPage();
+
+    /**
+     * Returns the next page.
+     *
+     * @return integer The next page.
+     *
+     * @throws PagerfantaException If there is not next page.
+     *
+     * @api
+     */
+    function getNextPage();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/DefaultView.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,182 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+
+/**
+ * DefaultInterface.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class DefaultView implements ViewInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array())
+    {
+        $options = array_merge(array(
+            'proximity'          => 2,
+            'previous_message'   => 'Previous',
+            'next_message'       => 'Next',
+            'css_disabled_class' => 'disabled',
+            'css_dots_class'     => 'dots',
+            'css_current_class'  => 'current',
+        ), $options);
+
+        $currentPage = $pagerfanta->getCurrentPage();
+
+        $startPage = $currentPage - $options['proximity'];
+        $endPage = $currentPage + $options['proximity'];
+
+        if ($startPage < 1) {
+            $endPage = min($endPage + (1 - $startPage), $pagerfanta->getNbPages());
+            $startPage = 1;
+        }
+        if ($endPage > $pagerfanta->getNbPages()) {
+            $startPage = max($startPage - ($endPage - $pagerfanta->getNbPages()), 1);
+            $endPage = $pagerfanta->getNbPages();
+        }
+
+        $pages = array();
+
+        // previous
+        if ($pagerfanta->hasPreviousPage()) {
+            $pages[] = array($pagerfanta->getPreviousPage(), $options['previous_message']);
+        } else {
+            $pages[] = sprintf('<span class="%s">%s</span>', $options['css_disabled_class'], $options['previous_message']);
+        }
+
+        // first
+        if ($startPage > 1) {
+            $pages[] = array(1, 1);
+            if (3 == $startPage) {
+                $pages[] = array(2, 2);
+            } elseif (2 != $startPage) {
+                $pages[] = sprintf('<span class="%s">...</span>', $options['css_dots_class']);
+            }
+        }
+
+        // pages
+        for ($page = $startPage; $page <= $endPage; $page++) {
+            if ($page == $currentPage) {
+                $pages[] = sprintf('<span class="%s">%s</span>', $options['css_current_class'], $page);
+            } else {
+                $pages[] = array($page, $page);
+            }
+        }
+
+        // last
+        if ($pagerfanta->getNbPages() > $endPage) {
+            if ($pagerfanta->getNbPages() > ($endPage + 1)) {
+                if ($pagerfanta->getNbPages() > ($endPage + 2)) {
+                    $pages[] = sprintf('<span class="%s">...</span>', $options['css_dots_class']);
+                } else {
+                    $pages[] = array($endPage + 1, $endPage + 1);
+                }
+            }
+
+            $pages[] = array($pagerfanta->getNbPages(), $pagerfanta->getNbPages());
+        }
+
+        // next
+        if ($pagerfanta->hasNextPage()) {
+            $pages[] = array($pagerfanta->getNextPage(), $options['next_message']);
+        } else {
+            $pages[] = sprintf('<span class="%s">%s</span>', $options['css_disabled_class'], $options['next_message']);
+        }
+
+        // process
+        $pagesHtml = '';
+        foreach ($pages as $page) {
+            if (is_string($page)) {
+                $pagesHtml .= $page;
+            } else {
+                $pagesHtml .= '<a href="'.$routeGenerator($page[0]).'">'.$page[1].'</a>';
+            }
+        }
+
+        return '<nav>'.$pagesHtml.'</nav>';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'default';
+    }
+}
+
+/*
+
+CSS:
+
+.pagerfanta {
+}
+
+.pagerfanta a,
+.pagerfanta span {
+    display: inline-block;
+    border: 1px solid blue;
+    color: blue;
+    margin-right: .2em;
+    padding: .25em .35em;
+}
+
+.pagerfanta a {
+    text-decoration: none;
+}
+
+.pagerfanta a:hover {
+    background: #ccf;
+}
+
+.pagerfanta .dots {
+    border-width: 0;
+}
+
+.pagerfanta .current {
+    background: #ccf;
+    font-weight: bold;
+}
+
+.pagerfanta .disabled {
+    border-color: #ccf;
+    color: #ccf;
+}
+
+COLORS:
+
+.pagerfanta a,
+.pagerfanta span {
+    border-color: blue;
+    color: blue;
+}
+
+.pagerfanta a:hover {
+    background: #ccf;
+}
+
+.pagerfanta .current {
+    background: #ccf;
+}
+
+.pagerfanta .disabled {
+    border-color: #ccf;
+    color: #cf;
+}
+
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/OptionableView.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+
+/**
+ * OptionableView.
+ *
+ * This view renders another view with a default options to reuse them in a project.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class OptionableView implements ViewInterface
+{
+    private $view;
+    private $defaultOptions;
+
+    /**
+     * Constructor.
+     *
+     * @param ViewInterface $view    A view.
+     * @param array         $options An array of default options (optional).
+     *
+     * @api
+     */
+    public function __construct(ViewInterface $view, array $defaultOptions = array())
+    {
+        $this->view = $view;
+        $this->defaultOptions = $defaultOptions;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array())
+    {
+        return $this->view->render($pagerfanta, $routeGenerator, array_merge($this->defaultOptions, $options));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'optionable';
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/TwitterBootstrapView.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,133 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+
+/**
+ * TwitterBootstrapView.
+ *
+ * View that can be used with the pagination module 
+ * from the Twitter Bootstrap CSS Toolkit
+ * http://twitter.github.com/bootstrap/
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ * @author Jan Sorgalla <jsorgalla@gmail.com>
+ *
+ * @api
+ */
+class TwitterBootstrapView implements ViewInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array())
+    {
+        $options = array_merge(array(
+            'proximity'           => 3,
+            'prev_message'        => '&larr; Previous',
+            'prev_disabled_href'  => '',
+            'next_message'        => 'Next &rarr;',
+            'next_disabled_href'  => '',
+            'dots_message'        => '&hellip;',
+            'dots_href'           => '',
+            'css_container_class' => 'pagination',
+            'css_prev_class'      => 'prev',
+            'css_next_class'      => 'next',
+            'css_disabled_class'  => 'disabled',
+            'css_dots_class'      => 'disabled',
+            'css_active_class'    => 'active',
+        ), $options);
+
+        $currentPage = $pagerfanta->getCurrentPage();
+
+        $startPage = $currentPage - $options['proximity'];
+        $endPage = $currentPage + $options['proximity'];
+
+        if ($startPage < 1) {
+            $endPage = min($endPage + (1 - $startPage), $pagerfanta->getNbPages());
+            $startPage = 1;
+        }
+        if ($endPage > $pagerfanta->getNbPages()) {
+            $startPage = max($startPage - ($endPage - $pagerfanta->getNbPages()), 1);
+            $endPage = $pagerfanta->getNbPages();
+        }
+
+        $pages = array();
+
+        // previous
+        $class = $options['css_prev_class'];
+        $url   = $options['prev_disabled_href'];
+        if (!$pagerfanta->hasPreviousPage()) {
+            $class .= ' '.$options['css_disabled_class'];
+        } else {
+            $url = $routeGenerator($pagerfanta->getPreviousPage());
+        }
+
+        $pages[] = sprintf('<li class="%s"><a href="%s">%s</a></li>', $class, $url, $options['prev_message']);
+
+
+        // first
+        if ($startPage > 1) {
+            $pages[] = sprintf('<li><a href="%s">%s</a></li>', $routeGenerator(1), 1);
+            if (3 == $startPage) {
+                $pages[] = sprintf('<li><a href="%s">%s</a></li>', $routeGenerator(2), 2);
+            } elseif (2 != $startPage) {
+                $pages[] = sprintf('<li class="%s"><a href="%s">%s</a></li>', $options['css_dots_class'], $options['dots_href'], $options['dots_message']);
+            }
+        }
+
+        // pages
+        for ($page = $startPage; $page <= $endPage; $page++) {
+            $class = '';
+            if ($page == $currentPage) {
+                $class = sprintf(' class="%s"', $options['css_active_class']);
+            }
+
+            $pages[] = sprintf('<li%s><a href="%s">%s</a></li>', $class, $routeGenerator($page), $page);
+        }
+
+        // last
+        if ($pagerfanta->getNbPages() > $endPage) {
+            if ($pagerfanta->getNbPages() > ($endPage + 1)) {
+                if ($pagerfanta->getNbPages() > ($endPage + 2)) {
+                    $pages[] = sprintf('<li class="%s"><a href="%s">%s</a></li>', $options['css_dots_class'], $options['dots_href'], $options['dots_message']);
+                } else {
+                    $pages[] = sprintf('<li><a href="%s">%s</a></li>', $routeGenerator($endPage + 1), $endPage + 1);
+                }
+            }
+
+            $pages[] = sprintf('<li><a href="%s">%s</a></li>', $routeGenerator($pagerfanta->getNbPages()), $pagerfanta->getNbPages());
+        }
+
+        // next
+        $class = $options['css_next_class'];
+        $url   = $options['next_disabled_href'];
+        if (!$pagerfanta->hasNextPage()) {
+            $class .= ' '.$options['css_disabled_class'];
+        } else {
+            $url = $routeGenerator($pagerfanta->getNextPage());
+        }
+
+        $pages[] = sprintf('<li class="%s"><a href="%s">%s</a></li>', $class, $url, $options['next_message']);
+
+        return sprintf('<div class="%s"><ul>%s</ul></div>', $options['css_container_class'], implode('', $pages));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'twitter_bootstrap';
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/ViewFactory.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+use Pagerfanta\Exception\InvalidArgumentException;
+
+/**
+ * ViewFactory.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+class ViewFactory implements ViewFactoryInterface
+{
+    private $views;
+
+    /**
+     * Constructor.
+     *
+     * @api
+     */
+    public function __construct()
+    {
+        $this->views = array();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function set($name, ViewInterface $view)
+    {
+        $this->views[$name] = $view;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function has($name)
+    {
+        return isset($this->views[$name]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add(array $views)
+    {
+        foreach ($views as $name => $view) {
+            $this->set($name, $view);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($name)
+    {
+        if (!$this->has($name)) {
+            throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name));
+        }
+
+        return $this->views[$name];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($name)
+    {
+        if (!$this->has($name)) {
+            throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name));
+        }
+
+        unset($this->views[$name]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function all()
+    {
+        return $this->views;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function clear()
+    {
+        $this->views = array();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/ViewFactoryInterface.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+
+/**
+ * ViewFactoryInterface.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+interface ViewFactoryInterface
+{
+    /**
+     * Sets a view.
+     *
+     * @param string        $name The view name.
+     * @param ViewInterface $view The view.
+     */
+    function set($name, ViewInterface $view);
+
+    /**
+     * Returns whether a view exists or not.
+     *
+     * @param string $name The name.
+     *
+     * @return Boolean Whether a view exists or not.
+     *
+     * @api
+     */
+    function has($name);
+
+    /**
+     * Adds views.
+     *
+     * @param array $views An array of views.
+     *
+     * @api
+     */
+    function add(array $views);
+
+    /**
+     * Returns a view.
+     *
+     * @param string $name The name.
+     *
+     * @return ViewInterface The view.
+     *
+     * @throws \InvalidArgumentException If the view does not exist.
+     *
+     * @api
+     */
+    function get($name);
+
+    /**
+     * Returns all the views.
+     *
+     * @return array The views.
+     */
+    function all();
+
+    /**
+     * Removes a view.
+     *
+     * @param string $name The name.
+     *
+     * @throws \InvalidArgumentException If the view does not exist.
+     *
+     * @api
+     */
+    function remove($name);
+
+    /**
+     * Clears the views.
+     *
+     * @api
+     */
+    function clear();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/bundles/Pagerfanta/View/ViewInterface.php	Fri Oct 21 17:10:54 2011 +0200
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Pagerfanta package.
+ *
+ * (c) Pablo Díez <pablodip@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Pagerfanta\View;
+
+use Pagerfanta\PagerfantaInterface;
+
+/**
+ * ViewInterface.
+ *
+ * @author Pablo Díez <pablodip@gmail.com>
+ *
+ * @api
+ */
+interface ViewInterface
+{
+    /**
+     * Renders a pagerfanta.
+     *
+     * The route generator is any callable to generate the routes receiving the page number
+     * as first and unique argument.
+     *
+     * @param PagerfantaInterface $pagerfanta      A pagerfanta.
+     * @param mixed               $routeGenerator A callable to generate the routes.
+     * @param array               $options        An array of options (optional).
+     *
+     * @api
+     */
+    function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array());
+
+    /**
+     * Returns the canonical name.
+     *
+     * @return string The canonical name.
+     *
+     * @api
+     */
+    function getName();
+
+}