<?php
/**
 * phpaga
 *
 * Miscellaneous
 *
 * Miscellaneous routines needed by most files.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @version $Id: misc.php 753 2006-10-25 21:52:28Z 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 PHPAGA_ERR_NONE;

    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 = true;

    $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"]);

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

    $tpl->display("header.tpl.html");

    define("PHPAGA_HEADER_SENT", true);
}


/**
 * Prints the footer template
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_footer ()
{
    if (defined("PHPAGA_FOOTER_SENT"))
        return PHPAGA_ERR_NONE;

    $tpl = new psmarty;
    $tpl->display("footer.tpl.html");

    define("PHPAGA_FOOTER_SENT", true);
    return PHPAGA_ERR_NONE;
}


/**
 * Gets a array of job categories from the database.
 *
 * @param string $jcatList  List of job categories as content of an array
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_jobcat_getList (&$jcatList)
{
    $jcatList = array();

    phpaga_db_connect($db);

    $query = "SELECT jcat_id, jcat_title ".
        "FROM jobcat ".
        "ORDER BY jcat_title";

    $result = $db->query ($query);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow ())
    {
        phpaga_sanitizeArray ($qrow);
        $jcatList[$qrow["jcat_id"]] = $qrow["jcat_title"];
    }

    $result->free();
    return PHPAGA_ERR_NONE;
}


/**
 * Gets a array of available languages from the database.
 *
 * @param string &$languages  Array of available languages
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getLanguages (&$languages)
{

    global $phpaga_languages;

    $languages = array();

    phpaga_db_connect($db);

    $query = "SELECT lng_id, lng_name, lng_code ".
        "FROM languages ".
        "ORDER BY lng_name";

    $result = $db->query($query);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizeArray($qrow);

        /* if this language is in the array of installed languages,
         * then add it to this array */

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

    $result->free();
    return PHPAGA_ERR_NONE;
}


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

function phpaga_getLanguageCode ($lng_id, &$lng_code)
{

    $lng_code = "";

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

    phpaga_db_connect($db);

    $query = "SELECT lng_code ".
        "FROM languages ".
        "where lng_id = $lng_id";

    $result = $db->query ($query);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;


    if ($qrow = $result->fetchRow ())
    {
        phpaga_sanitizeArray ($qrow);
        $lng_code = $qrow["lng_code"];
    }

    $result->free ();
    return PHPAGA_ERR_NONE;
}


/**
 * Gets a list of billing stati. An operation can be billed or ot
 * billed, and you can search for both, too.
 *
 * @param  array &$billedList   Array of billing stati
 *
 * @return int   $result        A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getBilledSelection (&$billedList)
{

    $billedList = array();

    $billedList[PHPAGA_BILL_BILLED] = _("billed");
    $billedList[PHPAGA_BILL_NOTBILLED] = _("not billed");

    return PHPAGA_ERR_NONE;
}


/**
 * 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"));
}

/**
 * Returns the text of an error message
 *
 * @param  mixed   $error      Error text, error id, or array of error ids
 * @üaram  string  $separator  Separator to be inserted between multiple error messages
 *
 * @return string  $errmsg     Error text
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_errortext ($error, $separator = "&nbsp; | &nbsp;")
{
    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 .= $separator;

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

        $error = $h_error;
    }

    if (!$error)
        return "GENERIC ERROR";
    else
        return $error;
}


/**
 * Returns false if no error has occurred, true otherwise.
 * This little function is useful when checking the return code/error code
 * of a function.
 *
 * @param  mixed   $error  Error text, error id, or array of error ids
 * @return bool    $err    False if no error has happened, true otherwise
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_errorhappened ($error)
{
    if (!isset($error) ||
        ($error == PHPAGA_ERR_NONE) ||
        (is_array($error) && (count($error) == 0)) ||
        (is_string($error) && !strlen($error))
        )
    {
        /* no error has occurred */
        return false;
    }
    else
    {
        /* an error has occured */
        return true;
    }
}


/**
 * 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 actually set,
 * otherwise (if it is 0000-00-00)
 * converts it into an empty string
 *
 * @param  string  $date  Date
 * @return string  $date  Formatted date
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_checknulldate ($date)
{
    if ($date == "0000-00-00")
        $date = "";

    return $date;
}

/**
 * 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 ((phpaga_checknulldate ($date) == "") && $canbeEmpty)
        return true;

    if (!ereg
        ("([0-9]{4})[-/.]{1}([0-9]{2})[-/.]{1}([0-9]{2})", $date, $match))
    {
        /* invalid date format */
        return false;
    }
    else
    {
        /* 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 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;
    }
    else
    {
        /* check non empty hour for validity */

        if (($match[1] >= 0) && ($match[1] < 24)
            && ($match[2] >= 0) && ($match[2] < 60))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

/**
 * Checks if a duration is valid
 *
 * @param  string  $hour    Duration
 * @return bool    $result  True when duration 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;
    }
    else
    {
        /* check non empty duration for validity */

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


/**
 * Returns the image type for a given image file. Right now this is a
 * very simple (read: stupid) check of the file extension. When I find
 * a minute I'll have to replace it with something more serious.
 *
 * @param  string  $filename   Filename of the image
 * @return string  $imagetype  Image type
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_getImageType ($filename)
{
    /* recognize image type by file extension */
    eregi ('[.](.+)$', $filename, $matches);

    switch (strtolower ($matches[1]))
    {
        case "jpg":
            $imagetype = "jpeg";
            break;

        case "png":
            $imagetype = "png";
            break;

        case "gif":
            $imagetype = "gif";
            break;

        default:
            $imagetype = "jpeg";
            break;

    }

    return $imagetype;
}

/**
 * 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)
{
    /* walk through all the keys of this array and make sure they are
     * at least set to "" */

    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 = "";

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

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

        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;
    }
    else
    {
        if (!eregi
            ("^([a-z0-9_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,4}$",
             $emailaddr))
        {
            /* invalid email address format */
            return false;
        }
        else
        {
            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)
{
    /* an empty duration is 0 minutes */
    if ($duration == "")
    {
        return 0;
    }
    else
    {
        if (!ereg ("([0-9]*)[:.]{1}([0-5]{1}[0-9]{1})", $duration, $match))
        {
            /* invalid duration format */
            return false;
        }
        else
        {
            /* check non empty duration for validity */

            if (($match[1] >= 0) && ($match[2] >= 0) && ($match[2] < 60))
            {
                /* duration has valid format, return value in minutes */
                return($match[1] * 60 + $match[2]);
            }
            else
            {
                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))
    {
        /* invalid minutes format */
        return false;
    }
    else
    {
        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))
    {
        /* invalid minutes format */
        return false;
    }
    else
    {
        /* 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 = "",
                          $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)
            || ($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 "";
    else
        return(number_format($amount,
                             $decimals,
                             PHPAGA_SEPARATOR_DECIMALS,
                             PHPAGA_SEPARATOR_THOUSANDS));
}


/**
 * Formats a number with PHPAGA_SEPARATOR_THOUSANDS and no decimals.
 * This is used as a wrapper for callback functions needed for the
 * graphs.
 *
 * @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_formatint ($amount)
{

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

    return(phpaga_formatamount($amount, 0));

}

/**
 * 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 "";
    else
        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>", htmlentities($t)));
}


/**
 * 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));
}

/**
 * Makes 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));
}


/**
 * Outputs a debug message if PHPAGA_ERR_VERBOSE is set and true.
 *
 * @param string $debug    Debug message
 *
 * @return void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_debug ($text)
{
    if(defined("PHPAGA_ERR_VERBOSE") && (PHPAGA_ERR_VERBOSE == true))
        phpaga_error($text);
}


/**
 * 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:
 *                           PEAR_LOG_EMERG, PEAR_LOG_ALERT, PEAR_LOG_CRIT,
 *                           PEAR_LOG_ERR, PEAR_LOG_WARNING, PEAR_LOG_NOTICE,
 *                           PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
 *
 * @return        void
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

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

    $conf = array('dsn' => PHPAGA_DSN);
    $logger = &Log::singleton("sql", "log", $ident, $conf);
    $logger->log($message, $priority);

}


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

function phpaga_errlog ($text, $function = null)
{
    if(defined("PHPAGA_ERRLOG") && PHPAGA_ERRLOG)
    {
        if (strlen($function))
            $text = $function.": ".$text;

        phpaga_log($text, PEAR_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(&$entries, $search = null)
{

    $entries = array();
    $where = array();

    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 ";

    phpaga_db_connect($db);

    if (isset($search["priority"]) && is_numeric($search["priority"]))
        $where[] = "priority = ".$db->quoteSmart($search["priority"]);

    if (isset($search["ident"]) && strlen($search["ident"]))
        $where[] = "ident = ".$db->quoteSmart($search["ident"]);

    if (isset($search["message"]) && strlen($search["message"]))
        $where[] = "message LIKE '%".$db->escapeSimple($search["message"])."%'";

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

    $query .= "ORDER BY logtime DESC";

    if (isset($search["num"]) && is_numeric($search["num"]))
        $result = $db->limitQuery($query, 0, $search["num"]);
    else
        $result = $db->query($query);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
        $entries[] = $qrow;

    $result->free();
    return PHPAGA_ERR_NONE;

}


/**
 * Gets task category summary statistics
 *
 * @param array  $summary Summary information
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getSummary (&$summary)
{

    $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;

    }

    phpaga_db_connect($db);

    $result = $db->query($query1);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
    {
        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 = $db->query($query2);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
    {
        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 = $db->query($query3);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);
/*
        $qrow["sum_duration"] = (int) $qrow["sum_duration"];

        $summary["projects"]["rows"][] = $qrow;
        $summary["projects"]["duration"] += $qrow["sum_duration"];
*/
      $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 = $db->query($query4);

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
    {
        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"];
    }

    $result->free();
    return PHPAGA_ERR_NONE;

}


/**
 * 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)
{

    $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;
    }

    phpaga_db_connect($db);

    $result = $db->query("SELECT version() as version");

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    if ($qrow = $result->fetchRow())
        $sysinfo["rdbms"]["version"] = $qrow["version"];


    $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" => _("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" => _("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"]))

            $result = $db->query(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

            $result = $db->query(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 (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        if ($qrow = $result->fetchRow())
            $stats[] = $qrow;
    }

    $sysinfo["stats"] = $stats;

    $result->free();
    return PHPAGA_ERR_NONE;

}


/**
 * 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
 *
 * @param  int    $id       Priority
 *
 * @return string $priotxt  Priority name
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_getlogidents(&$idents)
{

    $idents  = array();

    phpaga_db_connect($db);
    $result = $db->query("SELECT DISTINCT ident FROM log ORDER BY ident");

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    while ($qrow = $result->fetchRow())
        $idents[$qrow["ident"]] = $qrow["ident"];

    $result->free();
    return PHPAGA_ERR_NONE;

}


/**
 * 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;

}


/**
 * Sets the default handling for errors thrown by PEAR and its
 * components. If PHPAGA_ERR_VERBOSE is set, an error message is
 * output. The error message is the logged (even if PHPAGA_ERR_VERBOSE
 * is not set).
 *
 * @param  mixed   $error  PEAR error
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_errorhandler($error)
{

    /* avoid recursion */
    if (defined("PHPAGA_ERRHANDLER_NORECURSION"))
        return;

    define("PHPAGA_ERRHANDLER_NORECURSION", true);

    $revision = null;

    preg_match('/^\$Revision: ([0-9]+) .*$/', '$Revision: 753 $', $_rev);

    if (is_array($_rev) && isset($_rev[1]))
        $revision = $_rev[1];

    $errtxt = sprintf(_("Revision: %s\nDebug information:\n%s\n\nBacktrace:\n%s"),
                      $revision,
                      $error->getDebugInfo(),
                      phpaga_format_backtrace($error->backtrace, true));

    if (defined("PHPAGA_ERR_VERBOSE") && PHPAGA_ERR_VERBOSE)
        if (function_exists("phpaga_error") && class_exists("psmarty"))
            phpaga_error($errtxt);
        else
            print(nl2br($errtxt));

    phpaga_errlog($errtxt);
}


/**
 * 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));
    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));

}

?>
