<?php
/**
 * phpaga
 *
 * Miscellaneous
 *
 * Miscellaneous routines needed by most files.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @version $Id: misc.php 884 2007-11-02 00:04:08Z florian $
 *
 * Copyright (c) 2002, Florian Lanthaler <florian@phpaga.net>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the
 *      distribution.
 *
 *    * Neither the name of Florian Lanthaler nor the names of his
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * Prints the header template
 *
 * @param int  $style   Full template or header only?
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_header($params = null)
{

    if (defined("PHPAGA_HEADER_SENT"))
        return true;

    if (($params == null) || !is_array($params))
        $params = array();

    if (isset($params["menu"]))
        $menu = $params["menu"];
    else
        $menu = true;

    if (isset($params["menuitem"]) && in_array($params["menuitem"], array('reports', 'core', 'finance', 'administration', 'system')))
        $menuitem = $params["menuitem"];
    else
        $menuitem = 'reports';

    if (isset($params["calendar"]))
        $calendar = $params["calendar"];
    else
        $calendar = false;

    $skins = array();
    $userskin = PHPAGA_DEFAULT_SKIN;
    $skins_tmp = phpaga_getSkinNames();

    /* get rid of user's skin of choice*/
    foreach($skins_tmp as $skin_tmp)
        if ($skin_tmp != $userskin)
            $skins[] = $skin_tmp;

    natsort($skins);

    if  (isset($_SESSION["auth_user"]["skin"]))
        $userskin = $_SESSION["auth_user"]["skin"];

    $tpl = new psmarty;
    $tpl->assign("userskin", $userskin);
    $tpl->assign("skins", $skins);
    $tpl->assign("calendar", $calendar);
    $tpl->assign("menu", $menu);
    $tpl->assign("menuitem", $menuitem);
    $tpl->assign("LANG", $_SESSION["auth_user"]["lng_code"]);
    $tpl->assign("currtime", phpaga_getCurrentDateTimeFormatted());

    if (isset($_SESSION["auth_user"]["usr_name"]))
        $tpl->assign("USR_NAME", $_SESSION["auth_user"]["usr_name"]);
    else
        $tpl->assign("USR_NAME", _("UNKNOWN"));

    header('Content-Type: text/html; charset=utf-8');
    $tpl->display("header.tpl.html");

    define("PHPAGA_HEADER_SENT", true);
}


/**
 * Prints the footer template
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_footer($params = null)
{
    if (defined('PHPAGA_FOOTER_SENT'))
        return true;

    if (($params == null) || !is_array($params))
        $params = array();

    $sidebar = true;

    if (isset($params['sidebar']) && !$params['sidebar'])
        $sidebar = false;

    $info = phpaga_getSideBarInfo();

    $tpl = new psmarty;
    $tpl->assign('info', $info);
    $tpl->assign('sidebar', $sidebar);
                 $tpl->display("footer.tpl.html");

    define('PHPAGA_FOOTER_SENT', true);
}


/**
 * Gets a array of job categories from the database.
 *
 * @return array  $jcatList  Job categories as content of an array. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_jobcat_getList()
{

    $jcatList = array();

    $d = new PhPagaDbData;

    $r = $d->queryAll('SELECT jcat_id, jcat_title FROM jobcat ORDER BY jcat_title');

    if (PhPagaError::isError($r))
        return $r;

    foreach ($r as $qrow) {
        phpaga_sanitizeArray($qrow);
        $jcatList[$qrow["jcat_id"]] = $qrow["jcat_title"];
    }

    return $jcatList;
}


/**
 * Gets a array of available languages from the database.
 *
 * @return array  $languages  Array of available languages. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getLanguages()
{

    global $phpaga_languages;

    $languages = array();

    $d = new PhPagaDbData;

    $r = $d->queryAll('SELECT lng_id, lng_name, lng_code FROM languages ORDER BY lng_name');

    if (PhPagaError::isError($r))
        return $r;

    foreach ($r as $qrow) {

        phpaga_sanitizeArray($qrow);

        /* If this language is in the array of languages supported by
         * phpaga, then add it to this array */

        if (isset($phpaga_languages[$qrow['lng_code']]))
            $languages[$qrow['lng_id']] = $qrow['lng_name'];
    }

    return $languages;
}


/**
 * Gets the language code that belongs to a certain language.
 *
 * @param int     $lng_id    Language ID
 *
 * @return string $lng_code  Language code. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getLanguageCode($lng_id)
{

    $lng_code = null;

    if (!isset($lng_id) || !is_numeric($lng_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_INVALID_PARAMTYPE);

    $d = new PhPagaDbData;
    $lng_code = $d->queryOneVal('SELECT lng_code FROM languages WHERE lng_id = ?', $lng_id);

    return $lng_code;
}


/**
 * Returns a formatted message
 *
 * @param  string  $title    Message title
 * @param  string  $message  Message text
 *
 * @return string  $message  Formatted message
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_returnmessage($title = '', $message = '')
{
    global $phpaga_error;

    $h_msg = '';

    if (is_numeric($message))
        /* this looks a little bit weird now, but it makes sense. go
         * on. */
        $message = array($message);

    if (is_array($message))
    {
        foreach ($message as $err_id)
        {
            if (strlen($h_msg))
                 $h_msg .= "\n";

            if (is_numeric($err_id))
                $h_msg .= $phpaga_error[$err_id];
            else
                $h_msg .= $err_id;
        }

        $message = $h_msg;
    }

    if (!strlen($message))
        return '';

    $tpl = new psmarty;
    $tpl->assign("TITLE", $title);
    $tpl->assign("MESSAGE", $message);
    return($tpl->fetch("message.tpl.html"));
}

/**
 * Prints a formatted message
 *
 * @param  string  $title    Message title
 * @param  string  $message  Message text
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_message ($title = '', $message = '')
{
    $tpl = new psmarty;
    $tpl->assign("TITLE", $title);
    $tpl->assign("MESSAGE", $message);
    $tpl->display("message.tpl.html");
}

/**
 * Returns a formatted error message
 *
 * @param  mixed   $error  Error text, error id, or array of error ids
 * @return string  $errmsg Formatted error message
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_returnerror($error)
{
    global $phpaga_error;

    $h_error = '';

    if (is_numeric($error))
        /* this looks a little bit weird now, but it makes sense. go
         * on. */
        $error = array($error);

    if (is_array($error))
    {
        foreach ($error as $err_id)
        {
            if (strlen($h_error))
                $h_error .= "\n";

            if (is_numeric($err_id))
                $h_error .= $phpaga_error[$err_id];
            else
                $h_error .= $err_id;
        }

        $error = $h_error;
    }

    if (!$error)
        $error = _('GENERIC ERROR');

    $tpl = new psmarty;
    $tpl->assign("type_err", true);
    $tpl->assign("TITLE", _("The following error occured"));
    $tpl->assign("MESSAGE", $error);
    return($tpl->fetch("message.tpl.html"));
}


/**
 * Prints an error message
 *
 * @param  mixed   $error  Error text, error id, or array of error ids
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_error($error)
{
    if (!defined('PHPAGA_HEADER_SENT'))
        phpaga_header();

    print(phpaga_returnerror($error));
}


/**
 * Returns all available skin names in an array
 *
 * @return array $skins  Array containing skin names
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getSkinNames()
{

    $skins = array();

    if ($dh = dir(PHPAGA_DOCPATH.'styles/'))
    {
        while($entry = $dh->read())
        {
            if (is_readable(PHPAGA_DOCPATH."styles/$entry")
                && ($entry != '.')
                && ($entry != '..')
                && ($entry != '.svn')
                && ($entry != 'CVS')
                && (substr($entry, -4) == '.css')
                )
            {
                $entry = str_replace('.css', '', $entry);
                $skins["$entry"] = $entry;
            }
        }
    }

    return $skins;
}

/**
 * Returns all available template sets in an array
 *
 * @return array $templates  Array containing template sets
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_getTemplateSetNames()
{

    $templates = array();

    if ($dh = dir(PHPAGA_BASEPATH.'templates/'))
    {
        while($entry = $dh->read())
        {
            if (is_readable(PHPAGA_BASEPATH."templates/$entry")
                && ($entry != '.')
                && ($entry != '..')
                && ($entry != '.svn')
                && ($entry != 'CVS')
                )
            {
                $entry = str_replace('.css', '', $entry);
                $templates["$entry"] = $entry;
            }
        }
    }

    return $templates;
}


/**
 * Returns a nicely formatted short location.
 *
 * @param  string  $city      city name
 * @param  string  $region    region name
 *
 * @return string  $location  formatted location
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getLocation($city = '', $region = '')
{
    $location = '';

    if (strlen($city) && strlen($region))
    {
        $location = str_replace("%CITY", $city, PHPAGA_SHORTLOCATION_FORMAT);
        $location = str_replace("%REGION", $region, $location);
    }
    elseif (strlen($city))
        $location = $city;
    elseif (strlen($region))
        $location = $region;

    return(trim($location));
}

/**
 * Returns a nicely formatted extended location.
 *
 * @param  string  $city      city name
 * @param  string  $region    region name
 * @param  string  $zip       zip code
 * @param  string  $country   country
 *
 * @return string  $full_location  formatted location
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getFullLocation($city = '', $region = '', $zip = '', $country = '')
{
    $location = '';

    $location = str_replace("%CITY", $city, PHPAGA_LOCATION_FORMAT);
    $location = str_replace("%REGION", $region, $location);
    $location = str_replace("%ZIP", $zip, $location);
    $location = str_replace("%COUNTRY", $country, $location);

    return(trim($location));
}


/**
 * Checks if a date is valid. A valid date has the format yyyy-mm-dd.
 *
 * @param  string  $date        Date
 * @param  bool    $canbeEmpty  if set to true, the date is also valid when left empty
 * @return bool    $result      True when date is valid,
 *                              otherwise false
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_checkdate($date, $canbeEmpty = true)
{
    /* An empty date is valid */
    if ((!strlen($date) || ($date == '0000-00-00')) && $canbeEmpty)
        return true;

    if (!ereg
        ("([0-9]{4})[-/.]{1}([0-9]{2})[-/.]{1}([0-9]{2})", $date, $match))
        /* Invalid date format */
        return false;

    /* Check non empty date for validity */
    return(checkdate($match[2], $match[3], $match[1]));
}


/**
 * Checks if an hour is valid
 *
 * @param  string  $hour    Hour
 * @return bool    $result  True when hour is valid, otherwise false
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_checkhour($hour)
{
    /* An empty hour is valid */
    if ($hour == "")
        return true;

    if (!ereg ("([0-9]{2})[:.]{1}([0-5]{1}[0-9]{1})", $hour, $match))
        /* Invalid hour format */
        return false;

    /* Check non empty hour for validity */
    if (($match[1] >= 0) && ($match[1] < 24) && ($match[2] >= 0) && ($match[2] < 60))
        return true;

    return false;
}


/**
 * Checks if a duration is valid
 *
 * @param  string  $hour    Duration
 * @return bool    $result  True when duration is valid, otherwise false
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_checkduration($duration)
{
    /* an empty duration is valid */
    if ($duration == "")
        return true;

    if (!ereg ("([0-9]*)[:.]{1}([0-5]{1}[0-9]{1})", $duration, $match))
        /* invalid duration format */
        return false;

    /* check non empty duration for validity */

    if (($match[1] >= 0) && ($match[2] >= 0) && ($match[2] < 60))
        return true;

    return false;
}


/**
 * Sanitizes an array. Some arrays can have NULL values, for example
 * when they are fetched from a query. To avoid warnings/errors when
 * accessing these keys we set everything that does not have a real
 * value to ''.
 *
 * @param  array  $san_array   Array to be sanitized
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_sanitizeArray(&$san_array)
{
    if (is_array($san_array))

        foreach (array_keys ($san_array) as $key)
            if (!is_array($san_array[$key]) && (!isset ($san_array[$key]) || !strlen($san_array[$key])))
                $san_array[$key] = '';
}


/**
 * Adds the pair "" => OPTION to an array. This is needed for option
 * fields for example on search pages where you want to add an "< All >"
 * as first option.
 *
 * @param  array  $data    Array to be treated
 * @param  int    $otype   Type of option to be added
 * @param  mixed  $ovalue  Value of the option to be added
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_arrayAddOption(&$data, $otype, $ovalue = '')
{

    $h_text = null;

    if (isset($otype) && is_numeric($otype)) {

        if (!is_array($data))
            $data = array($data);

        switch ($otype) {
            case PHPAGA_OPTION_SELECT:
                $h_text = "< "._("Select")." >";
                break;

            case PHPAGA_OPTION_NEW:
                $h_text = "< "._("New")." >";
                break;

            case PHPAGA_OPTION_ALL:
                $h_text = "< "._("All")." >";
                break;

            case PHPAGA_OPTION_NONE:
                $h_text = '';
                break;

            default:
                break;
        }

        $data = array($ovalue => $h_text) + $data;
    }
}


/**
 * Checks if an email address is valid
 *
 * @param  string  $emailaddr  Email address
 * @return bool    $result     True when address valid,
 *                             otherwise false
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_checkemailaddress($emailaddr)
{
    /* An empty email address is valid */
    if (!isset($emailaddr) || !strlen($emailaddr))
        return true;

    if (!eregi ("^([a-z0-9_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,4}$", $emailaddr))
        return false;

    return true;
}


/**
 * Converts duration to minutes
 *
 * @param  string  $duration   Duration
 * @return int     $minutes    Minutes
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_duration2minutes($duration)
{

    if (!strlen($duration))
        return 0;

    if (!ereg ("([0-9]*)[:.]{1}([0-5]{1}[0-9]{1})", $duration, $match))
        return false;

    if (($match[1] >= 0) && ($match[2] >= 0) && ($match[2] < 60))
        return($match[1] * 60 + $match[2]);

    return false;
}


/**
 * Converts minutes to duration
 *
 * @param   int     $minutes    Minutes
 * @return  string  $duration   Duration
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_minutes2duration($minutes = 0)
{
    if (!is_numeric($minutes))
        return false;

    return(floor($minutes / 60).":".sprintf("%02d", ($minutes % 60)));
}


/**
 * Converts hours to duration
 *
 * @param   int     $hours      Hours
 * @return  string  $duration   Duration
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_hours2duration($hours = 0)
{
    if (!is_numeric($hours))
        return false;

    /* convert to minutes, take care of "industrial time" (for
     * example 5.6 hours) */

    $minutes = $hours * 60;
    return(phpaga_minutes2duration($minutes));
}


/**
 * Returns a navigation bar
 *
 * @param   string  $location   Location to point to
 * @param   int     $count      Total number of records
 * @param   int     $offset     Start from record number offset
 * @param   int     $limit      Number of records to display
 *
 * @return  string  $browse     Navigation bar
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_navigate($location = null, $count = 0, $offset = 0, $limit = 0)
{
    /* Check if location already contains some parameters, in that
     * case add an &, otherwise add a ? to it */

    if (!strrchr ($location, "?"))
        $location .= "?";
    else
        $location .= "&amp;";

    if ($limit > 0)
        $pages = intval($count / $limit);
    else
        $pages = 1;

    $browse = '';

    if ($count % $limit)
        $pages++;

    if ($offset > 1) {

        $old_offset = $offset - $limit;
        $browse .=
            "[&nbsp;<a href=\"$location"."offset=$old_offset\">".
            _("PREV")."</a>&nbsp;]&nbsp\n";
    }

    for ($i = 1; (($i <= $pages) && ($i <= 10)); $i++) {

        $new_offset = $limit * ($i - 1);
        if ($offset == $new_offset) {

            if ($count > $limit)
                $browse .= "<b>$i</b>&nbsp;\n";
        } else
            $browse .= "<a href=\"$location"."offset=$new_offset\">$i</a>&nbsp;\n";
    }

    if ($pages > 10)
        $browse .= "...&nbsp;\n";

    if (($pages != 1) && !(($offset / $limit) == $pages)) {

        $new_offset = $offset + $limit;

        if ($new_offset < $count) {
            $browse .=
                "[&nbsp;<a href=\"$location"."offset=$new_offset\">"._("NEXT")."</a>&nbsp;]\n";
        }
    }

    return $browse;
}


/**
 * Checks whether $date1 is greater than or equal to $date2.
 *
 *
 * @param   string  $date1        First date
 * @param   string  $date2        Second date
 *
 * @return  bool    $result       True if the first date is set and greater than
 *                                or equal to the second date, false otherwise.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1.2
 */

function phpaga_dategreater($date1 = '', $date2 = '')
{

    if (isset($date1) && ($date1) && (mktime() >= strtotime($date1)) &&
        (!isset($date2) || !strlen($date2) || (strtotime ($date2) > strtotime ($date1))))

        return true;
    else
        return false;

}


/**
 * Returns the md5 hash of a string. phpaga uses this method to encrypt
 * the passwords that will be stored in the database.
 *
 * @param  string $plaintext     Plaintext string to be encoded
 *
 * @return string $cryted        md5 has of the input string
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_password($plaintext = '')
{
    return(md5($plaintext));
}


/**
 * Defines a value if it is not already defined
 *
 * @param  string $def     Definition name
 * @param  mixed  $val     Definition value
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_define($def, $val)
{
    if (!defined($def))
        define($def, $val);
}


/**
 * Defines an error message if it is not already set
 *
 * @param  string $def     Error name
 * @param  mixed  $val     Error message
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_errdefine($def, $val)
{
    global $phpaga_error;

    if (!isset($phpaga_error[constant($def)]) || !strlen($phpaga_error[constant($def)]))
        $phpaga_error[constant($def)] = $val;
}


/**
 * Formats an amount of money not specific to a currency.
 *
 * @param  float  $amount     Amount
 * @param  int    $decimals   Number of decimals to be displayed
 *                            (optional)
 *
 * @return string $famount    Formatted amount
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_formatamount($amount, $decimals = 2)
{

    if (!isset($amount) || !is_numeric($amount))
        return '';

    return(number_format($amount,
                         $decimals,
                         PHPAGA_SEPARATOR_DECIMALS,
                         PHPAGA_SEPARATOR_THOUSANDS));
}


/**
 * Formats a percentage value.
 *
 * @param  float  $perc    Percentage value
 *
 * @return string $fperc   Formatted value
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_formatpercentage($perc)
{

    if (!isset($perc) || !is_numeric($perc))
        return '';

    return(number_format($perc,
                         2,
                         PHPAGA_SEPARATOR_DECIMALS,
                         PHPAGA_SEPARATOR_THOUSANDS));

}


/**
 * Sanitize a string that is to be shown in a JavaScript message box
 * or as a tooltip.
 *
 * @param  string $str  Text
 *
 * @return string $sstr Sanitized text
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_popup_sanitizestr($str)
{
    $t = addslashes(ereg_replace("\r\n", "\n", $str));
    return(ereg_replace("(\n|\r)", "<br>", htmlspecialchars($t, ENT_COMPAT, 'UTF-8')));
}


/**
 * Sanitize a string that is to be shown as a javascript message.
 *
 * @param  string $str  Text
 *
 * @return string $sstr Sanitized text
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_jsmessage_sanitizestr($str)
{
    $tmp = ereg_replace("(\"|')", "", $str);
    return(ereg_replace("(\n|\r)", " ", $tmp));
}


/**
 * Turns a text containing a URL into a link.
 *
 * @param  string $url  Text containing a URL
 *
 * @return string $link Link pointing to $url
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_urlmodifier($url)
{
    return(preg_replace('|(\w+)://([^\s"<]*)([\w#?/&=])|',
                        '<a href="\1://\2\3">\1://\2\3</a>',
                        $url));
}


/**
 * Apply stripslashes to all elements of an array.
 *
 * @param  array  $arr   Array to be stripslahes()ed
 *
 * @return array  $arr   stripslashes()ed array
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_stripslashesArray($arr)
{

    foreach (array_keys($arr) as $key) {
        if (!is_array($arr[$key]))
            $arr[$key] = stripslashes($arr[$key]);
        else
            $arr[$key] = phpaga_stripslashesArray($arr[$key]);
    }

    return $arr;
}


/**
 * Log a message.
 *
 * @param mixed   $message   String or object containing the message to log.
 * @param string  $priority  The priority of the message.  Valid values are
 *                           PHPAGA_LOG_* as defined in etc/globals.php
 *
 * @return        void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_log($message, $priority = PHPAGA_LOG_INFO)
{
    if (isset($_SESSION['auth_user']['usr_login']))
        $ident = $_SESSION['auth_user']['usr_login'];
    else
        $ident = '_';

    $d = new PhPagaDbData;

    /* This can yield an error when issued within a transaction */
    $id = $d->nextId('log_id');

    if (is_object($id) || !is_numeric($id))
        return;

    $ts = $d->queryOneVal('SELECT CURRENT_TIMESTAMP');

    if (PhPagaError::isError($ts))
        $ts = date('Y-m-d H:i:s'); // This is ugly

    $fields = array('id'=> $id,
                    'logtime' => $ts,
                    'ident' => $ident,
                    'priority' => (int)$priority,
                    'message' => $message);

    $d->insert('log', $fields);
}


/**
 * Logs an error message to the database if PHPAGA_ERRLOG is set.
 *
 * @param string $error       Error object or message
 * @param string $function    Function name (optional)
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_errlog($error, $function = null)
{
    $t = null;

    if(defined('PHPAGA_ERRLOG') && PHPAGA_ERRLOG) {

        if (PhPagaError::isError($error))
            $t = $error->getMessage();

        if (is_string($error))
            $t = $error;

        if (strlen($function))
            $t = $function.': '.$t;

        phpaga_log($error, PHPAGA_LOG_ERR);
    }
}


/**
 * Fetches the log entries from the system log
 *
 * @param  array  $entries     Logentries
 * @param  array  $search      Array with search options
 *
 * @return int    $result      A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getsyslog($search = null)
{

    $entries = array();
    $where = array();
    $qparam = array();
    $offset = null;
    $limit = null;

    if (!is_array($search))
        $search = array($search);

    if (PHPAGA_DB_SYSTEM == 'pgsql')
        $_logtime = "to_char(logtime, 'yyyy-mm-dd hh24:mi:ss'::text) as logtime";
    else
        $_logtime = 'logtime';

    $query = 'SELECT '.$_logtime.', ident, priority, message FROM log ';

    if (isset($search['priority']) && is_numeric($search['priority'])) {
        $where[] = 'priority = ?';
        $qparam[] = $search['priority'];
    }

    if (isset($search['ident']) && strlen($search['ident'])) {
        $where[] = 'ident = ?';
        $qparam[] = $search['ident'];
    }

    if (isset($search['message']) && strlen($search['message'])) {
        $where[] = 'message LIKE ?';
        $qparam[] = '%'.$search['message'].'%';
    }

    if (count($where) > 0)
        $query .= 'WHERE '.implode(' AND ', $where).' ';

    $query .= 'ORDER BY logtime DESC';

    $d = new PhPagaDbData;

    if (isset($search['num']) && is_numeric($search['num'])) {
        $offset = 0;
        $limit = $search['num'];
    }

    $entries = $d->queryAll($query, $qparam, $offset, $limit);

    return $entries;
}


/**
 * Gets task category summary statistics
 *
 * @return array  $summary Summary information. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getSummary()
{

    $summary = array("persons" => array("duration" => 0,
                                        "rows" => array(),
                                        "num" => 0),

                     "projects" => array("tracked_duration" => 0,
                                         "tracked_cost_material" => 0,
                                         "tracked_cost_manhours" => 0,
                                         "tracked_cost_total" => 0,
                                         "planned_duration" => 0,
                                         "planned_cost_manhours" => 0,
                                         "planned_cost_material" => 0,
                                         "planned_cost_total" => 0,
                                         "rows" => array(),
                                         "num" => 0),

                     "opcats" => array("duration" => 0,
                                       "rows" => array(),
                                       "num" => 0),

                     "customers" => array()
        );


    switch(PHPAGA_DB_SYSTEM)
    {

        case "mysql":

            $query1 = "select o.pe_id, pe_lastname, pe_firstname, sum(op_duration) as sum_duration ".
                "from operations o left join persons p on p.pe_id = o.pe_id ".
                "group by o.pe_id, pe_lastname, pe_firstname ".
                "order by sum_duration desc";

            $query2 = "select opcat_title, sum(op_duration) as sum_duration ".
                "from operations o left join operationcat c on c.opcat_id = o.opcat_id ".
                "group by opcat_title ".
                "order by sum_duration desc";

            /*
             $query3 = "select o.prj_id, prj_title, sum(op_duration) as sum_duration ".
             "from operations o left join projects p on p.prj_id = o.prj_id ".
             "group by o.prj_id, prj_title ".
             "order by sum_duration desc";
            */

            $query3 = "select o.prj_id, prj_title, sum(op_duration) as sum_duration, prj_planned_manhours, ".
                "prj_planned_cost_manhours, prj_planned_cost_material, ".
                "(select sum(mat_price) from material m where m.prj_id = o.prj_id) as prj_tracked_cost_material, ".

                "sum(case when (ph.phr_hourlyrate IS NOT NULL) then ph.phr_hourlyrate ".
                "ELSE case when prjmem_hourlyrate IS NOT NULL THEN prjmem_hourlyrate ELSE phn.phr_hourlyrate END END * (op_duration/ 60)) AS prj_tracked_cost_manhours ".

                "from operations o left join projects p on p.prj_id = o.prj_id ".
                "LEFT JOIN project_members pm ON (pm.prj_id = o.prj_id AND pm.pe_id = o.pe_id) ".

                "LEFT JOIN projects_hourlyrates ph ON (ph.opcat_id = o.opcat_id and ph.prj_id = o.prj_id AND ph.pe_id = o.pe_id) ".
                "LEFT JOIN projects_hourlyrates phn ON (phn.opcat_id = o.opcat_id and phn.prj_id = o.prj_id AND phn.pe_id IS NULL) ".

                " group by o.prj_id, prj_title, prj_planned_manhours, prj_planned_cost_manhours, prj_planned_cost_material ".
                "order by prj_title";

            $query4 = "select b.cpn_id, cpn_name, sum(bill_endsum) as sum_amount, curr_name, b.curr_id ".
                "from bills b left join companies c on c.cpn_id = b.cpn_id ".
                "left join currencies u on u.curr_id = b.curr_id ".
                "group by b.curr_id, curr_name, b.cpn_id, cpn_name ".
                "order by curr_name, sum_amount desc";

            break;

        default:

            $query1 = "select o.pe_id, pe_lastname, pe_firstname, sum(op_duration) as sum_duration ".
                "from operations o left join persons p on p.pe_id = o.pe_id ".
                "group by o.pe_id, pe_lastname, pe_firstname ".
                "order by sum(op_duration) desc";

            $query2 = "select opcat_title, sum(op_duration) as sum_duration ".
                "from operations o left join operationcat c on c.opcat_id = o.opcat_id ".
                "group by opcat_title ".
                "order by sum(op_duration) desc";

            /*
             $query3 = "select o.prj_id, prj_title, sum(op_duration) as sum_duration ".
             "from operations o left join projects p on p.prj_id = o.prj_id ".
             "group by o.prj_id, prj_title ".
             "order by sum(op_duration) desc";
            */

            $query3 = "select o.prj_id, prj_title, sum(op_duration) as sum_duration, prj_planned_manhours, ".
                "prj_planned_cost_manhours, prj_planned_cost_material, ".
                "(select sum(mat_price) from material m where m.prj_id = o.prj_id) as prj_tracked_cost_material, ".

                "sum(case when (ph.phr_hourlyrate IS NOT NULL) then ph.phr_hourlyrate ".
                "ELSE case when prjmem_hourlyrate IS NOT NULL THEN prjmem_hourlyrate ELSE phn.phr_hourlyrate END END * (cast(op_duration AS FLOAT)/ 60)) AS prj_tracked_cost_manhours ".

                "from operations o left join projects p on p.prj_id = o.prj_id ".
                "LEFT JOIN project_members pm ON (pm.prj_id = o.prj_id AND pm.pe_id = o.pe_id) ".

                "LEFT JOIN projects_hourlyrates ph ON (ph.opcat_id = o.opcat_id and ph.prj_id = o.prj_id AND ph.pe_id = o.pe_id) ".
                "LEFT JOIN projects_hourlyrates phn ON (phn.opcat_id = o.opcat_id and phn.prj_id = o.prj_id AND phn.pe_id IS NULL) ".

                " group by o.prj_id, prj_title, prj_planned_manhours, prj_planned_cost_manhours, prj_planned_cost_material ".
                "order by prj_title";


            $query4 = "select b.cpn_id, cpn_name, sum(bill_endsum) as sum_amount, curr_name, b.curr_id ".
                "from bills b left join companies c on c.cpn_id = b.cpn_id ".
                "left join currencies u on u.curr_id = b.curr_id ".
                "group by b.curr_id, curr_name, b.cpn_id, cpn_name ".
                "order by curr_name, sum(bill_endsum) desc";

            break;

    }

    $d = new PhPagaDbData;

    $result = $d->queryAll($query1);

    if (PhPagaError::isError($result))
        return $result;

    foreach ($result as $qrow) {

        phpaga_sanitizearray($qrow);

        $qrow["sum_duration"] = (int) $qrow["sum_duration"];

        $qrow["pe_person"] =
            phpaga_persons_formatname($qrow["pe_lastname"],
                                      $qrow["pe_firstname"]);

        $summary["persons"]["rows"][] = $qrow;
        $summary["persons"]["duration"] += $qrow["sum_duration"];
    }

    $summary["persons"]["num"] = count($summary["persons"]["rows"]);

    $result = $d->queryAll($query2);

    if (PhPagaError::isError($result))
        return $result;

    foreach ($result as $qrow) {

        phpaga_sanitizearray($qrow);

        $qrow["sum_duration"] = (int) $qrow["sum_duration"];

        $summary["opcats"]["rows"][] = $qrow;
        $summary["opcats"]["duration"] += $qrow["sum_duration"];
    }

    $summary["opcats"]["num"] = count($summary["opcats"]["rows"]);

    $result = $d->queryAll($query3);

    if (PhPagaError::isError($result))
        return $result;

    foreach ($result as $qrow) {
        phpaga_sanitizearray($qrow);

        $qrow["prj_tracked_manhours"] = (int) $qrow["sum_duration"];
        $qrow["prj_planned_manhours"] = (int) $qrow["prj_planned_manhours"] * 60;

        if ($qrow["prj_planned_manhours"] != 0)
            $qrow["prj_perc_manhours"] = ($qrow["prj_tracked_manhours"] * 100) / $qrow["prj_planned_manhours"];
        else
            $qrow["prj_perc_manhours"] = null;

       if ($qrow["prj_planned_cost_manhours"] != 0)
            $qrow["prj_perc_cost_manhours"] = ($qrow["prj_tracked_cost_manhours"] * 100) / $qrow["prj_planned_cost_manhours"];
        else
            $qrow["prj_perc_cost_manhours"] = null;

       if ($qrow["prj_planned_cost_material"] != 0)
            $qrow["prj_perc_cost_material"] = ($qrow["prj_tracked_cost_material"] * 100) / $qrow["prj_planned_cost_material"];
        else
            $qrow["prj_perc_cost_material"] = null;


        $qrow["tracked_cost_total"] = ($qrow["prj_tracked_cost_material"] + $qrow["prj_tracked_cost_manhours"]);
        $qrow["planned_cost_total"] = ($qrow["prj_planned_cost_material"] + $qrow["prj_planned_cost_manhours"]);

        if ($qrow["planned_cost_total"] != 0)
            $qrow["perc_cost_total"] = ($qrow["tracked_cost_total"] * 100) / $qrow["planned_cost_total"];
        else
            $qrow["perc_cost_total"] = null;


        $summary["projects"]["rows"][] = $qrow;
        $summary["projects"]["tracked_duration"] += $qrow["sum_duration"];
        $summary["projects"]["planned_duration"] += $qrow["prj_planned_manhours"];
        $summary["projects"]["planned_cost_material"] += $qrow["prj_planned_cost_material"];
        $summary["projects"]["planned_cost_manhours"] += $qrow["prj_planned_cost_manhours"];
        $summary["projects"]["tracked_cost_material"] += $qrow["prj_tracked_cost_material"];
        $summary["projects"]["tracked_cost_manhours"] += $qrow["prj_tracked_cost_manhours"];

        $summary["projects"]["planned_cost_total"] += $qrow["planned_cost_total"];
        $summary["projects"]["tracked_cost_total"] += $qrow["tracked_cost_total"];
    }

    $summary["projects"]["num"] = count($summary["projects"]["rows"]);

    $result = $d->queryAll($query4);

    if (PhPagaError::isError($result))
        return $result;

    foreach ($result as $qrow) {
        phpaga_sanitizearray($qrow);

        $qrow["sum_amount"] = (float) $qrow["sum_amount"];

        if (isset($summary["customers"][$qrow["curr_id"]]["num"]))
            $summary["customers"][$qrow["curr_id"]]["num"]++;
        else
            $summary["customers"][$qrow["curr_id"]]["num"] = 1;

        $summary["customers"][$qrow["curr_id"]]["rows"][] = $qrow;

        if (!isset($summary["customers"][$qrow["curr_id"]]["curr_name"]))
            $summary["customers"][$qrow["curr_id"]]["curr_name"] = $qrow["curr_name"];

        if (!isset($summary["customers"][$qrow["curr_id"]]["curr_id"]))
            $summary["customers"][$qrow["curr_id"]]["curr_id"] = $qrow["curr_id"];

        if (isset($summary["customers"][$qrow["curr_id"]]["amount"]))
            $summary["customers"][$qrow["curr_id"]]["amount"] += $qrow["sum_amount"];
        else
            $summary["customers"][$qrow["curr_id"]]["amount"] = $qrow["sum_amount"];
    }

    return $summary;
}


/**
 * Gets information about the system
 *
 * @param array  $sysinfo  System information
 *
 * @return int   $result   A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getSysInfo()
{

    $sysinfo = array();
    $stats = array();

    switch(PHPAGA_DB_SYSTEM)
    {

        case "pgsql":
            $sysinfo["rdbms"] = array("name" => "PostgreSQL",
                                      "version" => null);
            break;

        case "mysql":
             $sysinfo["rdbms"] = array("name" => "MySQL",
                                       "version" => null);
            break;

        default:
            break;
    }

    $d = new PhPagaDbData;

    $v = $d->queryOneVal('SELECT version() as version');

    if (PhPagaError::isError($v))
        return $v;

    $sysinfo["rdbms"]["version"] = $v;

    $stats_cat = array(array("name" => _("Invoices"),
                             "table" => "bills",
                             "count_field" => "bill_id",
                             "minmax_field" => "bill_date"),

                       array("name" => _("Quotations"),
                             "table" => "quotations",
                             "count_field" => "quot_id",
                             "minmax_field" => "quot_date"),

                       array("name" => _("Line items"),
                             "table" => "lineitems",
                             "count_field" => "lit_id",
                             "minmax_field" => null),

                       array("name" => _("Projects"),
                             "table" => "projects",
                             "count_field" => "prj_id",
                             "minmax_field" => "prj_startdate"),

                       array("name" => _("Tasks"),
                             "table" => "operations",
                             "count_field" => "op_id",
                             "minmax_field" => "op_timestamp_start"),

                       array("name" => _("Expenses"),
                             "table" => "expenses",
                             "count_field" => "exp_id",
                             "minmax_field" => "exp_date"),

                       array("name" => _("Companies"),
                             "table" => "companies",
                             "count_field" => "cpn_id",
                             "minmax_field" => null),

                       array("name" => _("Persons"),
                             "table" => "persons",
                             "count_field" => "pe_id",
                             "minmax_field" => null),

                       array("name" => _("Products"),
                             "table" => "products",
                             "count_field" => "prod_id",
                             "minmax_field" => "prod_created"),

                       array("name" => _("Material"),
                             "table" => "material",
                             "count_field" => "mat_id",
                             "minmax_field" => "mat_added"),

                       array("name" => _("Countries"),
                             "table" => "countries",
                             "count_field" => "ctr_id",
                             "minmax_field" => null),

                       array("name" => _("Currencies"),
                             "table" => "currencies",
                             "count_field" => "curr_id",
                             "minmax_field" => null),

                       array("name" => _("Company categories"),
                             "table" => "companycat",
                             "count_field" => "ccat_id",
                             "minmax_field" => null),

                       array("name" => _("Job categories"),
                             "table" => "jobcat",
                             "count_field" => "jcat_id",
                             "minmax_field" => null),

                       array("name" => _("Task categories"),
                             "table" => "operationcat",
                             "count_field" => "opcat_id",
                             "minmax_field" => null),

                       array("name" => _("Person categories"),
                             "table" => "personcat",
                             "count_field" => "pecat_id",
                             "minmax_field" => null),

                       array("name" => _("Project categories"),
                             "table" => "projectcat",
                             "count_field" => "prjcat_id",
                             "minmax_field" => null),

                       array("name" => _("Product categories"),
                             "table" => "productcat",
                             "count_field" => "prcat_id",
                             "minmax_field" => null),

                       array("name" => _("Project status"),
                             "table" => "projectstatus",
                             "count_field" => "prjstat_id",
                             "minmax_field" => null),

                       array("name" => _("Users"),
                             "table" => "users",
                             "count_field" => "usr_id",
                             "minmax_field" => null),

                       array("name" => _("Billing plugins"),
                             "table" => "billing_methods",
                             "count_field" => "bmt_id",
                             "minmax_field" => null),

                       array("name" => _("Files"),
                             "table" => "files",
                             "count_field" => "file_id",
                             "minmax_field" => "file_added"),

        );

    foreach($stats_cat as $sc) {

        if (strlen($sc["minmax_field"]))
            $r = $d->queryRow(sprintf("select '%s' as name, count(%s) as num, min(%s) as first, max(%s) as last from %s",
                                      $sc["name"], $sc["count_field"], $sc["minmax_field"], $sc["minmax_field"], $sc["table"]));
        else
            $r = $d->queryRow(sprintf("select '%s' as name, count(%s) as num, null as first, null as last from %s",
                                      $sc["name"], $sc["count_field"], $sc["table"]));

        if (PhPagaError::isError($r))
            return $r;

        $stats[] = $r;
    }

    $sysinfo["stats"] = $stats;
    return $sysinfo;
}


/**
 * Gets the name of a log priority value.
 *
 * @param  int    $id       Priority
 *
 * @return string $priotxt  Priority name
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getlogprioritytxt($id)
{

    global $phpaga_logpriorities;

    if (isset($id) && isset($phpaga_logpriorities[$id]))
        return $phpaga_logpriorities[$id];
    else
        return null;

}


/**
 * Gets a list of distinct idents from the log
 *
 * @return array  $idents  Array of log idents. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getlogidents()
{

    $idents  = array();

    $d = new PhPagaDbData;
    $r = $d->queryAll('SELECT DISTINCT ident FROM log ORDER BY ident');

    if (PhPagaError::isError($r))
        return $r;

    foreach ($r as $qrow)
        $idents[$qrow["ident"]] = $qrow["ident"];

    return $idents;
}


/**
 * Outputs a message(or information) then halts program execution.
 *
 * @param  int    $id       Priority
 *
 * @return string $priotxt  Priority name
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_stoppage($title = null, $message = null)
{
    phpaga_header();
    phpaga_message($title, $message);
    phpaga_footer();
    exit;
}

/**
 * Returns a string containing the relevant information of a
 * backtrace.
 *
 * @param  array  $backtrace   Backtrace
 *
 * @return string $bt          Relevant backtrace information
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_format_backtrace($backtrace)
{
    if (!is_array($backtrace) || !count($backtrace))
        return null;

    $ret = null;

    foreach($backtrace as $b)
        $ret .= sprintf("File: %s, Line: %s, Function: %s, Class: %s\n",
                        isset($b["file"]) ? $b["file"] : null,
                        isset($b["line"]) ? $b["line"] : null,
                        isset($b["function"]) ? $b["function"] : null,
                        isset($b["class"]) ? $b["class"] : null);

    return $ret;

}


/**
 * Sanitizes HTML template output (used as a Smarty modifier).
 *
 * @param  string  $string     String to be sanitized
 *
 * @return string $s           Sanitized output
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_sanitize_htmlout($string)
{

    if (strlen($string))
        return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8'));
    else
        return null;

}


/**
 * Returns a numer formatted as B/KB/MB, depending on its size.
 *
 * @return string  $size  Formattes size string
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_byteformat($bytes)
{
    if (!is_numeric($bytes))
        return null;

    $bytes = (int)$bytes;

    if ($bytes < 1024)
        return sprintf("%s B", $bytes);

    if ($bytes < (1024 * 1024))
        return sprintf("%.2f kB", $bytes / 1024);

    return sprintf("%.2f MB", $bytes / (1024 * 1024));
}


/**
 * Applies utf8_decode to all elements of an array. This is used for
 * the PDF export, as the PDF library seems to have problems with
 * UTF-8.
 *
 * @param  array  $arr   Array to be decoed
 *
 * @return array  $arr   decodeded array
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_utf8decodeArray($arr)
{

    foreach (array_keys($arr) as $key)
    {
        if (!is_array($arr[$key])) {
            if (is_string($arr[$key]) && !is_numeric($arr[$key]))
                $arr[$key] = utf8_decode($arr[$key]);
        } else
            $arr[$key] = phpaga_utf8decodeArray($arr[$key]);
    }

    return $arr;
}


/**
 * If the mbstring extension is available, use its functions to handle
 * string operations, otherwise use the default functions available.
 *
 * @param  string  $string  String
 * @param  int     $start   Start position
 * @param  int     $length  Length (optional)
 *
 * @return string  $part    Part of the string
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_substr($string, $start, $length = null) {
    if (function_exists('mb_substr'))
        return mb_substr($string, $start, $length, 'utf-8');
    else
        return substr($string, $start, $length);
}


/**
 * If the mbstring extension is available, use its functions to handle
 * string operations, otherwise use the default functions available.
 *
 * @param  string  $string  String
 *
 * @return int     $length  String length
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_strlen($string) {
    if (function_exists('mb_strlen'))
        return mb_strlen($string, 'utf-8');
    else
        return strlen($string);
}


/**
 * Truncates a string at a certain length.
 *
 * @param  string  $string  String
 * @param  int     $length  Length (optional)
 * @param  string  $cont    Pattern to be added at the end
                            if the string is truncated (optional)
 *
 * @return string  $part    Part of the string
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_truncate($string, $length = 60, $cont = '...') {
    if (phpaga_strlen($string) > $length)
        return phpaga_substr($string, 0, $length).$cont;
    else
        return $string;
}


/**
 * Returns the information to be displayed in the sidebar.
 *
 * @return array $info   Info to be displayed in the sidebar. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_getSideBarInfo() {

    $info = array('bills' => array(),
                  'unbilled' => array()
        );

    $count = 0;

    if (phpaga_user_hasperm(PHPAGA_PERM_VIEW_FINANCE)) {

        $bills = phpaga_bills_search($count, array('bill_paid' => PHPAGA_BILL_NOTPAID));

        if (!PhPagaError::isError($bills))
            $info['bills'] = $bills;


        $summary = phpaga_bills_getSummaryNotBilledHours(false);

        if (!PhPagaError::isError($summary))
            $info['unbilled'] = $summary;

    }

    return $info;
}


/**
 * Returns the current date and time, formatted accoring to the
 * selected locale.
 *
 * @return string $date  Date and time
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.4
 */

function phpaga_getCurrentDateTimeFormatted() {
    $d = null;
    $oldlocale = setlocale(LC_TIME, 0);
    setlocale(LC_TIME, PHPAGA_LANGNAME);
    $d = strftime("%A, %x %R");
    setlocale(LC_TIME, $oldlocale);
    return $d;
}

?>