Location: PHPKode > projects > ETraxis > etraxis-2.1.1/src/dbo/states.php
<?php

/**
 * States
 *
 * This module provides API to work with eTraxis states.
 * See also {@link http://www.etraxis.org/docs-schema.php#tbl_states tbl_states} database table.
 *
 * @package DBO
 * @subpackage States
 */

//--------------------------------------------------------------------------------------------------
//
//  eTraxis - Records tracking web-based system.
//  Copyright (C) 2005-2009 by Artem Rodygin
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//--------------------------------------------------------------------------------------------------
//  Author                  Date            Description of modifications
//--------------------------------------------------------------------------------------------------
//  Artem Rodygin           2005-03-06      new-001: Records tracking web-based system should be implemented.
//  Artem Rodygin           2005-07-24      new-009: Records filter.
//  Artem Rodygin           2005-08-22      bug-046: Query 'filters/fsdelall.sql' is not found.
//  Artem Rodygin           2005-08-23      bug-047: Removable state will not be removed in some cases.
//  Artem Rodygin           2005-08-23      new-053: All the calls of DAL API functions should be moved to DBO API.
//  Artem Rodygin           2005-09-01      bug-079: String database columns are not enough to store UTF-8 values.
//  Artem Rodygin           2005-09-04      bug-087: New state with empty name or abbreviation can be created.
//  Artem Rodygin           2005-09-12      new-108: Increase maximum length of state name up to 50 characters.
//  Artem Rodygin           2005-09-27      new-141: Source code review.
//  Artem Rodygin           2005-10-22      bug-166: Some filters & subscriptions should be removed when a project, template, or state has been deleted.
//  Artem Rodygin           2005-10-22      bug-163: Some filters are malfunctional.
//  Artem Rodygin           2006-02-10      new-209: Default permissions for new states.
//  Artem Rodygin           2006-03-16      new-175: Implement user roles in permissions.
//  Artem Rodygin           2006-04-21      bug-242: Unexpected message "State with entered name or abbreviation already exists".
//  Artem Rodygin           2006-04-21      new-247: The 'responsible' user role should be obliterated.
//  Artem Rodygin           2006-06-25      new-222: Email reminders.
//  Artem Rodygin           2006-10-17      new-361: Extended custom queries.
//  Artem Rodygin           2007-01-05      new-491: [SF1647212] Group-wide transition permission.
//  Artem Rodygin           2007-09-10      new-579: Rework "state abbreviation" into "state short name".
//  Artem Rodygin           2007-09-11      new-574: Filter should allow to specify several states.
//  Yury Udovichenko        2007-11-19      new-623: Default state in states list.
//  Artem Rodygin           2007-11-27      new-633: The 'dbx' extension should not be used.
//  Artem Rodygin           2008-01-28      new-531: LDAP Guest users
//  Artem Rodygin           2008-02-03      new-601: [SF1814666] Export and Import Templates
//  Artem Rodygin           2008-03-20      bug-687: "XML parser error" on template import, if zero is specified in 'critical_age' template's parameter.
//  Artem Rodygin           2008-04-20      new-703: Separated permissions set for current responsible.
//  Artem Rodygin           2008-11-10      new-749: Guest access for unauthorized users.
//  Artem Rodygin           2009-01-08      new-774: 'Anyone' system role permissions.
//  Artem Rodygin           2009-03-24      bug-803: "XML parser error" on import of preliminary exported template.
//  Artem Rodygin           2009-06-12      new-824: PHP 4 is discontinued.
//  Artem Rodygin           2009-06-17      bug-825: Database gets empty strings instead of NULL values.
//  Artem Rodygin           2009-09-09      new-826: Native unicode support for Microsoft SQL Server.
//--------------------------------------------------------------------------------------------------

/**#@+
 * Dependency.
 */
require_once('../engine/engine.php');
require_once('../dbo/fields.php');
/**#@-*/

//--------------------------------------------------------------------------------------------------
//  Definitions.
//--------------------------------------------------------------------------------------------------

/**#@+
 * Data restriction.
 */
define('MAX_STATE_NAME', 50);
define('MAX_STATE_ABBR', 50);
/**#@-*/

/**#@+
 * State type.
 */
define('STATE_TYPE_INITIAL',      1);
define('STATE_TYPE_INTERMEDIATE', 2);
define('STATE_TYPE_FINAL',        3);
/**#@-*/

/**#@+
 * State responsibility.
 */
define('STATE_RESPONSIBLE_REMAIN', 1);
define('STATE_RESPONSIBLE_ASSIGN', 2);
define('STATE_RESPONSIBLE_REMOVE', 3);
/**#@-*/

/**#@+
 * State role.
 */
define('STATE_ROLE_AUTHOR',      -1);
define('STATE_ROLE_RESPONSIBLE', -2);
define('STATE_ROLE_REGISTERED',  -3);
define('MIN_STATE_ROLE', STATE_ROLE_REGISTERED);
/**#@-*/

//--------------------------------------------------------------------------------------------------
//  Functions.
//--------------------------------------------------------------------------------------------------

/**
 * Finds in database and returns the information about specified state.
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id State ID}.
 * @return array Array with data if template is found in database, FALSE otherwise.
 */
function state_find ($id)
{
    debug_write_log(DEBUG_TRACE, '[state_find]');
    debug_write_log(DEBUG_DUMP,  '[state_find] $id = ' . $id);

    $rs = dal_query('states/fndid.sql', $id);

    return ($rs->rows == 0 ? FALSE : $rs->fetch());
}

/**
 * Returns {@link CRecordset DAL recordset} which contains all existing states of specified template,
 * sorted in accordance with current sort mode.
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_templates_template_id Template ID}.
 * @param int &$sort Sort mode (used as output only). The function retrieves current sort mode from
 * client cookie ({@link COOKIE_STATES_SORT}) and updates it, if it's out of valid range.
 * @param int &$page Number of current page tab (used as output only). The function retrieves current
 * page from client cookie ({@link COOKIE_STATES_PAGE}) and updates it, if it's out of valid range.
 * @return CRecordset Recordset with list of states.
 */
function state_list ($id, &$sort, &$page)
{
    debug_write_log(DEBUG_TRACE, '[state_list]');
    debug_write_log(DEBUG_DUMP,  '[state_list] $id = ' . $id);

    $sort_modes = array
    (
        1 => 'state_name asc',
        2 => 'state_abbr asc',
        3 => 'state_type asc, state_name asc',
        4 => 'responsible asc, state_name asc',
        5 => 'state_name desc',
        6 => 'state_abbr desc',
        7 => 'state_type desc, state_name desc',
        8 => 'responsible desc, state_name desc',
    );

    $sort = try_request('sort', try_cookie(COOKIE_STATES_SORT, 3));
    $sort = ustr2int($sort, 1, count($sort_modes));

    $page = try_request('page', try_cookie(COOKIE_STATES_PAGE));
    $page = ustr2int($page, 1, MAXINT);

    save_cookie(COOKIE_STATES_SORT, $sort);
    save_cookie(COOKIE_STATES_PAGE, $page);

    return dal_query('states/list.sql', $id, $sort_modes[$sort]);
}

/**
 * Validates state information before creation or modification.
 *
 * @param string $state_name {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_name State name}.
 * @param string $state_abbr {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_abbr State abbreviation}.
 * @return int Error code:
 * <ul>
 * <li>{@link NO_ERROR} - data are valid</li>
 * <li>{@link ERROR_INCOMPLETE_FORM} - at least one of required field is empty</li>
 * </ul>
 */
function state_validate ($state_name, $state_abbr)
{
    debug_write_log(DEBUG_TRACE, '[state_validate]');
    debug_write_log(DEBUG_DUMP,  '[state_validate] $state_name = ' . $state_name);
    debug_write_log(DEBUG_DUMP,  '[state_validate] $state_abbr = ' . $state_abbr);

    if (ustrlen($state_name) == 0 ||
        ustrlen($state_abbr) == 0)
    {
        debug_write_log(DEBUG_NOTICE, '[state_validate] At least one required field is empty.');
        return ERROR_INCOMPLETE_FORM;
    }

    return NO_ERROR;
}

/**
 * Creates new state.
 *
 * @param int $template_id {@link http://www.etraxis.org/docs-schema.php#tbl_templates_template_id ID} of template which new state will belong to.
 * @param string $state_name {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_name State name}.
 * @param string $state_abbr {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_abbr State abbreviation}.
 * @param int $state_type {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_type Type of state}.
 * @param int $next_state_id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state, which should be next by default in this dataflow (NULL by default).
 * @param int $responsible {@link http://www.etraxis.org/docs-schema.php#tbl_templates_description State responsibility} ({@STATE_RESPONSIBLE_REMOVE} by default).
 * @return int Error code:
 * <ul>
 * <li>{@link NO_ERROR} - state is successfully created</li>
 * <li>{@link ERROR_ALREADY_EXISTS} - state with specified {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_name name}
 * or {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_abbr abbreviation} already exists</li>
 * <li>{@link ERROR_NOT_FOUND} - failure on attempt to create state</li>
 * </ul>
 */
function state_create ($template_id, $state_name, $state_abbr, $state_type, $next_state_id = NULL, $responsible = STATE_RESPONSIBLE_REMOVE)
{
    debug_write_log(DEBUG_TRACE, '[state_create]');
    debug_write_log(DEBUG_DUMP,  '[state_create] $template_id   = ' . $template_id);
    debug_write_log(DEBUG_DUMP,  '[state_create] $state_name    = ' . $state_name);
    debug_write_log(DEBUG_DUMP,  '[state_create] $state_abbr    = ' . $state_abbr);
    debug_write_log(DEBUG_DUMP,  '[state_create] $state_type    = ' . $state_type);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $next_state_id = ' . $next_state_id);
    debug_write_log(DEBUG_DUMP,  '[state_create] $responsible   = ' . $responsible);

    // Check that there is no state with the same name or abbreviation in the specified template.
    $rs = dal_query('states/fndk.sql', $template_id, ustrtolower($state_name), ustrtolower($state_abbr));

    if ($rs->rows != 0)
    {
        debug_write_log(DEBUG_NOTICE, '[state_create] State already exists.');
        return ERROR_ALREADY_EXISTS;
    }

    // Create a state.
    dal_query('states/create.sql',
              $template_id,
              $state_name,
              $state_abbr,
              $state_type,
              is_null($next_state_id) ? NULL : $next_state_id,
              $responsible);

    $rs = dal_query('states/fndk.sql', $template_id, ustrtolower($state_name), ustrtolower($state_abbr));

    if ($rs->rows == 0)
    {
        debug_write_log(DEBUG_NOTICE, '[state_create] State cannot be found.');
        return ERROR_NOT_FOUND;
    }

    return NO_ERROR;
}

/**
 * Modifies specified state.
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state to be modified.
 * @param int $template_id {@link http://www.etraxis.org/docs-schema.php#tbl_templates_template_id ID} of template which the state belongs to.
 * @param string $state_name New {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_name state name}.
 * @param string $state_abbr New {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_abbr state abbreviation}.
 * @param int $next_state_id New {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state, which will be next by default in this dataflow.
 * @param int $responsible New {@link http://www.etraxis.org/docs-schema.php#tbl_templates_description state responsibility}.
 * @return int Error code:
 * <ul>
 * <li>{@link NO_ERROR} - state is successfully modified</li>
 * <li>{@link ERROR_ALREADY_EXISTS} - state with specified {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_name name}
 * or {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_abbr abbreviation} already exists</li>
 * </ul>
 */
function state_modify ($id, $template_id, $state_name, $state_abbr, $next_state_id, $responsible)
{
    debug_write_log(DEBUG_TRACE, '[state_modify]');
    debug_write_log(DEBUG_DUMP,  '[state_modify] $id            = ' . $id);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $template_id   = ' . $template_id);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $state_name    = ' . $state_name);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $state_abbr    = ' . $state_abbr);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $next_state_id = ' . $next_state_id);
    debug_write_log(DEBUG_DUMP,  '[state_modify] $responsible   = ' . $responsible);

    // Check that there is no state with the same name or abbreviation, besides this one.
    $rs = dal_query('states/fndku.sql', $id, $template_id, ustrtolower($state_name), ustrtolower($state_abbr));

    if ($rs->rows != 0)
    {
        debug_write_log(DEBUG_NOTICE, '[state_modify] State already exists.');
        return ERROR_ALREADY_EXISTS;
    }

    // Modify the state.
    dal_query('states/modify.sql',
              $id,
              $state_name,
              $state_abbr,
              is_null($next_state_id) ? NULL : $next_state_id,
              $responsible);

    return NO_ERROR;
}

/**
 * Checks whether state can be deleted.
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state to be deleted.
 * @return bool TRUE if state can be deleted, FALSE otherwise.
 */
function is_state_removable ($id)
{
    debug_write_log(DEBUG_TRACE, '[is_state_removable]');
    debug_write_log(DEBUG_DUMP,  '[is_state_removable] $id = ' . $id);

    $rs = dal_query('states/efndc.sql', $id);

    return ($rs->fetch(0) == 0);
}

/**
 * Deletes specified state.
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state to be deleted.
 * @return int Always {@link NO_ERROR}.
 */
function state_delete ($id)
{
    debug_write_log(DEBUG_TRACE, '[state_delete]');
    debug_write_log(DEBUG_DUMP,  '[state_delete] $id = ' . $id);

    dal_query('filters/fadelalls.sql', $id);
    dal_query('filters/fdelalls.sql',  $id);

    dal_query('states/rdelall.sql',  $id);
    dal_query('states/ftdelall.sql', $id);
    dal_query('states/fsdelall.sql', $id);
    dal_query('states/lvdelall.sql', $id);
    dal_query('states/fpdelall.sql', $id);
    dal_query('states/fdelall.sql',  $id);
    dal_query('states/gtdelall.sql', $id);
    dal_query('states/rtdelall.sql', $id);
    dal_query('states/delete.sql',   $id);

    return NO_ERROR;
}

/**
 * Marks specified state as initial in its dataflow.
 *
 * @param int $template_id {@link http://www.etraxis.org/docs-schema.php#tbl_templates_template_id ID} of template which the state belongs to.
 * @param int $state_id {@link http://www.etraxis.org/docs-schema.php#tbl_states_state_id ID} of state to be made initial.
 * @return int Always {@link NO_ERROR}.
 */
function state_set_initial ($template_id, $state_id)
{
    debug_write_log(DEBUG_TRACE, '[state_set_initial]');
    debug_write_log(DEBUG_DUMP,  '[state_set_initial] $template_id = ' . $template_id);
    debug_write_log(DEBUG_DUMP,  '[state_set_initial] $state_id    = ' . $state_id);

    dal_query('states/clrinit.sql', $template_id);
    dal_query('states/setinit.sql', $template_id, $state_id);

    return NO_ERROR;
}

/**
 * Exports all states of the specified template to XML code (see also {@link template_export}).
 *
 * @param int $id {@link http://www.etraxis.org/docs-schema.php#tbl_templates_template_id ID} of template, which states should be exported.
 * @param array &$groups Array of IDs of groups, affected by this template (used for output only).
 * @return string Generated XML code.
 */
function state_export ($id, &$groups)
{
    debug_write_log(DEBUG_TRACE, '[state_export]');
    debug_write_log(DEBUG_DUMP,  '[state_export] $id = ' . $id);

    // Allocation of state types to XML code.
    $state_type = array
    (
        STATE_TYPE_INITIAL      => 'initial',
        STATE_TYPE_INTERMEDIATE => 'intermed',
        STATE_TYPE_FINAL        => 'final',
    );

    // Allocation of state responsibility to XML code.
    $state_resp = array
    (
        STATE_RESPONSIBLE_REMAIN => 'remain',
        STATE_RESPONSIBLE_ASSIGN => 'assign',
        STATE_RESPONSIBLE_REMOVE => 'remove',
    );

    $xml = "    <states>\n";

    // List all states of the template.
    $rs = dal_query('states/list2.sql', $id);

    // Add XML code for each found state.
    while (($state = $rs->fetch()))
    {
        // Add XML code for general state information.
        $xml .= sprintf("      <state name=\"%s\" abbr=\"%s\" type=\"%s\" responsible=\"%s\"",
                        ustr2html($state['state_name']),
                        ustr2html($state['state_abbr']),
                        $state_type[$state['state_type']],
                        $state_resp[$state['responsible']]);

        // Add XML code for "next state by default", if such information is specified for the state.
        $xml .= (is_null($state['next_state']) ? ">\n" : " next=\"" . ustr2html($state['next_state']) . "\">\n");

        // If state is not final, enumerate all possible transition from this state.
        if ($state['state_type'] != STATE_TYPE_FINAL)
        {
            $xml .= "        <transitions>\n";

            // List all transitions for system role "author".
            $rst = dal_query('states/rtlist2.sql', $state['state_id'], STATE_ROLE_AUTHOR);

            if ($rst->rows != 0)
            {
                $xml .= "          <author>\n";

                while (($next = $rst->fetch()))
                {
                    $xml .= "            <state>" . ustr2html($next['state_name']) . "</state>\n";
                }

                $xml .= "          </author>\n";
            }

            // List all transitions for system role "responsible".
            $rst = dal_query('states/rtlist2.sql', $state['state_id'], STATE_ROLE_RESPONSIBLE);

            if ($rst->rows != 0)
            {
                $xml .= "          <responsible>\n";

                while (($next = $rst->fetch()))
                {
                    $xml .= "            <state>" . ustr2html($next['state_name']) . "</state>\n";
                }

                $xml .= "          </responsible>\n";
            }

            // List all transitions for system role "registered".
            $rst = dal_query('states/rtlist2.sql', $state['state_id'], STATE_ROLE_REGISTERED);

            if ($rst->rows != 0)
            {
                $xml .= "          <registered>\n";

                while (($next = $rst->fetch()))
                {
                    $xml .= "            <state>" . ustr2html($next['state_name']) . "</state>\n";
                }

                $xml .= "          </registered>\n";
            }

            // Enumerate local groups of the same project and all global groups.
            $rsg = dal_query('groups/list.sql', $state['project_id'], 'is_global, group_name');

            while (($group = $rsg->fetch()))
            {
                // List all transitions for this group.
                $rst = dal_query('states/gtlist2.sql', $state['state_id'], $group['group_id']);

                if ($rst->rows != 0)
                {
                    // Save ID of processed group for future reference.
                    array_push($groups, $group['group_id']);

                    // Add XML code for group name and type.
                    $xml .= sprintf("          <group name=\"%s\" type=\"%s\">\n",
                                    ustr2html($group['group_name']),
                                    (is_null($group['project_id']) ? 'global' : 'local'));

                    // Add XML code for transition information.
                    while (($next = $rst->fetch()))
                    {
                        $xml .= "            <state>" . ustr2html($next['state_name']) . "</state>\n";
                    }

                    $xml .= "          </group>\n";
                }
            }

            $xml .= "        </transitions>\n";
        }

        // Export all existing fields of the state.
        $xml .= field_export($state['state_id'], $groups);
        $xml .= "      </state>\n";
    }

    $xml .= "    </states>\n";

    return $xml;
}

?>
Return current item: ETraxis