<?php
/**
 * phpaga
 *
 * Project management functionality.
 *
 * This file contains the necessary routines to manage projects.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @version $Id: projects.php 726 2006-08-05 22:34:30Z 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.
 */

if (defined("PHPAGA_GRAPHVIZ_ENABLED") && PHPAGA_GRAPHVIZ_ENABLED)
    require_once 'Image/GraphViz.php';

/**
 * Gets all projects matching the search parmeters
 * (from $offset to $limit, if set)
 *
 * @param array  $rows      Array containing the results
 * @param int    $count     Number of records matching the query
 * @param string $duration  Total duration of matching projects
 * @param array  $prjInfo   Project information
 * @param array  $prjIds    IDs of the resulting projects
 * @param int    $order     Specifies the order by field
 *
 * @param int    $offset    Offset (optional)
 * @param int    $limit     Limit (optional)
 *
 * @return int   $result    A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @author Mark Parssey <markpa@users.sourceforge.net>
 * @since phpaga 0.3
 */

function phpaga_projects_search (&$rows,
                                 &$count,
                                 &$duration,
                                 &$prjIds,
                                 $prjInfo,
                                 $order = 0,
                                 $offset = "",
                                 $limit = "")
{

    $count = 0;
    $rows = array();
    $prjIds = array();
    $where = array();
    $where_clause = "";
    $duration = "0";

    /* check if current user can view all projects or only those she is a member of */

    $viewall = (/*phpaga_user_hasperm(PHPAGA_PERM_MANAGE_PROJECTS)
                ||
                */
                phpaga_user_hasperm(PHPAGA_PERM_VIEW_OTHERPROJECTS)
                || phpaga_user_hasperm(PHPAGA_PERM_MANAGE_OTHERPROJECTS));

    /* if the current user can only view projects she is a member of, brutally
     * overwrite the pe_id prjInfo option with her pe_id
     */

//    if (!(isset($prjInfo["pe_id"]) && is_numeric($prjInfo["pe_id"])))
//        $prjInfo["pe_id"] = $_SESSION["auth_user"]["pe_id"];

    if (!$viewall)
        $prjInfo["pe_id"] = $_SESSION["auth_user"]["pe_id"];

    phpaga_db_connect($db);

    /* total number of matching records? */

    $countquery = "SELECT COUNT(projects.prj_id) AS count_prj ".
        "FROM projects ";
//        "LEFT JOIN project_members on project_members.prj_id = projects.prj_id ";

    /* select matching records from projects */

    $query =
        "SELECT projects.prj_id, prj_title, prj_startdate, prj_estimatedend, ".
        "prj_deadline, prj_enddate, projects.cpn_id, cpn_name, prjcat_title, ".
        "prjstat_id, prjstat_title, projects.prj_status, prj_priority, ".
        "sum(op_duration) as prj_duration, prj_desc, prj_billabletype ".
        "FROM projects ".

        "LEFT JOIN companies ON companies.cpn_id = projects.cpn_id ".
        "LEFT JOIN projectstatus ON projectstatus.prjstat_id = projects.prj_status ".
        "LEFT JOIN projectcat ON projectcat.prjcat_id = projects.prjcat_id ".
        "LEFT JOIN persons on persons.pe_id = projects.pe_id ".
        "LEFT JOIN operations on operations.prj_id = projects.prj_id ";

    /* collect elements of the where clause */

    /* if person being searched for check project (created by, project
     * manager) or project_members */

    if (isset($prjInfo["pe_id"]) && is_numeric($prjInfo["pe_id"])) {

        $countquery .= "LEFT JOIN project_members pm on (pm.prj_id = projects.prj_id and pm.pe_id = ".
            $db->quoteSmart($prjInfo["pe_id"]).") ";

        $query .= "LEFT JOIN project_members pm on (pm.prj_id = projects.prj_id and pm.pe_id = ".
            $db->quoteSmart($prjInfo["pe_id"]).") ";

        $where[] = "(projects.pe_id = ".$db->quoteSmart($prjInfo["pe_id"]).
            " OR pm.pe_id = ".$db->quoteSmart($prjInfo["pe_id"]).
            " OR projects.prj_manager_pe_id = ".$db->quoteSmart($prjInfo["pe_id"]).")";
    }

    if (isset($prjInfo["bill_id"]) && is_numeric($prjInfo["bill_id"]))
        $where[] = "projects.bill_id = ".$db->quoteSmart($prjInfo["bill_id"]);

    if (isset($prjInfo["prj_id"]) && is_numeric($prjInfo["prj_id"]))
        $where[] = "projects.prj_id = ".$db->quoteSmart($prjInfo["prj_id"]);

    if (isset($prjInfo["prjcat_id"]) && is_numeric($prjInfo["prjcat_id"]))
        $where[] = "projects.prjcat_id = ".$db->quoteSmart($prjInfo["prjcat_id"]);

    if (isset($prjInfo["cpn_id"]) && is_numeric($prjInfo["cpn_id"]))
        $where[] = "projects.cpn_id = ".$db->quoteSmart($prjInfo["cpn_id"]);

    if (isset($prjInfo["cpn_id_issuebill"]) && is_numeric($prjInfo["cpn_id_issuebill"]))
        $where[] = "projects.cpn_id_issuebill = ".$db->quoteSmart($prjInfo["cpn_id_issuebill"]);

    if (isset($prjInfo["prj_status"]) && is_numeric($prjInfo["prj_status"]))
        $where[] = "projects.prj_status = ".$db->quoteSmart($prjInfo["prj_status"]);

    if (isset($prjInfo["prj_priority"]) && is_numeric($prjInfo["prj_priority"]))
        $where[] = "projects.prj_priority = ".$db->quoteSmart($prjInfo["prj_priority"]);

    if (isset($prjInfo["prj_billabletype"]) && is_numeric($prjInfo["prj_billabletype"]))
        $where[] = "projects.prj_billabletype = ".$db->quoteSmart($prjInfo["prj_billabletype"]);

    if (isset($prjInfo["prj_desc"]) && ($prjInfo["prj_desc"] != ""))
        $where[] = "projects.prj_desc LIKE '%".$db->escapeSimple($prjInfo["prj_desc"])."%'";

    if (isset($prjInfo["prj_title"]) && ($prjInfo["prj_title"] != ""))
        $where[] = "projects.prj_title LIKE ".$db->escapeSimple($prjInfo["prj_title"]);

    if (isset($prjInfo["prj_startdatefrom"])
        && ($prjInfo["prj_startdatefrom"] != ""))
    {
        $where[] =
            "projects.prj_startdate >= ".
            strtotime ($prjInfo["prj_startdatefrom"]);
    }

    if (isset($prjInfo["prj_startdateto"])
        && ($prjInfo["prj_startdateto"] != ""))
    {
        $where[] =
            "projects.prj_startdate <= ".
            strtotime ($prjInfo["prj_startdateto"]." 23:59:59");
    }

    /* build the where clause */
    if (count($where) > 0)
    {
        $query .= "WHERE ".implode(" AND ", $where)." ";
        $countquery .= "WHERE ".implode(" AND ", $where)." ";
    }

    /* add the group by clause */
    $query .=
        "GROUP BY projects.prj_id, prj_title, prj_startdate, prj_estimatedend, ".
        "prj_deadline, prj_enddate, projects.cpn_id, cpn_name, prjcat_title, ".
        "prjstat_id, prjstat_title, projects.prj_status, prj_priority, prj_desc, prj_billabletype ";

    $query .= " ORDER BY ";

    /* build the order-by clause */
    /* check validity of order value, eventually set it to default  */
    if (($order == "") || ($order == 0) || !is_numeric($order))
        $order = ORDER_PRJ_STARTDATE_DESC;

    switch ($order)
    {
        case ORDER_PRJ_PR_ASC:
            $query .= "prj_title ASC";
            break;
        case ORDER_PRJ_PR_DESC:
            $query .= "prj_title DESC";
            break;
        case ORDER_PRJ_PRJSTATUS_ASC:
            $query .= "prjstat_title ASC";
            break;
        case ORDER_PRJ_PRJSTATUS_DESC:
            $query .= "prjstat_title DESC";
            break;
        case ORDER_PRJ_PRIORITY_ASC:
            $query .= "prj_priority ASC";
            break;
        case ORDER_PRJ_PRIORITY_DESC:
            $query .= "prj_priority DESC";
            break;
        case ORDER_PRJ_CUSTOMER_ASC:
            $query .= "cpn_name ASC";
            break;
        case ORDER_PRJ_CUSTOMER_DESC:
            $query .= "cpn_name DESC";
            break;
        case ORDER_PRJ_STARTDATE_ASC:
            $query .= "prj_startdate ASC";
            break;
        case ORDER_PRJ_STARTDATE_DESC:
            $query .= "prj_startdate DESC";
            break;
        case ORDER_PRJ_PROJENDDATE_ASC:
            $query .= "prj_estimatedend ASC";
            break;
        case ORDER_PRJ_PROJENDDATE_DESC:
            $query .= "prj_estimatedend DESC";
            break;
        case ORDER_PRJ_PROJDEADLINE_ASC:
            $query .= "prj_deadline ASC";
            break;
        case ORDER_PRJ_PROJDEADLINE_DESC:
            $query .= "prj_deadline DESC";
            break;
        case ORDER_PRJ_PRJCAT_ASC:
            $query .= "prjcat_title ASC";
            break;
        case ORDER_PRJ_PRJCAT_DESC:
            $query .= "prjcat_title DESC";
            break;
        case ORDER_PRJ_DURATION_ASC:
            $query .= "prj_duration ASC";
            break;
        case ORDER_PRJ_DURATION_DESC:
            $query .= "prj_duration DESC";
            break;
        default:
            $query .= "prj_startdate DESC";
            break;
    }

    /* first do the count stuff */

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

    if ((!DB::isError($result)) && ($result->numRows() > 0)
        && ($countrow = $result->fetchRow()))
    {
        $count = $countrow["count_prj"];
    }

    /* limit the number of results starting from $offset if both $offset
     * and $limit are set and valid */

    if (isset($offset) && is_numeric($offset) && isset($limit)
        && is_numeric($limit) && ($limit > 0))

        $result = $db->limitQuery($query, $offset, $limit);
    else
        $result = $db->query($query);

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

    if ($result->numRows() < 1)
        return PHPAGA_ERR_DB_NO_ROWS;

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

        phpaga_sanitizearray($qrow);

        /* is the project overdue? */

        if (isset($qrow["prj_deadline"])
            && ($qrow["prj_deadline"] != "")
            && (mktime () >= strtotime($qrow["prj_deadline"]))
            && (!isset($qrow["prj_enddate"])
                || ($qrow["prj_enddate"] == "")))

            $qrow["prj_overdue"] = true;
        else
            $qrow["prj_overdue"] = false;

        $duration += $qrow["prj_duration"];
        $rows[] = $qrow;
        $prjIds[] = $qrow["prj_id"];
    }

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

/**
 * Gets a project's information
 *
 * @param int    $prjID   Project ID
 * @param array  $prjInfo Array containing the results.
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getInfo ($prjID, &$prjInfo)
{

    global $phpaga_projects_billabletype;

    $prjInfo = array();

    if (!isset($prjID) || (!is_numeric($prjID)))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT p.prj_id, p.prj_title, p.prj_desc, p.prj_startdate, ".
        "p.prj_estimatedend, p.prj_deadline, p.prj_enddate, p.prj_status, p.prj_priority, ".
        "p.cpn_id, cpn.cpn_name, prjcat_title, ".
        "p.prjcat_id, prjstat_title, p.pe_id, ".
        "p.cpn_id_issuebill, cpnbill.cpn_name as cpn_name_issuebill, ".
        "persons.pe_lastname, persons.pe_firstname, ".
        "p.prj_planned_manhours, p.prj_planned_cost_manhours, ".
        "p.prj_planned_cost_material, p.prj_prj_id, p.prj_billabletype, ".
        "ppr.prj_title as parent_prj_title, ".
        "p.quot_id, quot_number, quot_date, ".
        "p.prj_manager_pe_id, persons2.pe_lastname as prj_manager_lastname, ".
        "persons2.pe_firstname as prj_manager_firstname ".
        "FROM projects p ".
        "LEFT JOIN projectcat ON projectcat.prjcat_id = p.prjcat_id ".
        "LEFT JOIN companies cpn on cpn.cpn_id = p.cpn_id ".
        "LEFT JOIN companies cpnbill on cpnbill.cpn_id = p.cpn_id_issuebill ".
        "LEFT JOIN projectstatus on projectstatus.prjstat_id = p.prj_status ".
        "LEFT JOIN persons ON persons.pe_id = p.pe_id ".
        "LEFT JOIN persons persons2 ON persons2.pe_id = p.prj_manager_pe_id ".
        "LEFT JOIN projects ppr ON ppr.prj_id = p.prj_prj_id ".
        "LEFT JOIN quotations q ON q.quot_id = p.quot_id ".
        "WHERE p.prj_id = ".$db->quoteSmart($prjID);

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

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

    if ($result->numRows() < 1)
        return PHPAGA_ERR_DB_NO_ROWS;

    if ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);

        $qrow["prj_startdate"] = phpaga_checknulldate($qrow["prj_startdate"]);
        $qrow["prj_estimatedend"] = phpaga_checknulldate($qrow["prj_estimatedend"]);
        $qrow["prj_deadline"] = phpaga_checknulldate($qrow["prj_deadline"]);
        $qrow["prj_enddate"] = phpaga_checknulldate($qrow["prj_enddate"]);

        $qrow["prj_owner"] = phpaga_persons_formatname($qrow["pe_lastname"], $qrow["pe_firstname"]);
        $qrow["prj_manager"] = phpaga_persons_formatname($qrow["prj_manager_lastname"], $qrow["prj_manager_firstname"]);

        /* If the prj_billabletype flag is not set, make the project
         * billable by default this is to insure that data from
         * previous versions is treated as billable
         */

        if (!isset($qrow["prj_billabletype"]) || ($qrow["prj_billabletype"] == null) || !strlen($qrow["prj_billabletype"]))
            $qrow["prj_billabletype"] = PHPAGA_BILLABLETYPE_YES;

        if (isset($phpaga_projects_billabletype[$qrow["prj_billabletype"]]))
            $qrow["prj_billabletype_text"] = $phpaga_projects_billabletype[$qrow["prj_billabletype"]];
        else
            $qrow["prj_billabletype_text"] = _("unknown");

        if (isset($qrow["prj_deadline"])
            && ($qrow["prj_deadline"] != "")
            && (mktime () >= strtotime($qrow["prj_deadline"]))
            && (!isset($qrow["prj_enddate"])
                || ($qrow["prj_enddate"] == "")))

            $qrow["prj_overdue"] = true;
        else
            $qrow["prj_overdue"] = false;

        $opInfo = array("prj_id" => $prjID);
        $oprows = array();
        $opcount = 0;
        $opduration = 0;
        $opIds = array();
        $humancost = array();

        if (phpaga_operations_search($oprows, $opcount, $opduration, $opIds, $opInfo) != PHPAGA_ERR_NONE)
        {
            $qrow["operations"] = array();
            $sum_time = 0;
        }
        else
        {
            $qrow["operations"] = $oprows;
            $sum_time = phpaga_duration2minutes($opduration);

            if (phpaga_operations_gethumancost($opIds, $humancost) != PHPAGA_ERR_NONE)
                $humancost = array();
        }

        $qrow["humancost"] = $humancost;

        $qrow["prj_duration_minutes"] = $sum_time;

        if (isset($qrow["prj_planned_manhours"]) && is_numeric($qrow["prj_planned_manhours"])
            && ($qrow["prj_planned_manhours"] > 0))

            $qrow["prj_perc_manhours"] = ($sum_time / ($qrow["prj_planned_manhours"] * 60)) * 100;
        else
            $qrow["prj_perc_manhours"] = null;

        if (isset($qrow["prj_planned_cost_manhours"]) && is_numeric($qrow["prj_planned_cost_manhours"])
            && ($qrow["prj_planned_cost_manhours"] > 0))
        {
            if (isset($humancost["total_amount"]) && is_numeric($humancost["total_amount"]))
                $qrow["prj_perc_cost_manhours"] = ($humancost["total_amount"] / $qrow["prj_planned_cost_manhours"]) * 100;
            else
                $qrow["prj_perc_cost_manhours"] = null;
        }
        else
            $qrow["prj_perc_cost_manhours"] = null;

        $matInfo = array("prj_id" => $prjID);
        $materials = array();

        if (phpaga_materials_getmaterials($matInfo, $materials) != PHPAGA_ERR_NONE)
            $qrow["materials"] = array("total_amount" => 0,
                                       "details" => array());
        else
        {
            $qrow["materials"] = array("total_amount" => 0,
                                       "details" => $materials);

            foreach($materials as $m)
                if (isset($m["mat_price"]) && is_numeric($m["mat_price"]))
                    $qrow["materials"]["total_amount"] += $m["mat_price"];
        }

        if (isset($qrow["prj_planned_cost_material"]) && is_numeric($qrow["prj_planned_cost_material"])
            && ($qrow["prj_planned_cost_material"] > 0))
        {
            if (isset($qrow["materials"]["total_amount"]) && is_numeric($qrow["materials"]["total_amount"]))
                $qrow["prj_perc_cost_material"] = ($qrow["materials"]["total_amount"] / $qrow["prj_planned_cost_material"]) * 100;
            else
                $qrow["prj_perc_cost_material"] = null;
        }
        else
            $qrow["prj_perc_cost_material"] = null;

        $expInfo = array("prj_id" => $prjID);
        $expenses = array();

        if (phpaga_expenses_getexpenses($expInfo, $expenses) != PHPAGA_ERR_NONE)
            $qrow["expenses"] = array();
        else
            $qrow["expenses"] = $expenses;

        $prj_members = array();

        if (phpaga_project_getmembers($prjID, $prj_members) != PHPAGA_ERR_NONE)
            $qrow["prj_members"] = array();
        else
            $qrow["prj_members"] = $prj_members;

        $cres = phpaga_projects_getChildProjects($prjID, $prjchilds);
        if ($cres == PHPAGA_ERR_NONE)
            $qrow["childs"] = $prjchilds;


        $files = array();
        $fres = phpaga_files_getAllRel(PHPAGA_RELTYPE_PROJECT, $prjID, $files);
        if ($fres == PHPAGA_ERR_NONE)
            $qrow["files"] = $files;

        $opcats = array();
        $ocres = phpaga_projects_gethourlyrates($prjID, $opcats);
        if ($ocres == PHPAGA_ERR_NONE)
            $qrow["opcatlist"] = $opcats;
        else
            $qrow["opcatlist"] = array();

        $prjInfo = $qrow;
    }

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


/**
 * Gets a list of a project's childs.
 *
 * @param int    $prj_id      Project ID
 * @param array  &$prjchilds  Array containing the results.
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getChildProjects ($prj_id,
                                           &$prjchilds)
{

    $prjchilds = array();

    if (!isset($prj_id) || (!is_numeric($prj_id)))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT prj_id, prj_title ".
        "FROM projects ".
        "WHERE prj_prj_id = ".$db->quoteSmart($prj_id);

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

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

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

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


/**
 * Gets all relations (parents and childs) of a specific project.
 *
 * @param int    $prj_id      Project ID
 * @param array  &$prjchilds  Array containing the results.
 *
 * @return int   $result      A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getParentChildProjects ($prj_id, $prj_prj_id,
                                                 &$relations)
{

    $where = array();

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

    if (!isset($prj_id) || (!is_numeric($prj_id)))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT prj_id, prj_title, prj_prj_id ".
        "FROM projects ";

    $where[] = "prj_prj_id = ".$db->quoteSmart($prj_id);

    if (is_numeric($prj_prj_id) && !isset($relations[$prj_prj_id]))
        $where[] = "prj_id = ".$db->quoteSmart($prj_prj_id);

    $query .= "WHERE ".implode(" OR ", $where);

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

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

    while ($qrow = $result->fetchRow()) {
        $relations[$qrow["prj_id"]] = $qrow;

        if ($qrow["prj_prj_id"] != $prj_prj_id)
            $p = $qrow["prj_prj_id"];
        else
            $p = null;

        $fres = phpaga_projects_getParentChildProjects ($qrow["prj_id"],
                                                        $p,
                                                        $relations);

        if ($fres != PHPAGA_ERR_NONE)
            return $fres;
    }

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


/**
 * Draws a graph of all relations (parents and childs) of a specific
 * project.
 *
 * @param int     $prj_id      Project ID
 * @param bool    $map         Output image map?
 *
 * @return string $coord       Image map coordinates (if $map is true)
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getRelationGraph ($prj_id, $map = false)
{

    if (!defined("PHPAGA_GRAPHVIZ_ENABLED") || !PHPAGA_GRAPHVIZ_ENABLED)
        return PHPAGA_ERR_DISABLED;

    $relations = array();
    $p = array();

    phpaga_db_connect($db);

    $result = $db->query("SELECT prj_id, prj_title, prj_prj_id FROM projects ".
                         "WHERE prj_id = ".$db->quoteSmart($prj_id));

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

    if (!($prjInfo = $result->fetchRow()))
        return false;

    $relations[$prj_id] = array("prj_id" => $prj_id,
                                "prj_title" => $prjInfo["prj_title"],
                                "prj_prj_id" => $prjInfo["prj_prj_id"]);

    $result = phpaga_projects_getParentChildProjects($prj_id, $prjInfo["prj_prj_id"], $relations);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    $graph = new Image_GraphViz();

    foreach ($relations as $rel)
    {

        if (isset($rel["prj_id"]) && is_numeric($rel["prj_id"])) {

            if (!isset($p[$rel["prj_id"]]))
            {


                if ($rel["prj_id"] == $prj_id) {
                    $color = "#00ff00";
                    $fillcolor = "#ddffdd";
                } else {
                    $color = "#000000";
                    $fillcolor = "#eeeeee";
                }

                $graph->addNode(
                    $rel["prj_id"],
                    array(
                        "label" => phpaga_jsmessage_sanitizestr($rel["prj_title"]),
                        "shape" => "box",
                        "color" => $color,
                        "fontsize" => 10,
                        "height" => .1,
                        "width" => .1,
                        "fillcolor" => $fillcolor,
                        "style" => "filled",
                        "URL" => "project.php?id=".$rel["prj_id"]
                        )
                    );

                $p[$rel["prj_id"]] = true;
            }

            if (isset($rel["prj_prj_id"]) && strlen($rel["prj_prj_id"]))
            {
                $graph->addEdge(
                    array(
                        $rel["prj_prj_id"] => $rel["prj_id"]
                        ),
                    array(
                        'color' => '#444444'
                        )
                    );
            }
        }
    }

    if ($map)
        return($graph->fetch("cmapx"));
    else
        $graph->image(PHPAGA_IMAGETYPE);
}


/**
 * Draws a graph of all projects a person is owner or member of.
 *
 * @param int     $pe_id       Person ID
 * @param bool    $map         Output image map?
 *
 * @return string $coord       Image map coordinates (if $map is true)
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getRelationGraphperPerson ($pe_id, $map = false)
{

    if (!defined("PHPAGA_GRAPHVIZ_ENABLED") || !PHPAGA_GRAPHVIZ_ENABLED)
        return PHPAGA_ERR_DISABLED;

    $relations = array();
    $p = array();

    $result = phpaga_projects_getpersonprojects($persprj, $pe_id);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    $graph = new Image_GraphViz();

    foreach ($persprj as $rel)
    {

        if (isset($rel["prj_id"]) && is_numeric($rel["prj_id"])) {

            if (!isset($p[$rel["prj_id"]]))
            {
                if ($rel["relation"] == _("owner")) {
                    $color = "#00ff00";
                    $fillcolor = "#ddffdd";
                } else {
                    $color = "#000000";
                    $fillcolor = "#eeeeee";
                }

                $graph->addNode(
                    $rel["prj_id"],
                    array(
                        "label" => $rel["prj_title"],
                        "shape" => "box",
                        "color" => $color,
                        "fontsize" => 10,
                        "height" => .1,
                        "width" => .1,
                        "fillcolor" => $fillcolor,
                        "style" => "filled",
                        "URL" => "project.php?id=".$rel["prj_id"]
                        )
                    );

                $p[$rel["prj_id"]] = true;
            }

       }
    }

    if ($map)
        return($graph->fetch("cmapx"));
    else
        $graph->image(PHPAGA_IMAGETYPE);
}


/**
 * Gets a project's total duration
 *
 * @param int     $prjID    Project ID
 * @param string  $duration Duration
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getDuration ($prjID = 0,
                                      &$duration)
{
    $duration = "";

    if (!isset($prjID) || (!is_numeric($prjID)))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT SUM(op_duration) AS duration ".
        "FROM operations ".
        "WHERE prj_id = ".$db->quoteSmart($prjID);

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

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

    if ($result->numRows() < 1)
        $duration = phpaga_minutes2duration(0);
    else
        if ($qrow = $result->fetchRow())
            if ($qrow["duration"] == "")
                $duration = phpaga_minutes2duration(0);
            else
                $duration = phpaga_minutes2duration($qrow["duration"]);

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


/**
 * Gets a list of project categories from the database and returns them as the
 * content of an array.
 *
 * @param array $prjcatList  Array of project categories
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getCategoryList (&$prjcatList)
{

    $prjcatList = array();

    phpaga_db_connect($db);

    $query = "SELECT prjcat_id, prjcat_title ".
        "FROM projectcat ".
        "ORDER BY prjcat_title";

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

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

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);
        $prjcatList[$qrow["prjcat_id"]] = $qrow["prjcat_title"];
    }

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


/**
 * Gets a list of project stati from the database and returns them as the
 * content of an array.
 *
 * @param array $prjstatList  Array of project stati
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getStatusList (&$prjstatList)
{
    $prjstatList = array();

    phpaga_db_connect($db);

    $query = "SELECT prjstat_id, prjstat_title ".
        "FROM projectstatus ".
        "ORDER BY prjstat_title";

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

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

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);
        $prjstatList[$qrow["prjstat_id"]] = $qrow["prjstat_title"];
    }

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


/**
 * Checks if all fields of a project contain valid values.
 *
 * @param  array  $prjInfo  Array containing the project's information.
 *
 * @return array  $errors   Array containing error codes
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_validdata ($prjInfo)
{

    $errors = array();

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

    if (!phpaga_checkdate($prjInfo["prj_startdate"]))
        $errors[] = PHPAGA_ERR_PRJ_STARTDATE;

    if (!phpaga_checkdate($prjInfo["prj_estimatedend"]))
        $errors[] = PHPAGA_ERR_PRJ_ESTIMATEDENDDATE;

    if (!phpaga_checkdate($prjInfo["prj_deadline"]))
        $errors[] = PHPAGA_ERR_PRJ_DEADLINEDATE;

    if (!isset($prjInfo["prj_title"]) || !strlen(trim($prjInfo["prj_title"])))
        $errors[] = PHPAGA_ERR_PROJECT_INVALIDTITLE;

    if (isset($prjInfo["cpn_id"]) && !is_numeric($prjInfo["cpn_id"]))
        $errors[] = PHPAGA_ERR_COMPANY_INVALIDID;

    if (isset($prjInfo["prjcat_id"]) && !is_numeric($prjInfo["prjcat_id"]))
        $errors[] = PHPAGA_ERR_PRJCAT_INVALIDID;

    if (!isset($prjInfo["pe_id"]) || !is_numeric($prjInfo["pe_id"]))
        $errors[] = PHPAGA_ERR_PERSON_INVALIDID;

    if (isset($prjInfo["prj_prj_id"])
        && strlen($prjInfo["prj_prj_id"])
        && !is_numeric($prjInfo["prj_prj_id"]))

        $errors[] = PHPAGA_ERR_INVALID_PARENTPRJ;

    if (isset($prjInfo["prj_planned_manhours"])
        && strlen($prjInfo["prj_planned_manhours"])
        && !is_numeric($prjInfo["prj_planned_manhours"]))

        $errors[] = PHPAGA_ERR_INVALID_MANHOURS;

    if (isset($prjInfo["prj_planned_cost_manhours"])
        && strlen($prjInfo["prj_planned_cost_manhours"])
        && !is_numeric($prjInfo["prj_planned_cost_manhours"]))

        $errors[] = PHPAGA_ERR_INVALID_COST_MANHOURS;

    if (isset($prjInfo["prj_planned_cost_material"])
        && strlen($prjInfo["prj_planned_cost_material"])
        && !is_numeric($prjInfo["prj_planned_cost_material"]))

        $errors[] = PHPAGA_ERR_INVALID_COST_MATERIAL;

    if (isset($prjInfo["prj_billabletype"])
        && strlen($prjInfo["prj_billabletype"])
        && !is_numeric($prjInfo["prj_billabletype"]))

        $errors[] = PHPAGA_ERR_INVALID_BILLABLETYPE;

    return $errors;
}


/**
 * Adds a project to the database
 *
 * @param array  $prjInfo Array containing the project information
 * @param int    $prjID   Newly assigned project ID
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_add ($prjInfo, &$prjID)
{

    $prjID = "";

    $result = phpaga_projects_validdata ($prjInfo);

    if (count($result) > 0)
        return $result;

    phpaga_db_connect($db);

    $prjID = $db->nextId("projects_prj_id");

    if (!isset($prjInfo["cpn_id_issuebill"]) || (!is_numeric($prjInfo["cpn_id_issuebill"])))
        $prjInfo["cpn_id_issuebill"] = NULL;

    if (!isset($prjInfo["prj_title"]) || !strlen($prjInfo["prj_title"]))
        $prjInfo["prj_title"] = NULL;

    if (!isset($prjInfo["prj_desc"]) || !strlen($prjInfo["prj_desc"]))
        $prjInfo["prj_desc"] = NULL;

    if (!isset($prjInfo["prj_startdate"]) || !strlen($prjInfo["prj_startdate"]))
        $prjInfo["prj_startdate"] = NULL;

    if (!isset($prjInfo["prj_estimatedend"]) || !strlen($prjInfo["prj_estimatedend"]))
        $prjInfo["prj_estimatedend"] = NULL;

    if (!isset($prjInfo["prj_deadline"]) || !strlen($prjInfo["prj_deadline"]))
        $prjInfo["prj_deadline"] = NULL;

    if (!isset($prjInfo["prj_enddate"]) || !strlen($prjInfo["prj_enddate"]))
        $prjInfo["prj_enddate"] = NULL;

    if (!isset($prjInfo["prj_prj_id"]) || !is_numeric($prjInfo["prj_prj_id"]))
        $prjInfo["prj_prj_id"] = NULL;

    if (!isset($prjInfo["prj_manager_pe_id"]) || !is_numeric($prjInfo["prj_manager_pe_id"]))
        $prjInfo["prj_manager_pe_id"] = NULL;

    if (!isset($prjInfo["prj_planned_manhours"]) || !is_numeric($prjInfo["prj_planned_manhours"]))
        $prjInfo["prj_planned_manhours"] = NULL;

    if (!isset($prjInfo["prj_planned_cost_manhours"]) || !is_numeric($prjInfo["prj_planned_cost_manhours"]))
        $prjInfo["prj_planned_cost_manhours"] = NULL;

    if (!isset($prjInfo["prj_planned_cost_material"]) || !is_numeric($prjInfo["prj_planned_cost_material"]))
        $prjInfo["prj_planned_cost_material"] = NULL;

    if (!isset($prjInfo["prj_status"]) || !strlen($prjInfo["prj_status"]))
        $prjInfo["prj_status"] = PHPAGA_PROJECTSTATUS_OPEN;

    if (!isset($prjInfo["prj_billabletype"]) || !is_numeric($prjInfo["prj_billabletype"]))
        $prjInfo["prj_billabletype"] = NULL;

    if (!isset($prjInfo["prj_priority"]) || !is_numeric($prjInfo["prj_priority"]))
        $prjInfo["prj_priority"] = NULL;

    if (!isset($prjInfo["quot_id"]) || !is_numeric($prjInfo["quot_id"]))
        $prjInfo["quot_id"] = NULL;

    if (!isset($prjInfo["opcatlist"]) || !is_array($prjInfo["opcatlist"]))
        $prjInfo["opcatlist"] = array();

    $fields = array("prj_id" => $prjID,
                    "prj_title" => $prjInfo["prj_title"],
                    "prj_desc" => $prjInfo["prj_desc"],
                    "prj_startdate" => $prjInfo["prj_startdate"],
                    "prj_estimatedend" => $prjInfo["prj_estimatedend"],
                    "prj_deadline" => $prjInfo["prj_deadline"],
                    "prj_enddate" => $prjInfo["prj_enddate"],
                    "prj_status" => $prjInfo["prj_status"],
                    "cpn_id" => $prjInfo["cpn_id"],
                    "quot_id" => $prjInfo["quot_id"],
                    "prjcat_id" => $prjInfo["prjcat_id"],
                    "prj_planned_manhours" => $prjInfo["prj_planned_manhours"],
                    "prj_planned_cost_manhours" => $prjInfo["prj_planned_cost_manhours"],
                    "prj_planned_cost_material" => $prjInfo["prj_planned_cost_material"],
                    "pe_id" => $prjInfo["pe_id"],
                    "prj_prj_id" => $prjInfo["prj_prj_id"],
                    "cpn_id_issuebill" => $prjInfo["cpn_id_issuebill"],
                    "prj_billabletype" => $prjInfo["prj_billabletype"],
                    "prj_priority" => $prjInfo["prj_priority"],
                    "prj_manager_pe_id" => $prjInfo["prj_manager_pe_id"]
        );

    $result = $db->autoExecute("projects", $fields, DB_AUTOQUERY_INSERT);

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

    $result = phpaga_projects_sethourlyrates($prjID, $prjInfo["opcatlist"]);

    return $result;

}


/**
 * Updates a project
 *
 * @param array  $prjInfo Array containing the project information
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_update ($prjInfo)
{

    $result = phpaga_projects_validdata ($prjInfo);

    if (count($result) > 0)
        return $result;

    if (!isset($prjInfo["cpn_id_issuebill"]) || (!is_numeric($prjInfo["cpn_id_issuebill"])))
        $prjInfo["cpn_id_issuebill"] = NULL;

    if (!isset($prjInfo["prj_title"]) || !strlen($prjInfo["prj_title"]))
        $prjInfo["prj_title"] = NULL;

    if (!isset($prjInfo["prj_desc"]) || !strlen($prjInfo["prj_desc"]))
        $prjInfo["prj_desc"] = NULL;

    if (!isset($prjInfo["prj_startdate"]) || !strlen($prjInfo["prj_startdate"]))
        $prjInfo["prj_startdate"] = NULL;

    if (!isset($prjInfo["prj_estimatedend"]) || !strlen($prjInfo["prj_estimatedend"]))
        $prjInfo["prj_estimatedend"] = NULL;

    if (!isset($prjInfo["prj_deadline"]) || !strlen($prjInfo["prj_deadline"]))
        $prjInfo["prj_deadline"] = NULL;

    if (!isset($prjInfo["prj_enddate"]) || !strlen($prjInfo["prj_enddate"]))
        $prjInfo["prj_enddate"] = NULL;

    if (!isset($prjInfo["prj_prj_id"]) || !is_numeric($prjInfo["prj_prj_id"]))
        $prjInfo["prj_prj_id"] = NULL;

    if (!isset($prjInfo["prj_manager_pe_id"]) || !is_numeric($prjInfo["prj_manager_pe_id"]))
        $prjInfo["prj_manager_pe_id"] = NULL;

    if (!isset($prjInfo["prj_planned_manhours"]) || !is_numeric($prjInfo["prj_planned_manhours"]))
        $prjInfo["prj_planned_manhours"] = NULL;

    if (!isset($prjInfo["prj_planned_cost_manhours"]) || !is_numeric($prjInfo["prj_planned_cost_manhours"]))
        $prjInfo["prj_planned_cost_manhours"] = NULL;

    if (!isset($prjInfo["prj_planned_cost_material"]) || !is_numeric($prjInfo["prj_planned_cost_material"]))
        $prjInfo["prj_planned_cost_material"] = NULL;

    if (!isset($prjInfo["prj_status"]) || !strlen($prjInfo["prj_status"]))
        $prjInfo["prj_status"] = PHPAGA_PROJECTSTATUS_OPEN;

    if (!isset($prjInfo["prj_billabletype"]) || !is_numeric($prjInfo["prj_billabletype"]))
        $prjInfo["prj_billabletype"] = NULL;

    if (!isset($prjInfo["prj_priority"]) || !is_numeric($prjInfo["prj_priority"]))
        $prjInfo["prj_priority"] = NULL;

    if (!isset($prjInfo["opcatlist"]) || !is_array($prjInfo["opcatlist"]))
        $prjInfo["opcatlist"] = array();

    $fields = array("prj_title" => $prjInfo["prj_title"],
                    "prj_desc" => $prjInfo["prj_desc"],
                    "prj_startdate" => $prjInfo["prj_startdate"],
                    "prj_estimatedend" => $prjInfo["prj_estimatedend"],
                    "prj_deadline" => $prjInfo["prj_deadline"],
                    "prj_enddate" => $prjInfo["prj_enddate"],
                    "prj_status" => $prjInfo["prj_status"],
                    "cpn_id" => $prjInfo["cpn_id"],
                    "prjcat_id" => $prjInfo["prjcat_id"],
                    "prj_planned_manhours" => $prjInfo["prj_planned_manhours"],
                    "prj_planned_cost_manhours" => $prjInfo["prj_planned_cost_manhours"],
                    "prj_planned_cost_material" => $prjInfo["prj_planned_cost_material"],
                    "pe_id" => $prjInfo["pe_id"],
                    "prj_prj_id" => $prjInfo["prj_prj_id"],
                    "cpn_id_issuebill" => $prjInfo["cpn_id_issuebill"],
                    "prj_billabletype" => $prjInfo["prj_billabletype"],
                    "prj_priority" => $prjInfo["prj_priority"],
                    "prj_manager_pe_id" => $prjInfo["prj_manager_pe_id"]
        );

    phpaga_db_connect($db);
    $result = $db->autoExecute("projects", $fields, DB_AUTOQUERY_UPDATE,
                               "prj_id = ".$db->quoteSmart($prjInfo["prj_id"]));

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

    $result = phpaga_projects_sethourlyrates($prjInfo["prj_id"], $prjInfo["opcatlist"]);

    return $result;
}


/**
 * Delete a project.
 *
 * @param  int     $prj_id       Project to be deleted
 * @param  bool    $force        Force delete the project even if some
 *                               dependencies exist (in this case delete the
 *                               dependencies also). This does not work if
 *                               any of the operations associated to this
 *                               project are already billed.
 *
 * @return int     $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1.2
 */

function phpaga_projects_delete ($prj_id, $force = false)
{
    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    /* If operations are filed for this project which are also linked
     * to a bill, then this project cannot be deleted (not even when
     * $force is true).
     */

    $query = "SELECT prj_id ".
        "FROM operations ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        "AND bill_id IS NOT NULL; ";

    $result = $db->query($query);
    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

    /* any record matching the query? in that case we cannot delete
     * this project, as there are associated operations that are
     * already billed
     */

    if ($result->numRows() > 0)
        return PHPAGA_ERR_DEL_PRJBILLDEPENDENCIES;

    /* if $force is true the delete this project and all associated
     * operations, project memberships, and expenses
     */

    if ($force)
    {

        /* delete all
         * We need to remove the records from each table "manually"
         * as not all supported database systems can handle
         * relationships and triggers that could handle the
         * same job
         */

        $result = $db->query("DELETE FROM projects_hourlyrates WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        $result = $db->query("DELETE FROM project_members WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        $result = $db->query("DELETE FROM operations WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        $result = $db->query("DELETE FROM expenses WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        /* update all projects that have this project as their parent */

        $fields = array("prj_prj_id" => null);
        $result = $db->autoExecute("projects", $fields, DB_AUTOQUERY_UPDATE,
                                   "prj_prj_id = ".$db->quoteSmart($prj_id));

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

        $result = $db->query("DELETE FROM projects WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;
        else
            return PHPAGA_ERR_NONE;

    }
    else
    {
        /* $force is false, therefore remove the project only if there
         * are no other records depending on it
         */

        $query = "SELECT prj_id ".
            "FROM operations ".
            "WHERE prj_id = ".$db->quoteSmart($prj_id);

        $result = $db->query($query);
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        /* any record matching the query? in that case we cannot delete
         * this project, as there are dependencies
         */

        if ($result->numRows() > 0)
            return PHPAGA_ERR_DEL_DEPENDENCIES;

        $query = "SELECT prj_id ".
            "FROM expenses ".
            "WHERE prj_id = ".$db->quoteSmart($prj_id);

        $result = $db->query($query);
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        /* any record matching the query? in that case we cannot delete
         * this project, as there are dependencies
         */

        if ($result->numRows() > 0)
            return PHPAGA_ERR_DEL_DEPENDENCIES;

        $query = "SELECT prj_id ".
            "FROM project_members ".
            "WHERE prj_id = ".$db->quoteSmart($prj_id);

        $result = $db->query($query);
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        /* any record matching the query? in that case we cannot delete
         * this project, as there are dependencies
         */

        if ($result->numRows() > 0)
            return PHPAGA_ERR_DEL_DEPENDENCIES;

        /* do any projects have this project as their parent? */
        $result = $db->query("SELECT prj_id FROM projects WHERE prj_prj_id = ".
                             $db->quoteSmart($prj_id));

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

        if ($result->numRows() > 0)
            return PHPAGA_ERR_DEL_DEPENDENCIES;

        $result = $db->query("DELETE FROM projects_hourlyrates WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        $result = $db->query("DELETE FROM projects WHERE prj_id = ".$db->quoteSmart($prj_id));
        if (DB::isError($result))
            return PHPAGA_ERR_DB_EXEC_QUERY;

        return PHPAGA_ERR_NONE;
    }
}


/**
 * Gets a list of projects from the database and returns them as the
 * content of an array.
 *
 * @param array  $projectList  Array of projects
 *
 * @return int   $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getProjectList (&$projectList)
{
    $projectList = array();

    /* check if current user can view all projects or only those she is a member of */

    $viewall = (phpaga_user_hasperm(PHPAGA_PERM_MANAGE_PROJECTS)
                || phpaga_user_hasperm(PHPAGA_PERM_VIEW_OTHERPROJECTS)
                || phpaga_user_hasperm(PHPAGA_PERM_MANAGE_OTHERPROJECTS));

    phpaga_db_connect($db);

    $query = "SELECT distinct projects.prj_id, prj_title ".
        "FROM projects ";

    if (!$viewall)
        $query .= "LEFT JOIN project_members on project_members.prj_id = projects.prj_id ".
            "WHERE (projects.pe_id = ".$db->quoteSmart($_SESSION["auth_user"]["pe_id"]).
            " OR project_members.pe_id = ".$db->quoteSmart($_SESSION["auth_user"]["pe_id"]).")";

    $query .= " ORDER BY prj_title";

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

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

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);
        $projectList[$qrow["prj_id"]] = $qrow["prj_title"];
    }
}


/**
 * Gets a list of projects and dates for the projects Gantt chart.
 *
 * @param  array  $projectList  Array of projects
 *
 * @return int    $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getProjectDatesList (&$projectList)
{
    $projectList = array();
    $h_end = "";
    $h_perc = 0;
    $h_status = "";

    phpaga_db_connect($db);

    $query = "SELECT projects.prj_id, prj_title, prj_startdate, ".
        "prj_enddate, prj_estimatedend ".
        "FROM projects ";

    if (!(phpaga_user_hasperm(PHPAGA_PERM_VIEW_OTHERPROJECTS)
          || phpaga_user_hasperm(PHPAGA_PERM_MANAGE_OTHERPROJECTS)))
    {

        $pe_id = $_SESSION["auth_user"]["pe_id"];

        $query .= "LEFT JOIN project_members pm on (pm.prj_id = projects.prj_id and pm.pe_id = ".
            $db->quoteSmart($pe_id).") ".
            "WHERE (projects.pe_id = ".$db->quoteSmart($pe_id).
            " OR pm.pe_id = ".$db->quoteSmart($pe_id).
            " OR projects.prj_manager_pe_id = ".$db->quoteSmart($pe_id).") ";
    }

    $query .= "ORDER BY prj_startdate, prj_enddate, prj_estimatedend";

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

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

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

        /* if we do not have a valid project end or estimated end
         * then take the current day as project end
         */

        if (isset($qrow["prj_enddate"])
            && strlen($qrow["prj_enddate"]))
        {
            $h_end = $qrow["prj_enddate"];
            $h_perc = 100;
            $h_status = PHPAGA_PROJECTSTATUS_CLOSED;
        }
        else
        {

            $h_perc = 0;

            if (isset($qrow["prj_estimatedend"])
                && strlen($qrow["prj_estimatedend"]))
            {
                $h_end = $qrow["prj_estimatedend"];

                if (strtotime($qrow["prj_estimatedend"]) < mktime())
                    $h_status = PHPAGA_PROJECTSTATUS_OVERDUE;
                else
                    $h_status = PHPAGA_PROJECTSTATUS_OPEN;
            }
            else
            {
                $h_end = date("Y-m-d");
                $h_status = PHPAGA_PROJECTSTATUS_OPEN;
            }
        }

        $projectList[] = array("prj_id" => $qrow["prj_id"],
                               "prj_title" => $qrow["prj_title"],
                               "prj_startdate" => $qrow["prj_startdate"],
                               "enddate" => $h_end,
                               "percentage" => $h_perc,
                               "status" => $h_status
            );
    }

    return PHPAGA_ERR_NONE;
}


/**
 * Loads all project categories into an array.
 *
 * @param array  $prjcatInfo   Array containing the results
 *
 * @return int   $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getPrjcats (&$prjcatInfo)
{
    $prjcatInfo = array();

    phpaga_db_connect($db);

    $query = "SELECT prjcat_id, prjcat_title ".
        "FROM projectcat ORDER BY prjcat_title ";

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

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

    if ($result->numRows() < 1)
        return PHPAGA_ERR_DB_NO_ROWS;

    while ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);
        $prjcatInfo[] = $qrow;
    }

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


/**
 * Add a new project category.
 *
 * @param  string  $prjcat_title  Project category to be added
 *
 * @return int     $result        A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_addPrjcat ($prjcat_title)
{

    if (!isset($prjcat_title) || !strlen ($prjcat_title))
        return PHPAGA_ERR_PRJCAT_INVALIDTITLE;

    phpaga_db_connect($db);

    $fields = array("prjcat_id" => $db->nextId("projectcat_prjcat_id"),
                    "prjcat_title" => $prjcat_title
        );

    $result = $db->autoExecute("projectcat", $fields, DB_AUTOQUERY_INSERT);

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

    return PHPAGA_ERR_NONE;
}


/**
 * Delete a project category.
 *
 * @param  int     $prjcat_id    Project category to be deleted
 *
 * @return int     $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_deletePrjcat ($prjcat_id)
{
    if (!isset($prjcat_id) || !is_numeric($prjcat_id))
        return PHPAGA_ERR_PRJCAT_INVALIDID;

    phpaga_db_connect($db);

    /* first check, if this prjcat is needed by any table relationship
     * if it is not needed, then delete it, otherwise don't */

    $query = "SELECT prj_id ".
        "FROM projects WHERE prjcat_id = ".$db->quoteSmart($prjcat_id);

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

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

    /* any record matching the query? in that case we cannot delete
     * this project category, as there are dependencies
     */

    if ($result->numRows() > 0)
        return PHPAGA_ERR_DEL_DEPENDENCIES;

    $query = "DELETE FROM projectcat ".
        "WHERE prjcat_id = ".$db->quoteSmart($prjcat_id);

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

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;
    else
        return PHPAGA_ERR_NONE;
}


/**
 * Loads all project stati into an array.
 *
 * @param array  $prjstatInfo   Array containing the results
 *
 * @return int   $result       A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_getPrjstatus (&$prjstatInfo, $assoc = false)
{
    $prjstatInfo = array();

    phpaga_db_connect($db);

    $query = "SELECT prjstat_id, prjstat_title ".
        "FROM projectstatus ".
        "ORDER BY prjstat_title ";

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

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

    if ($result->numRows() < 1)
        return PHPAGA_ERR_DB_NO_ROWS;

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

        if (!$assoc)
            $prjstatInfo[] = $qrow;
        else
            $prjstatInfo[$qrow["prjstat_id"]] = $qrow["prjstat_title"];
    }

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


/**
 * Adds a new project status.
 *
 * @param  string  $prjstat_title  Project status to be added
 *
 * @return int     $result         A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_addPrjstatus ($prjstat_title)
{

    if (!isset($prjstat_title) || !strlen ($prjstat_title))
        return PHPAGA_ERR_PRJSTAT_INVALIDTITLE;

    phpaga_db_connect($db);

    $fields = array("prjstat_id" => $db->nextId("projectstatus_prjstat_id"),
                    "prjstat_title" => $prjstat_title
        );

    $result = $db->autoExecute("projectstatus", $fields, DB_AUTOQUERY_INSERT);

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

    return PHPAGA_ERR_NONE;
}


/**
 * Deletes a project status.
 *
 * @param  int     $prjstat_id    Project status to be added
 *
 * @return int     $result        A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_deletePrjstatus ($prjstat_id)
{
    if (!isset($prjstat_id) || !is_numeric($prjstat_id))
        return PHPAGA_ERR_PRJSTAT_INVALIDID;

    phpaga_db_connect($db);

    /* first check, if this prjstat is needed by any table relationship
     * if it is not needed, then delete it, otherwise don't
     */

    $query = "SELECT prj_id ".
        "FROM projects WHERE prj_status = ".$db->quoteSmart($prjstat_id);

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

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

    /* any record matching the query? in that case we cannot delete
     * this project status, as there are dependencies
     */

    if ($result->numRows() > 0)
        return PHPAGA_ERR_DEL_DEPENDENCIES;

    $query = "DELETE FROM projectstatus ".
        "WHERE prjstat_id = ".$db->quoteSmart($prjstat_id);

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

    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;
    else
        return PHPAGA_ERR_NONE;
}


/**
 * Checks if all fields of a project member contain valid values.
 *
 * @param  array  $member  Array containing the project's information.
 *
 * @return array  $errors   Array containing error codes
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_projects_validmemberdata ($member)
{

    $errors = array();

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

    if (isset($member["prj_id"]) && !is_numeric($member["prj_id"]))
        $errors[] = PHPAGA_ERR_PROJECT_INVALIDID;

    if (isset($member["jcat_id"]) && !is_numeric($member["jcat_id"]))
        $errors[] = PHPAGA_ERR_JCAT_INVALIDID;

    if (!isset($member["pe_id"]) || !is_numeric($member["pe_id"]))
        $errors[] = PHPAGA_ERR_PERSON_INVALIDID;

    if (isset($member["prjmem_hourlyrate"]) && strlen($member["prjmem_hourlyrate"]) &&
        !is_numeric($member["prjmem_hourlyrate"]))

        $errors[] = PHPAGA_ERR_INVALID_HOURLYRATE;

    return $errors;
}


/**
 * Adds a project member
 *
 * @param  array  $member  Array containing the membership information
 *
 * @return int    $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_projects_addmember ($member)
{

    $result = phpaga_projects_validmemberdata($member);

    if (count($result) > 0)
        return $result;

    phpaga_db_connect($db);

    if (!isset($member["jcat_id"]) || !is_numeric($member["jcat_id"]))
        $member["jcat_id"] = NULL;

    if (!isset($member["prjmem_pe_id"]) || !is_numeric($member["prjmem_pe_id"]))
        $member["prjmem_pe_id"] = NULL;

    if (!isset($member["prjmem_hourlyrate"])
        || !is_numeric($member["prjmem_hourlyrate"]))

        $member["prjmem_hourlyrate"] = NULL;

    $fields = array("prj_id" => $member["prj_id"],
                    "pe_id" => $member["pe_id"],
                    "jcat_id" => $member["jcat_id"],
                    "prjmem_pe_id" => $member["prjmem_pe_id"],
                    "prjmem_hourlyrate" => $member["prjmem_hourlyrate"]
        );

    $result = $db->autoExecute("project_members", $fields, DB_AUTOQUERY_INSERT);

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

    if (isset($member["opcats"]) && is_array($member["opcats"]))
        $result = phpaga_projects_sethourlyrates($member["prj_id"], $member["opcats"], $member["pe_id"]);

    return $result;
}


/**
 * Updates a project member's information
 *
 * @param  array  $member  Array containing the membership information
 *
 * @return int    $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_updatemember ($member)
{

    $result = phpaga_projects_validmemberdata($member);

    if (count($result) > 0)
        return $result;

    phpaga_db_connect($db);

    if (!isset($member["jcat_id"]) || !is_numeric($member["jcat_id"]))
        $member["jcat_id"] = NULL;

    if (!isset($member["prjmem_pe_id"]) || !is_numeric($member["prjmem_pe_id"]))
        $member["prjmem_pe_id"] = NULL;

    if (!isset($member["prjmem_hourlyrate"])
        || !is_numeric($member["prjmem_hourlyrate"]))

        $member["prjmem_hourlyrate"] = NULL;

    $fields = array("jcat_id" => $member["jcat_id"],
                    "prjmem_pe_id" => $member["prjmem_pe_id"],
                    "prjmem_hourlyrate" => $member["prjmem_hourlyrate"]
        );

    $result = $db->autoExecute("project_members", $fields, DB_AUTOQUERY_UPDATE,
                               "prj_id = ".$db->quoteSmart($member["prj_id"]).
                               " AND pe_id = ".$db->quoteSmart($member["pe_id"]));

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

    if (isset($member["opcats"]) && is_array($member["opcats"]))
        $result = phpaga_projects_sethourlyrates($member["prj_id"], $member["opcats"], $member["pe_id"]);

    return $result;
}


/**
 * Fetches the members of a project into an array.
 *
 * @param int    $prj_id      Project ID
 * @param array  $prj_members Array containing the members information
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_project_getmembers ($prj_id, &$prj_members)
{

    $prj_members = array();

    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT project_members.pe_id, ".
        "persons.pe_lastname, persons.pe_firstname, ".
        "project_members.jcat_id, jcat_title, prjmem_added, ".
        "prjmem_pe_id, pmpers.pe_lastname AS pe_lastname_owner, ".
        "pmpers.pe_firstname AS pe_firstname_owner, ".
        "prjmem_hourlyrate ".
        "FROM project_members ".
        "LEFT JOIN persons ON persons.pe_id = project_members.pe_id ".
        "LEFT JOIN jobcat ON jobcat.jcat_id = project_members.jcat_id ".
        "LEFT JOIN persons pmpers ON pmpers.pe_id = prjmem_pe_id ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        "ORDER BY pe_lastname, pe_firstname ";

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

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

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

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

        $qrow["pe_person_owner"] =
            phpaga_persons_formatname($qrow["pe_lastname_owner"],
                                      $qrow["pe_firstname_owner"]);

        $prj_members[] = $qrow;
    }

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


/**
 * Fetches information about a specific member of a project.
 *
 * @param int    $prj_id      Project ID
 * @param int    $pe_id       Pe ID
 * @param array  $prj_members Array containing the information
 *
 * @return int   $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_project_getMemberInfo ($prj_id, $pe_id, &$member)
{

    $member = array();

    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;
    elseif (!isset($pe_id) || !is_numeric($pe_id))
        return PHPAGA_ERR_PERSON_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT project_members.pe_id, ".
        "persons.pe_lastname, persons.pe_firstname, ".
        "project_members.jcat_id, jcat_title, prjmem_added, ".
        "prjmem_pe_id, pmpers.pe_lastname AS pe_lastname_owner, ".
        "pmpers.pe_firstname AS pe_firstname_owner, ".
        "prjmem_hourlyrate, project_members.prj_id ".
        "FROM project_members ".
        "LEFT JOIN persons ON persons.pe_id = project_members.pe_id ".
        "LEFT JOIN jobcat ON jobcat.jcat_id = project_members.jcat_id ".
        "LEFT JOIN persons pmpers ON pmpers.pe_id = prjmem_pe_id ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        " AND project_members.pe_id = ".$db->quoteSmart($pe_id);

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

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

    if ($qrow = $result->fetchRow())
    {
        phpaga_sanitizearray($qrow);

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

        $qrow["pe_person_owner"] =
            phpaga_persons_formatname($qrow["pe_lastname_owner"],
                                      $qrow["pe_firstname_owner"]);

        $member = $qrow;
    }

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


/**
 * Checks whether a person is a member of a the secified project
 *
 * @param int   $prj_id     Project ID
 * @param int   $pe_id      Person-ID
 * @param bool  &$ismember  TRUE is person is member of project
 *
 * @return int   $result    A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_project_is_member ($prj_id, $pe_id, &$ismember)
{

    $ismember = false;

    if (!isset($prj_id) || !is_numeric($prj_id)
        || !isset($pe_id) || !is_numeric($pe_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    phpaga_db_connect($db);

    $query = "SELECT prj_id ".
        "FROM project_members ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        "AND pe_id = ".$db->quoteSmart($pe_id);

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

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

    if ($result->numRows() > 0)
        $ismember = true;

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


/**
 * Checks whether a person can view a specific project. This is true
 * is one of the following cases applies:
 *
 * - the user is the project's owner and has the permission to manage
 *   projects
 * - the user is the project's manager
 * - the user is a member of the project
 * - the user can view other people's projects
 * - the user can manage other people's projects
 *
 * @param int   $prj_id   Project ID
 * @param int   $pe_id    Person-ID (optional)
 *
 * @return bool $result   TRUE if person can view project
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_project_canview ($prj_id, $pe_id = 0)
{

    if (!isset($prj_id) || !is_numeric($prj_id)
        || !isset($pe_id) || !is_numeric($pe_id))
        return false;

    if ($pe_id == 0)
        $pe_id = $_SESSION["auth_user"]["pe_id"];

    $ismember = false;
    $isowner = false;
    $ismanager = false;

    $result = phpaga_project_is_member($prj_id, $pe_id, $ismember);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    $result = phpaga_projects_isowner($isowner, $prj_id, $pe_id);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    $result = phpaga_projects_ismanager($ismanager, $prj_id, $pe_id);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    return($isowner && phpaga_user_hasperm(PHPAGA_PERM_MANAGE_PROJECTS)
           || $ismanager || $ismember
           || phpaga_user_hasperm(PHPAGA_PERM_VIEW_OTHERPROJECTS)
           || phpaga_user_hasperm(PHPAGA_PERM_MANAGE_OTHERPROJECTS));

}


/**
 * Checks whether a person can manage a specific project. This is true
 * is one of the following cases applies:
 *
 * - the user is the project's owner and has the permission to manage
 *   projects
 * - the user is the project's manager
 * - the user can manage other people's projects
 *
 * @param int   $prj_id   Project ID
 * @param int   $pe_id    Person-ID (optional)
 *
 * @return bool $result   TRUE if person can view project
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_project_canmanage ($prj_id, $pe_id = 0)
{

    if (!isset($prj_id) || !is_numeric($prj_id)
        || !isset($pe_id) || !is_numeric($pe_id))
        return false;

    if ($pe_id == 0)
        $pe_id = $_SESSION["auth_user"]["pe_id"];

    $isowner = false;
    $ismanager = false;

    $result = phpaga_projects_isowner($isowner, $prj_id, $pe_id);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    $result = phpaga_projects_ismanager($ismanager, $prj_id, $pe_id);
    if ($result != PHPAGA_ERR_NONE)
        return false;

    return($isowner && phpaga_user_hasperm(PHPAGA_PERM_MANAGE_PROJECTS)
           || $ismanager
           || phpaga_user_hasperm(PHPAGA_PERM_MANAGE_OTHERPROJECTS));

}


/**
 * Deletes a project member.
 *
 * @param  int     $prj_id      Project ID
 * @param  int     $pe_id       Person ID
 *
 * @return int     $result      A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.2
 */

function phpaga_projects_deletemember ($prj_id, $pe_id)
{
    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;
    elseif (!isset($pe_id) || !is_numeric($pe_id))
        return PHPAGA_ERR_PERSON_INVALIDID;

    phpaga_db_connect($db);

    if (phpaga_db_transaction_begin($db) != PHPAGA_ERR_NONE)
        return PHPAGA_ERR_DB_TRANSACTBEGIN;

    $query = "DELETE FROM projects_hourlyrates ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        "AND pe_id = ".$db->quoteSmart($pe_id);

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

    if (DB::isError($result))
    {
        if (phpaga_db_transaction_rollback($db) != PHPAGA_ERR_NONE)
            return PHPAGA_ERR_DB_TRANSACTROLLBACK;
        else
            return PHPAGA_ERR_DB_EXEC_QUERY;
    }

    $query = "DELETE FROM project_members ".
        "WHERE prj_id = ".$db->quoteSmart($prj_id)." ".
        "AND pe_id = ".$db->quoteSmart($pe_id);

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

    if (DB::isError($result))
    {
        if (phpaga_db_transaction_rollback($db) != PHPAGA_ERR_NONE)
            return PHPAGA_ERR_DB_TRANSACTROLLBACK;
        else
            return PHPAGA_ERR_DB_EXEC_QUERY;
    }

    if (phpaga_db_transaction_commit($db) != PHPAGA_ERR_NONE)
        return PHPAGA_ERR_DB_TRANSACTCOMMIT;

    /*
     $t = phpaga_mail_gettemplate("prjmember_deleted.txt");

     if (phpaga_mail_enabled() && $t)
     {
     $body = sprintf($t, $prj_title, $date, $pe_name);

        $headers = array("From" => "",
                         "To" => "",
                         "Subject" => _("phpaga: Project member deleted"));

        $result = phpaga_mail_send($headers, $body);

        if ($result != PHPAGA_ERR_NONE)
            return $result;
    }
    */

    return PHPAGA_ERR_NONE;
}


/**
 * Determine whether some person is the owner of a project. If pe_id
 * is omitted, the current user's pe_id is used.
 *
 * @param bool   &$isowner  Is pe_id the owner of prj_id?
 * @param int    $prj_id    Project ID
 *
 * @param int    $pe_id     Person ID (optional, if omitted, the
 *                          current user's pe_id is used)
 *
 * @return int   $result    A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_isowner (&$isowner, $prj_id, $pe_id = null)
{

    $isowner = false;

    if (($pe_id == null) && isset($_SESSION["auth_user"])
        && isset($_SESSION["auth_user"]["pe_id"])
        && is_numeric($_SESSION["auth_user"]["pe_id"]))

        $pe_id = $_SESSION["auth_user"]["pe_id"];

    phpaga_db_connect($db);

    $prjpe_id = $db->getOne("SELECT pe_id FROM projects WHERE prj_id = ".
                            $db->quoteSmart($prj_id));

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

    $isowner = ($prjpe_id == $pe_id);

    return PHPAGA_ERR_NONE;
}


/**
 * Determine whether some person is the manager of a project. If pe_id
 * is omitted, the current user's pe_id is used.
 *
 * @param bool   &$ismanager  Is pe_id the manager of prj_id?
 * @param int    $prj_id    Project ID
 *
 * @param int    $pe_id     Person ID (optional, if omitted, the
 *                          current user's pe_id is used)
 *
 * @return int   $result    A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_ismanager (&$ismanager, $prj_id, $pe_id = null)
{

    $ismanager = false;

    if (($pe_id == null) && isset($_SESSION["auth_user"])
        && isset($_SESSION["auth_user"]["pe_id"])
        && is_numeric($_SESSION["auth_user"]["pe_id"]))

        $pe_id = $_SESSION["auth_user"]["pe_id"];

    phpaga_db_connect($db);

    $prjpe_id = $db->getOne("SELECT prj_manager_pe_id FROM projects WHERE prj_id = ".
                            $db->quoteSmart($prj_id));

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

    $ismanager = ($prjpe_id == $pe_id);

    return PHPAGA_ERR_NONE;
}


/**
 * Get a list of projects a person is the owner or a member of.
 *
 * @param bool &persprj     Array containing the projects a person is
 *                          either the owner or a member of.
 *
 * @param int    $pe_id     Person ID (optional, if omitted, the
 *                          current user's pe_id is used)
 *
 * @return int   $result    A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getpersonprojects (&$persprj, $pe_id = null)
{

    $persprj = array();

    $data = array();

    if (($pe_id == null) && isset($_SESSION["auth_user"])
        && isset($_SESSION["auth_user"]["pe_id"])
        && is_numeric($_SESSION["auth_user"]["pe_id"]))

        $pe_id = $_SESSION["auth_user"]["pe_id"];


    phpaga_db_connect($db);

    $data = $db->getAll("SELECT prj_id, prj_title FROM projects WHERE pe_id = ".
                        $db->quoteSmart($pe_id).
                        " ORDER by prj_title");

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

    foreach ($data as $d)
        $persprj[$d["prj_id"]] = array("prj_id" => $d["prj_id"],
                                       "prj_title" => $d["prj_title"],
                                       "relation" => _("owner"));

    $data = array();

    $data = $db->getAll("SELECT p.prj_id, prj_title FROM projects p ".
                        "LEFT JOIN project_members m ".
                        "ON m.prj_id = p.prj_id ".
                        "WHERE m.pe_id = ".
                        $db->quoteSmart($pe_id).
                        " ORDER by prj_title");

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

    foreach ($data as $d)
        if (isset($persprj[$d["prj_id"]]))
            $persprj[$d["prj_id"]]["relation"] .= ", "._("member");
        else
            $persprj[$d["prj_id"]] = array("prj_id" => $d["prj_id"],
                                           "prj_title" => $d["prj_title"],
                                           "relation" => _("member"));

    return PHPAGA_ERR_NONE;
}


/**
 * Get the highest project ID.
 *
 * @param int    $prj_id    Project ID
 *
 * @return int   $result    A result code.
 *
 * @author Mark Parssey <markpa@users.sourceforge.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getLastProject (&$prj_id)
{

    phpaga_db_connect($db);

    $query = "SELECT max(prj_id) as prj_id from projects ";

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

    if ((!DB::isError($result)) && ($result->numRows() > 0)
        && ($row = $result->fetchRow()))
    {
        $prj_id = $row["prj_id"];
    }

    return PHPAGA_ERR_NONE;
}


/**
 * Sets a list of project priorities and returns them as the content
 * of an array.
 *
 * @param array $prjpriorityList  Array of project priorities
 *
 * @return int   $result  A result code.
 *
 * @author Mark Parssey <markpa@users.sourceforge.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getPriorityList (&$prjpriorityList)
{
    $prjpriorityList = array(1 => 1,2,3,4,5,6,7,8,9);

    phpaga_arrayAddOption($prjpriorityList, PHPAGA_OPTION_NONE);

}


/**
 * Returns the sort order for the various fields that are displayed in
 * the projects list (required for the templates). of an array.
 *
 * @param  int    $order  Selected "order by"
 *
 * @return array  $sort   Sort order
 *
 * @author Mark Parssey <markpa@users.sourceforge.net>
 * @since phpaga 0.3
 */

function phpaga_projects_getSortOrder($order)
{

    $sort = array();

    switch ($order)
    {

        case ORDER_PRJ_PRJSTATUS_ASC:
            $sort["pr"] = ORDER_PRJ_PR_DESC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_DESC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_CUSTOMER_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_DESC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_STARTDATE_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_DESC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_PROJENDDATE_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_DESC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_PROJDEADLINE_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_DESC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_PRJCAT_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_DESC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_DURATION_ASC:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_DESC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_PR_ASC:
            $sort["pr"] = ORDER_PRJ_PR_DESC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;

        case ORDER_PRJ_PRIORITY_ASC:
            $sort["pr"] = ORDER_PRJ_PR_DESC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_DESC;
            break;

        default:
            $sort["pr"] = ORDER_PRJ_PR_ASC;
            $sort["prjstat"] = ORDER_PRJ_PRJSTATUS_ASC;
            $sort["cpn"] = ORDER_PRJ_CUSTOMER_ASC;
            $sort["start"] = ORDER_PRJ_STARTDATE_ASC;
            $sort["end"] = ORDER_PRJ_PROJENDDATE_ASC;
            $sort["deadline"] = ORDER_PRJ_PROJDEADLINE_ASC;
            $sort["prjcat"] = ORDER_PRJ_PRJCAT_ASC;
            $sort["duration"] = ORDER_PRJ_DURATION_ASC;
            $sort["prio"] = ORDER_PRJ_PRIORITY_ASC;
            break;
    }

    return $sort;
}


/**
 * Sets the default hourly rates per task type for a given project. If
 * the person ID is set, the settings for that particular person and
 * project are stored.
 *
 * @param  int    $prj_id  Project ID
 * @param  array  $opcats  Array with hourly rates per task type
 * @param  int    $pe_id   Person ID (optional)
 *
 * @return int    $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_sethourlyrates($prj_id, $opcats, $pe_id = null)
{

    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    if (!isset($opcats) || !is_array($opcats))
        return PHPAGA_ERR_INVALID_PARAMTYPE;

    if (isset($pe_id) && strlen($pe_id) && !is_numeric($pe_id))
        return PHPAGA_ERR_INVALID_PARAMTYPE;

    phpaga_db_connect($db);

    if (phpaga_db_transaction_begin($db) != PHPAGA_ERR_NONE)
        return PHPAGA_ERR_DB_TRANSACTBEGIN;

    if ($pe_id == null)
        $equals_pe_id = "IS NULL ";
    else
        $equals_pe_id = "= ".$db->quoteSmart($pe_id);

    $result = $db->query("DELETE FROM projects_hourlyrates WHERE pe_id ".$equals_pe_id.
                         " AND prj_id = ".$db->quoteSmart($prj_id));

    if (DB::isError($result))
    {
        if (phpaga_db_transaction_rollback($db) != PHPAGA_ERR_NONE)
            return PHPAGA_ERR_DB_TRANSACTROLLBACK;
        else
            return PHPAGA_ERR_DB_EXEC_QUERY;
    }

    $sth = $db->prepare("INSERT INTO projects_hourlyrates (phr_id, pe_id_lastmod, prj_id, pe_id, opcat_id, phr_hourlyrate) ".
                        "VALUES (?, ?, ?, ?, ?, ?)");

    foreach($opcats as $opcat) {
        if (isset($opcat["opcat_id"]) && is_numeric($opcat["opcat_id"])
            && isset($opcat["opcat_hourlyrate"]) && is_numeric($opcat["opcat_hourlyrate"]))
        {
            $fields = array($db->nextId("projects_hourlyrates_phr_id"),
                            $_SESSION["auth_user"]["pe_id"],
                            $prj_id,
                            $pe_id,
                            $opcat["opcat_id"],
                            $opcat["opcat_hourlyrate"]);

            $result = $db->execute($sth, $fields);

            if (DB::isError($result))
            {
                if (phpaga_db_transaction_rollback($db) != PHPAGA_ERR_NONE)
                    return PHPAGA_ERR_DB_TRANSACTROLLBACK;
                else
                    return PHPAGA_ERR_DB_EXEC_QUERY;
            }
        }
    }

    if (phpaga_db_transaction_commit($db) != PHPAGA_ERR_NONE)
        return PHPAGA_ERR_DB_TRANSACTCOMMIT;

    return PHPAGA_ERR_NONE;

}


/**
 * Retrieves the default hourly rates per task type for a given
 * project. If task type is not set on project level, its title is
 * fetched from the task type list and the default rate is presumed to
 * be null. If the person ID is set, the settings for that particular
 * person and project are fetched.
 *
 * @param  int    $prj_id  Project ID
 * @param  array  &$opcats Array with hourly rates per task type
 * @param  int    $pe_id   Person ID (optional)
 *
 * @return int    $result  A result code.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_projects_gethourlyrates($prj_id, &$opcats, $pe_id = null)
{

    $opcats = array();

    if (!isset($prj_id) || !is_numeric($prj_id))
        return PHPAGA_ERR_PROJECT_INVALIDID;

    if (isset($pe_id) && strlen($pe_id) && !is_numeric($pe_id))
        return PHPAGA_ERR_INVALID_PARAMTYPE;

    phpaga_db_connect($db);


    if ($pe_id == null)
        $equals_pe_id = "IS NULL ";
    else
        $equals_pe_id = "= ".$db->quoteSmart($pe_id);

    $query = "(SELECT p.opcat_id, opcat_title, opcat_color, p.phr_hourlyrate as opcat_hourlyrate ".
        "FROM projects_hourlyrates p ".
        "LEFT JOIN operationcat o ON o.opcat_id = p.opcat_id ".
        "WHERE p.pe_id ".$equals_pe_id." AND p.prj_id = ".$db->quoteSmart($prj_id).") ".
        "UNION (SELECT opcat_id, opcat_title, opcat_color, NULL as opcat_hourlyrate ".
        "FROM operationcat ".
        "WHERE opcat_id NOT IN ".
        "(SELECT opcat_id FROM projects_hourlyrates WHERE pe_id ".$equals_pe_id.
        " AND prj_id = ".$db->quoteSmart($prj_id).")) order by opcat_title ";

    $result = $db->query($query);
    if (DB::isError($result))
        return PHPAGA_ERR_DB_EXEC_QUERY;

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

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

?>