2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
37 require_once('include/MVC/View/SugarView.php');
40 * Main SugarCRM controller
43 class SugarController{
45 * remap actions in here
46 * e.g. make all detail views go to edit views
47 * $action_remap = array('detailview'=>'editview');
49 protected $action_remap = array('index'=>'listview');
51 * The name of the current module.
53 public $module = 'Home';
55 * The name of the target module.
57 public $target_module = null;
59 * The name of the current action.
61 public $action = 'index';
63 * The id of the current record.
67 * The name of the return module.
69 public $return_module = null;
71 * The name of the return action.
73 public $return_action = null;
75 * The id of the return record.
77 public $return_id = null;
79 * If the action was remapped it will be set to do_action and then we will just
80 * use do_action for the actual action to perform.
82 protected $do_action = 'index';
84 * If a bean is present that set it.
90 public $redirect_url = '';
92 * any subcontroller can modify this to change the view
94 public $view = 'classic';
96 * this array will hold the mappings between a key and an object for use within the view.
98 public $view_object_map = array();
101 * This array holds the methods that handleAction() will invoke, in sequence.
103 protected $tasks = array(
109 * List of options to run through within the process() method.
110 * This list is meant to easily allow additions for new functionality as well as
111 * the ability to add a controller's own handling.
113 public $process_tasks = array(
122 * Whether or not the action has been handled by $process_tasks
126 protected $_processed = false;
128 * Map an action directly to a file
131 * Map an action directly to a file. This will be loaded from action_file_map.php
133 protected $action_file_map = array();
135 * Map an action directly to a view
138 * Map an action directly to a view. This will be loaded from action_view_map.php
140 protected $action_view_map = array();
143 * This can be set from the application to tell us whether we have authorization to
144 * process the action. If this is set we will default to the noaccess view.
146 public $hasAccess = true;
149 * Map case sensitive filenames to action. This is used for linux/unix systems
150 * where filenames are case sensitive
152 public static $action_case_file = array(
153 'editview'=>'EditView',
154 'detailview'=>'DetailView',
155 'listview'=>'ListView'
159 * Constructor. This ie meant tot load up the module, action, record as well
160 * as the mapping arrays.
162 function SugarController(){
166 * Called from SugarApplication and is meant to perform the setup operations
170 public function setup($module = ''){
171 if(empty($module) && !empty($_REQUEST['module']))
172 $module = $_REQUEST['module'];
175 $this->setModule($module);
177 if(!empty($_REQUEST['target_module']) && $_REQUEST['target_module'] != 'undefined') {
178 $this->target_module = $_REQUEST['target_module'];
180 //set properties on the controller from the $_REQUEST
181 $this->loadPropertiesFromRequest();
182 //load the mapping files
183 $this->loadMappings();
186 * Set the module on the Controller
188 * @param object $module
190 public function setModule($module){
191 $this->module = $module;
195 * Set properties on the Controller from the $_REQUEST
198 private function loadPropertiesFromRequest(){
199 if(!empty($_REQUEST['action']))
200 $this->action = $_REQUEST['action'];
201 if(!empty($_REQUEST['record']))
202 $this->record = $_REQUEST['record'];
203 if(!empty($_REQUEST['view']))
204 $this->view = $_REQUEST['view'];
205 if(!empty($_REQUEST['return_module']))
206 $this->return_module = $_REQUEST['return_module'];
207 if(!empty($_REQUEST['return_action']))
208 $this->return_action = $_REQUEST['return_action'];
209 if(!empty($_REQUEST['return_id']))
210 $this->return_id = $_REQUEST['return_id'];
214 * Load map files for use within the Controller
217 private function loadMappings(){
218 $this->loadMapping('action_view_map');
219 $this->loadMapping('action_file_map');
220 $this->loadMapping('action_remap', true);
224 * Given a record id load the bean. This bean is accessible from any sub controllers.
226 public function loadBean()
228 if(!empty($GLOBALS['beanList'][$this->module])){
229 $class = $GLOBALS['beanList'][$this->module];
230 if(!empty($GLOBALS['beanFiles'][$class])){
231 require_once($GLOBALS['beanFiles'][$class]);
232 $this->bean = new $class();
233 if(!empty($this->record)){
234 $this->bean->retrieve($this->record);
236 $GLOBALS['FOCUS'] = $this->bean;
243 * Generic load method to load mapping arrays.
245 private function loadMapping($var, $merge = false){
246 $$var = sugar_cache_retrieve("CONTROLLER_". $var . "_".$this->module);
248 if($merge && !empty($this->$var)){
253 if(file_exists('include/MVC/Controller/'. $var . '.php')){
254 require('include/MVC/Controller/'. $var . '.php');
256 if(file_exists('modules/'.$this->module.'/'. $var . '.php')){
257 require('modules/'.$this->module.'/'. $var . '.php');
259 if(file_exists('custom/modules/'.$this->module.'/'. $var . '.php')){
260 require('custom/modules/'.$this->module.'/'. $var . '.php');
262 if(file_exists('custom/include/MVC/Controller/'. $var . '.php')){
263 require('custom/include/MVC/Controller/'. $var . '.php');
266 // entry_point_registry -> EntryPointRegistry
268 $varname = str_replace(" ","",ucwords(str_replace("_"," ", $var)));
269 if(file_exists("custom/application/Ext/$varname/$var.ext.php")){
270 require("custom/application/Ext/$varname/$var.ext.php");
272 if(file_exists("custom/modules/{$this->module}/Ext/$varname/$var.ext.php")){
273 require("custom/modules/{$this->module}/Ext/$varname/$var.ext.php");
276 sugar_cache_put("CONTROLLER_". $var . "_".$this->module, $$var);
282 * This method is called from SugarApplication->execute and it will bootstrap the entire controller process
284 final public function execute()
290 if(!empty($this->view))
292 $this->processView();
294 elseif(!empty($this->redirect_url))
301 $this->handleException($e);
310 * @param Exception $e
312 protected function handleException(Exception $e)
314 $GLOBALS['log']->fatal('Exception in Controller: ' . $e->getMessage());
315 $logicHook = new LogicHook();
317 if (isset($this->bean))
319 $logicHook->setBean($this->bean);
320 $logicHook->call_custom_logic($this->bean->module_dir, "handle_exception", $e);
324 $logicHook->call_custom_logic('', "handle_exception", $e);
329 * Display the appropriate view.
331 private function processView(){
332 if(!isset($this->view_object_map['remap_action']) && isset($this->action_view_map[strtolower($this->action)]))
334 $this->view_object_map['remap_action'] = $this->action_view_map[strtolower($this->action)];
336 $view = ViewFactory::loadView($this->view, $this->module, $this->bean, $this->view_object_map, $this->target_module);
337 $GLOBALS['current_view'] = $view;
338 if(!empty($this->bean) && !$this->bean->ACLAccess($view->type) && $view->type != 'list'){
339 ACLController::displayNoAccess(true);
342 if(isset($this->errors)){
343 $view->errors = $this->errors;
349 * Meant to be overridden by a subclass and allows for specific functionality to be
350 * injected prior to the process() method being called.
352 public function preProcess()
356 * if we have a function to support the action use it otherwise use the default action
359 * 2) check for action
361 public function process(){
362 $GLOBALS['action'] = $this->action;
363 $GLOBALS['module'] = $this->module;
365 //check to ensure we have access to the module.
366 if($this->hasAccess){
367 $this->do_action = $this->action;
369 $file = self::getActionFilename($this->do_action);
374 foreach($this->process_tasks as $process){
376 if($this->_processed)
387 * This method is called from the process method. I could also be called within an action_* method.
388 * It allows a developer to override any one of these methods contained within,
389 * or if the developer so chooses they can override the entire action_* method.
391 * @return true if any one of the pre_, do_, or post_ methods have been defined,
392 * false otherwise. This is important b/c if none of these methods exists, then we will run the
393 * action_default() method.
395 protected function handle_action(){
397 foreach($this->tasks as $task){
398 $processed = ($this->$task() || $processed);
400 $this->_processed = $processed;
404 * Perform an action prior to the specified action.
405 * This can be overridde in a sub-class
407 private function pre_action(){
408 $function = 'pre_' . $this->action;
409 if($this->hasFunction($function)){
410 $GLOBALS['log']->debug('Performing pre_action');
418 * Perform the specified action.
419 * This can be overridde in a sub-class
421 private function do_action(){
422 $function = 'action_'. strtolower($this->do_action);
423 if($this->hasFunction($function)){
424 $GLOBALS['log']->debug('Performing action: '.$function.' MODULE: '.$this->module);
432 * Perform an action after to the specified action has occurred.
433 * This can be overridde in a sub-class
435 private function post_action(){
436 $function = 'post_' . $this->action;
437 if($this->hasFunction($function)){
438 $GLOBALS['log']->debug('Performing post_action');
446 * If there is no action found then display an error to the user.
448 protected function no_action(){
449 sugar_die($GLOBALS['app_strings']['LBL_NO_ACTION']);
453 * The default action handler for instances where we do not have access to process.
455 protected function no_access(){
456 $this->view = 'noaccess';
459 ///////////////////////////////////////////////
460 /////// HELPER FUNCTIONS
461 ///////////////////////////////////////////////
464 * Determine if a given function exists on the objects
465 * @param function - the function to check
466 * @return true if the method exists on the object, false otherwise
468 protected function hasFunction($function){
469 return method_exists($this, $function);
474 * Set the url to which we will want to redirect
476 * @param string url - the url to which we will want to redirect
478 protected function set_redirect($url){
479 $this->redirect_url = $url;
483 * Perform redirection based on the redirect_url
486 protected function redirect(){
488 if(!empty($this->redirect_url))
489 SugarApplication::redirect($this->redirect_url);
492 ////////////////////////////////////////////////////////
493 ////// DEFAULT ACTIONS
494 ///////////////////////////////////////////////////////
501 * Do some processing before saving the bean to the database.
503 public function pre_save(){
504 if(!empty($_POST['assigned_user_id']) && $_POST['assigned_user_id'] != $this->bean->assigned_user_id && $_POST['assigned_user_id'] != $GLOBALS['current_user']->id && empty($GLOBALS['sugar_config']['exclude_notifications'][$this->bean->module_dir])){
505 $this->bean->notify_on_save = true;
507 $GLOBALS['log']->debug("SugarController:: performing pre_save.");
508 require_once('include/SugarFields/SugarFieldHandler.php');
509 $sfh = new SugarFieldHandler();
510 foreach($this->bean->field_defs as $field => $properties) {
511 $type = !empty($properties['custom_type']) ? $properties['custom_type'] : $properties['type'];
512 $sf = $sfh->getSugarField(ucfirst($type), true);
513 if(isset($_POST[$field])) {
514 if(is_array($_POST[$field]) && !empty($properties['isMultiSelect'])) {
515 if(empty($_POST[$field][0])) {
516 unset($_POST[$field][0]);
518 $_POST[$field] = encodeMultienumValue($_POST[$field]);
520 $this->bean->$field = $_POST[$field];
521 } else if(!empty($properties['isMultiSelect']) && !isset($_POST[$field]) && isset($_POST[$field . '_multiselect'])) {
522 $this->bean->$field = '';
525 $sf->save($this->bean, $_POST, $field, $properties);
529 foreach($this->bean->relationship_fields as $field=>$link){
530 if(!empty($_POST[$field])){
531 $this->bean->$field = $_POST[$field];
534 if(!$this->bean->ACLAccess('save')){
535 ACLController::displayNoAccess(true);
538 $this->bean->unformat_all_fields();
542 * Perform the actual save
544 public function action_save(){
545 $this->bean->save(!empty($this->bean->notify_on_save));
549 public function action_spot()
551 $this->view = 'spot';
556 * Specify what happens after the save has occurred.
558 protected function post_save(){
559 $module = (!empty($this->return_module) ? $this->return_module : $this->module);
560 $action = (!empty($this->return_action) ? $this->return_action : 'DetailView');
561 $id = (!empty($this->return_id) ? $this->return_id : $this->bean->id);
563 $url = "index.php?module=".$module."&action=".$action."&record=".$id;
564 $this->set_redirect($url);
572 * Perform the actual deletion.
574 protected function action_delete(){
575 //do any pre delete processing
576 //if there is some custom logic for deletion.
577 if(!empty($_REQUEST['record'])){
578 if(!$this->bean->ACLAccess('Delete')){
579 ACLController::displayNoAccess(true);
582 $this->bean->mark_deleted($_REQUEST['record']);
584 sugar_die("A record number must be specified to delete");
589 * Specify what happens after the deletion has occurred.
591 protected function post_delete(){
592 $return_module = isset($_REQUEST['return_module']) ?
593 $_REQUEST['return_module'] :
594 $GLOBALS['sugar_config']['default_module'];
595 $return_action = isset($_REQUEST['return_action']) ?
596 $_REQUEST['return_action'] :
597 $GLOBALS['sugar_config']['default_action'];
598 $return_id = isset($_REQUEST['return_id']) ?
599 $_REQUEST['return_id'] :
601 $url = "index.php?module=".$return_module."&action=".$return_action."&record=".$return_id;
603 //eggsurplus Bug 23816: maintain VCR after an edit/save. If it is a duplicate then don't worry about it. The offset is now worthless.
604 if(isset($_REQUEST['offset']) && empty($_REQUEST['duplicateSave'])) {
605 $url .= "&offset=".$_REQUEST['offset'];
608 $this->set_redirect($url);
611 * Perform the actual massupdate.
613 protected function action_massupdate(){
614 if(!empty($_REQUEST['massupdate']) && $_REQUEST['massupdate'] == 'true' && (!empty($_REQUEST['uid']) || !empty($_REQUEST['entire']))){
615 if(!empty($_REQUEST['Delete']) && $_REQUEST['Delete']=='true' && !$this->bean->ACLAccess('delete')
616 || (empty($_REQUEST['Delete']) || $_REQUEST['Delete']!='true') && !$this->bean->ACLAccess('save')){
617 ACLController::displayNoAccess(true);
621 set_time_limit(0);//I'm wondering if we will set it never goes timeout here.
622 // until we have more efficient way of handling MU, we have to disable the limit
623 $GLOBALS['db']->setQueryLimit(0);
624 require_once("include/MassUpdate.php");
625 require_once('modules/MySettings/StoreQuery.php');
626 $seed = loadBean($_REQUEST['module']);
627 $mass = new MassUpdate();
628 $mass->setSugarBean($seed);
629 if(isset($_REQUEST['entire']) && empty($_POST['mass'])) {
630 $mass->generateSearchWhere($_REQUEST['module'], $_REQUEST['current_query_by_page']);
632 $mass->handleMassUpdate();
633 $storeQuery = new StoreQuery();//restore the current search. to solve bug 24722 for multi tabs massupdate.
634 $temp_req = array('current_query_by_page' => $_REQUEST['current_query_by_page'], 'return_module' => $_REQUEST['return_module'], 'return_action' => $_REQUEST['return_action']);
635 if($_REQUEST['return_module'] == 'Emails') {
636 if(!empty($_REQUEST['type']) && !empty($_REQUEST['ie_assigned_user_id'])) {
637 $this->req_for_email = array('type' => $_REQUEST['type'], 'ie_assigned_user_id' => $_REQUEST['ie_assigned_user_id']); // Specifically for My Achieves
641 $_REQUEST = unserialize(base64_decode($temp_req['current_query_by_page']));
642 unset($_REQUEST[$seed->module_dir.'2_'.strtoupper($seed->object_name).'_offset']);//after massupdate, the page should redirect to no offset page
643 $storeQuery->saveFromRequest($_REQUEST['module']);
644 $_REQUEST = array('return_module' => $temp_req['return_module'], 'return_action' => $temp_req['return_action']);//for post_massupdate, to go back to original page.
646 sugar_die("You must massupdate at least one record");
650 * Specify what happens after the massupdate has occurred.
652 protected function post_massupdate(){
653 $return_module = isset($_REQUEST['return_module']) ?
654 $_REQUEST['return_module'] :
655 $GLOBALS['sugar_config']['default_module'];
656 $return_action = isset($_REQUEST['return_action']) ?
657 $_REQUEST['return_action'] :
658 $GLOBALS['sugar_config']['default_action'];
659 $url = "index.php?module=".$return_module."&action=".$return_action;
660 if($return_module == 'Emails'){//specificly for My Achieves
661 if(!empty($this->req_for_email['type']) && !empty($this->req_for_email['ie_assigned_user_id'])) {
662 $url = $url . "&type=".$this->req_for_email['type']."&assigned_user_id=".$this->req_for_email['ie_assigned_user_id'];
665 $this->set_redirect($url);
668 * Perform the listview action
670 protected function action_listview(){
671 $this->view_object_map['bean'] = $this->bean;
672 $this->view = 'list';
677 //THIS IS HANDLED IN ACTION_REMAP WHERE INDEX IS SET TO LISTVIEW
678 function action_index(){
683 * Action to handle when using a file as was done in previous versions of Sugar.
685 protected function action_default(){
686 $this->view = 'classic';
690 * this method id used within a Dashlet when performing an ajax call
692 protected function action_callmethoddashlet(){
693 if(!empty($_REQUEST['id'])) {
694 $id = $_REQUEST['id'];
695 $requestedMethod = $_REQUEST['method'];
696 $dashletDefs = $GLOBALS['current_user']->getPreference('dashlets', 'Home'); // load user's dashlets config
697 if(!empty($dashletDefs[$id])) {
698 require_once($dashletDefs[$id]['fileLocation']);
700 $dashlet = new $dashletDefs[$id]['className']($id, (isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array()));
702 if(method_exists($dashlet, $requestedMethod) || method_exists($dashlet, '__call')) {
703 echo $dashlet->$requestedMethod();
713 * this method is used within a Dashlet when the options configuration is posted
715 protected function action_configuredashlet(){
716 global $current_user, $mod_strings;
718 if(!empty($_REQUEST['id'])) {
719 $id = $_REQUEST['id'];
720 $dashletDefs = $current_user->getPreference('dashlets', $_REQUEST['module']); // load user's dashlets config
721 require_once($dashletDefs[$id]['fileLocation']);
723 $dashlet = new $dashletDefs[$id]['className']($id, (isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array()));
724 if(!empty($_REQUEST['configure']) && $_REQUEST['configure']) { // save settings
725 $dashletDefs[$id]['options'] = $dashlet->saveOptions($_REQUEST);
726 $current_user->setPreference('dashlets', $dashletDefs, 0, $_REQUEST['module']);
728 else { // display options
729 $json = getJSONobj();
730 return 'result = ' . $json->encode((array('header' => $dashlet->title . ' : ' . $mod_strings['LBL_OPTIONS'],
731 'body' => $dashlet->displayOptions())));
741 * Global method to delete an attachment
743 * If the bean does not have a deleteAttachment method it will return 'false' as a string
747 protected function action_deleteattachment()
749 $this->view = 'edit';
750 $GLOBALS['view'] = $this->view;
752 if(method_exists($this->bean, 'deleteAttachment')) {
753 echo $this->bean->deleteAttachment($_REQUEST['isDuplicate']) ? 'true' : 'false';
764 public static function getActionFilename($action) {
765 if(isset(self::$action_case_file[$action])) {
766 return self::$action_case_file[$action];
771 /********************************************************************/
773 /********************************************************************/
776 * Given the module and action, determine whether the super/admin has prevented access
777 * to this url. In addition if any links specified for this module, load the links into
780 * @return true if we want to stop processing, false if processing should continue
782 private function blockFileAccess(){
783 //check if the we have enabled file_access_control and if so then check the mappings on the request;
784 if(!empty($GLOBALS['sugar_config']['admin_access_control']) && $GLOBALS['sugar_config']['admin_access_control']){
785 $this->loadMapping('file_access_control_map');
786 //since we have this turned on, check the mapping file
787 $module = strtolower($this->module);
788 $action = strtolower($this->do_action);
789 if(!empty($this->file_access_control_map['modules'][$module]['links'])){
790 $GLOBALS['admin_access_control_links'] = $this->file_access_control_map['modules'][$module]['links'];
793 if(!empty($this->file_access_control_map['modules'][$module]['actions']) && (in_array($action, $this->file_access_control_map['modules'][$module]['actions']) || !empty($this->file_access_control_map['modules'][$module]['actions'][$action]))){
795 if(!empty($this->file_access_control_map['modules'][$module]['actions'][$action]['params'])){
797 $params = $this->file_access_control_map['modules'][$module]['actions'][$action]['params'];
798 foreach($params as $param => $paramVals){
799 if(!empty($_REQUEST[$param])){
800 if(!in_array($_REQUEST[$param], $paramVals)){
807 $this->_processed = true;
811 $this->_processed = true;
816 $this->_processed = false;
820 * This code is part of the entry points reworking. We have consolidated all
821 * entry points to go through index.php. Now in order to bring up an entry point
822 * it will follow the format:
823 * 'index.php?entryPoint=download'
824 * the download entry point is mapped in the following file: entry_point_registry.php
827 private function handleEntryPoint(){
828 if(!empty($_REQUEST['entryPoint'])){
829 $this->loadMapping('entry_point_registry');
830 $entryPoint = $_REQUEST['entryPoint'];
832 if(!empty($this->entry_point_registry[$entryPoint])){
833 require_once($this->entry_point_registry[$entryPoint]['file']);
834 $this->_processed = true;
841 * Checks to see if the requested entry point requires auth
843 * @param $entrypoint string name of the entrypoint
844 * @return bool true if auth is required, false if not
846 public function checkEntryPointRequiresAuth($entryPoint)
848 $this->loadMapping('entry_point_registry');
850 if ( isset($this->entry_point_registry[$entryPoint]['auth'])
851 && !$this->entry_point_registry[$entryPoint]['auth'] )
857 * Meant to handle old views e.g. DetailView.php.
860 protected function callLegacyCode()
862 $file = self::getActionFilename($this->do_action);
863 if ( isset($this->action_view_map[strtolower($this->do_action)]) ) {
864 $action = $this->action_view_map[strtolower($this->do_action)];
867 $action = $this->do_action;
869 // index actions actually maps to the view.list.php view
870 if ( $action == 'index' ) {
874 if ((file_exists('modules/' . $this->module . '/'. $file . '.php')
875 && !file_exists('modules/' . $this->module . '/views/view.'. $action . '.php'))
876 || (file_exists('custom/modules/' . $this->module . '/'. $file . '.php')
877 && !file_exists('custom/modules/' . $this->module . '/views/view.'. $action . '.php'))
879 // A 'classic' module, using the old pre-MVC display files
880 // We should now discard the bean we just obtained for tracking as the pre-MVC module will instantiate its own
881 unset($GLOBALS['FOCUS']);
882 $GLOBALS['log']->debug('Module:' . $this->module . ' using file: '. $file);
883 $this->action_default();
884 $this->_processed = true;
889 * If the action has been remapped to a different action as defined in
890 * action_file_map.php or action_view_map.php load those maps here.
893 private function handleActionMaps(){
894 if(!empty($this->action_file_map[strtolower($this->do_action)])){
896 $GLOBALS['log']->debug('Using Action File Map:' . $this->action_file_map[strtolower($this->do_action)]);
897 require_once($this->action_file_map[strtolower($this->do_action)]);
898 $this->_processed = true;
899 }elseif(!empty($this->action_view_map[strtolower($this->do_action)])){
900 $GLOBALS['log']->debug('Using Action View Map:' . $this->action_view_map[strtolower($this->do_action)]);
901 $this->view = $this->action_view_map[strtolower($this->do_action)];
902 $this->_processed = true;
908 * Actually remap the action if required.
911 protected function remapAction(){
912 if(!empty($this->action_remap[$this->do_action])){
913 $this->action = $this->action_remap[$this->do_action];
914 $this->do_action = $this->action;