Files
pmgr/site/app_controller.php

1180 lines
41 KiB
PHP

<?php
/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
/**
* Short description for file.
*
* This file is application-wide controller file. You can put all
* application-wide controller-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package cake
* @subpackage cake.app
* @since CakePHP(tm) v 0.2.9
* @version $Revision: 7945 $
* @modifiedby $LastChangedBy: gwoo $
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
/**
* Short description for class.
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @package cake
* @subpackage cake.app
*/
class AppController extends Controller {
var $uses = array('Option', 'Permission');
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
var $components = array('DebugKit.Toolbar');
var $sidemenu = array('areas' => array('SITE' => false, 'CONTROLLER' => false, 'ACTION' => false, 'SANDBOX' => false));
var $std_area = 10;
var $admin_area = 20;
var $dev_area = 30;
var $op_area = 40;
var $new_area = 50;
function __construct() {
$this->params['dev'] = false;
$this->params['admin'] = false;
parent::__construct();
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: sideMenuAreaVerify
* - Verifies the validity of the sidemenu area/subarea/priority,
* and ensures the class member is set to appropriately handle it.
*/
function sideMenuAreaVerify(&$area, $subarea, $priority = null) {
$area = strtoupper($area);
if (!array_key_exists($area, $this->sidemenu['areas']))
$this->INTERNAL_ERROR("Sidemenu link '{$area}': Unknown");
if ($area == 'SITE')
$name = 'Navigation';
elseif ($area == 'CONTROLLER')
$name = Inflector::humanize($this->params['controller']);
elseif ($area == 'ACTION')
$name = Inflector::humanize(Inflector::singularize($this->params['controller']));
elseif ($area == 'SANDBOX')
$name = 'Sandbox';
if (empty($this->sidemenu['areas'][$area]))
$this->sidemenu['areas'][$area]
= array('enable' => true, 'name' => $name, 'subareas' => array());
if (empty($subarea))
return;
$subname = $name;
if ($subarea == $this->std_area)
$subname .= '';
elseif ($subarea == $this->op_area)
//$subname .= '-Ops';
$subname = 'Actions';
elseif ($subarea == $this->new_area)
//$subname .= '-New';
$subname = 'Creation';
elseif ($subarea == $this->admin_area)
$subname .= '-Admin';
elseif ($subarea == $this->dev_area)
$subname .= '-Dev';
else
$subname .= '-' . $subarea;
if (empty($this->sidemenu['areas'][$area]['subareas'][$subarea]))
$this->sidemenu['areas'][$area]['subareas'][$subarea]
= array('enable' => true, 'name' => $subname, 'priorities' => array());
if (empty($priority))
return;
if (empty($this->sidemenu['areas'][$area]['subareas'][$subarea]['priorities'][$priority]))
$this->sidemenu['areas'][$area]['subareas'][$subarea]['priorities'][$priority]
= array();
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: sideMenuAreaName
* - Sets the name of the sidemenu area/subarea
*/
function sideMenuAreaName($name, $area, $subarea = null) {
$this->sideMenuAreaVerify($area, $subarea);
if (empty($subarea))
$this->sidemenu['areas'][$area]['name'] = $name;
else
$this->sidemenu['areas'][$area]['subareas'][$subarea]['name'] = $name;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: sideMenuAreaActivate
* - Sets the selected area/subarea to be active when the
* page is first loaded.
*/
function sideMenuAreaActivate($area, $subarea = null) {
$this->sidemenu['active'] = compact('area', 'subarea');
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: sideMenuEnable
* - Enables/Disables an area or subarea of the sidemenu
*/
function sideMenuEnable($area, $subarea = null, $enable = true) {
$this->sideMenuAreaVerify($area, $subarea);
if (isset($subarea))
$this->sidemenu['areas'][$area]['subareas'][$subarea]['enable'] = $enable;
else
$this->sidemenu['areas'][$area]['enable'] = $enable;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addSideMenuLink
* - Adds another link to the sidemenu area/subarea/priority
*/
function addSideMenuLink($name, $url, $extra, $area, $subarea = null, $priority = 10) {
if (empty($subarea))
$subarea = $this->std_area;
$this->sideMenuAreaVerify($area, $subarea);
$this->sidemenu['areas'][$area]['subareas'][$subarea]['priorities'][$priority][]
= array('name' => $name, 'url' => $url) + (empty($extra) ? array() : $extra);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addDefaultSideMenuLinks
* - Adds the standard links present on all generated pages
*/
function addDefaultSideMenuLinks() {
$this->addSideMenuLink('Site Map',
array('controller' => 'maps', 'action' => 'view', 1), null,
'SITE');
$this->addSideMenuLink('Unit Sizes',
array('controller' => 'unit_sizes', 'action' => 'index'), null,
'SITE');
$this->addSideMenuLink('Units',
array('controller' => 'units', 'action' => 'index'), null,
'SITE');
$this->addSideMenuLink('Leases',
array('controller' => 'leases', 'action' => 'index'), null,
'SITE');
$this->addSideMenuLink('Customers',
array('controller' => 'customers', 'action' => 'index'), null,
'SITE');
$this->addSideMenuLink('Deposits',
array('controller' => 'transactions', 'action' => 'deposit'), null,
'SITE');
$this->addSideMenuLink('Accounts',
array('controller' => 'accounts', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Contacts',
array('controller' => 'contacts', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Ledgers',
array('controller' => 'ledgers', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Tenders',
array('controller' => 'tenders', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Transactions',
array('controller' => 'transactions', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Ldgr Entries',
array('controller' => 'ledger_entries', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Stmt Entries',
array('controller' => 'statement_entries', 'action' => 'index'), null,
'SITE', $this->admin_area);
$this->addSideMenuLink('Un-Nuke',
'#', array('htmlAttributes' =>
array('onclick' => '$(".pr-section").show(); return false;')),
'SITE', $this->dev_area);
$this->addSideMenuLink('New Ledgers',
array('controller' => 'accounts', 'action' => 'newledger'), null,
'SITE', $this->dev_area);
//array('name' => 'RESET DATA', array('controller' => 'accounts', 'action' => 'reset_data'));
$this->addSideMenuLink('New Receipt',
array('controller' => 'customers', 'action' => 'receipt'), null,
'SITE', $this->op_area);
$this->addSideMenuLink('New Invoice',
array('controller' => 'leases', 'action' => 'invoice'), null,
'SITE', $this->op_area);
$this->addSideMenuLink('Move-In',
array('controller' => 'customers', 'action' => 'move_in'), null,
'SITE', $this->op_area);
$this->addSideMenuLink('Move-Out',
array('controller' => 'leases', 'action' => 'move_out'), null,
'SITE', $this->op_area);
$this->addSideMenuLink('New Deposit',
array('controller' => 'tenders', 'action' => 'deposit'), null,
'SITE', $this->op_area);
if (!empty($this->params['admin']))
$this->addSideMenuLink('Assess Charges',
array('controller' => 'leases', 'action' => 'assess_all'), null,
'SITE', $this->op_area);
$url_components = array('plugin', 'controller', 'action', 'named');
if (devbox()) {
/* $sources = ConnectionManager::sourceList(); */
/* $db = ConnectionManager::getDataSource($sources[0])->config['database']; */
/* $this->sideMenuAreaName($db, 'SANDBOX', $this->std_area); */
$this->sideMenuAreaName('DevBox', 'SANDBOX', $this->std_area);
$this->addSideMenuLink('Rebuild DevBox',
array('controller' => 'util', 'action' => 'rebuild_devbox'), null,
'SANDBOX');
}
elseif (sandbox()) {
$this->addSideMenuLink('Rebuild Sandbox',
array('controller' => 'util', 'action' => 'rebuild_sandbox'), null,
'SANDBOX');
$this->addSideMenuLink('Leave Sandbox',
array('sand_route' => false)
+ array_intersect_key($this->params, array_flip($url_components))
+ $this->params['pass'],
null, 'SANDBOX');
}
else {
$this->addSideMenuLink('Enter Sandbox',
array('sand_route' => true)
+ array_intersect_key($this->params, array_flip($url_components))
+ $this->params['pass'],
null, 'SANDBOX');
}
// REVISIT <AP>: 20090824
// Depending on preference, we may put this into the gridView
// function, making the links available only when navigating.
$this->addGridViewSideMenuLinks();
}
/**************************************************************************
**************************************************************************
**************************************************************************
* virtual: addGridViewSideMenuLinks
* - Adds the grid view specific navigation links, if overridden.
*/
function addGridViewSideMenuLinks() {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* hook: beforeFilter
* - Called just before the action function
*/
function beforeFilter() {
$this->params['user'] = $this->Permission->User->currentUser();
$this->params['admin'] = $this->Option->enabled('admin');
$this->params['dev'] = devbox();
if ($this->params['dev'] && !$this->Option->enabled('dev'))
$this->redirect("/");
if (!$this->params['dev'])
Configure::write('debug', '0');
$this->addDefaultSideMenuLinks();
//$this->sideMenuEnable('SITE', $this->op_area, false);
foreach ($this->sidemenu['areas'] AS $area_name => $area) {
if (empty($this->params['dev']))
$this->sideMenuEnable($area_name, $this->dev_area, false);
if (empty($this->params['admin']))
$this->sideMenuEnable($area_name, $this->admin_area, false);
}
$this->authorize("controller.{$this->params['controller']}");
$this->authorize("action.{$this->params['controller']}.{$this->params['action']}");
$this->log('----------------------------------------------------------------------', 'request');
$this->log('----------------------------------------------------------------------', 'request');
$this->log($this->params, 'request');
}
/**************************************************************************
**************************************************************************
**************************************************************************
* hook: beforeRender
* - Called just before rendering the page
*/
function beforeRender() {
// Stupid Cake... our constructor sets admin/dev,
// but cake stomps it somewhere along the way
// after constructing the CakeError controller.
if ($this->name === 'CakeError') {
$this->params['dev'] = false;
$this->params['admin'] = false;
}
foreach ($this->sidemenu['areas'] AS $aname => &$area) {
if (empty($area['enable']))
$area = array();
if (empty($area['subareas']))
continue;
ksort($area['subareas']);
foreach ($area['subareas'] AS $sname => &$subarea) {
if (empty($subarea['enable']))
$subarea = array();
if (empty($subarea['priorities']))
continue;
ksort($subarea['priorities']);
foreach ($subarea['priorities'] AS $pname => &$priority) {
if (empty($priority))
unset($subarea['priorities'][$pname]);
}
unset($priority);
if (empty($subarea['priorities']))
unset($area['subareas'][$sname]);
}
unset($subarea);
if (empty($area['subareas']))
unset($this->sidemenu['areas'][$aname]);
}
unset($area);
// Activate a default section (unless already specified)
foreach (array_reverse(array_diff_key($this->sidemenu['areas'], array('SANDBOX'=>1))) AS $area_name => $area) {
if (empty($area))
continue;
if (empty($this->sidemenu['active']) ||
empty($this->sidemenu['areas'][$this->sidemenu['active']['area']]))
$this->sideMenuAreaActivate($area_name);
}
//pr($this->sidemenu);
$this->set('sidemenu', $this->sidemenu);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* override: redirect
*/
function redirect($url, $status = null, $exit = true) {
// OK, since the controller will not be able to
// utilize our overriden url function in AppHelper,
// we'll have to do it manually here.
App::import('Helper', 'Html');
$url = HtmlHelper::url($url, true);
if (headers_sent()) {
// If we've already sent the headers, it's because
// we're debugging, and our debug output has gotten
// out before the redirect. That's probably a good
// thing, as we don't typically want pages to be
// jerked out from under us while trying to read
// the debug output. So, since we can't redirect
// anyway, we may as well go with the flow and just
// render this page instead, using an empty template
$this->set('message',
("Intended redirect:<P><BR>" .
'<A HREF="'.$url.'">'.$url.'</A>'));
echo $this->render('/empty');
if ($exit)
$this->_stop();
}
return parent::redirect($url, $status, $exit);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* helper: gridView
* - called by derived controllers to create an index listing
*/
function index() {
$names = Inflector::humanize(Inflector::pluralize($this->params['controller']));
$this->gridView('All ' . $names, 'all');
}
function gridView($title, $action = null, $element = null) {
$this->sideMenuEnable('SITE', $this->op_area);
$this->sideMenuAreaActivate('CONTROLLER');
$this->set('title', $title);
// The resulting page will contain a grid, which will
// use ajax to obtain the actual data for this action
$this->set('action', $action ? $action : $this->params['action']);
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
}
/**************************************************************************
**************************************************************************
**************************************************************************
**************************************************************************
**************************************************************************
* action: gridData
* - Fetches the actual data requested by grid as XML
*/
function gridData() {
// Grab a copy of the parameters that control this request
$params = array();
if (isset($this->params['url']) && is_array($this->params['url']))
$params = $this->params['url'];
// Do any preliminary setup necessary
$this->gridDataSetup($params);
// Get the top level model for this grid
$model = $this->gridDataModel($params);
// Get the number of records prior to pagination
$count = $this->gridDataCount($params, $model);
// Determine pagination configuration (and save to $params)
$pagination = $this->gridDataPagination($params, $model, $count);
// Retreive the appropriate subset of data
$records = $this->gridDataRecords($params, $model, $pagination);
// Post process the records
$this->gridDataPostProcess($params, $model, $records);
// Output the resulting record set
$this->gridDataOutput($params, $model, $records, $pagination);
// Call out to finalize everything
$this->gridDataFinalize($params);
}
/**************************************************************************
**************************************************************************
**************************************************************************
**************************************************************************
**************************************************************************
* virtual: gridData* functions
* - These set up the context for the grid data, and will
* need to be overridden in the derived class for anything
* other than the most basic of grids.
*/
/**************************************************************************
**************************************************************************
**************************************************************************
* gridData SETUP / CLEANUP
*/
function gridDataModel(&$params) {
return $this->{$this->modelClass};
}
function gridDataSetup(&$params) {
// Debug only if requested
$params['debug'] = !empty($this->passedArgs['debug']);
if ($params['debug']) {
ob_start();
}
else {
$this->layout = null;
$this->autoLayout = false;
$this->autoRender = false;
Configure::write('debug', '0');
}
// Establish some defaults (except for serialized items)
$params = array_merge(array('page' => 1,
'rows' => 20),
$params);
// Unserialize our complex structure of post data.
// This SHOULD always be set, except when debugging
if (isset($params['post']))
$params['post'] = unserialize($params['post']);
else
$params['post'] = array();
// Unserialize our complex structure of dynamic post data
if (isset($params['dynamic_post']))
$params['dynamic_post'] = unserialize($params['dynamic_post']);
else
$params['dynamic_post'] = null;
// Unserialize our complex structure of dynamic post data
if (isset($params['dynamic_post_replace']))
$params['dynamic_post_replace'] = unserialize($params['dynamic_post_replace']);
else
$params['dynamic_post_replace'] = null;
// Merge the static and dynamic post data
if (!empty($params['dynamic_post']))
$params['post'] = array_merge_recursive($params['post'], $params['dynamic_post']);
if (!empty($params['dynamic_post_replace']))
$params['post'] = array_merge($params['post'], $params['dynamic_post_replace']);
// This SHOULD always be set, except when debugging
if (!isset($params['post']['fields']))
$params['post']['fields'] = array($this->{$this->modelClass}->alias
.'.'.
$this->{$this->modelClass}->primaryKey);
// Make sure the action parameter at least exists, and
// promote it to the top level (since it drives the operation).
if (isset($params['post']['action']))
$params['action'] = $params['post']['action'];
else
$params['action'] = null;
}
function gridDataFinalize(&$params) {
if ($params['debug']) {
$xml = ob_get_contents();
ob_end_clean();
$xml = preg_replace("/&/", "&amp;", $xml);
$xml = preg_replace("/</", "&lt;", $xml);
$xml = preg_replace("/>/", "&gt;", $xml);
echo ("\n<PRE>\n$xml\n</PRE>\n");
$this->render_empty();
}
}
/**************************************************************************
**************************************************************************
**************************************************************************
* gridData COUNTING
*/
function gridDataCount(&$params, &$model) {
// Establish the tables and conditions for counting
$query = array_intersect_key($this->gridDataCountTableSet($params, $model),
array('link'=>1, 'contain'=>1));
// Conditions for the count
$query['conditions'] = $this->gridDataCountConditionSet($params, $model);
// Grouping (which would not be typical)
$query['group'] = $this->gridDataCountGroup($params, $model);
if ($params['debug'])
$params['count_query'] = $query;
// Get the number of records prior to pagination
return $this->gridDataCountExecute($params, $model, $query);
}
function gridDataCountExecute(&$params, &$model, $query) {
return $model->find('count', $query);
}
function gridDataCountTables(&$params, &$model) {
// Same tables for counting as for retreiving
return $this->gridDataTables($params, $model);
}
function gridDataCountTableSet(&$params, &$model) {
// Preliminary set of tables
$query = array_intersect_key($this->gridDataCountTables($params, $model),
array('link'=>1, 'contain'=>1));
// Perform filtering based on user request: $params['post']['filter']
return array_intersect_key($this->gridDataFilterTables($params, $model, $query),
array('link'=>1, 'contain'=>1));
}
function gridDataCountConditions(&$params, &$model) {
// Same conditions for counting as for retreiving
return $this->gridDataConditions($params, $model);
}
function gridDataCountConditionSet(&$params, &$model) {
// Conditions for the count
$conditions = $this->gridDataCountConditions($params, $model);
// Perform filtering based on user request: $params['post']['filter']
return $this->gridDataFilterConditions($params, $model, $conditions);
}
function gridDataCountGroup(&$params, &$model) {
// Grouping will screw up the count, since it
// causes the results to be split into chunks
// based on the GROUP BY clause. We can't have
// more than one row of data in the count query,
// just a _single_ row with the actual count.
return null;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* gridData FILTERING
*/
function gridDataFilterTables(&$params, &$model, $query) {
if (isset($query['contain']))
$link = 'contain';
else
$link = 'link';
if (empty($params['post']['filter']))
return $query;
foreach ($params['post']['filter'] AS $filter => $filter_value) {
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
/* pr(array('AppController::gridDataFilterTable' => */
/* array('checkpoint' => "Filter split") */
/* + compact('split'))); */
$table = $this->gridDataFilterTablesTable($params, $model, $split['table']);
if (!$table)
continue;
$config = $this->gridDataFilterTablesConfig($params, $model, $split['table']);
/* pr(array('AppController::gridDataFilterTable' => */
/* array('checkpoint' => "Add filter config to query") */
/* + array('query[link]' => $query[$link]) */
/* + compact('config'))); */
// If the table is already part of the query, append to it
if ($table == $model->alias) {
$query[$link] =
array_merge_recursive(array_diff_key($config, array('fields'=>1)),
$query[$link]);
}
elseif (isset($query[$link][$table])) {
$query[$link][$table] =
array_merge_recursive($config, $query[$link][$table]);
}
elseif (in_array($table, $query[$link])) {
// REVISIT <AP>: 20090727
// This presents a dilema. $config may specify fields
// as array(), whereas the user has already indicated
// they desire ALL fields (by the fact that table is
// a member of the query['link'] array without anything
// specified). However, $config _could_ specify a
// non-standard field in the array, such as using SUM()
// or other equation. In that case, we'd have to add
// in all the fields of table to the array. That
// wouldn't be very hard, but I'll ignore it at the
// moment and wait until it's needed.
$key = array_search($table, $query[$link]);
$query[$link][$table] = array('fields' => null) + $config;
unset($query[$link][$key]);
}
else {
// Table is NOT already part of the query... set it now
$query[$link][$table] = $config;
}
/* pr(array('post-filter-table-config' => */
/* array('query[link]' => $query[$link]))); */
}
return $query;
}
function gridDataFilterTablesTable(&$params, &$model, $table) {
// By default, don't add anything if the filter
// condition occurs on the actual model table
if ($table == $model->alias)
return null;
return $this->gridDataFilterTableName($params, $model, $table);
}
function gridDataFilterTablesConfig(&$params, &$model, $table) {
return array('fields' => array());
}
function gridDataFilterConditions(&$params, &$model, $conditions) {
if (empty($params['post']['filter']))
return $conditions;
foreach ($params['post']['filter'] AS $filter => $filter_value) {
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
$table = $this->gridDataFilterConditionsTable($params, $model, $split['table']);
$key = $this->gridDataFilterConditionsKey($params, $model, $split['table'], $split['field']);
if (!$key)
continue;
$conditions[]
= $this->gridDataFilterConditionsStatement($params, $model, $table, $key,
array_intersect_key
($split,
array('value'=>1,
'value_present'=>1)));
}
return $conditions;
}
function gridDataFilterConditionsTable(&$params, &$model, $table) {
return $this->gridDataFilterTableName($params, $model, $table);
}
function gridDataFilterConditionsKey(&$params, &$model, $table, $id) {
// REVISIT <AP>: 20090722
// When $id is null, we could instantiate the table,
// and use the _actual_ primary key. However, I don't
// expect that functionality to be used, and will just
// stick with 'id' for now.
return $id ? $id : 'id';
}
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
$key = (empty($table)?"":"{$table}.") . $key;
if (isset($value['value_present']) && $value['value_present'])
return array($key => $value['value']);
else
return array($key);
}
function gridDataFilterSplit(&$params, &$model, $filter, $value) {
$value_present = true;
if (!is_string($filter)) {
// only a filter condition was set, not filter=>value
$filter = $value;
$value = null;
$value_present = false;
}
$table = $field = null;
if (preg_match("/^\w+\.\w+(\s*[!<>=]+)?$/", $filter)) {
list($table, $field) = explode(".", $filter);
}
elseif (preg_match('/^[A-Z][A-Za-z]*$/', $filter)) {
$table = $filter;
$field = null;
}
elseif (preg_match('/^\w+(\s*[!<>=]+)?$/', $filter)) {
$table = $model->alias;
$field = $filter;
}
else {
// $filter must be a function or other complicated condition
$table = null;
$field = $filter;
}
return compact('table', 'field', 'value', 'value_present');
}
function gridDataFilterTableName(&$params, &$model, $table) {
return Inflector::camelize($table);
}
/**************************************************************************
**************************************************************************
* gridData PAGINATION
*/
function gridDataPagination(&$params, &$model, $record_count) {
// Retrieve, or calculate, all parameters necesssary for
// pagination. Verify the passed in parameters are valid.
$limit = $params['rows'] > 0 ? $params['rows'] : 10;
$total = ($record_count < 0) ? 0 : ceil($record_count/$limit);
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
$start = $limit * ($page - 1);
return compact('record_count', 'limit', 'page', 'start', 'total');
}
/**************************************************************************
**************************************************************************
* gridData RETREIVAL
*/
function gridDataRecords(&$params, &$model, $pagination) {
// Establish the tables for this query
$query = array_intersect_key($this->gridDataTableSet($params, $model),
array('link'=>1, 'contain'=>1));
// Specify the fields for the query
$query['fields'] = $this->gridDataFields($params, $model);
// Conditions of the query
$query['conditions'] = $this->gridDataConditionSet($params, $model);
// Data record grouping
$query['group'] = $this->gridDataGroup($params, $model);
// The subset of data based on pagination
$query['limit'] = $this->gridDataLimit($params, $model, $pagination['start'], $pagination['limit']);
// Ordering based on user request
$query['order'] = $this->gridDataOrder($params, $model,
isset($params['sidx']) ? $params['sidx'] : null,
isset($params['sord']) ? $params['sord'] : null);
if ($params['debug'])
$params['query'] = $query;
return $this->gridDataRecordsExecute($params, $model, $query);
}
function gridDataRecordsExecute(&$params, &$model, $query) {
return $model->find('all', $query);
}
function gridDataTables(&$params, &$model) {
return array('link' => array());
}
function gridDataTableSet(&$params, &$model) {
// Preliminary set of tables
$query = array_intersect_key($this->gridDataTables($params, $model),
array('link'=>1, 'contain'=>1));
// Perform filtering based on user request: $params['post']['filter']
$query = array_intersect_key($this->gridDataFilterTables($params, $model, $query),
array('link'=>1, 'contain'=>1));
return $query;
}
function gridDataConditions(&$params, &$model) {
$searches = array();
if (isset($params['_search']) && $params['_search'] === 'true') {
if (isset($params['searchOper'])) {
$searches[] = array('op' => $params['searchOper'],
'field' => $params['searchField'],
'value' => $params['searchString']);
}
else {
// DOH! Crappy mechanism puts toolbar search terms
// directly into params as name/value pairs. No
// way to know which elements of params are search
// terms, so skipping this at the moment.
}
}
elseif (isset($params['filt']) && $params['filt']) {
$searches[] = array('op' => 'bw',
'field' => $params['filtField'],
'value' => $params['filtValue']);
}
$ops = array('eq' => array('op' => null, 'pre' => '', 'post' => ''),
'ne' => array('op' => '<>', 'pre' => '', 'post' => ''),
'lt' => array('op' => '<', 'pre' => '', 'post' => ''),
'le' => array('op' => '<=', 'pre' => '', 'post' => ''),
'gt' => array('op' => '>', 'pre' => '', 'post' => ''),
'ge' => array('op' => '>=', 'pre' => '', 'post' => ''),
'bw' => array('op' => 'LIKE', 'pre' => '', 'post' => '%'),
'ew' => array('op' => 'LIKE', 'pre' => '%', 'post' => ''),
'cn' => array('op' => 'LIKE', 'pre' => '%', 'post' => '%'),
);
$conditions = array();
foreach ($searches AS $search) {
$op = $ops[$search['op']];
$field = $search['field'] . ($op['op'] ? ' '.$op['op'] : '');
$value = $op['pre'] . $search['value']. $op['post'];
$conditions[] = array($field => $value);
}
if (isset($params['action']) && $params['action'] === 'idlist') {
if (count($params['post']['idlist']))
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['post']['idlist']);
else
$conditions[] = '0=1';
}
return $conditions;
}
function gridDataConditionSet(&$params, &$model) {
// Conditions for record retrieval
$conditions = $this->gridDataConditions($params, $model);
// Perform filtering based on user request: $params['post']['filter']
return $this->gridDataFilterConditions($params, $model, $conditions);
}
function gridDataFields(&$params, &$model) {
$db = &$model->getDataSource();
$fields = $db->fields($model, $model->alias);
return $fields;
}
function gridDataGroup(&$params, &$model) {
return $model->alias.'.'.$model->primaryKey;
}
function gridDataOrder(&$params, &$model, $index, $direction) {
return $index ? array($index .' '. $direction) : null;
}
function gridDataLimit(&$params, &$model, $start, $limit) {
return $start . ', ' . $limit;
}
/**************************************************************************
**************************************************************************
* gridData POST PROCESSING
*/
function gridDataPostProcess(&$params, &$model, &$records) {
// Add grid IDs to each record
$this->gridDataPostProcessGridIDs($params, $model, $records);
// Add the calculated fields (if any), to the model fields
$this->gridDataPostProcessCalculatedFields($params, $model, $records);
// Perform any requested subtotaling of fields
$this->gridDataPostProcessSubtotal($params, $model, $records);
// Add in any needed hyperlinks
$this->gridDataPostProcessLinks($params, $model, $records, array());
// DEBUG PURPOSES ONLY!
//if ($params['debug'])
//$params['records'] = $records;
}
function gridDataPostProcessGridIDs(&$params, &$model, &$records) {
$model_alias = $model->alias;
$id = $model->primaryKey;
foreach ($records AS &$record) {
$record['grid_id'] = $record[$model_alias][$id];
}
}
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
$model_alias = $model->alias;
foreach ($records AS &$record) {
// Add the calculated fields (if any), to the model fields
if (isset($record[0])) {
$record[$model_alias] = $record[0] + $record[$model_alias];
unset($record[0]);
}
}
}
function gridDataPostProcessSubtotal(&$params, &$model, &$records) {
$model_alias = $model->alias;
// REVISIT <AP>: 20090722
// Horrible solution to something that should be done
// in SQL. But, it works for now, so what the heck...
$subtotals = array();
foreach ($params['post']['fields'] AS $field) {
if (preg_match('/subtotal-(.*)$/', $field, $matches))
$subtotals[] = array('field' => $matches[1],
'name' => $field,
'amount' => 0);
}
foreach ($records AS &$record) {
foreach ($subtotals AS &$subtotal) {
$field = $subtotal['field'];
if (preg_match("/\./", $field)) {
list($tbl, $col) = explode(".", $field);
$record['subtotal-'.$tbl][$col] =
($subtotal['amount'] += $record[$tbl][$col]);
}
else {
$record[$model->alias]['subtotal-'.$field] =
($subtotal['amount'] += $record[$model->alias][$field]);
}
}
}
}
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
// Don't create any links if ordered not to.
if (isset($params['post']['nolinks']))
return;
App::import('Helper', 'Html');
foreach ($links AS $table => $fields) {
$special = array('controller', 'action', 'id');
$controller = Inflector::pluralize(Inflector::underscore($table));
$action = 'view';
$id = 'id';
extract(array_intersect_key($fields, array_flip($special)));
foreach ($records AS &$record) {
if (!isset($record[$table]))
continue;
foreach (array_diff_key($fields, array_flip($special)) AS $field) {
if (!isset($record[$table][$id]) || !isset($record[$table][$field]))
continue;
// DEBUG PURPOSES ONLY!
//if ($params['debug'])
//$params['linkrecord'][] = compact('table', 'field', 'id', 'controller', 'record');
$record[$table][$field] =
'<A HREF="' .
HtmlHelper::url(array('controller' => $controller,
'action' => $action,
$record[$table][$id])) .
'">' .
$record[$table][$field] .
'</A>';
}
}
}
}
/**************************************************************************
**************************************************************************
* gridData OUTPUT
*/
function gridDataOutput(&$params, &$model, &$records, $pagination) {
$this->gridDataOutputHeader($params, $model);
$this->gridDataOutputXMLHeader($params, $model);
$this->gridDataOutputRootNodeBegin($params, $model);
$this->gridDataOutputSummary($params, $model, $pagination);
$this->gridDataOutputRecords($params, $model, $records);
$this->gridDataOutputRootNodeEnd($params, $model);
}
function gridDataOutputHeader(&$params, &$model) {
if (!$params['debug'])
header("Content-type: text/xml;charset=utf-8");
}
function gridDataOutputXMLHeader(&$params, &$model) {
echo "<?xml version='1.0' encoding='utf-8'?>\n";
}
function gridDataOutputRootNodeBegin(&$params, &$model) {
echo "<rows>\n";
}
function gridDataOutputRootNodeEnd(&$params, &$model) {
echo "</rows>\n";
}
function gridDataOutputSummary(&$params, &$model, $pagination) {
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
echo " <page>{$pagination['page']}</page>\n";
echo " <total>{$pagination['total']}</total>\n";
echo " <records>{$pagination['record_count']}</records>\n";
if (isset($params['userdata'])) {
foreach ($params['userdata'] AS $field => $value)
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
}
}
function gridDataOutputRecords(&$params, &$model, &$records) {
$id_field = 'grid_id';
foreach ($records AS $record) {
$this->gridDataOutputRecord($params, $model, $record,
$record[$id_field], $params['post']['fields']);
}
}
function gridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
echo " <row id='$id'>\n";
foreach ($fields AS $field) {
$this->gridDataOutputRecordField($params, $model, $record, $field);
}
echo " </row>\n";
}
function gridDataOutputRecordField(&$params, &$model, &$record, $field) {
if (preg_match("/\./", $field)) {
list($tbl, $col) = explode(".", $field);
//pr(compact('record', 'field', 'tbl', 'col'));
$data = $record[$tbl][$col];
}
else {
$data = $record[$model->alias][$field];
}
$this->gridDataOutputRecordCell($params, $model, $record, $field, $data);
}
function gridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
// be sure to put text data in CDATA
if (preg_match("/^\d*$/", $data))
echo " <cell>$data</cell>\n";
else
echo " <cell><![CDATA[$data]]></cell>\n";
}
function authorize($name) {
if ($this->Permission->deny($name))
$this->UNAUTHORIZED("Unauthorized: $name");
}
function UNAUTHORIZED($msg) {
//$this->redirect('controller' => '???', 'action' => 'login');
//$this->render('/unauthorized');
$this->set('message', '<H2>' . $msg . '</H2>');
$this->render_empty();
}
function INTERNAL_ERROR($msg, $depth = 0) {
INTERNAL_ERROR($msg, false, $depth+1);
$this->render_empty();
}
function render_empty() {
echo $this->render('/empty');
$this->_stop();
}
}
?>