<?php
//------------------------------------------------------------------------------
//
// eTraxis - Records tracking web-based system
// Copyright (C) 2005-2009 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 3 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, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
/**
* States
*
* This module provides API to work with eTraxis states.
* See also {@link http://code.google.com/p/etraxis/wiki/DatabaseSchema#tbl_states tbl_states} database table.
*
* @package DBO
* @subpackage States
*/
/**#@+
* 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 type resources.
$state_type_res = array
(
STATE_TYPE_INITIAL => RES_INITIAL_ID,
STATE_TYPE_INTERMEDIATE => RES_INTERMEDIATE_ID,
STATE_TYPE_FINAL => RES_FINAL_ID,
);
/**#@+
* State responsibility.
*/
define('STATE_RESPONSIBLE_REMAIN', 1);
define('STATE_RESPONSIBLE_ASSIGN', 2);
define('STATE_RESPONSIBLE_REMOVE', 3);
/**#@-*/
// State responsibility resources.
$state_responsible_res = array
(
STATE_RESPONSIBLE_REMAIN => RES_REMAIN_ID,
STATE_RESPONSIBLE_ASSIGN => RES_ASSIGN_ID,
STATE_RESPONSIBLE_REMOVE => RES_REMOVE_ID,
);
/**#@+
* 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 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 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 states_list ($id, &$sort, &$page)
{
debug_write_log(DEBUG_TRACE, '[states_list]');
debug_write_log(DEBUG_DUMP, '[states_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 => 'next_state asc, state_name asc',
6 => 'state_name desc',
7 => 'state_abbr desc',
8 => 'state_type desc, state_name desc',
9 => 'responsible desc, state_name desc',
10 => 'next_state 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 State name.
* @param string $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 ID of template which new state will belong to.
* @param string $state_name State name.
* @param string $state_abbr State abbreviation.
* @param int $state_type Type of state.
* @param int $next_state_id ID of state, which should be next by default in this dataflow (NULL by default).
* @param int $responsible 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 name or 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 ID of state to be modified.
* @param int $template_id ID of template which the state belongs to.
* @param string $state_name New state name.
* @param string $state_abbr New state abbreviation.
* @param int $next_state_id New ID of state, which will be next by default in this dataflow.
* @param int $responsible New state responsibility.
* @return int Error code:
* <ul>
* <li>{@link NO_ERROR} - state is successfully modified</li>
* <li>{@link ERROR_ALREADY_EXISTS} - state with specified name or 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 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 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/clrdef.sql', $id);
dal_query('states/delete.sql', $id);
return NO_ERROR;
}
/**
* Marks specified state as initial in its dataflow.
*
* @param int $template_id ID of template which the state belongs to.
* @param int $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 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;
}
?>