Friday 31 January 2014

CakePHP 2.x: Saving paging data in the session

Quite some time ago I wrote a blog post about saving CakePHP 1.x paging data in the session so that they can be available at next page visit. The basic idea was that you could store the page, sort and direction named parameters in the session and restore them back when no paging parameters were available.

When I tried applying the same technique in CakePHP 2.x, I run into a very serious obstacle and that was the fact that the Paginator::numbers() function does not anymore include the page:1 named parameter when creating the link for the first (or previous) page. This created a phenomenon that when someone visited a page without any paging parameters, it was impossible to know whether they were there because of a link to the first page or as a result of a redirect from somewhere else, in which case the session had to be checked and paging data to be restored.

The thing had been puzzling me for some days now. I tried different remedies without any luck or results until I cried for help at stackoverflow.com. It was there that Ilie Pandia shook things a bit and then I managed, thanks to his advice, to create the following component that mimics the original Cake 1.x PaginationRecallComponent by mattc found in the Bakery, from which I borrowed the name and structure.

This version is new (April-2015) and hopefully includes the bug-fixes pointed out by all comments. The tests where made using CakePHP version 2.6.3.

<?php
App::uses('Component', 'Controller');

/**
 * Pagination Recall CakePHP Component
 * CakePHP 2.x version. Thanassis Bakalidis abakalidis.blogspot.com=
 *
 * @author  Thanassis Bakalidis
 * @version  2.2
 * @license  MIT
 * @property SessionComponent $Sesion Session handler to save paging data into
 */
class PaginationRecallComponent extends Component {
    const PREV_DATA_KEY = 'Paginaion-PrevData';

    public $components = ['Session'];
    private $_controller = NULL;
    private $_action = NULL;
    private $_previousUrl;

    public function initialize(\Controller $controller)
    {
        $this->_controller = $controller;
        $this->_action = $controller->params['action'];
    }

    public function startup(Controller $controller)
    {
        if ($this->_controller->name === 'CakeError')
            return;

        $this->_restorePagingParams();

        // save the current controller and action for the next time
        $this->Session->write(
            self::PREV_DATA_KEY,
            [
                'controller' => $this->_controller->name,
                'action' => $this->_action
            ]
        );
    }

    private function _restorePagingParams()
    {
        $sessionKey = "Pagination.{$this->_controller->name}.{$this->_action}";

        // extract paging data from the request parameters
        $pagingParams = $this->_extractPagingParams();

        // if paging data exist write them in the session
        if (!empty($pagingParams)) {
            $this->Session->write( $sessionKey, $pagingParams);
            return;
        }

        // no paging data.
        // construct the previous URL
        $this->_previousUrl = $this->Session->check(self::PREV_DATA_KEY)
            ? $this->Session->read(self::PREV_DATA_KEY)
            : [
                'controller' => '',
                'action' => ''
            ];

        // and check if the current page is the same as the previous
        if ($this->_previousUrl['controller'] === $this->_controller->name &&
            $this->_previousUrl['action'] === $this->_action) {
            // in this case we have a link from our own paging::numbers() function
            // to move to page 1 pf the current page, delete any paging data
            $this->Session->delete($sessionKey);
            return;
        }

        // we are comming from a different page so if we have any session data
        if ($this->Session->check($sessionKey))
            // then restore and use them
            $this->_controller->request->params['named'] = array_merge(
                $this->_controller->request->params['named'],
                $this->Session->read($sessionKey)
            );
    }

    private function _extractPagingParams()
    {
        $pagingParams = $this->_controller->request->params['named'];
        $vars = ['page', 'sort', 'direction'];
        $keys = array_keys($pagingParams);
        $count = count($keys);

        for ($i = 0; $i < $count; $i++)
            if (!in_array($keys[$i], $vars))
                unset($pagingParams[$keys[$i]]);

        return $pagingParams;
    }
}

Note: It turns out that the components shutdown method is not called when the owner controller's action returns a redirect(). Hence, this last update was about getting rid of any shutdown() functionality and performing everything in the components startup() moethod code. This hopefully, fixes the bug of loosing paging data after a call to the delete() method which returns an immediate redirect..

Sunday 12 January 2014

Enable MP3 on Fedora 20

A very short how to enable mp3 playback on your brand new fedora installation.

  1. Enable PRM Fusion repository.
  2. Install the vlc phonon-backend. (Fedora comes with GStreamer).
    . yum install phonon-backend-vlc
  3. Goto system settings → Multimedia → Audio and Video Settings. Select the backend tab. There should be two entries there: GStreamer and VLC. Select the VLC entry and click the Prefer buttom below the list. Finally click Apply. The final result should be something like this:
  4. Log out and back in for the changes to take effect.

After that you can play and kind of context directly from Dolphin, you can install additional music players like amarok, or vlc and enjoy the full music, audio and video experence.

yum install vlc
yum install amarok amarok-doc