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('Locks', array('controller' => 'locks', '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('Unit Summary', array('controller' => 'units', 'action' => 'overview'), null, 'REPORT'); $this->addSideMenuLink('Lease Up', array('controller' => 'leases', 'action' => 'overview'), null, 'REPORT'); /* $this->addSideMenuLink('Monthly Income', */ /* array('controller' => 'statement_entries', 'action' => 'incomebymonth'), null, */ /* 'REPORT'); */ /* $this->addSideMenuLink('Monthly Expenses', */ /* array('controller' => 'statement_entries', 'action' => 'expensebymonth'), null, */ /* 'REPORT'); */ /* $this->addSideMenuLink('Quickbook Invoice', */ /* array('controller' => 'statement_entries', 'action' => 'incomebymonth', 4, 1), null, */ /* 'REPORT'); */ /* $this->addSideMenuLink('Quickbook Credits', */ /* array('controller' => 'statement_entries', 'action' => 'expensebymonth', 4, 0), null, */ /* 'REPORT'); */ $this->addSideMenuLink('Monthly Income', array('controller' => 'statement_entries', 'action' => 'netbymonth'), 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 : 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() || ($this->dev() && $this->Option->enabled('dev'))) { // 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:


" . ''.$url.'')); 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); // If subtotaling, figure out the running total before pagination... $this->gridDataRecordsRunningSubtotal($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); echo ("\n

\n$xml\n
\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['fields'] = array($this->gridDataCountField($params, $model)); // 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 gridDataCountField(&$params, &$model) { return "COUNT(DISTINCT `".$model->alias.'`.`'.$model->primaryKey."`) AS 'count'"; } 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 : 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 : 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']); } // Translate a user specified date into the SQL date format foreach ($searches AS &$search) { if (preg_match('/(_date|stamp)$/', $search['field']) && preg_match('%(\d{1,2})[-/](\d{1,2})[-/](\d{2,4})%', $search['value'], $matches)) { $search['value'] = sprintf('%04d%02d%02d', $matches[3] + ($matches[3] < 50 ? 2000 : ($matches[3] < 100 ? 1900 : 0)), $matches[2], $matches[1]); } } unset($search); $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); } function gridDataRecordsRunningSubtotal(&$params, $model, $pagination) { // REVISIT : 20090722 // Horrible solution to something that should be done // in SQL. Doesn't really work, but for a grid that contains // ALL records, and is sorted on the correct field, it does // actually work. // // If this function worked correctly, this mechanism would also // work for grids that did not contain ALL records. $subtotals = array(); foreach ($params['post']['fields'] AS $field) { if (preg_match('/subtotal-(.*)$/', $field, $matches)) $subtotals[] = array('field' => $matches[1], 'name' => $field, 'amount' => 0); } // This part, if functioning, should do a sub-total off all records // that are not part of the grid, instead of starting at zero, so that // the totals come out correctly for the each record entry. /* $pagination['start'] = $pagination['start'] + $pagination['limit']; */ /* $pagination['limit'] = 10000000; */ /* // Retreive the appropriate subset of data */ /* $params_copy = $params; */ /* $records = $this->gridDataRecords($params_copy, $model, $pagination); */ /* 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]); */ /* } */ /* } */ /* } */ $params['subtotals'] = $subtotals; } /************************************************************************** ************************************************************************** * 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 : 20090722 // Horrible solution to something that should be done // in SQL. Doesn't really work, but for a grid that contains // ALL records, and is sorted on the correct field, it does // actually work. $subtotals = $params['subtotals']; 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] = '' . $record[$table][$field] . ''; } } } } /************************************************************************** ************************************************************************** * 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 "\n"; } function gridDataOutputRootNodeBegin(&$params, &$model) { echo "\n"; } function gridDataOutputRootNodeEnd(&$params, &$model) { echo "\n"; } function gridDataOutputSummary(&$params, &$model, $pagination) { if ($params['debug']) echo " \n"; echo " {$pagination['page']}\n"; echo " {$pagination['total']}\n"; echo " {$pagination['record_count']}\n"; if (isset($params['userdata'])) { foreach ($params['userdata'] AS $field => $value) echo ' ' . "{$value}\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 " \n"; foreach ($fields AS $field) { $this->gridDataOutputRecordField($params, $model, $record, $field); } echo " \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 " $data\n"; else echo " \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', '

' . $msg . '

'); $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(); } } ?>