Wednesday 15 October 2014

Listbox mnipulation with jQuery

This is my easy to paste reference code for list-box manipulation using jQuery. By the way, the HTML <input "type"="select"> element, will create what desktop programmers usually refer as a combo-box. To make the input display as a list box, we need to specify an additional size="N" attribute where N refers to the number of visible items of the list.

So, suppose that we have a select input somewhere in an HTML page and that we need to add a button that removes the selected item from the input's options list.

       // remove the selected item from the list box
       jQuery('#deleteButton').click(function(){
            jQuery("#myListBox option:selected").remove();
            return false;
        });

By the same token, we can also create an other button that empties the entire options list

        // remove all items from list box
        jQuery('#deleteAllButton').click(function(){
            // check that there is something to remove
            if (jQuery("#myListBox option").length == 0)
                return false;

            // verify that user really wants to do this
            if (!confirm(''))
                return false;

            // empty list box
            jQuery("#myListBox option").remove();
            return false;
        });
    });

Finally, to add a new item at the end of the options box we can use a code fragment that looks more or less like this:

                    var newOptionhtml ='<option value="'
                        + response.id
                        + '">'
                        + response.name
                        + ' </option>';

                    jQuery("#myListBox").append(newOptionhtml);

Needles to say that all the above have been collected from various Stack Overflow questions and answers.

Saturday 13 September 2014

GNOME3: How to enable desktop icons and right click menu

This is the third time I am looking this up, so I am making a quick howto note here.

So in order to enable desktop icons on a newly installed gnome3 desktop perform the following steps:

  1. Open the dconf-editor program.
  2. Locate the page following path org → gnome → desktop → background
  3. Check the Show desktop icons option.

Monday 14 July 2014

CakePHP Find out the link that brough us to the current page

Most web applications today have some kind of menu build with anchor tags. The problem that the following script -- courtesy of my friend Anestis from alfasoftware.gr -- solves, is how to determine the actual link that was used to bring the user to the currently displayed page.

To use it, place it inside a script tag towards the end of your app/Views/Layout/default.ctp file (just before the closing body tag, will do just fine) and a little bit of CSS code in your appropriate style file (again app/webroot/css/cake.generic.css is a good candidate).

The CSS code is pretty easy :

a.active {
    background-color: yellow; // add anything you like
}

and the javascript :


        <script type="text/javascript">
            // add the "active" class to the navigation link that brought us to this page
            jQuery( function() {
                // retrieve the relative url of the current page
                var curUrl = "<?php echo (Router::url( NULL, FALSE)); ?>";
                // in a cake application a relative URL is usually like
                // /application/controller/action/param1/param2 ...
                // so what we really need is the first three pieses of the URL
                var tokens = curUrl.split("/").slice(0,4);
                curUrl = tokens.join("/");
                // if there is any pagination information then the word "index" also appears in the URL
                // in this case we need to remove it, so it can match generated URL
                var indexIndex = curUrl.indexOf('index');
                if ( indexIndex > -1)
                    curUrl = curUrl.substring( 0, indexIndex - 1);

                // for each page anchor tag
                $('a').each( function() {
                    // retrieve the arnchor's target
                    var ref = $(this).attr("href");
                    // so if that anchor points to the current page
                    if(ref === curUrl){
                        $(this).addClass("active");
                    }

                });
            });
        </script>

Enjoy!

Sunday 18 May 2014

How to remove a particular host key from SSH's ./ssh/known_hosts file

I will leave the sed solution for the real gurus. As far as I am concerned the easy way to get the job down is use the ssh-keygen -R your_host command.

If you are working in a environment with a DNS suffix, then you will probably need a second invocation like: ssh-keygen -R your_host.your_domain.

Friday 4 April 2014

Setting up Environment variables before Apache starts

That was the third time I had to look this up, so I am putting it down for reference.

On Red Hat machines, apachectl sources /etc/sysconfig/httpd, so any variables you set there will be available to the apache runtime environment. Debian based systems use /etc/apache2/envvars for the same purpose.

Thursday 3 April 2014

Linux (EL6) Required packages for installing the 64 bit Oracle 12c Client

The Oracle documentation has a list of the packages required in order to install the database client software for Enterprise Linux 6.

Since the minimum versions requirements are satisfied on an updated EL6 system, all one has to do is just copy and paste the names into a single yum command in order to complete the step. Did it once this morning and I hope that if I ever have to repeat it, I will just copy and paste the following:

yum -y install binutils compat-libcap1 compat-libstdc++-33.i686 compat-libstdc++-33.x86_64 \
gcc gcc-c++ glibc.i686 glibc.x86_64 glibc-devel.x86_64 glibc-devel.i686 \
ksh libgcc.i686 libgcc.x86_64 \
libstdc++.x86_64 libstdc++.i686 libstdc++-devel.x86_64 libstdc++-devel.i686 \
libaio.x86_64 libaio.i686 libaio-devel.x86_64 libaio-devel.i686 \
libXext.x86_64 libXext.i686 libXtst.x86_64 libXtst.i686 \
libX11.x86_64 libX11.i686 \
libXau.x86_64 libXau.i686 \
libxcb.x86_64 libxcb.i686 \
libXi.x86_64 libXi.i686 \
make sysstat

PS: If not running this as root, remember to prefix this with sudo :)

Thursday 20 March 2014

CakePHP-2 and AJAX The dependent list boxes problem (remake)

Forward

Back in 2010, I had written a post regarding the case of dependent list boxes in a CakePHP view. Back at those days CakePHP was at version 1.2 and support for Javascript and AJAX was very limited. Today I shall revise this using JQuery and JSON encoding which will make things simpler and easier to understand, implement and maintain.

The long story short

Suppose you have a page with two list boxes. One contains a standard set of values while the second one's list of values must be dynamically updated depending on the actual selected value of the first.

In our (sort of) real life example we have a list of commissions (aka production orders) that produce a series of products of varying lengths. The list of lengths that each commission is allowed to produce is available via a detail table and our goal is write an addProduct action and view that allows the user to specify the commission based on which the actual product was produced and the actual length of the product that should be one of the assigned commission lengths. The Product model has a commission_id and an actual_length fields. So each time the commission combo box changes the actual length field input options should also change in order to contain the commissions list of allowed lengths.

Getting Started

To begin with our tutorial make sure that your standard layout references the jQuery library. The easiest was to do this would be to open APP/View/Layout/default.ctp and make sure that a line like

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>

can be found somewhere in your html's head section.

The next thing that needs to be done is to add the RequestHandler component in our AppController. Add or modify your existing source so it looks more or less like this.

class AppController extends Controller {
    public $components = array(
        'RequestHandler', 
        ...
    );

    ,,,
}

Next we need to inform our routing system that it should also parse json URLs. So the line

Router::parseExtensions('json');

should be added to the APP/Config/routes.php file right below the other Router::xxx() commands.

Create the AJAX method and view to return the JSON encoded list of lengths

The next thing to do is create the action that will return the list of lengths to be sent back to the view given commission id. The best place to put it will be the commissions controller. The code for the function looks a lot like the one baked by view. We just need to make sure that the data returned "contain" the correct detail information

    public function getRequestedLengths($id)
    {
        if (!$this->Commission->exists($id)) 
            throw new NotFoundException(__('Invalid record'));
        
        $options = array(
            'conditions' => array(
                'Commission.' . $this->Commission->primaryKey => $id
            ),
            'contain' => array(
                'CommissionProduct',
            )
        );
        $this->set('commission', $this->Commission->find('first', $options));
    }

The view for the method should be placed in: APP/View/Commissions/json/get_requested_lengths.ctp.

<?php
/*
 * Create and echo a json encoded list of the allowed lengths
 ^/
$output = array();
if (!empty($commission['CommissionProduct']))
    foreach ($commission['CommissionProduct'] as $commissionProduct)
        $output[] = $commissionProduct['actual_length'];


echo json_encode($output);

So now if we point our browser towards http://yourserver/projectPath/Commissions/getRequestedLengths/7.json, we will receive a response with a JSON array containing all allowed lengths for commission id 7. The actual response will be something like ["45900","23400"].

Building the addProduct view

So far we have the mechanism to retrieve the required data. The final step will be to use it in the actual add product view, the PHP part of which should look more or less like this:

<div class="products form">
    <?php echo $this->Form->create('Product'); ?>
        <fieldset>
            <legend>Add Product</legend>
            <?php echo $this->Form->input('commission_id', array('empty' => __('Please select a commission'))); ?>
            <?php echo $this->Form->input('operator_id'); ?>
            <?php echo $this->Form->input('shift'); ?>
            <?php
                echo $this->Form->input(
                'status',
                array(
                    'type' => 'select',
                    'options' => $statusList
                )
                );
            ?>
            <?php echo $this->Form->input('actual_length', array('type' => 'select')); ?>
            <?php echo $this->Form->input('gross_weight'); ?>
        </fieldset>
    <?php echo $this->Form->end('Save'); ?>
</div>

The final part will be the adding of Javascript code to make our form responsive.

<script type="text/javascript">
    var commissionsCombo;
    var allowedLengthsCombo;

    jQuery(function() {
        commissionsCombo = jQuery('#ProductCommissionId');
        allowedLengthsCombo = jQuery('#ProductActualLength');

        commissionsCombo.change( function() {
            var selectedCommission = this.value;  // or $(this).val()
            // build the url that contained the selected commission code
            var ajaxUrl =
                    '<?php echo Router::url(array('controller' => 'Commissions', 'action' => 'getRequestedLengths', 'admin' => FALSE), TRUE)?>'
                    + '/'
                    + selectedCommission
                    + '.json';

            // do a "synchronous" AJAX call
            jQuery.ajax({
                    type:'GET',
                    async: false,
                    cache: false,
                    url: ajaxUrl,
                    success: function(response) {
                        // remove all options from the allowed metres per bobbin
                        allowedLengthsCombo.find('option').remove();

                        // add all returned values
                        for (var i = 0; i < response.length; i++) {
                            var currentKey = response[i];
                            var currentKeyDescr = response[i] + ' metres';
                            var optionText = '<option value="'
                                    + currentKey
                                    + '">'
                                    + currentKeyDescr
                                    + '</option>';
                            allowedLengthsCombo.append(optionText);
                        }
                    }
            });
        });
    });
</script>

As a last statement The code for manipulating the select input options comes from the very consise post from StackOverflow.com

Thursday 13 March 2014

CentOS 6.5 does not like my handware or their combionation

Spent the whole morning trying to install CentOS 6.5 on a relatively new machine using a Gigabyte GA-B85-HD3 motherboard, with a NVIDIA GeForce GT 620. The message I got was that the hardware (or a combination of what) I 'm using is incompatible with CentOS.

As I had Fedora 20 already running on this very machine, I figured that the problem was related to UEFI and wasted a lot of time trying to the get rid of the EFI partition on the Linux disk. Eventually after updating the BIOS and trying all sorts of magic. I disabled the on board Intel graphics adapter and everything worked like a charm.

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