<?php
/**
* Calia - Support functions and startup code.
*
* @package Calia
* @author Andrew Gray <hide@address.com>
* @version CVS: $Id: local.inc,v 1.14 2010/04/27 20:30:53 blargh2015 Exp $
*
* Copyright 2001-2008 Andrew Gray
*
* This file is part of Calia.
*
* Calia 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.
*
* Calia 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 Calia; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
**/
require 'install.inc';
$GLOBALS['core-version'] = '1.5.5';
$GLOBALS['database-time'] = 0.0;
$GLOBALS['render-start'] = microtime(TRUE);
// Exception handler
function CaliaExceptionHandler($obj)
{
if($GLOBALS['debug-mode'] === TRUE)
{
$backtrace = $obj->getTrace();
$btText = '';
foreach($backtrace as $bt)
{
$btText .= 'File '.$bt['file'].', line '.$bt['line'].', calling '.$bt['function'].'(';
$btArgText = array();
foreach($bt['args'] as $arg) $btArgText[] = var_export($arg, TRUE);
$btText .= implode(', ', $btArgText).').<hr>';
}
LibHtmlMessageBoxError("<b><u>Internal Error:</u> ".$obj->getMessage()."</b>".
(get_class($obj) == 'dbException'?"<br><b><u>Database Error Message:</u> ".
$obj->InternalError()."</b>":'').
"<hr><b><u>From:</u></b> ".$obj->getFile().", line ".$obj->getLine().".".
"<br><b><u>Backtrace:</u></b><br>".$btText); //.str_replace("\n",'<br>',$obj->getTraceAsString()));
}
exit;
}
set_exception_handler('CaliaExceptionHandler');
// Error handler
function CaliaErrorHandler($errno, $errstr, $errfile, $errline)
{
if($GLOBALS['debug-mode'] === TRUE)
{
$backtrace = debug_backtrace();
$btText = '';
foreach($backtrace as $bt)
{
$btText .= 'File '.$bt['file'].', line '.$bt['line'].', calling '.$bt['function'].'(';
$btArgText = array();
//foreach($bt['args'] as $arg) $btArgText[] = var_export($arg, TRUE);
$btText .= implode(', ', $btArgText).').<hr>';
}
switch($errno)
{
case E_ERROR:
case E_USER_ERROR:
case E_RECOVERABLE_ERROR:
LibHtmlMessageBoxError("<b><u>PHP Error:</u> $errstr</b><hr><b><u>From:</u></b> ".$errfile.", line $errline.");
exit;
default:
//DebugPrint($errfile, $errline, $btText);
//print("<b><u>PHP Warning $errno:</u> $errstr</b><hr><b><u>From:</u></b> ".$errfile.", line $errline.<br><b><u>Backtrace:</b></u><br>$btText");
LibHtmlMessageBoxWarning("<b><u>PHP Warning $errno:</u> $errstr</b><hr><b><u>From:</u></b> ".$errfile.", line $errline.<br><b><u>Backtrace:</b></u><br>$btText");
break;
}
}
return NULL;
}
set_error_handler('CaliaErrorHandler');
/**
* Load and activate an object (if needed), return the object.
*/
function ModuleObject($moduleGuid)
{
global $db;
if(!isset($GLOBALS['modules'][$moduleGuid]['object']))
{
if(!isset($GLOBALS['modules'][$moduleGuid]))
{
throw new Exception("Unknown module $moduleGuid was requested.", 0);
} else {
require_once $GLOBALS['include-path'].'modules/'.$GLOBALS['modules'][$moduleGuid]['classFilename'];
$GLOBALS['modules'][$moduleGuid]['object'] = new $GLOBALS['modules'][$moduleGuid]['className']($moduleGuid, unserialize($GLOBALS['modules'][$moduleGuid]['cfgData']));
}
}
return $GLOBALS['modules'][$moduleGuid]['object'];
}
// Localization.
bindtextdomain('Calia', getcwd().'/locale');
textdomain('Calia');
// Load our interface definitions.
require_once 'core_interfaces.inc';
// If we are running code from the modules directory, make sure our paths are correct.
if( (strstr($_SERVER['SCRIPT_FILENAME'], 'modules')) || (isset($_SERVER['PWD']) && (substr($_SERVER['PWD'], -7) == 'modules')))
{
$GLOBALS['include-path'] = '../';
}
else
{
$GLOBALS['include-path'] = '';
}
// Load CaliaLib
require_once $GLOBALS['include-path'].'lib/includes.inc';
// Load database configuration
require_once $GLOBALS['include-path'].'site.inc';
// Init DB module only at this point, since it needs special work.
// Load.
require_once $GLOBALS['include-path'].'modules/'.$dbInformation[0];
// Quotes check.
if(get_magic_quotes_gpc())
{
LibHtmlMessageBoxError(_('Magic Quotes is active and must be disabled for Calia to function correctly.'));
exit;
}
// Connect to our database.
try {
$db = new $dbInformation[1](NULL, $dbInformation[2]);
} catch(Exception $e) {
LibHtmlStart('System Unavailable');
LibHtmlMessageBoxError('Unable to connect to the system database. Please try again later.');
LibHtmlEnd();
exit;
}
// Clear database configuration since we don't need it any more and to help prevent security leaks.
unset($dbInformation);
// Init some globals from our config table.
try {
foreach($db->FetchTable('core_config') as $cfgRow)
{
switch($cfgRow['name'])
{
case 'menu-data':
$cData = unserialize($cfgRow['data']);
$GLOBALS['menu-data'][] = $cData;
break;
case 'callbacks':
$cData = unserialize($cfgRow['data']);
$GLOBALS['callbacks'][$cData[0]][] = $cData[1];
break;
case 'perm-group':
$cData = unserialize($cfgRow['data']);
$GLOBALS['perm-group'][$cData[1]] = $cData;
break;
case 'edit-handler':
$cData = unserialize($cfgRow['data']);
$GLOBALS['edit-handler'][$cData[0]][] = $cData;
break;
case 'edit-chain':
$cData = unserialize($cfgRow['data']);
$GLOBALS['edit-chain'][$cData[0]][] = $cData;
break;
case 'commit-table':
$cData = unserialize($cfgRow['data']);
$GLOBALS['commit-table'][] = $cData[0];
break;
case 'commit-func':
$cData = unserialize($cfgRow['data']);
$GLOBALS['commit-func'][] = $cData;
break;
case 'commit-time':
$cData = unserialize($cfgRow['data']);
$GLOBALS['commit-time'] = $cData;
break;
}
}
} catch (dbException $e) {
LibHtmlMessageBoxError(_('The database server is not reachable. Please contact the administrator.'));
exit;
}
$GLOBALS['modules'] = $db->Fetch2DArray('core_modules','guid');
foreach($GLOBALS['modules'] as $modGuid=>$modData)
{
$tArray = array();
foreach(explode(',', $GLOBALS['modules'][$modGuid]['classImplements']) as $c) $tArray[$c] = $c;
$GLOBALS['modules'][$modGuid]['classImplements'] = $tArray;
if(isset($tArray['authzInterface'])) $GLOBALS['authz-object'] = ModuleObject($modGuid);
}
// Autoloader to support other modules as needed
function __autoload($class)
{
global $db;
// Is it a class?
foreach($GLOBALS['modules'] as $modRow)
{
if($modRow['className'] == $class)
{
require_once $GLOBALS['include-path'].'modules/'.$modRow['classFilename'];
return;
}
}
// No. Data object?
foreach($GLOBALS['edit-handler'] as $table=>$handlers)
{
foreach($handlers as $handlerRow)
{
if($handlerRow[2] == $class)
{
require_once $GLOBALS['include-path'].'modules/'.$handlerRow[1];
return;
}
}
}
throw new Exception("Unknown class $class was requested.", 0);
}
/**
* Load and activate an object (if needed) by name, assuming it is a non-multiple, return the object.
*/
function ModuleObjectByName($moduleName)
{
global $db;
foreach($GLOBALS['modules'] as $moduleGuid=>$modData)
if($modData['className'] == $moduleName) return ModuleObject($moduleGuid);
throw new Exception(sprintf(_("A module named %s was requested, but is not presently installed."), $moduleName));
}
/**
* Get an installed module GUID. Only really works sanely on non-multiples.
*/
function ModuleGuidByName($moduleName)
{
global $db;
foreach($GLOBALS['modules'] as $moduleGuid=>$modData)
if($modData['className'] == $moduleName) return $moduleGuid;
throw new Exception(sprintf(_("A module named %s was requested, but is not presently installed."), $moduleName));
}
/**
* Return the configuration data for module.
*/
function ModuleConfig($moduleGuid)
{
return unserialize($GLOBALS['modules'][$moduleGuid]['cfgData']);
}
/**
* Return available servers.
*/
function AvailableServers()
{
$ret = array();
foreach($GLOBALS['modules'] as $modGuid=>$modData)
{
if(isset($modData['classImplements']['serverInterface']))
{
$cfgData = unserialize($modData['cfgData']);
$ret[$modGuid] = $cfgData['_inst_title'];
}
}
return $ret;
}
/**
* Attempt to authenticate based on username and password.
*
* @param string $username Username to try
* @param string $password Unencrypted password to try
*
* @return boolean TRUE if authentication was valid, FALSE if not.
*/
function Authenticate($username, $password)
{
global $db;
// Try all AuthN sources
foreach($GLOBALS['modules'] as $sourceId=>$moduleData)
{
if(isset($moduleData['classImplements']['authnInterface']))
{
$authnObject = ModuleObject($sourceId);
$authnRes = $authnObject->Authenticate($username, $password);
if($authnRes !== FALSE)
{
// Map UIDs and GIDs
$authUser = GetUID($authnRes[0], $sourceId);
$authGroups = array();
foreach($authnRes[1] as $groupName)
{
$gid = GetGID($groupName, $sourceId);
$authGroups[$gid] = $gid;
}
$GLOBALS['user-guid'] = $authUser;
$GLOBALS['user-gids'] = $authGroups;
$userInfo = $authnObject->GetUserInformation($username);
$GLOBALS['user-name'] = $userInfo['_printed-name'];
$db->SetAuditSessionData($GLOBALS['user-name']);
return TRUE;
}
}
}
return FALSE;
}
function DebugPrint($obj, $tableMode = FALSE)
{
/*
if($tableMode)
{
LibHtmlRenderTable(array_keys(reset($obj)), $obj);
}
else
{*/
//print('<pre>'); print(htmlentities(print_r($obj, TRUE))); print('</pre>'); print("<br><hr>");
print('<pre>'); var_dump($obj); print('</pre>'); print("<br><hr>");
/*
}
*/
}
function GetUID($username, $authnSourceId)
{
global $db;
try {
$row = $db->FetchRecordA('core_user_ids', array('authn_id'=>$authnSourceId, 'authn_data'=>$username));
return $row['guid'];
} catch(dbException $e) {
$db->Insert('core_user_ids', array('authn_id'=>$authnSourceId, 'authn_data'=>$username));
return $db->GetInsertGuid('core_user_ids', 'guid');
}
}
function GetGID($groupname, $authnSourceId)
{
global $db;
try {
$row = $db->FetchRecordA('core_group_ids', array('authn_id'=>$authnSourceId, 'authn_data'=>$groupname));
return $row['guid'];
} catch(dbException $e) {
$db->Insert('core_group_ids', array('authn_id'=>$authnSourceId, 'authn_data'=>$groupname));
return $db->GetInsertGuid('core_group_ids', 'guid');
}
}
function PermsCheck($table, $itemGuid, $permLevel)
{
//DebugPrint(array($table, $itemGuid, $permLevel));
return $GLOBALS['authz-object']->Check($table, $itemGuid, $permLevel);
}
/**
* Calia's start of rendering.
*
* @param string $title Page title.
* @param array $breadcrumbing Array of MenuText=>Pointer URL.
* @param string $headerText Header text
*
* @return void
*/
function DisplayStart($title, $breadcrumbing, $headerText = NULL)
{
global $db;
// Start our buffering, just in case we choose to abort and do something else.
ob_start();
register_shutdown_function('DisplayEnd');
LibHtmlStart($title, FALSE, array($GLOBALS['BASE_URL'].'project.css'));
foreach($breadcrumbing as $key=>$data) $breadcrumbing[$key] = $data;
// Check commit tables.
if(!isset($GLOBALS['commit-time'])) $GLOBALS['commit-time'] = 0;
// FIXME: DB agnostic needed here.
$tableMods = $db->Fetch1DArray('db_postgres.table_mod', 'tab', 'lastmod');
$needCommit = FALSE;
if(isset($GLOBALS['commit-table']))
{
foreach($GLOBALS['commit-table'] as $comTab)
{
if(isset($tableMods[$comTab]))
{
$tabModTime = strtotime($tableMods[$comTab]);
if($tabModTime > $GLOBALS['commit-time']) $needCommit = TRUE;
}
}
}
// Header
print('<div id="header"><table width="100%"><tr valign="middle"><td>'.
'<font size="+2">Calia </font><font size="-2"><i>v'.$GLOBALS['core-version'].'</i></font><br>'.
'<font size="-2">'.implode(' >> ', array_map('LibHtmlMakeLink', $breadcrumbing, array_keys($breadcrumbing))).'</font>'.
'</td><td align="right" class="header">'.
sprintf(_('You are logged in as %s.'),$GLOBALS['user-name']).'<br>'.
($needCommit?(sprintf(_('Changes are pending. %s.<br>'), LibHtmlMakeLink($GLOBALS['BASE_URL'].'commit.php', _('Commit them')))):'').
'</td></tr></table></div>');
// Build our menu.
$horizMenu = array();
$horizMenuTargets = array();
foreach($GLOBALS['menu-data'] as $menuItem)
{
list($permsArray, $menuHierarchy, $targetUrl) = $menuItem;
$horizMenuTargets[implode('|',$menuHierarchy)] = $menuItem;
}
ksort($horizMenuTargets);
foreach($horizMenuTargets as $menuItem)
{
list($permsArray, $menuHierarchy, $targetUrl) = $menuItem;
if( ($permsArray === NULL) || (PermsCheck($permsArray[0], $permsArray[1], $permsArray[2])) )
{
if(!strpos($targetUrl, ':')) $targetUrl = $GLOBALS['BASE_URL'].$targetUrl;
$arrPtr = &$horizMenu;
foreach($menuHierarchy as $lev=>$itemName)
if($lev == (count($menuHierarchy) - 1))
$arrPtr[$itemName] = array($targetUrl, array());
else
{
if(!isset($arrPtr[$itemName]))
$arrPtr[$itemName] = array('', array());
$arrPtr = &$arrPtr[$itemName][1];
}
}
}
// If there are no menu options, reject the user authentication.
if(count($horizMenu) == 0)
{
ob_end_clean();
printf('<h1>%s</h1>%s',_('No access'),_('Your account credentials are valid, but you have been assigned no rights to this system. Please contact the administrator if you feel this is in error.'));
exit;
}
// Render it.
print('<div id="sidebar">');
LibHtmlHorizontalMenu($horizMenu);
print('</div><div id="content">');
if($headerText !== NULL) print('<p class="header">'.$headerText.'</p>');
RenderMessages();
if(!isset($GLOBALS['pre-displayend'])) $GLOBALS['pre-displayend'] = array();
}
function DisplayEnd()
{
global $db;
$db->Commit();
// Make sure we're not in CSV output mode.
$hdrs = headers_list();
foreach($hdrs as $hdr) if(!strncmp($hdr, "Content-Type: text/csv", 22)) exit;
print("</div>\n");
print('<hr><p align="center"><font size="-3"><i>'.sprintf(_('Page took %0.2fs to render, %0.2fs of which was spent waiting for the database.'), microtime(TRUE) - $GLOBALS['render-start'], $GLOBALS['database-time'])."</i></font></p>\n");
// Output anything (like scripts and such) that was queued.
if(isset($GLOBALS['pre-displayend'])) print(implode("\n", $GLOBALS['pre-displayend']));
LibHtmlEnd();
}
function StoreMessage($type, $message)
{
global $db;
$db->Insert('core_session_data', array('user'=>$GLOBALS['user-guid'], 'ip'=>$_SERVER['REMOTE_ADDR'], 'data'=>"STORED-MESSAGE:".($type[0]).":$message"));
}
function StoreFunction($type, $file, $function, $data = NULL)
{
global $db;
$db->Insert('core_session_data', array('user'=>$GLOBALS['user-guid'], 'ip'=>$_SERVER['REMOTE_ADDR'], 'data'=>sprintf('STORED-FUNCTION:%s:%s:%s:%s', $type, $file, $function, $data)));
}
function RenderMessages()
{
global $db;
$messages = $db->Fetch1DArray('core_session_data', 'id', 'data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-MESSAGE:%'));
if(count($messages))
{
$db->Delete('core_session_data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-MESSAGE:%'));
foreach($messages as $msgString)
{
switch($msgString[15])
{
case 'c':
LibHtmlMessageBoxConfirm(substr($msgString, 17));
break;
case 'w':
LibHtmlMessageBoxWarning(substr($msgString, 17));
break;
case 'e':
LibHtmlMessageBoxError(substr($msgString, 17));
break;
}
}
}
$messages = $db->Fetch1DArray('core_session_data', 'id', 'data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-FUNCTION:%'));
if(count($messages))
{
$db->Delete('core_session_data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-FUNCTION:%'));
foreach($messages as $msgString)
{
$msgParts = explode(':', $msgString);
require_once $GLOBALS['include-path'].$msgParts[2];
$text = call_user_func($msgParts[3], $msgParts[4]);
if(strlen($text))
{
switch($msgParts[1])
{
case 'c':
LibHtmlMessageBoxConfirm($text);
break;
case 'w':
LibHtmlMessageBoxWarning($text);
break;
case 'e':
LibHtmlMessageBoxError($text);
break;
}
}
}
}
}
/**
* Master function for handling listing, editing, and deleting records. Handles, literally, everything.
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param array $baseBC Base breadcrumbing - edit, create, and delete will be based off this.
* @param string $listTitle Title string to use in listing
* @param boolean $defaultListAll Default to listing all records?
* @param string $titleField Field in the records to use in breadcrumbing (will be substituted for a %s in the BC)
* @param string $editBC Title of breadcrumbing to use for editing an existing record - %s replaced by titleField
* @param string $createBC Title of breadcrumbing to use for creating a new record
* @param string $delConfBC Title of breadcrumbing to use for delete confirmations - %s replaced by titleField
* @param string $baseURL Base URL that will return back to this function
* @param array $addRowFuncs Additional functions each row - Edit and Delete are generated automatically.
* @param array $addBotFuncs Additional functions at the bottom - Add New is generated automatically.
*/
function DataHandler($mainGuid, $tableName, $baseBC, $listTitle, $defaultListAll, $titleField, $editBC, $createBC, $delConfBC, $baseURL, $addRowFuncs = array(), $addBotFuncs = array())
{
global $db;
if(isset($_REQUEST['dh-mode']))
{
switch($_REQUEST['dh-mode'])
{
case 'copy':
case 'edit':
case 'save':
case 'view':
$thisBC = $baseBC;
if(isset($_REQUEST['guid']) && (strlen($_REQUEST['guid'])))
{
// We do it this way because some modules have SQL operators in titleField (as opposed to just FetchRecord'ing it)
$thisRec = $db->Fetch1DArray($tableName, 'guid', $titleField, array('guid'=>$_REQUEST['guid']));
//DebugPrint(array('thisRec'=>$thisRec, 'guid'=>$_REQUEST['guid']));
$thisRecKeys = array_keys($thisRec);
$guid = reset($thisRecKeys);
$title = reset($thisRec);
if($_REQUEST['dh-mode'] == 'copy')
{
$thisBC[$createBC] = $baseURL.'&dh-mode=copy&guid='.$guid;
$thisTitle = sprintf(_('%s (Copied from %s)'), $createBC, $title);
$saveUrl = $baseURL.'&dh-mode=save';
}
else
{
$thisBC[sprintf($editBC, $title)] = $baseURL.'&dh-mode=edit&guid='.$guid;
$thisTitle = sprintf($editBC, $title);
$saveUrl = $baseURL.'&dh-mode=save&guid='.$guid;
}
}
else
{
$thisBC[$createBC] = $baseURL.'&dh-mode=edit';
$thisTitle = $createBC;
$guid = NULL;
$saveUrl = $baseURL.'&dh-mode=save';
}
if($_REQUEST['dh-mode'] == 'edit' || $_REQUEST['dh-mode'] == 'copy' || $_REQUEST['dh-mode'] == 'view')
{
DisplayStart('Calia', $thisBC, $thisTitle);
DataHandlerEdit($mainGuid, $tableName, $guid, $saveUrl);
exit;
}
else
{
DataHandlerSave($mainGuid, $tableName, $guid, array('DisplayStart', 'Calia', $thisBC, $thisTitle),
$saveUrl, $baseURL);
exit;
}
break;
case 'delete':
$thisRec = $db->Fetch1DArray($tableName, 'guid', $titleField, array('guid'=>$_REQUEST['guid']));
$thisRecKeys = array_keys($thisRec);
$guid = reset($thisRecKeys);
$title = reset($thisRec);
$thisBC = $baseBC;
$thisBC[sprintf($delConfBC, $title)] = $baseURL.'&dh-mode=delete&guid='.$guid;
$thisTitle = sprintf($delConfBC, $title);
DataHandlerDelete($mainGuid, $tableName, $guid, array('DisplayStart', 'Calia', $thisBC, $thisTitle),
$baseURL.'&dh-mode=delete&guid='.$guid, $baseURL);
exit;
}
}
DisplayStart('Calia', $baseBC, $listTitle);
$rowFunctions = $addRowFuncs;
$botFunctions = $addBotFuncs;
$botFunctions[_('Add New')] = array($baseURL.'&dh-mode=edit', 'c');
$botFunctions[_('Add New Bulk')] = array($baseURL.'&dh-mode=edit&bulk=1', 'c');
DataHandlerList($mainGuid, $tableName, $baseURL, $rowFunctions, $botFunctions, $defaultListAll);
exit;
}
/**
* Master function to generate a listing of data, with possible editing and such.
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param string $returnUrl URL to come back to this same spot (for filtering and CSV export)
* @param array $functions Functions, in Button Title=>array(URL, PermCheckNeeded) format, where PermCheckNeeded is the permission
* character required for this function. Leave unset to have it always available.
* @param string $bottomFuncs Functions to add at the bottom (i.e. empty row), (e.x. Add New) in the same format as $functions.
* @param boolean $defaultAll Start with all records listed. For small tables this should be TRUE, for ones that can grow huge,
* set to FALSE.
* @param boolean $recursive Set to TRUE if we're calling ourselves for a chain (and thus just return data, not do anything).
*/
function DataHandlerList($mainGuid, $tableName, $returnUrl, $functions = array(), $bottomFuncs = array(), $defaultAll = TRUE, $recursive = FALSE)
{
global $db;
//DebugPrint(array($mainGuid, $tableName, $returnUrl, $functions, $bottomFuncs, $defaultAll, $recursive));
$dataHandlerObjs = array();
$tableData = array();
$searchFields = array();
$filteredUrl = $returnUrl.'&f=1';
$showResults = ((isset($_REQUEST['f']) && ($_REQUEST['f'] == 1)) || $defaultAll === TRUE)?TRUE:FALSE;
$allHeaders = $allTables = $allFields = $allWhereDB = $allWhereHuman = $allCallbacks = array();
// Go through all registered handlers, loading mods and getting search criteria
foreach($GLOBALS['edit-handler'][$tableName] as $editHandler)
{
require_once $GLOBALS['include-path'].'modules/'.$editHandler[1];
if($editHandler[3] !== NULL)
$obj = new $editHandler[2](NULL, $editHandler[3]);
else
$obj = new $editHandler[2](NULL);
$searchCrit = array(); $searchData = array();
foreach($obj->SearchFields() as $sk=>$sf)
{
$fn = 'search-'.$editHandler[2].$sk;
if(isset($_REQUEST[$fn]))
{
$searchCrit[$sk] = $_REQUEST[$fn];
$filteredUrl .= '&'.$fn.'='.htmlspecialchars($_REQUEST[$fn]);
}
$searchFields[$fn] = $sf;
}
if($showResults)
{
$res = $obj->SearchExecute($searchCrit);
$hHeaders = $res[0];
$hTables = $res[1];
$hFields = $res[2];
$hWhereDB = $res[3];
$hWhereHuman = $res[4];
if(isset($res[5])) $hCallbacks = $res[5]; else $hCallbacks = array();
}
else
list($hHeaders, $hTables, $hFields, $hWhereDB, $hWhereHuman, $hCallbacks) = array(array(), array(), array(), array(), array(), array());
// Merge in results to the $allXXX variables from the $hXXX variables (all <= handler)
$allHeaders = array_merge($allHeaders, $hHeaders);
$allTables = array_merge($allTables, $hTables);
$allFields = array_merge($allFields, $hFields);
$allWhereHuman = array_merge($allWhereHuman, $hWhereHuman);
if(count($hWhereDB))
{
if(count($allWhereDB)) array_unshift($allWhereDB, '&');
$allWhereDB = array_merge($allWhereDB, $hWhereDB);
}
$allCallbacks = array_merge($allCallbacks, $hCallbacks);
$dataHandlerObjs[] = $obj;
}
// Handle edit chains.
if(isset($GLOBALS['edit-chain'][$tableName]))
{
foreach($GLOBALS['edit-chain'][$tableName] as $editChain)
{
// Call the handler NULL.
list($hHeaders, $hTables, $hFields, $hWhereDB, $hWhereHuman, $hSearchFields, $hCallbacks) = DataHandlerList($mainGuid, $editChain[2], $returnUrl, array(), array(), $defaultAll, TRUE);
$allHeaders = array_merge($allHeaders, $hHeaders);
// If the callback isn't a complicated tables list (ie with other joins), drop it, as we join it below.
$allFields = array_merge($allFields, $hFields);
$allWhereHuman = array_merge($allWhereHuman, $hWhereHuman);
$searchFields = array_merge($searchFields, $hSearchFields);
if(count($hWhereDB))
{
if(count($allWhereDB)) array_unshift($allWhereDB, '&');
$allWhereDB = array_merge($allWhereDB, $hWhereDB);
}
$allCallbacks = array_merge($allCallbacks, $hCallbacks);
// Add in the chain table
//print('Merging:<table border="1"><tr><th>AllTables<th>hTables<th>Edit Chain<th>Result<tr><td>'); DebugPrint($allTables); print("<td>"); DebugPrint($hTables); print("<td>"); DebugPrint($editChain);
$allTables = array_merge($allTables, $hTables);
array_unshift($allTables, ':LO');
$allTables = array_merge($allTables, array('=', $editChain[0].'.'.$editChain[1], $editChain[2].'.'.$editChain[3]));
//print("<td>"); DebugPrint($allTables); print("<tr><td colspan=4>"); $aT = $allTables; print($db->SafeTable($aT)); print("</table>");
}
}
// If we are recursing, return.
if($recursive) return array($allHeaders, $allTables, $allFields, $allWhereDB, $allWhereHuman, $searchFields, $allCallbacks);
//DebugPrint(array($allHeaders, $allTables, $allFields, $allWhereDB, $allWhereHuman, $searchFields));
// This is our outer level. Some higher level chains may have requested a field reordering or redesign.
// Search through listed allFields, looking for table/column names. If they appear later, remove those and their associated header.
$fieldsToClear = array();
foreach($allFields as $i1=>$f1)
{
foreach($allFields as $i2=>$f2)
{
if(strstr($f1, $f2) && $i1 != $i2 && $i2 > $i1) $fieldsToClear[$i2] = $i2;
}
}
foreach($fieldsToClear as $ftc)
{
unset($allHeaders[$ftc]);
unset($allFields[$ftc]);
}
// DebugPrint(array($allHeaders, $allTables, $allFields, $allWhereDB, $allWhereHuman, $searchFields));
// If we had CSV requested, dump what we have so far on the floor and output.
if( isset($_REQUEST['savecsv']) && ($_REQUEST['savecsv'] == 1))
{
ob_end_clean();
if(count($allTables) == 0) $allTables[] = $tableName;
$allData = $db->Fetch2DArraySpecific($allTables, NULL, $allFields, $allWhereDB);
OutputCSVFile('export.csv', $allHeaders, $allData);
exit;
}
// Output filter form.
print('<p class="data-filter"><a class="data-filter" href="#" onclick="var e=document.getElementById(\'searchFilter\'); if(e.style.display == \'block\') e.style.display = \'none\'; else e.style.display = \'block\';">Filters</a>');
// If filtered, offer to clear..
if(count($allWhereHuman)) print('<a class="data-filter" href="'.$returnUrl.'"> (Clear)</a>');
// Offer to save these results as CSV if we have data
if($showResults) print('<a class="data-filter" href="'.$filteredUrl.'&savecsv=1"> Save</a>');
print('</p><div id="searchFilter" style="display:'.($showResults?'none':'block').'">');
$searchData = array(); foreach($searchFields as $sk=>$sd) if(isset($_REQUEST[$sk])) $searchData[$sk] = $_REQUEST[$sk];
FormRender($searchFields, $searchData, array('SubmitURL'=>$filteredUrl, 'SubmitLabel'=>_('Apply'), 'Caption'=>_('Search Filters')));
print('</div>');
$showFunctions = FALSE;
if($showResults)
{
// If our table layout was REALLY simple, we never did any joins, etc., then allTables will be empty. Add in the table we've been called with.
if(count($allTables) == 0) $allTables[] = $tableName;
$allFields[] = $tableName.'.guid';
$allData = $db->Fetch2DArraySpecific($allTables, NULL, $allFields, $allWhereDB);
foreach($allData as $rowId=>$row)
{
foreach($allCallbacks as $cbField=>$cbFunc)
if(substr($cbField,0,1) != '*')
$allData[$rowId][$cbField] = call_user_func($cbFunc, $row[$cbField], FALSE);
$rowGuid = array_pop($allData[$rowId]);
if(!PermsCheck($tableName, $rowGuid, 'r'))
{
unset($allData[$rowId]);
}
else
{
$allData[$rowId]['Functions'] = '';
foreach($functions as $funcName=>$funcArray)
{
if( (!isset($funcArray[1])) || (PermsCheck($tableName, $rowGuid, $funcArray[1])) )
{
$showFunctions = TRUE;
$allData[$rowId]['Functions'] .= LibHtmlMakeLink($funcArray[0].$rowGuid, $funcName).' ';
}
}
// Do our normal fields
if(PermsCheck($tableName, $rowGuid, 'm'))
{ $showFunctions = TRUE; $allData[$rowId]['Functions'] .= LibHtmlMakeLink($returnUrl.'&dh-mode=edit&guid='.$rowGuid, _('Edit')).' '; }
else
if(PermsCheck($tableName, $rowGuid, 'r'))
{ $showFunctions = TRUE; $allData[$rowId]['Functions'] .= LibHtmlMakeLink($returnUrl.'&dh-mode=view&guid='.$rowGuid, _('View')).' '; }
if(PermsCheck($tableName, $rowGuid, 'c'))
{ $showFunctions = TRUE; $allData[$rowId]['Functions'] .= LibHtmlMakeLink($returnUrl.'&dh-mode=copy&guid='.$rowGuid, _('Copy')).' '; }
if(PermsCheck($tableName, $rowGuid, 'm'))
{ $showFunctions = TRUE; $allData[$rowId]['Functions'] .= LibHtmlMakeLink($returnUrl.'&dh-mode=delete&guid='.$rowGuid, _('Delete')).' '; }
if(!strlen($allData[$rowId]['Functions'])) unset($allData[$rowId]['Functions']);
}
foreach($allCallbacks as $cbField=>$cbFunc)
if(substr($cbField,0,1) == '*')
{
$res = call_user_func($cbFunc, $allData[$rowId], FALSE);
if($res === FALSE)
unset($allData[$rowId]);
else
$allData[$rowId] = $res;
}
}
}
if($showFunctions) $allHeaders[] = _('Functions');
// Print human-readable filter text.
if(count($allWhereHuman))
{
$headerText = 'Filtered on '.implode(', ', $allWhereHuman).'.';
print('<p class="header">'.$headerText.'</p>');
}
// Quick nasty kludge.
if($tableName == 'network_devices')
{
unset($allHeaders[11]);
unset($allHeaders[12]);
unset($allHeaders[13]);
unset($allHeaders[14]);
foreach(array('Contact_Person','Contact_Phone','Location','Notes') as $field)
{
foreach($allData as $idx=>$datum) unset($allData[$idx][$field]);
}
}
if($showResults)
{
LibHtmlRenderTable($allHeaders, $allData, NULL, array('Functions'));
printf('<p><font size="-1"><i>'._('%d records matched filters.').'</i></font></p>', count($allData));
}
foreach($bottomFuncs as $funcName=>$funcArray)
{
if( (!isset($funcArray[1])) || (PermsCheck($mainGuid, NULL, $funcArray[1])) )
{
print(LibHtmlQuickButton($funcArray[0], $funcName));
}
}
}
/**
* Master function for editing a record
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param mixed $editGuid Guid for editing.
* @param string $saveUrl URL for saving.
* @param string $fieldPrefix Field prefix, used for edit chains.
* @param boolean $reedit Set to TRUE if we are re-editting.
*/
function DataHandlerEdit($mainGuid, $tableName, $editGuid, $saveUrl, $fieldPrefix = '', $reedit = FALSE, $recurse = FALSE)
{
global $db;
$editFields = array(); $editData = array();
// Go through all registered handlers, loading mods and getting edit fields.
foreach($GLOBALS['edit-handler'][$tableName] as $editHandler)
{
require_once $GLOBALS['include-path'].'modules/'.$editHandler[1];
if($editHandler[3] !== NULL)
$obj = new $editHandler[2]($editGuid, $editHandler[3]);
else
$obj = new $editHandler[2]($editGuid);
$searchCrit = array();
list($thisData, $thisFields) = $obj->EditFields();
// Check for GUIDPTRs.
foreach($thisFields as $fieldKey=>$fieldData)
{
if($fieldData[0] == 'guidptr')
{
$childPrefix = $fieldPrefix.$fieldData[3].':';
list($childData, $childFields) = DataHandlerEdit($mainGuid, $fieldData[3], $thisData[$fieldKey], $saveUrl, $childPrefix, $reedit, TRUE);
$rD = array(); $rF = array();
foreach($childFields as $k=>$d)
{
$rF[$childPrefix.$k] = $d;
$rD[$childPrefix.$k] = $childData[$k];
}
$thisFields[$fieldKey] = array('section', $fieldData[1], $fieldData[2], $rF, array());
$thisData[$fieldKey] = $rD;
}
}
$editData = array_merge($editData, $thisData);
$editFields = array_merge($editFields, $thisFields);
}
if($_REQUEST['dh-mode'] != 'copy')
{
$editFields['guid'] = array('hidden', 'guid', 'guid');
$editData['guid'] = $editGuid;
}
// Handle edit chains.
if($editGuid !== NULL) $rec = $db->FetchRecord($tableName, 'guid', $editGuid);
if(isset($GLOBALS['edit-chain'][$tableName]))
{
foreach($GLOBALS['edit-chain'][$tableName] as $editChainId=>$editChain)
{
// Call ourselves once for each field that matches, plus NULL. We assume the edit field definitions are the same regardless of data provided.
if($editGuid !== NULL) $children = $db->Fetch1DArray($editChain[2], 'guid', 'guid', array($editChain[3]=>$rec[$editChain[1]])); else $children = array();
if(count($children) == 0) { $children[] = NULL; $ignoreData = TRUE; } else { $ignoreData = FALSE; }
foreach($children as $childGuid)
{
$childPrefix = $fieldPrefix.$editChain[2].':'.$childGuid.':';
list($childData, $childFields) = DataHandlerEdit($mainGuid, $editChain[2], $childGuid, $saveUrl, $childPrefix, $reedit, TRUE);
$rD = array(); $rF = array();
foreach($childFields as $k=>$d)
{
$rF[$k] = $d;
$rD[$k] = $childData[$k];
}
$editFields[$editChain[2]] = array('dynlist', NULL, NULL, $rF, _('Add'), _('Remove'), TRUE);
if($ignoreData) $editData[$editChain[2]] = array(); else $editData[$editChain[2]][] = $rD;
}
}
}
if($recurse) {
return array($editData, $editFields);
}
//DebugPrint(array($editData, $editFields));
// If re-called from edit mode, reset values.
if($reedit) $editData = DataHandlerEditSetValues($editFields);
$sessId = SaveSessionData(array('ef'=>$editFields));
// If we're in bulk mode, supply the template, otherwise, render the form.
if(isset($_REQUEST['bulk']))
{
if(!isset($_REQUEST['bulkdata']))
{
$txt = _("# Bulk Editing Instructions\n# Copy and paste your additions, following the template provided.\n# Lines starting with a '#' are ignored in their entirety.\n# The first line is the list of fields, and must be maintained.\n# Any fields surrounded by []s are optional, and can be left blank, but all lines must have the correct number of fields.\n");
$fn = DataHandlerEditExtractFieldNames($editFields);
$txt .= implode(',',$fn)."\n";
}
else
{
$txt = $_REQUEST['bulkdata'];
}
$newFields = array('bulkdata'=>array('textarea', 'Bulk Import', '', 120, 20));
$newData = array('bulkdata'=>$txt);
// Save fields.
$newFields['sess_id'] = array('hidden','', '');
$newData['sess_id'] = $sessId;
$newFields['bulk'] = array('hidden', '', '');
$newData['bulk'] = 1;
FormRender($newFields, $newData, array('SubmitURL'=>$saveUrl, 'SubmitLabel'=>_('Import')));
}
else
{
// Save fields.
$editFields['sess_id'] = array('hidden', '', '');
$editData['sess_id'] = $sessId;
// If view mode only, change.
if($_REQUEST['dh-mode'] == 'view')
{
list($editFields, $editData) = MakeFieldsNonedit($editFields, $editData);
}
else
{
// Check permissions to edit, just to be sure.
if(!PermsCheck($tableName, $mainGuid, 'm'))
{
if(!PermsCheck($tableName, $mainGuid, 'r'))
{
HtmlRedirect($GLOBALS['BASE_URL']);
exit;
}
// They can't edit, but can view.
list($editFields, $editData) = MakeFieldsNonedit($editFields, $editData);
$_REQUEST['dh-mode'] = 'view';
}
}
if($_REQUEST['dh-mode'] != 'view')
FormRender($editFields, $editData, array('SubmitURL'=>$saveUrl, 'SubmitLabel'=>_('Save')));
else
FormRender($editFields, $editData, array());
// Audit Log
if(strlen($editGuid))
print('<br><p align="right">'.LibHtmlMakeLink($GLOBALS['BASE_URL'].'audit.php?guid='.$editGuid, _('Audit Log')).'</p>');
}
}
function DataHandlerEditSetValues($editFields)
{
$ret = array();
foreach($editFields as $fk=>$fd)
{
$ret[$fk] = FormValidateGeneric($fk, $fd);
}
return $ret;
}
function DataHandlerEditExtractFieldNames($editFields)
{
//DebugPrint($editFields);
$fn = array();
foreach($editFields as $ek=>$ef)
{
if(!isset($ef['req'])) $ef[1] = '['.$ef[1].']';
switch($ef[0])
{
case 'dynlist':
$r = DataHandlerEditExtractFieldNames($ef[3]);
foreach($r as $k=>$d)
$fn[$ek.'|'.$k] = $d;
//$fn = array_merge($fn, $r);
break;
case 'hidden':
break;
case 'select':
// Hack for the moment.
if($ek != 'sub')
{
// Strip off _guid if it is there.
$fn[$ek] = $ef[1];
}
break;
default:
if($ek != '[guid]' && $ek != 'guid')
$fn[$ek] = $ef[1];
break;
}
}
return $fn;
}
/**
* Master function for saving a record
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param mixed $editGuid Guid for editing.
* @param array $resaveCallback Callback function if re-editing needed (generally DisplayStart with args)
* @param string $resaveUrl URL for saving if we need to come back for more editing
* @param string $finishUrl URL to redirect to on success.
*/
function DataHandlerSave($mainGuid, $tableName, $editGuid, $resaveCallback, $resaveUrl, $finishUrl)
{
global $db;
// Start a transaction in case something goes horribly, horribly wrong.
$db->Begin();
$reedit = FALSE;
$editFields = array();
$dataObjects = array();
// Retrieve editFields
$sessData = GetSessionData($_REQUEST['sess_id']);
$editFields = $sessData['ef'];
$validFields = FormValidate($editFields, array('SubmitURL'=>$resaveUrl, 'SubmitLabel'=>'Save'), TRUE, $resaveCallback);
// If in bulk mode, extract the lines into fields and call the save helper one by one. Start a transaction,
// so we rollback EVERYTHING if ANYTHING is wrong.
if(isset($_REQUEST['bulk']))
{
$lines = explode("\n", $_REQUEST['bulkdata']);
// Trim lines, eliminate any comments, trailing spaces, newlines, etc.
$ef = DataHandlerEditExtractFieldNames($editFields);
$dataLines = array();
foreach($lines as $lineNum=>$lineData)
if(strlen(trim($lineData)) && ($lineData != '') && (substr($lineData,0,1) != '#'))
{
$lineFields = explode(',', trim($lineData));
$lineData = array();
$fNum = 0;
foreach($ef as $ek=>$ed)
$lineData[$ek] = $lineFields[$fNum ++];
$dataLines[$lineNum] = DataHandlerSaveBulkHelper($editFields, $lineData);
}
// Strip off the first line, which are our fields.
array_shift($dataLines);
$errorMessages = array();
$rollback = FALSE;
$db->Begin();
// Go through each result.
foreach($dataLines as $lineNum=>$dataLine)
{
$dataGood = DataHandlerSaveHelper($mainGuid, $tableName, NULL, $dataLine, '');
// Grab saved messages, tack on line number, mark us bad, and continue (to try and catch as many errors as possible).
$errorMessages[$lineNum] = $db->Fetch1DArray('core_session_data', 'id', 'data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-%'));
$db->Delete('core_session_data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-%'));
if(!$dataGood) { $rollback = TRUE; $errorMessages[$lineNum][] = 'STORED-MESSAGE:e,'.sprintf(_('Data did not verify.')); }
}
foreach($errorMessages as $lineNum=>$msgs)
{
foreach($msgs as $msg)
{
switch($msg[7])
{
case 'F': // STORED-FUNCTION:
$dParts = explode(':', $msg);
StoreFunction($dParts[1], $dParts[2], $dParts[3], $dParts[4]);
break;
default:
switch($msg[15])
{
case 'c':
// Confirmed, no error.
StoreMessage('c', sprintf(_('Line %d: %s'), $lineNum, substr($msg, 17)));
break;
case 'w':
// Warning, continue.
StoreMessage('w', sprintf(_('Line %d: %s'), $lineNum, substr($msg, 17)));
break;
case 'e':
// Error, abort.
StoreMessage('e', sprintf(_('Line %d: %s'), $lineNum, substr($msg, 17)));
$rollback = TRUE;
break;
}
}
}
}
if($rollback)
{
$func = array_shift($resaveCallback);
call_user_func_array($func, $resaveCallback);
$db->Rollback();
DataHandlerEdit($mainGuid, $tableName, $editGuid, $resaveUrl, '', TRUE);
exit;
}
}
else
{
// Non-bulk mode.
// Passed basic forms validation, now do module validation. Begin a transaction in case dataGood is FALSE.
$db->Begin();
$dataGood = DataHandlerSaveHelper($mainGuid, $tableName, $editGuid, $validFields, $editGuid);
// If we need to reedit, do so.
if($dataGood === FALSE)
{
// Preserve messages but rollback everything else.
$messages = $db->Fetch1DArray('core_session_data', 'id', 'data', array('&', '&', '=', 'user', '|'.$GLOBALS['user-guid'], '=', 'ip', '|'.$_SERVER['REMOTE_ADDR'], '=L', 'data', '|STORED-%'));
$db->Rollback();
foreach($messages as $id=>$data) StoreMessage($data[15], substr($data, 17));
$func = array_shift($resaveCallback);
call_user_func_array($func, $resaveCallback);
DataHandlerEdit($mainGuid, $tableName, $editGuid, $resaveUrl, '', TRUE);
exit;
}
}
$db->Commit();
LibHtmlRedirect($finishUrl);
}
/**
* Helper function for converting bulk data lines into arrays.
*/
function DataHandlerSaveBulkHelper($editFields, $dataLine)
{
$ret = array();
foreach($editFields as $ek=>$ef)
{
switch($ef[0])
{
case 'dynlist':
$subLines = array();
$thisMatch = $ek.'|';
foreach($dataLine as $dk=>$dd)
if(substr($dk, 0, strlen($thisMatch)) == $thisMatch)
$subLines[substr($dk, strlen($thisMatch))] = $dd;
$ret[$ek] = array(DataHandlerSaveBulkHelper($ef[3], $subLines)); // This is expected to be an array of multiple rows - we only have one, thus the array()
break;
default:
$ret[$ek] = $dataLine[$ek];
}
}
return $ret;
}
/**
* Helper function for saving a record. Called from DataHandlerSave to handle the recursion.
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param mixed $editGuid Guid for editing.
* @param array $validFields The fields from Forms' validation function.
* @param string $fieldPrefix Current field prefix to use
*
* @return Returns FALSE if data was bad, otherwise the new GUID.
*/
function DataHandlerSaveHelper($mainGuid, $tableName, $editGuid, $validFields, $fieldPrefix, $linkedField = '', $linkedData = '')
{
global $db;
//DebugPrint(array($mainGuid, $tableName, $editGuid, $validFields, $fieldPrefix, $linkedField, $linkedData));
$dataGood = TRUE;
$dataFields = array();
if($editGuid == '') $editGuid = NULL;
// Do this one.
foreach($GLOBALS['edit-handler'][$tableName] as $editHandler)
{
require_once $GLOBALS['include-path'].'modules/'.$editHandler[1];
if($editHandler[3] !== NULL)
$obj = new $editHandler[2]($editGuid, $editHandler[3]);
else
$obj = new $editHandler[2]($editGuid);
list($thisData, $thisFields) = $obj->EditFields();
foreach($thisFields as $fieldKey=>$fieldData)
{
if($fieldData[0] == 'guidptr')
{
// Find the first colon.
$colon = strlen($fieldData[3]) + 1;
$ve = array_key_string_strip($validFields[$fieldKey], $colon);
//$ve = $validFields[$fieldKey];
$ret = DataHandlerSaveHelper($mainGuid, $fieldData[3], $thisData[$fieldKey], $ve, '', $validateMode, '', '');
if($ret === FALSE)
$dataGood = FALSE;
else
$dataFields[$fieldKey] = $ret;
}
else
{
$dataFields[$fieldKey] = $validFields[$fieldKey];
}
}
// Set linked data
if($linkedField != '') $dataFields[$linkedField] = $linkedData;
$editGuid = $obj->SaveFields($dataFields);
if($editGuid === FALSE) return FALSE;
if(is_array($editGuid))
{
$dataGood = FALSE;
foreach($editGuid as $errMsg)
{
StoreMessage($errMsg[0], $errMsg[1]);
}
return FALSE;
}
}
//DebugPrint(array('validFields'=>$validFields));
// Handle edit chains
if(isset($GLOBALS['edit-chain'][$tableName]))
foreach($GLOBALS['edit-chain'][$tableName] as $editChainId=>$editChain)
{
// ValidateFields returns us an array, indexed by the editChain table (element 2).
if(isset($validFields[$editChain[2]]) && is_array($validFields[$editChain[2]]))
{
// Go through the returned elements.
//if($editChain[0] == 'network_devices') $editChain[4] = TRUE;
$retGuids = array();
$curGuids = $db->Fetch1DArray($editChain[2], 'guid', 'guid', array($editChain[3]=>$editGuid));
foreach($validFields[$editChain[2]] as $validElement)
{
$firstKey = reset(array_keys($validElement));
// Find the second colon.
//$colon2 = strpos($firstKey, ':', strpos($firstKey, ':', 0) + 1) + 1;
//$ve = array_key_string_strip($validElement, $colon2);
$ve = $validElement;
if(strlen($ve['guid'])) $retGuids[$ve['guid']] = $ve['guid'];
$ret = DataHandlerSaveHelper($mainGuid, $editChain[2], $ve['guid'], $ve, '', $editChain[3], $editGuid);
if($ret === FALSE) $dataGood = FALSE;
}
// Now see what guids WEREN'T returned that we started with, and delete.
foreach($curGuids as $savedGuid)
if(!isset($retGuids[$savedGuid]))
$db->Delete($editChain[2], array('guid'=>$savedGuid));
}
}
if($dataGood === FALSE)
return FALSE;
else
return $editGuid;
}
function array_key_string_strip($arr, $l)
{
$ret = array();
foreach($arr as $k=>$d)
{
$newKey = substr($k, $l);
if(is_array($d))
$ret[$newKey] = $d;
//$ret[$newKey] = array_key_string_strip($d, $l);
else
$ret[$newKey] = $d;
}
return $ret;
}
/**
* Master function for deleting a record
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param string $tableName Table to process.
* @param mixed $delGuid Guid for deleting
* @param array $confCallback Callback before printing confirm box if it is needed.
* @param string $returnUrl URL to return to if verification is needed (should just re-call this function)
* @param string $doneUrl URL to return to on completion.
*/
function DataHandlerDelete($mainGuid, $tableName, $delGuid, $confCallback, $returnUrl, $doneUrl)
{
global $db;
if(isset($_REQUEST['delete-verified'])) $delVer = TRUE; else $delVer = FALSE;
$confirmMessages = array();
// Transaction for rolling back.
$db->Begin();
// Go through all registered handlers, loading mods and requesting the delete.
foreach($GLOBALS['edit-handler'][$tableName] as $editHandler)
{
require_once $GLOBALS['include-path'].'modules/'.$editHandler[1];
$obj = new $editHandler[2]($delGuid);
$res = $obj->Delete($delVer);
if($res !== TRUE)
$confirmMessages[] = $res;
}
// If confirm is needed, rollback and print.
if(count($confirmMessages))
{
$db->Rollback();
$func = array_shift($confCallback);
call_user_func_array($func, $confCallback);
LibHtmlMessageBoxProceed(implode('<br><br>',$confirmMessages), $returnUrl.'&delete-verified=1', $doneUrl);
exit;
}
$db->Commit();
LibHtmlRedirect($doneUrl);
}
/**
* Commonly used function to present data from the database for editting in table form. The various permission checks go by:
* PermsCheck($mainGuid, $dbArrayKeyGuid, $permChar). URLs will have the row GUID tacked on the end.
*
* @param mixed $mainGuid mainGuid for PermsCheck (usually module GUID)
* @param array $colHeaders Headers for the columns /without/ the end 'functions'.
* @param array $dbArray Data from the database, in guid=>data format.
* @param array $functions Functions, in Button Title=>array(URL, PermCheckNeeded) format, where PermCheckNeeded is the permission
* character required for this function. Leave unset to have it always available.
* @param string $bottomFuncs Functions to add at the bottom (i.e. empty row), (e.x. Add New) in the same format as $functions.
*
* @return void
*/
function EasyListing($mainGuid, $colHeaders, $dbData, $functions, $bottomFuncs)
{
$showFunctions = FALSE;
foreach($dbData as $rowGuid=>$row)
{
$dbData[$rowGuid]['Functions'] = '';
foreach($functions as $funcName=>$funcArray)
{
if( (!isset($funcArray[1])) || (PermsCheck($mainGuid, $rowGuid, $funcArray[1])) )
{
$showFunctions = TRUE;
$dbData[$rowGuid]['Functions'] .= LibHtmlQuickButton($funcArray[0].$rowGuid, $funcName);
}
}
}
if($showFunctions) $colHeaders[] = _('Functions');
LibHtmlRenderTable($colHeaders, $dbData, NULL, TRUE);
foreach($bottomFuncs as $funcName=>$funcArray)
{
if( (!isset($funcArray[1])) || (PermsCheck($mainGuid, NULL, $funcArray[1])) )
{
print(LibHtmlQuickButton($funcArray[0], $funcName));
}
}
}
/**
* Output a file as CSV.
*/
function OutputCSVFile($filename, $headers, $dataRows)
{
header('Content-Type: text/csv');
header('Content-Disposition: inline; filename="'.$filename.'"');
$outFp = fopen('php://output', 'w');
fputcsv($outFp, $headers); fprintf($outFp, "\r");
foreach($dataRows as $dataRow)
{ fputcsv($outFp, $dataRow); fprintf($outFp, "\r"); }
fclose($outFp);
}
/**
* Our normal search execute functions all do the exact same thing, just different fields. But this code keeps getting tweaked
* just slightly, so this function was made to replace all of those.
*
* @param array $condArray Conditions array (the argument to SearchExecute)
* @param string $condFieldName The name in condArray to work with.
* @param string $dbFieldName The database field name to reference should it be needed.
* @param string $humanFieldName The human field name.
* @param array $w The where array being built.
* @param array $h The human-readable filter array being built.
* @param boolean $caseInsensitive Whether to do matches case-insensitive.
* @param boolean $checkStars Handle stars (*) as wildcards.
* @param boolean $checkCIDR Handle CIDR notation.
*/
function SearchExecuteField($condArray, $condFieldName, $dbFieldName, $humanFieldName, &$w, &$h, $caseInsensitive = TRUE, $checkStars = TRUE, $checkCIDR = FALSE)
{
//DebugPrint(array($condArray, $condFieldName, $dbFieldName, $humanFieldName, &$w, &$h, $caseInsensitive, $checkStars, $checkCIDR));
if(!isset($condArray[$condFieldName]) || (strlen($condArray[$condFieldName]) == 0)) return;
$val = $condArray[$condFieldName];
if(count($w)) array_unshift($w, '&');
if($checkCIDR && (strpos($val, '/') !== FALSE))
{
$w[] = '<<'; $w[] = $dbFieldName; $w[] = '|'.$val;
$h[] = sprintf(_('%s inside %s'), $humanFieldName, $val);
return;
}
if($caseInsensitive) $matchOp = '=I'; else $matchOp = '=';
if($checkStars && (strpos($val, '*') !== FALSE))
{
$w[] = $matchOp.'L'; $w[] = $dbFieldName; $w[] = '|'.str_replace('*', '%', $val);
$h[] = sprintf(_('%s matches %s'), $humanFieldName, $val);
return;
}
$w[] = $matchOp; $w[] = $dbFieldName; $w[] = '|'.$val;
$h[] = sprintf(_('%s is %s'), $humanFieldName, $val);
}
/**
* Schedule something to be called in the future.
*/
function ScheduleLaunch($moduleGuid, $scriptName, $time)
{
global $db;
$db->Insert('core_cron', array('module_guid'=>$moduleGuid, 'filename'=>$scriptName, 'next_run'=>strftime('%Y-%m-%d %H:%M:%S', $time)));
}
/**
* Handle callbacks.
*/
function HandleCallbacks($name, $data)
{
$c = 0;
if(isset($GLOBALS['callbacks'][$name]))
{
foreach($GLOBALS['callbacks'][$name] as $cbData)
{
require_once $cbData[0];
call_user_func($cbData[1], $data, $cbData[2]);
$c ++;
}
}
return $c;
}
function DebugArrayTable($arr)
{
if(!is_array($arr)) return;
$keyArray = array();
foreach(reset($arr) as $key=>$val) $keyArray[$key] = $key;
LibHtmlRenderTable($keyArray, $arr, 'Key');
}
function SaveSessionData($arr)
{
global $db;
// Delete really old data
$db->Delete('core_session_data', array('<', 'created', '|'.strftime('%Y-%m-%d %H:%M:%S', time() - 86400)));
$db->Insert('core_session_data', array('user'=>$GLOBALS['user-guid'], 'data'=>serialize($arr), 'ip'=>$_SERVER['REMOTE_ADDR']));
return $db->GetInsertId('core_session_data', 'id');
}
function GetSessionData($id)
{
global $db;
$rec = $db->FetchRecord('core_session_data', 'id', $id);
if($rec['user'] != $GLOBALS['user-guid']) exit;
//$db->Delete('core_session_data', array('id'=>$id));
return unserialize($rec['data']);
}
function ReallyShortDuration($curValue)
{
if( ($curValue / 60) > 1)
if( ($curValue / 3600) > 1)
if( ($curValue / 86400) > 1)
$ret = sprintf('%ud %02uh',($curValue / 86400),($curValue % 86400) / 3600);
else
$ret = sprintf('%uh %02um',($curValue % 86400) / 3600,($curValue % 3600) / 60);
else
$ret = sprintf('%um %02us',($curValue % 3600) / 60,($curValue % 60));
else
$ret = sprintf('%us',($curValue % 60));
return $ret;
}
function TimestampToSecs($value)
{
$vParts = explode(' ', $value);
$dParts = explode('-', $vParts[0]);
$tParts = explode(':', $vParts[1]);
return @mktime($tParts[0], $tParts[1], $tParts[2], $dParts[1], $dParts[2], $dParts[0]);
}
function array_merge_norenumber($a1, $a2)
{
$ret = array();
if(is_array($a1)) { foreach($a1 as $key=>$values) $ret[$key] = $values; }
if(is_array($a2)) { foreach($a2 as $key=>$values) if(isset($ret[$key])) $ret[$key] = array_merge($ret[$key], $values); else $ret[$key] = $values; }
return $ret;
}
function TorFImage($val, $csv)
{
switch($val)
{
case 't':
return 'Yes';
case 'f':
return 'No';
default:
return $val;
}
}
function GetWorkDirectory()
{
$name = tempnam(sys_get_temp_dir(), 'CAL');
unlink($name);
mkdir($name, 0700);
return $name;
}
function MakeFieldsNonedit($fields, $data)
{
$newFields = array(); $newData = array(); $fieldInc = 0;
foreach($fields as $fK=>$fD)
{
$fN = 'f'.$fieldInc;
$newFields[$fN] = array('nonedit', $fD[1], $fD[2]);
$newData[$fN] = $data[$fK];
switch($fD[0])
{
case 'section':
$newFields[$fN][0] = 'section';
$newFields[$fN][4] = array();
list($newFields[$fN][3], $newData[$fN]) = MakeFieldsNonedit($fD[3], $data[$fK]);
break;
case 'dynlist':
unset($newFields[$fN]); unset($newData[$fN]);
foreach($data[$fK] as $i=>$thisData)
{
$fN = 'f'.$fieldInc.'-'.$i;
$newFields[$fN] = array('section', $fD[1], $fD[2], array(), array());
list($newFields[$fN][3], $newData[$fN]) = MakeFieldsNonedit($fD[3], $thisData);
}
break;
case 'duration':
$newData[$fN] = ReallyShortDuration($data[$fK]);
break;
case 'select':
$newData[$fN] = $fields[$fK][3][$data[$fK]];
break;
case 'hidden':
unset($newFields[$fN]); unset($newData[$fN]);
break;
case 'checkbox':
$curValue = $data[$fK];
if( ($curValue === 'N') || ($curValue === 0) || ($curValue === 'FALSE') || ($curValue === 'f') || ($curValue === 'false') ||
($curValue === FALSE) || ($curValue === '') || ($curValue === NULL)) $newData[$fN] = _('No'); else $newData[$fN] = _('Yes');
break;
default:
break;
}
$fieldInc ++;
}
return array($newFields, $newData);
}
function GetBacktrace()
{
$backtrace = debug_backtrace();
$btText = '';
foreach($backtrace as $bt)
{
$btText .= 'File '.$bt['file'].', line '.$bt['line'].', calling '.$bt['function'].'(';
$btArgText = array();
foreach($bt['args'] as $arg) $btArgText[] = var_export($arg, TRUE);
$btText .= implode(', ', $btArgText).').<hr>';
}
return $btText;
}
if ( !function_exists('sys_get_temp_dir') )
{
// Based on http://www.phpit.net/
// article/creating-zip-tar-archives-dynamically-php/2/
function sys_get_temp_dir()
{
return('/tmp/');
}
}
// If we have authentication data from Apache, try it.
$validUser = FALSE;
if(isset($_SERVER['PHP_AUTH_USER']))
{
$validUser = Authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
}
if(!$validUser)
{
// MUST figure out a better way to do this (for command line utils)
if(isset($GLOBALS['bypass-auth'])) return;
header('WWW-Authenticate: Basic realm="'.gettext('Calia Management System').'"');
header('HTTP/1.0 401 Unauthorized');
print("<HTML><HEAD>\n<TITLE>401 Authorization Required</TITLE>\n</HEAD><BODY>\n<H1>Authorization Required</H1>This server could not verify that you\nare authorized to access the document\nrequested. Either you supplied the wrong\ncredentials (e.g., bad password), or your\nbrowser doesn't understand how to supply\nthe credentials required.<P>\n<HR>\n".$_SERVER['SERVER_SIGNATURE']."\n</BODY></HTML>");
exit;
}