Monday
Jun 29,2009

As developer and designer, I am not only thriving after mere functionality when designing a product, but also try to make it an experience for the user. Therefore: When using a tool, it must work as convenient an enjoyable as possible. It must feel… right.

The same goes for the coding process. I personally use Eclipse, as it delivers a perfect set of tools for developing with PHP, XML, HTML, CSS and JavaScript. However, despite all the functionality Eclipse offers, it lacks one fundamental aspect, which affects the editor itself: The colorings. Like almost all editors the editing area has a white background with black as base text color.

When coding a lot with VI, where you usually have a black background and white as base text color, I realized that the strong contrast between a purely white background and black text color is quite harsh and tires your eyes more quickly than other color combinations. I did some research on the Internet and found out that staring on high-contrast perspectives not only tires your eyes more quickly, but also degrades your eye’s ability to differentiate between low contrasts, which is rather important, e.g. when driving your car at night. Therefore, when working a lot with coding, the right choice of color and font not only makes working with code more convenient, but also has direct effect on your productivity.

One weekend I spend some time trying various color combinations, adapting the settings constantly while coding. Now, after several weeks of experimenting, I developed an Eclipse editor theme which, at least for me, works pretty well. I used Adobe Kuler to select matching colors and also experimented with alternative font types, after reading Hamish Macpherson’s article “The Typography of Code”. Here’s the result:

Eclipse stores it’s workbench preferences (color, fonts, etc.) in the .metadata/.plugins/org.eclipse.core.runtime/.settings/ directory inside your workspace.

You can download my setting here. If you have any modifications or enhancements you are very welcome to share them!

So long!

Peter

Editing XML

Editing XML

Editing PHP

Editing PHP

Editing JavaScript

Editing JavaScript

Asterisk and zaptel

Tuesday
Apr 21,2009

When we tried to compile the zaptel package provided (1.4.12.1) on the Digium server, running make b410p exited with the following error:

scripts/Makefile.build:46: *** CFLAGS was changed in "/usr/local/src/zaptel-1.4.12.1/mISDN-1_1_7_2/drivers/isdn/hardware/mISDN/Makefile". Fix it to use EXTRA_CFLAGS.  Stop.
make[2]: *** [_module_/usr/local/src/zaptel-1.4.12.1/mISDN-1_1_7_2/drivers/isdn/hardware/mISDN] Error 2
make[2]: Leaving directory `/usr/src/linux-headers-2.6.26-1-686'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/usr/local/src/zaptel-1.4.12.1/mISDN-1_1_7_2'
make: *** [b410p] Error 2

Using KBUILD_NOPEDANTIC=1 as suggested in some blogs and comments led to a different error:

/usr/local/src/zaptel-1.4.12.1/mISDN-1_1_7_2/drivers/isdn/hardware/mISDN/avm_fritz.c:19:31:error: linux/isdn_compat.h: No such file or directory

In the end, we decided to try it with a newer version of the mISDN package to see if the bug was still there. For this purpose, we changed the URL to the mISDN tarball in the main Makefile, and guess what – now it compiles and seems to work just fine.

Here’s an excerpt of the original Makefile:

MISDNVERSION=1_1_7_2
MISDNUSERVERSION=1_1_7_2
...
b410p:
	[ -f mISDN-$(MISDNVERSION).tar.gz ] || $(DOWNLOAD) http://downloads.digium.com/pub/zaptel/b410p/mISDN-$(MISDNVERSION).tar.gz
	tar -zxf mISDN-$(MISDNVERSION).tar.gz
	$(MAKE) -C mISDN-$(MISDNVERSION) install
	[ -f mISDNuser-$(MISDNUSERVERSION).tar.gz ] || $(DOWNLOAD) http://downloads.digium.com/pub/zaptel/b410p/mISDNuser-$(MISDNUSERVERSION).tar.gz
	tar -zxf mISDNuser-$(MISDNUSERVERSION).tar.gz

And this is what we changed (you can download the modified version for zaptel 1.4.12.1 here):

MISDNVERSION=1_1_9.1
MISDNUSERVERSION=1_1_9.1
...
b410p:
	[ -f mISDN-$(MISDNVERSION).tar.gz ] || $(DOWNLOAD) http://www.misdn.org/downloads/releases/mISDN-$(MISDNVERSION).tar.gz
	tar -zxf mISDN-$(MISDNVERSION).tar.gz
	$(MAKE) -C mISDN-$(MISDNVERSION) install
	[ -f mISDNuser-$(MISDNUSERVERSION).tar.gz ] || $(DOWNLOAD) http://www.misdn.org/downloads/releases//mISDNuser-$(MISDNUSERVERSION).tar.gz
	tar -zxf mISDNuser-$(MISDNUSERVERSION).tar.gz

iXML Billing Templates

Thursday
Mar 19,2009

Dear Groupion users,

one basic step to get started with iXML is creating your own print templates. We have often been asked to provide some basic templates to get started with, so from now on you can find a set of print templates for the billing module in the Groupion download section: http://www.groupion.com/en/downloads/

The billing templates are available in English and German.

Further information can be found in the “Creating PDF and print templates with iXML” manual available at http://www.groupion.com/en/docs/

As a bonus you will find templates for two dashboard charts within the package :)

We hope that this will help you get started with iXML!

Cheers,

Johannes

Saturday
Mar 14,2009

As Johannes previously wrote, a version of Groupion is currently available for download. One of the major changes affects the SOAP API used for our Outlook Sync client. In some cases we encountered several problems during the synchronization process, which were releated to different versions of Microsoft Outlook as well as different client configurations. However, after excessive testing we were able to contain these problems and publish a new version of Outlook Sync, which is available for download on our homepage at http://www.groupion.com/downloads/

Please make sure you download the new version of Outlook Sync right away and check if your Groupion platform is running on the newest release!

Happy syncing! :-)

Groupion Version 3.102

Thursday
Mar 12,2009

Inhalte: Startdatum bei Aufgaben, Lagerausbuchungen zu Einkaufspreisen, Änderbare Steuerrate in Fakturierung und Beschaffung, iPhone E-Mail Versand

Content: Start-date for tasks, deplete stock items with purchase price from within billing, Editable tax rates within item lists of billing and procurement, iPhone email client.

###################
Lieber Groupion Nutzer,

folgende neuen Features sind in der Version 3.102 enthalten.

1.    Startdatum bei Aufgaben
Aufgaben haben ab sofort neben einem Fälligkeitsdatum auch ein Startdatum. Damit lassen sich nun zukünftige Aufgaben besser planen. Auch die Ausgabe von Aufgaben als Gant-Chart lässt sich so realisieren.

2.    Lagerausbuchungen zu Einkaufspreisen
Über eine globale Einstellung in der Systemdatei kann nun festgelegt werden, dass Lagerausgänge in der Bestandsverwaltung zu den entsprechenden Einkaufspreisen statt zu Verkaufspreisen ausgebucht werden.

3.    Änderbare Steuerrate in Fakturierung und Beschaffung
Die Steuerrate von Lagerartikeln lässt sich nun, wie auch die Preise, individuell für einzelne Dokumente in der Fakturierung und Beschaffung anpassen. So müssen nicht mehr zwei Lagerposten für den Versand in das Inland und Ausland gepflegt werden.

4.    iPhone E-Mail Versand
Mit Version 3.102 bietet die iPhone Nutzeroberfläche auch die Möglichkeit E-Mails direkt abzurufen und zu verfassen. So haben Sie jederzeit Zugriff auf Ihre Nachrichten und können gegebenenfalls direkt antworten.

################

Dear Groupion user,

Version 3.102 offers the following new features:

1.    Start-date for tasks
A new attribute was added to the master data of tasks: Start-date. This makes it easier to plan and schedule tasks in the future. Furthermore it is possible to use this data to generate outputs such as Gant-charts.

2.    Deplete stock items with purchase price from within billing
A new global setting in the system file allows you to define if you want to deplete stock items with purchase price or selling price from within billing.

3.    Editable tax rates within item lists of billing and procurement. The tax rate of individual stock items can now be adapted in billing and procurement documents. This way you do not have to create two stock items for shipping to domestic and foreign customers.

4.    iPhone email client
The iPhone user interface was enhanced in version 3.102 and can now be used to write emails. This way you have direct access to the emails in your Groupion platform and can react quickly even while you are out of office.

Wednesday
Mar 11,2009

Today we will present you some tips that can really save your time.

If you want to go back to MyPortal to check your tasks and reminders or to take a look at your agenda, you can click the “My Portal” link at the very top of the Groupion window. Since the button is rather small, it is often faster to simply click the Groupion logo at the top left corner of your Groupion window. This will bring you back to the MyPortal overview too.

Another function that is overlooked often are the shortcuts that automatically generate new elements and associate them with the original element. Furthermore certain information is transfered to the new element automatically e.g. the title. For example it is possible to create a new reminder from almost any element in Groupion. The reminder is then automatically associated with the element. Other examples are:

  • You can create a customer or supplier from the original contact
  • You can create new opportunities or business cases from an email
  • You can create an order from a business case

These shortcuts can be found in the right menu below options in the detail view of an element. Below you see the shortcuts that you can use for a contact:

Version 3.101

Monday
Mar 2,2009

Version 3.101 is now available. Several minor bugs of version 3.100 are fixed. Please update your platform via online update.

Update 3.100

Tuesday
Feb 24,2009

Dear Groupion User,

Today we present to you the changes of the new Groupion version 3.100. You will find the latest version on http://www.groupion.com/downloads/ - or simply use the update function of your existing platform. The most important improvements include:

—————————-
1.The new module Procurement
—————————-

This new module will replace the procurement functions that were part of the current Stock Calculation package. The procurement process will be structured in three steps: Order, Delivery and Invoice. You can now use the same functions and options that are available in the module Billing to organize your procurement (e.g. draft orders, create print templates for the individual steps etc.).

Further more the Supplier module will be restructured according to the Customers module and summarizes any elements associated with a certain supplier.

————————
2.Revised Billing module
————————

The module Billing becomes more flexible and an additional reminder function for invoices is added. You can now create drafts of documents in any process step – offer, order, delivery and invoice. To do this you can either refer to an element of the previous step or create a whole new element from scratch.

—————————-
3.Groupion-Magento interface
—————————-

You want to integrate a web-shop to your Groupion platform? The new version offers an integrated interface for Magento based e-commerce solutions. Customer data and incoming orders are synchronized with your Groupion platform so that you can efficiently handle this additonal sales channel. This way all orders and sales activities are bundled in a single platform.

———————-
4.Additional languages
———————-

The new version includes two new languages for the packages Organizer and Collaboration: Spain and Japanese.
Further more the beginning of the week in your calendar is now set to Sunday, which is the standard in most English speaking languages, if you use Groupion with English language settings.

———————–
5.Increased Performance
———————–

The overall performance of Groupion was increased through an optimized user and access rights management and several improvements regarding database operations.

—————
6.Other changes
—————

Since the Procurement module is introduced, the functions „Overview Orders“, „Register Orders“ and „Register Delivery“ were removed from the module Stock Calculation. Existing data will be transferred (as possible) to the new module automatically when you run the update on your platform.

The tab „Invoices“ within the detail view of the suppliers will be removed and the information of this tab will be included in the field „Associated Elements“.

With best regards,
Your Groupion Team

Thursday
Feb 19,2009

As Mac OS X is a genuine certified UNIX system, the backup scripts here will be very similar to the ones presented for Linux- and UNIX-based systems in part 2 of our backup guide. This episode will only outline some notable differences to that chapter, so it is essential that you read that first before you dive into this article.

The following tutorial assumes that you installed Groupion using the MAMP package (Mac OS X, Apache, MySQL, PHP) and that your Groupion root directory is /Applications/MAMP/htdocs/groupion.

Back Me Up

The backup script differs from the Linux version in several aspects: First, Mac OS X uses a slightly different directory structure than traditional UNIX and POSIX systems although it still has most of the well-known standard directories like /usr and /var, Mac OS X itself essentially uses them for the UNIX subsystem or symlinks them to another location on disk. The users’ home directories are stored in /Users instead of /home, and system files can be found in /Library and /System/Library.

So for our backup system, we will use /Library/Groupion as the directory to store our scripts and the SSH keyfile. Create the directory as an administrative user and a new file create-backup.sh in there with the following contents and adjust the paths in the script to your environment:

#!/bin/bash

# File and directory settings
GROUPION_DIR="/Applications/MAMP/htdocs/groupion"

BACKUP_ROOT="/Users/Shared/Groupion/Backups"
BACKUP_FILE="${BACKUP_ROOT}/groupion-backup-$(date +%Y-%m-%d-%H%M%S).tar.gz.gpg"

BACKUP_TEMP="/Library/Caches/com.example.backupserver"
DB_DUMP="${BACKUP_TEMP}/database.dump"
FILE_DIR="${BACKUP_TEMP}/files"

# Database settings: Backup user, password, database name
DB_USER="groupionbackup"
DB_PASS="password"
DB_NAME="groupion"

# Remote transfer settings
REMOTE_USER="isensebk"
REMOTE_HOST="backupserver.example.com"
REMOTE_DIR="/home/isensebk/backups"
SSH_KEYFILE="/Library/Groupion/keyfile"

# GnuPG settings
GPG_RECIPIENT="48BE6C86"
GPG_PASSPHRASE="password"
GPG_HOME_DIR="/var/root/.gnupg"

# Paths to various utilities
DUMP_COMMAND="/Applications/MAMP/Library/bin/mysqldump"
GPG_COMMAND="/usr/local/bin/gpg2"

# Created files and directories should be inaccessible by others
umask 077

echo "Processing data..."

# Fail if backup files already exist
if [ -e "${BACKUP_FILE}" ] || [ -e "${DB_DUMP}" ] || \
   [ -e "${FILE_DIR}" ] ; then
    echo "ERROR: Old backup files already exist." >&2
    exit 1
fi

# Create temp directories
mkdir -p "${BACKUP_ROOT}"
mkdir -p "${BACKUP_TEMP}"
mkdir -p "${FILE_DIR}"
if [ ! -d "${BACKUP_ROOT}" ]  || [ ! -d "${BACKUP_TEMP}" ] || \
   [ ! -d "${FILE_DIR}" ] ; then
    echo "ERROR: Cannot create backup directories." >&2
    exit 1
fi

# Dump database
"${DUMP_COMMAND}" -u "${DB_USER}" --password="${DB_PASS}" \
    --add-drop-table "${DB_NAME}" > "${DB_DUMP}"
EXITCODE="$?"
if [ "${EXITCODE}" -ne 0 ] ; then
    rm -rf "${BACKUP_TEMP}"
    echo "ERROR: Database dump failed, code ${EXITCODE}."
    exit 2
fi

# Copy Groupion files to temp directory
cp -R -p "${GROUPION_DIR}" "${FILE_DIR}"
EXITCODE="$?"
if [ "${EXITCODE}" -ne 0 ] ; then
    rm -rf "${BACKUP_TEMP}"
    echo "ERROR: File copy failed, code ${EXITCODE}."
    exit 3
fi

# Create compressed archive
set -o pipefail
tar -C "${BACKUP_TEMP}" -czf - "." | \
    "${GPG_COMMAND}" --batch \
        --always-trust \
        --homedir "${GPG_HOME_DIR}" \
        --passphrase "${GPG_PASSPHRASE}" \
        --default-key "${GPG_RECIPIENT}" \
            -s -e -r "${GPG_RECIPIENT}" > "${BACKUP_FILE}"
EXITCODE="$?"

# Delete temporary files
rm -rf "${BACKUP_TEMP}"

if [ "${EXITCODE}" -ne 0 ] ; then
    # Remove backup file in case of failure
    rm "${BACKUP_FILE}"
    echo "ERROR: tar failed, code: ${EXITCODE}." >&2
    exit 4
fi

# Send to remote server
scp -i "${SSH_KEYFILE}" "${BACKUP_FILE}" \
    "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}"
EXITCODE="$?"
if [ "${EXITCODE}" -ne 0 ] ; then
    # Remove backup file in case of failure
    rm "${BACKUP_FILE}"
    echo "ERROR: tar failed, code: ${EXITCODE}." >&2
    exit 4
fi

echo "Backup succeeded, file \"${BACKUP_FILE}\"."
exit 0

Before we can use the script, we have to generate an SSH key that will be used to log in to the remote server for off-site backup storage. For your convenience, I have reproduced the relevant parts of key generation and key transfer below from the Groupion backup guide for Linux and UNIX-based systems.

The key is created on the Mac OS X production system using ssh-keygen which will get you two files, keyfile containing the private key (i.e. the secret part of the key which will be used to login to another system) and keyfile.pub with the public key (which must be present on the system on which you want to login to).

Make sure that the secret key on the production server is only accessible by the owning user and not by the group or others, otherwise SSH might deny logins with that key (ssh-keygen should create the file with the correct permissions though). We will assume that the secret key is saved as /Library/Groupion/keyfile on the Mac OS X system and should not be distributed to any other systems (unless you want to back it up and can keep it safely so that it cannot be used by others to access your backup system).

As the script will later be run as the internal root user, it is necessary to create the SSH key as that user as well. This is accomplished by opening a terminal as an administrative user and running ssh-keygen with sudo (which might ask for your account’s password):

$ sudo ssh-keygen -t rsa -b 2048 -C "isensebk" \
       -f /Library/Groupion/keyfile

Leave the SSH key passphrase empty because the key will be used in a non-interactive script and therefore will provide virtually no additional security.

On the remote backup server, we create a dedicated user and group which will solely be used for receiving the backup sent by the production server (some old systems only use the first 8 characters of a group or user name, so we will use the name isensebk here to be on the safe side… this limitation may also apply to your passwords; you can test that by setting a password with more than 8 characters and then try to login using only the first 8 characters).

Also, we create a folder backups in the user’s home directory to receive and store the backups. If the backup server is a Linux/UNIX system, this would look like this:

$  groupadd isensebk
$  useradd -g isensebk -m -d /home/isensebk -s /bin/bash isensebk
$  mkdir -p /home/isensebk/backups
$  chown -R isensebk:isensebk /home/isensebk/backups
$  chmod -R go-rwx /home/isensebk

The SSH key’s public part, keyfile.pub, needs to be added to the user’s .authorized_keys file on the backup server. The file may contain multiple keys that can be used to login, each on a separate line.

To get the public key to the backup system, you can copy / paste the plain text contents of keyfile.pub from your Mac into /home/isensebk/.ssh/.authorized_keys on the backup server via terminal or copy the file with scp or a graphical SCP / SFTP client like Cyberduck. The.authorized_keys file may hold multiple public keys for logging in as that user, with each key in its own separate line. If the .ssh directory does not exist, create it (it might not be shown in a graphical client unless you explicitly configure it to show files starting with a dot “.”). Set the ownership of that directory and the authorized_keys file to user isensebk and group isensebk, and make the directory only accessible to the user:

$  chmod -R go-rwx /home/isensebk/.ssh

Make sure that an SSH server is running on the remote backup server and verify that you can successfully login from your Mac OS X production server to your backup server (here assumed to have the hostname backup-server):

$  ssh -i /Library/Groupion/keyfile isensebk@backup-server

You will be presented the server’s fingerprint which is a hash of the public key and asked whether to continue connecting to the server (this will appear only the first time you connect to a server; a list of known servers is stored in .ssh/known_hosts in your Mac’s home directory, which would be /var/root for the root user). Connecting to the backup server once before using the backup script is sort of mandatory because the script will fail without as the backup server’s authenticity cannot be verified (unless you explicitly disable strict host verification).

Finally, the backup script requires GnuPG for encryption. For Mac OS X, the GnuPG command-line tool can be downloaded as an Installer package (available in version 1.x, e.g. currently 1.4.8, or in version 2.x as “GnuPG v2.x”) at http://macgpg.sourceforge.net/ which also hosts graphical frontends to GnuPG for key management and for signing, encrypting, decrypting and verifying files.

After successful installation, you will find the GnuPG command-line program in /usr/local/bin/gpg. If using GnuPG 2.x, the program will probably be /usr/local/bin/gpg2 instead, so you will have to change the path to GnuPG in the scripts.

As the backup script will be run as the root user, we need to make our GnuPG key available to that account by opening a terminal where we export the secret key to a file, then use sudo to become root and import the secret key. Finally, the key file is securely removed with srm.

$  /usr/local/bin/gpg -a --export-secret-key 48BE6C86 \
       > groupionbackup-secret-key.asc
$  sudo -H bash
$  /usr/local/bin/gpg --import groupionbackup-secret-key.asc
$  srm groupionbackup-secret-key.asc
$  exit

Any outputs generated by the script will be written to the system’s console log, /var/log/system.log, and can be viewed in a text editor or with Mac OS X’s Console application in the /Applications/Utilities folder (select either All Messages or system.log).

Let the Machine Do the Work For You

However, Mac OS X 10.4 Tiger and 10.5 Leopard do not use cron to schedule tasks but have their own scheduling mechanism known as launchd that uses property list files (extension .plist) to define commands to run on particular events, at startup, login and / or periodically. While you can edit those property list files with any text editor (if the property list file is stored in XML format), Peter Borg’s Lingon provides a simple graphical front-end to it. For more useful information on launchd, take a look at Lingon’s help menu (which also provides access to the launchd / launchctl / launchd.plist man pages), Apple’s documentation or Mac In The Shell – launchd: Judge, Jury, Executioner (MacTech).

Using Lingon, we create a new User Daemon (“File”, “New”, “Users Daemons”) and give it a unique name by using a domain that we own (e.g. the remote server’s fully-qualified domain name). If you do not have a domain, use localhost or some other domain that does not conflict with others. The name is written in reverse notation, beginning with the top-level domain (such as com), followed by the domain / host name and a distinct name for the script.

For example, if your remote server is registered as backupserver.example.com, the script’s launchd property list file could be named com.example.backupserver.groupion.

It is recommended that you reboot your machine after making changes to the property list file to make sure the file is correctly loaded by launchd although it may suffice to just unload and load the file like this (as the root user):

$  cd /Library/LaunchDaemons
$  launchctl unload ./com.example.backupserver.groupion.plist
$  launchctl load ./com.example.backupserver.groupion.plist

The last thing to do is to make the remote server known to our machine by connecting to the server using ssh. When asked whether to connect to the server, reply with yes (you have to write it out).

$  ssh backupserver.example.com
The authenticity of host ' backupserver.example.com (192.168.0.10)' can't be established.
RSA key fingerprint is 86:cb:c1:65:17:4c:a9:79:fe:6f:3e:40:24:28:50:e2.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.10' (RSA) to the list of known hosts.

One Step Back is One Step Forward

Now is the time to check the backup system works by making the backup server ready and setting up a test machine that has the same configuration as the production system and that will be used to restore a backup to see if it works.

To make things easier, we will use an interactive restore script that decrypts and decompresses a backup archive, restores the Groupion database and copies the backup files back to their destined places. You can save the script as /Library/Groupion/restore-backup.sh on your test system (and your production system so that you can use it when you need it). Of course, some paths in the script may have to be customized to your environment.

#!/bin/bash

PATH="${PATH}:/Applications/MAMP/Library/bin"

# File and directory settings
# GROUPION_PARENT_DIR is the directory where that holds the Groupion directory,
# i.e. "/Applications/MAMP/htdocs" for "/Applications/MAMP/htdocs/groupion".
GROUPION_PARENT_DIR="/Applications/MAMP/htdocs"

BACKUP_ROOT="/Users/Shared/Groupion/Backups"
BACKUP_FILE="$1"

BACKUP_TEMP="/Library/Caches/com.example.backupserver"
DB_DUMP="${BACKUP_TEMP}/database.dump"
FILE_DIR="${BACKUP_TEMP}/files"

# The Groupion root directory in the restored backup temporary directory
FILE_GROUPION_DIR="${FILE_DIR}/groupion"

# Database settings: Backup user, password, database name
DB_RESTORE_USER="root"
DB_NAME="groupion"

# GnuPG settings
GPG_RECIPIENT="48BE6C86"
GPG_PASSPHRASE="password"
GPG_HOME_DIR="/var/root/.gnupg"

# Paths to various utilities
DUMP_COMMAND="/Applications/MAMP/Library/bin/mysqldump"
GPG_COMMAND="/usr/local/bin/gpg2"

# Check if an existing backup archive was specified
if [ ! -e "${BACKUP_FILE}" ] ; then
    echo "ERROR: No backup archive specified." >&2
    exit 1
fi

umask 077

echo "Restoring..."

# Abort if the backup temp directory exists
if [ -d "${BACKUP_TEMP}" ] ; then
    echo "ERROR: Temp directory already exists." >&2
    exit 2
fi

# Create temp directory
mkdir -p "${BACKUP_TEMP}"
if [ ! -d "${BACKUP_TEMP}" ] ; then
    echo "ERROR: Cannot create temp directory." >&2
    exit 3
fi

# Extract archive contents to temp directory
#tar -xzf "${BACKUP_FILE}" -C "${BACKUP_TEMP}"
set -o pipefail
gpg -d "${BACKUP_FILE}" | \
    tar -xzf - -C "${BACKUP_TEMP}"
EXITCODE="$?"
if [ "${EXITCODE}" -ne 0 ] ; then
    # Remove temp directory
    rm -rf "${BACKUP_TEMP}"
    echo "ERROR: tar failed, code: ${EXITCODE}." >&2
    exit 4
fi

# Restore database dump using MySQL's "root" user.
# MySQL will prompt you for the account's password.
echo "Restoring database dump..."
echo
echo "PLEASE PROVIDE THE PASSWORD FOR THE DATABASE USER \"${DB_RESTORE_USER}\"."
echo
mysql -u "${DB_RESTORE_USER}" -p -D "${DB_NAME}" < "${DB_DUMP}"
EXITCODE="$?"
if [ "${EXITCODE}" -ne 0 ] ; then
    rm -rf "${BACKUP_TEMP}"
    echo "ERROR: Database restore failed, code ${EXITCODE}."
    exit 5
fi

# Restore Groupion WWW files
echo "Copying \"${FILE_GROUPION_DIR}\" to \"${GROUPION_PARENT_DIR}\"..."
mkdir -p "$(dirname "${GROUPION_PARENT_DIR}")"
cp -R -p "${FILE_GROUPION_DIR}" "${GROUPION_PARENT_DIR}"
EXITCODE="$?"

rm -rf "${BACKUP_TEMP}"

if [ "${EXITCODE}" -ne 0 ] ; then
    echo "ERROR: File copy failed, code ${EXITCODE}."
    exit 6
fi

echo "Restore successful."
exit 0

The test system will need to have Apache configured appropriately so that you can access your restored Groupion installation, and the database must have an empty Groupion database set up (i.e. ”CREATE DATABASE groupion;”) to hold the contents from the restored database dump as well as a Groupion user account with the same name and password as it exists on your production server that has all required privileges on the Groupion database.

As the backups are encrypted, you will also need to install GnuPG and import the GnuPG secret key there using either the MacGPG’s GPG Keychain or the gpg command-line utility: On the production system, you can export the secret key to a file like this (which can also be used to create backups of your GPG key):

$  gpg -a --export-secret-key 48BE6C86 > gpg-key.asc

You can get the IDs (48BE6C86 in this case) of all your keys as follows:

$  gpg --list-keys
/root/.gnupg/pubring.gpg
---------------------------------
pub   1024D/48BE6C86 2009-01-27
uid                  Groupion Backup
sub   2048g/50407C32 2009-01-27

In this example, the key ID is 48BE6C86, i.e. the 8-hex-digit string in the line that begins with pub (you may have other keys in your key ring, so be sure to select the ID that belongs to your secret key).

Copy the key file to your test machine and import it into your keyring, then secure-erase the key files on your production and test server using either the Finder or the srm command-line program if you do not need them any more:

$  gpg --import < gpg-key.asc

When everything is setup, create a backup archive on the production server with the backup script, copy the archive file from the production or backup server to the test machine and run the restore script with the path to the backup as its argument:

$  ./restore-backup.sh /tmp/groupion-backup-2009-02-14-093732.tar.gz.gpg

You will be prompted twice for your passwords, once for the GnuPG passphrase and then for the MySQL superuser password.

When the restore process is finished, open your test system’s Groupion web page, login and verify that everything is working properly, e.g. adding appointments to your calendar or uploading a file.

If your test system’s web server user has different user or group IDs, you may have to adjust the ownerships before you can upload any data.

Final Thoughts

I hope to have given you enough information on making Groupion an even more reliable platform and some further insights into the tao of backup.

If you have any ideas, suggestions, criticism or other feedback, feel free to post a comment or open a new thread in our forum.

See also:

Friday
Feb 13,2009

We all know what Groupion looks like from the outside, but what about it’s intrinsic values?

In this series of articles we will reveal some of the qualities which make Groupion such a robust and performant platform. While we will talk about basic programming paradigm as well as pattern-based software design, we will take a closer look at simplified code fragments of the Groupion framework.

Our focus shall not so much lay on complex design issues, but more on simple concepts such as line-by-line performance optimization, error and exception handling as well as the proper methods securing consistency and integrity across your application. Furthermore we will sneak a peak into
a more holistic approach of object-oriented software development by demonstrating the employment of design patterns and the practise of basic rules like low coupling and high cohesion with reference to a practical and beneficial implementation.

Today’s Topic: Groupion vs. PHP (Part 1)

In this article we will show you some features that can significantly increase the performance of your application. Sometimes putting a little bit more thought into your code instead of taking the line of the least resistance by using the next best predefined PHP function can really make a difference.

Groupion vs. preg_split()

Lets consider converting a text into an array by splitting it after every line break.

Unfortunately the definition of a line break varies among different operating systems:

  • LF: Unix and Unix-like systems.
  • CR+LF: DOS and Microsoft Windows.
  • CR: Apple II family and Mac OS up to version 9.

Quote from Wikipedia: Systems based on ASCII or a compatible character set use either LF (Line feed, 0×0A) or CR (Carriage Return, 0×0D) individually, or CR followed by LF (CR+LF, 0×0D 0×0A). These characters are based on printer commands: The line feed indicated that one line of paper should feed out of the printer, and a carriage return indicated that the printer carriage should return to the beginning of the current line.

There is still an easy way to conduct the splitting operation taking into account the above-mentioned issue in one single line of native PHP code:

$lines = preg_split('/(?:\r\n|\r|\n)/', $text);

Although this statement performs 100% effectively it is common knowledge, that utilizing the regular expression engine of PHP and calling the preg_split function in particular is a very poor approach in terms of efficiency. We may significantly increase the performance by replacing the regular expression by a workaround that implements faster PHP functions:

  • Normalize the line breaks within the text by using str_replace.
  • Split the text by using explode with the normalized line break character as the delimiter.

An accordant functional wrapper would look like this:

function splitTextByLineBreaks($text) {
  return explode("\n", str_replace(array("\r\n", "\r"), "\n", $text));
}

Groupion vs. in_array()

Groupion performs a lot of array operations, especially searching and extracting. Sometimes it is required to search within the same array over and over again.

Checking the existence of a specific value within an array would usually be done by simply calling in_array:

$exists = in_array($value, $array);

If and only if the array is searched once (m = 1) then this is clearly the preferred way. Provided that n is the number of array elements (sizeof($array)), the average running time is n / 2 with the complexity being O(n).

But what will happen if we have multiple values to search for? Lets consider the following:

foreach ($values as $value)
  if (in_array($value, $array))
    ; // do something

We will now have to search the entire array repeatedly. This means that the average running time is now m * (n / 2) with the complexity being O(m * n).

As many of you might know, PHP implements associative arrays as hash maps in C. We can therefore retrieve any element through its corresponding key instantly (O(1)) as no key searching has to be conducted due to the nature of hash tables. We now know that searching for a key can always be done in roughly no time while searching for a value might even take n iterations in the worst case. How can we take advantage of this?

  • First we will build an associated array using the elements of the original array as keys, virtually flipping the key-value-pairs.
  • Next we may iterate through the values as previously, but now checking if the value exists as a key of the newly generated array by using the isset language construct instead of the original in_array function.
$hash = array();

foreach ($array as $element)
  $hash[$element] = true;

foreach ($values as $value)
  if (isset($hash[$value]))
    ; // do something

This step has momentously boosted the overall performance as isset is basically carried out instantly (O(1)). The overall average running time will now be n + (m * 1) with the complexity being O(n)

Comparing each of the two methods by their respective running time speaks for itself:

  • Methode 1 - Average Case: m * n / 2; Worst Case: m * n
  • Methode 2 - Average and Worst Case: n + m

We can further enhance this by factoring out the special case of a single value. For this best case scenario we will then pass on the creation of the hash table as it would only be operated on once, resulting in the search being conducted in n / 2 iterations on average:

if (sizeof($values) == 1) {
  if (in_array($values[0], $array))
    ; // do something
} else {
  $hash = array();

  foreach ($array as $element)
    $hash[$element] = true;

  foreach ($values as $value)
    if (isset($hash[$value]))
      ; // do something
}

Moral of the story: Put some thought into your coding!

In the next article we will discuss lazy initialization of objects.

My name is Ben and until then: Keep on coding!

Bookmark


Recent Comments