Monthly Archives: May 2014

Step the music, out cool

chill
Relax music for working well?
The great online music service in minimalistic turn is chillstep!
Go to chillstep.info.
Enjoy!

Primefaces DataTable sorting with Ajax request

pf_datatable_sort
You can be noticed when apply ajax request datatable sorting doesn’t work. This can when you are using for ex. a dialog with datatable inside.
I’m using PrimeFaces 3.4.2 version and found solution at http://stackoverflow.com/a/14376094.
So, here it is:

1. Button opens dialog:

<p:commandButton update=":form:selectedItems" 
    oncomplete="details.show(); selectedItemsWidget.sort($(PrimeFaces.escapeClientId('#{p:component('selectedItemName')}')), 'ASCENDING')">
        <f:setPropertyActionListener value="#{item}" target="#{SomeBean.selected}" />
</p:commandButton>

2. In dialog:

<p:hotkey bind="esc" handler="details.hide()"/>
<p:dialog id="dialog" header="Items" widgetVar="details" modal="true" showEffect="fade" hideEffect="fade" width="700"
          onShow="$('#Form\\:dialog_title').focus();">
    <p:dataTable id="selectedItems" widgetVar="selectedItemsWidget" value="#{SomeBean.selected.items}" var="item">
        <p:column id="selectedItemName" headerText="Name" sortBy="#{item.name}">
            <h:outputText value="#{item.name}"/>
        </p:column>

        <p:column headerText="Type" sortBy="#{item.itemType}">
            <h:outputText value="#{item.itemType}"/>
        </p:column>

        <p:column headerText="Status" sortBy="#{item.status}">
            <h:outputText value="#{item.status}"/>
        </p:column>
    </p:dataTable>
</p:dialog>

So, the solution is call sorting yourself:

selectedItemsWidget.sort($(PrimeFaces.escapeClientId('#{p:component('selectedItemName')}')), 'ASCENDING')

Great hack, thanx truemmer!

Web

Application configuration in AngularJS

angcfg
If you are developing angular web-application, you ask how to involve configuration in it. I propose next pattern: create constant service and inject it in your other modules, services etc.
About constant in AngularJS.

1. Create app module as app.js file:

var app = angular.module('app', []);

2. Define constant service as config.js file:

app.constant('config', {
    apiUrl: 'http://localhost/api/v1',
});

3. Get your service with injected config as services.js file:

app.factory('userService', ['$http', 'config',
    function ($http, config) {
        return {
            register: function (email, password) {
                return $http({
                    url: config.apiUrl + '/user',
                    method: 'POST',
                    data: {
                        'Email': email,
                        'Password': password
                    }
                });
            },
            login: function (email, password) {
                return $http({
                    url: config.apiUrl + '/auth',
                    method: 'POST',
                    data: {
                        'Email': email,
                        'Password': password
                    }
                });
            },
            logout: function () {
                return $http({
                    url: config.apiUrl + '/auth',
                    method: 'DELETE'
                });
            },
            checkAuth: function () {
                return $http({
                    url: config.apiUrl + '/auth',
                    method: 'GET'
                });
            }
        };
}]);

So, config service is a singleton can be injected in any other services, modules etc.

C#

Cross-origin resource sharing in ASP.NET Web Api

cors
Often frontend and backend are hosted on different domains. So, you may see error like

  No 'Access-Control-Allow-Origin' header is present on the requested resource.

To allow cross domain requests in your WebApi project you can use next Handler:

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace App.MessageHandlers
{
    class CorsMessageHandler : DelegatingHandler
    {
        private const string origin = "Origin";
        private const string accessControlRequestMethod = "Access-Control-Request-Method";
        private const string accessControlRequestHeaders = "Access-Control-Request-Headers";
        private const string accessControlAllowOrigin = "Access-Control-Allow-Origin";
        private const string accessControlAllowMethods = "Access-Control-Allow-Methods";
        private const string accessControlAllowHeaders = "Access-Control-Allow-Headers";

        protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var isCorsRequest = request.Headers.Contains(origin);
            var isPreflightRequest = request.Method == HttpMethod.Options;

            if (isCorsRequest)
            {
                if (isPreflightRequest)
                {
                    return Task.Factory.StartNew(() =>
                    {
                        var response = new HttpResponseMessage(HttpStatusCode.OK);
                        response.Headers.Add(accessControlAllowOrigin, request.Headers.GetValues(origin).First());

                        var currentAccessControlRequestMethod = request.Headers.GetValues(accessControlRequestMethod).FirstOrDefault();
                        if (currentAccessControlRequestMethod != null)
                        {
                            response.Headers.Add(accessControlAllowMethods, currentAccessControlRequestMethod);
                        }

                        var requestedHeaders = string.Join(", ", request.Headers.GetValues(accessControlRequestHeaders));
                        if (!string.IsNullOrEmpty(requestedHeaders))
                        {
                            response.Headers.Add(accessControlAllowHeaders, requestedHeaders);
                        }
                        return response;
                    }, cancellationToken);
                }
                else
                {
                    return base.SendAsync(request, cancellationToken).ContinueWith(t =>
                    {
                        var resp = t.Result;
                        resp.Headers.Add(accessControlAllowOrigin, request.Headers.GetValues(origin).First());
                        return resp;
                    });
                }
            }
            else
            {
                return base.SendAsync(request, cancellationToken);
            }
        }
    }
}

And then add it to your config:

GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsMessageHandler());

See also,
Implementing CORS support in ASP.NET Web APIs
CorsHandler.cs

Backup script for Linux Server in bash

bu
If you are a hardcore linux admin you may use complex backup system like the Bacula or AMANDA or other.
But for simple case it is enough to use bash script. It creates a folder with copied files from filesystem and dumped databases from MySql server. In my case here it is backup.sh:

#!/bin/sh

##--- SETTINGS
ROOT_PATH=$1
DAYS_KEEP=$2

BU_FILES="/etc"
BU_FILES="$BU_FILES;/usr/local/apache2"
BU_FILES="$BU_FILES;/usr/serv/apache/www"

EXCLUDE_PATH_1="/usr/serv/apache/www/stuff"

MYSQL_USER=backuper
MYSQL_PASS='backuper_passwd'

##--- COMMON ENV
DATESTAMP=$(date +%Y-%m-%d)
BU_PATH=${ROOT_PATH}/${DATESTAMP}

##--- BACKUP FILES
FILE_PATH=${BU_PATH}/files
if [ ! -e ${FILE_PATH} ]; then
    mkdir -p ${FILE_PATH}
fi

cd $FILE_PATH

# create backups securely - this will give 660 for new file creation
umask 006

for dir in $(echo $BU_FILES | tr ";" "\n")
do
  printf "Backup dir '${dir}' ... "
  if [ ! -e ${dir} ]; then
    echo "ERROR: Directory not found"
  else
    tar zcfP ${FILE_PATH}/`basename ${dir}`.tar.gz ${dir} --exclude ${EXCLUDE_PATH_1}
    echo "OK"
  fi
done

##--- BACKUP DATABASES
MYSQL_PATH=${BU_PATH}/mysql
if [ ! -e ${MYSQL_PATH} ]; then
    mkdir -p ${MYSQL_PATH}
fi

cd $MYSQL_PATH

# create backups securely - this will give 660 for new file creation
umask 006

# list MySQL databases and dump each one
MYSQL_DB_LIST=`mysql -u $MYSQL_USER -p"$MYSQL_PASS" -e'show databases;'`
MYSQL_DB_LIST=${MYSQL_DB_LIST##Database}
for DB in $MYSQL_DB_LIST;
do
  printf "Backup DB '${DB}' ... "
  mysqldump --single-transaction -u $MYSQL_USER -p"$MYSQL_PASS" --opt --flush-logs --databases $DB | gzip > ${MYSQL_PATH}/${DB}.sql.gz
  echo "OK"
done

# all dbs
printf "Backup all DBs ... "
mysqldump --single-transaction -u $MYSQL_USER -p"$MYSQL_PASS" -A --opt --events --ignore-table=mysql.event --flush-logs | gzip > ${MYSQL_PATH}/all_dbs.sql.gz
echo "OK"

##--- REMOVE backups older than $DAYS_KEEP
find ${ROOT_PATH}/* -mtime +$DAYS_KEEP -exec rm -rf {} \; 2> /dev/null

cd /var

The example usage:

  ./backup.sh /var/backups 30

where:
/var/backups – path to dir where backup will be placed;
30 – days count of backups to save;

You can download all files: backup_scripts