git-svn-id: file:///svn-source/pmgr/branches/v0.3_work/site@991 97e9348a-65ac-dc4b-aefc-98561f571b83
1231 lines
43 KiB
PHP
1231 lines
43 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, 'REPORT' => 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: dev/admin()
|
|
* - Indicates if the user has dev/admin access
|
|
*/
|
|
function dev() { return !empty($this->params['dev']); }
|
|
function admin() { return !empty($this->params['admin']); }
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* 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 == 'REPORT')
|
|
$name = 'Reports';
|
|
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 ($this->admin())
|
|
$this->addSideMenuLink('Assess Charges',
|
|
array('controller' => 'leases', 'action' => 'assess_all'), null,
|
|
'SITE', $this->op_area);
|
|
|
|
if ($this->admin()) {
|
|
$acct = new Account;
|
|
$this->addSideMenuLink('Collected Rent',
|
|
array('controller' => 'accounts',
|
|
'action' => 'collected',
|
|
$acct->rentAccountID()), null,
|
|
'REPORT');
|
|
$this->addSideMenuLink('Unpaid Charges',
|
|
array('controller' => 'statement_entries', 'action' => 'unpaid'), null,
|
|
'REPORT');
|
|
$this->addSideMenuLink('Lease Up',
|
|
array('controller' => 'leases', 'action' => 'overview'), null,
|
|
'REPORT');
|
|
$this->addSideMenuLink('Unit Summary',
|
|
array('controller' => 'units', 'action' => 'overview'), null,
|
|
'REPORT');
|
|
$this->addSideMenuLink('Monthly Charges',
|
|
array('controller' => 'statement_entries', 'action' => 'chargesbymonth'), null,
|
|
'REPORT');
|
|
}
|
|
else {
|
|
$this->sideMenuEnable('REPORT', null, false);
|
|
}
|
|
|
|
|
|
|
|
|
|
$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->dev() && !$this->Option->enabled('dev'))
|
|
$this->redirect("/");
|
|
|
|
if (!$this->dev())
|
|
Configure::write('debug', '0');
|
|
|
|
$this->addDefaultSideMenuLinks();
|
|
//$this->sideMenuEnable('SITE', $this->op_area, false);
|
|
|
|
foreach ($this->sidemenu['areas'] AS $area_name => $area) {
|
|
if (!$this->dev())
|
|
$this->sideMenuEnable($area_name, $this->dev_area, false);
|
|
if (!$this->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);
|
|
}
|
|
|
|
// If generating reports, don't display the controller menu.
|
|
// Each report comes from a controller, but there is no need
|
|
// to present the controller actions, so remove that section
|
|
// from the menu.
|
|
if ($this->sidemenu['active']['area'] == 'REPORT')
|
|
$this->sideMenuEnable('CONTROLLER', null, false);
|
|
|
|
//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("/&/", "&", $xml);
|
|
$xml = preg_replace("/</", "<", $xml);
|
|
$xml = preg_replace("/>/", ">", $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);
|
|
|
|
// Get the number of records prior to pagination
|
|
return $this->gridDataCountExecute($params, $model, $query);
|
|
}
|
|
|
|
function gridDataCountExecute(&$params, &$model, $query) {
|
|
return $this->gridDataFind($params, $model, '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);
|
|
|
|
// Adjust the limit upward, if multiple pages were requested.
|
|
$limit *= empty($params['npage']) ? 1 : $params['npage'];
|
|
|
|
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);
|
|
|
|
return $this->gridDataRecordsExecute($params, $model, $query);
|
|
}
|
|
|
|
function gridDataRecordsExecute(&$params, &$model, $query) {
|
|
return $this->gridDataFind($params, $model, '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' => ''),
|
|
'in' => array('op' => 'IN', '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;
|
|
}
|
|
|
|
function gridDataFind(&$params, &$model, $type, $query) {
|
|
if ($params['debug'])
|
|
$params['queries'][] = compact('type', 'query');
|
|
|
|
return $model->find($type, $query);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
* 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) {
|
|
if ($params['debug'])
|
|
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();
|
|
}
|
|
|
|
}
|
|
?>
|