]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/MVC/Controller/SugarController.php
Release 6.5.16
[Github/sugarcrm.git] / include / MVC / Controller / SugarController.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
5  * 
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.
12  * 
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
16  * details.
17  * 
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
21  * 02110-1301 USA.
22  * 
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.
25  * 
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.
29  * 
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  ********************************************************************************/
36
37 require_once('include/MVC/View/SugarView.php');
38
39 /**
40  * Main SugarCRM controller
41  * @api
42  */
43 class SugarController{
44         /**
45          * remap actions in here
46          * e.g. make all detail views go to edit views
47          * $action_remap = array('detailview'=>'editview');
48          */
49         protected $action_remap = array('index'=>'listview');
50         /**
51          * The name of the current module.
52          */
53         public $module = 'Home';
54         /**
55          * The name of the target module.
56          */
57         public $target_module = null;
58         /**
59          * The name of the current action.
60          */
61         public $action = 'index';
62         /**
63          * The id of the current record.
64          */
65         public $record = '';
66         /**
67          * The name of the return module.
68          */
69         public $return_module = null;
70         /**
71          * The name of the return action.
72          */
73         public $return_action = null;
74         /**
75          * The id of the return record.
76          */
77         public $return_id = null;
78         /**
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.
81          */
82         protected $do_action = 'index';
83         /**
84          * If a bean is present that set it.
85          */
86         public $bean = null;
87         /**
88          * url to redirect to
89          */
90         public $redirect_url = '';
91         /**
92          * any subcontroller can modify this to change the view
93          */
94         public $view = 'classic';
95         /**
96          * this array will hold the mappings between a key and an object for use within the view.
97          */
98         public $view_object_map = array();
99
100         /**
101          * This array holds the methods that handleAction() will invoke, in sequence.
102          */
103         protected $tasks = array(
104                                            'pre_action',
105                                            'do_action',
106                                            'post_action'
107                                            );
108         /**
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.
112          */
113         public $process_tasks = array(
114                                                 'blockFileAccess',
115                                                 'handleEntryPoint',
116                                                 'callLegacyCode',
117                                                 'remapAction',
118                                                 'handle_action',
119                                                 'handleActionMaps',
120                                         );
121         /**
122          * Whether or not the action has been handled by $process_tasks
123          *
124          * @var bool
125          */
126         protected $_processed = false;
127         /**
128          * Map an action directly to a file
129          */
130         /**
131          * Map an action directly to a file. This will be loaded from action_file_map.php
132          */
133         protected $action_file_map = array();
134         /**
135          * Map an action directly to a view
136          */
137         /**
138          * Map an action directly to a view. This will be loaded from action_view_map.php
139          */
140         protected $action_view_map = array();
141
142         /**
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.
145          */
146         public $hasAccess = true;
147
148         /**
149          * Map case sensitive filenames to action.  This is used for linux/unix systems
150          * where filenames are case sensitive
151          */
152         public static $action_case_file = array(
153                                                                                 'editview'=>'EditView',
154                                                                                 'detailview'=>'DetailView',
155                                                                                 'listview'=>'ListView'
156                                                                           );
157
158         /**
159          * Constructor. This ie meant tot load up the module, action, record as well
160          * as the mapping arrays.
161          */
162         function SugarController(){
163         }
164
165         /**
166          * Called from SugarApplication and is meant to perform the setup operations
167          * on the controller.
168          *
169          */
170         public function setup($module = ''){
171                 if(empty($module) && !empty($_REQUEST['module']))
172                         $module = $_REQUEST['module'];
173                 //set the module
174                 if(!empty($module))
175                         $this->setModule($module);
176
177                 if(!empty($_REQUEST['target_module']) && $_REQUEST['target_module'] != 'undefined') {
178                         $this->target_module = $_REQUEST['target_module'];
179                 }
180                 //set properties on the controller from the $_REQUEST
181                 $this->loadPropertiesFromRequest();
182                 //load the mapping files
183                 $this->loadMappings();
184         }
185         /**
186          * Set the module on the Controller
187          *
188          * @param object $module
189          */
190         public function setModule($module){
191                 $this->module = $module;
192         }
193
194         /**
195          * Set properties on the Controller from the $_REQUEST
196          *
197          */
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'];
211         }
212
213         /**
214          * Load map files for use within the Controller
215          *
216          */
217         private function loadMappings(){
218                 $this->loadMapping('action_view_map');
219                 $this->loadMapping('action_file_map');
220                 $this->loadMapping('action_remap', true);
221         }
222
223         /**
224          * Given a record id load the bean. This bean is accessible from any sub controllers.
225          */
226         public function loadBean()
227         {
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);
235                                         if($this->bean)
236                                                 $GLOBALS['FOCUS'] = $this->bean;
237                                 }
238                         }
239                 }
240         }
241
242         /**
243          * Generic load method to load mapping arrays.
244          */
245         private function loadMapping($var, $merge = false){
246                 $$var = sugar_cache_retrieve("CONTROLLER_". $var . "_".$this->module);
247                 if(!$$var){
248                         if($merge && !empty($this->$var)){
249                                 $$var = $this->$var;
250                         }else{
251                                 $$var = array();
252                         }
253                         if(file_exists('include/MVC/Controller/'. $var . '.php')){
254                                 require('include/MVC/Controller/'. $var . '.php');
255                         }
256                         if(file_exists('modules/'.$this->module.'/'. $var . '.php')){
257                                 require('modules/'.$this->module.'/'. $var . '.php');
258                         }
259                         if(file_exists('custom/modules/'.$this->module.'/'. $var . '.php')){
260                                 require('custom/modules/'.$this->module.'/'. $var . '.php');
261                         }
262                         if(file_exists('custom/include/MVC/Controller/'. $var . '.php')){
263                                 require('custom/include/MVC/Controller/'. $var . '.php');
264                         }
265
266             // entry_point_registry -> EntryPointRegistry
267
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");
271                 }
272                         if(file_exists("custom/modules/{$this->module}/Ext/$varname/$var.ext.php")){
273                                 require("custom/modules/{$this->module}/Ext/$varname/$var.ext.php");
274                         }
275
276                         sugar_cache_put("CONTROLLER_". $var . "_".$this->module, $$var);
277                 }
278                 $this->$var = $$var;
279         }
280
281         /**
282          * This method is called from SugarApplication->execute and it will bootstrap the entire controller process
283          */
284         final public function execute()
285     {
286
287         try
288         {
289             $this->process();
290             if(!empty($this->view))
291             {
292                 $this->processView();
293             }
294             elseif(!empty($this->redirect_url))
295             {
296                                 $this->redirect();
297             }
298         }
299         catch (Exception $e)
300         {
301             $this->handleException($e);
302         }
303
304
305
306         }
307
308     /**
309       * Handle exception
310       * @param Exception $e
311       */
312     protected function handleException(Exception $e)
313     {
314         $GLOBALS['log']->fatal('Exception in Controller: ' . $e->getMessage());
315         $logicHook = new LogicHook();
316
317         if (isset($this->bean))
318         {
319             $logicHook->setBean($this->bean);
320             $logicHook->call_custom_logic($this->bean->module_dir, "handle_exception", $e);
321         }
322         else
323         {
324             $logicHook->call_custom_logic('', "handle_exception", $e);
325         }
326     }
327
328         /**
329          * Display the appropriate view.
330          */
331         private function processView(){
332                 if(!isset($this->view_object_map['remap_action']) && isset($this->action_view_map[strtolower($this->action)]))
333                 {
334                   $this->view_object_map['remap_action'] = $this->action_view_map[strtolower($this->action)];
335                 }
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);
340                         sugar_cleanup(true);
341                 }
342                 if(isset($this->errors)){
343                   $view->errors = $this->errors;
344                 }
345                 $view->process();
346         }
347
348         /**
349          * Meant to be overridden by a subclass and allows for specific functionality to be
350          * injected prior to the process() method being called.
351          */
352         public function preProcess()
353         {}
354
355         /**
356          * if we have a function to support the action use it otherwise use the default action
357          *
358          * 1) check for file
359          * 2) check for action
360          */
361         public function process(){
362                 $GLOBALS['action'] = $this->action;
363                 $GLOBALS['module'] = $this->module;
364
365                 //check to ensure we have access to the module.
366                 if($this->hasAccess){
367                         $this->do_action = $this->action;
368
369                         $file = self::getActionFilename($this->do_action);
370
371                         $this->loadBean();
372
373                         $processed = false;
374                         foreach($this->process_tasks as $process){
375                                 $this->$process();
376                                 if($this->_processed)
377                                         break;
378                         }
379
380                         $this->redirect();
381                 }else{
382                         $this->no_access();
383                 }
384         }
385
386         /**
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.
390          *
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.
394          */
395         protected function handle_action(){
396                 $processed = false;
397                 foreach($this->tasks as $task){
398                         $processed = ($this->$task() || $processed);
399                 }
400                 $this->_processed = $processed;
401         }
402
403         /**
404          * Perform an action prior to the specified action.
405          * This can be overridde in a sub-class
406          */
407         private function pre_action(){
408                 $function = 'pre_' . $this->action;
409                 if($this->hasFunction($function)){
410                         $GLOBALS['log']->debug('Performing pre_action');
411                         $this->$function();
412                         return true;
413                 }
414                 return false;
415         }
416
417         /**
418          * Perform the specified action.
419          * This can be overridde in a sub-class
420          */
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);
425                         $this->$function();
426                         return true;
427                 }
428                 return false;
429         }
430
431         /**
432          * Perform an action after to the specified action has occurred.
433          * This can be overridde in a sub-class
434          */
435         private function post_action(){
436                 $function = 'post_' . $this->action;
437                 if($this->hasFunction($function)){
438                         $GLOBALS['log']->debug('Performing post_action');
439                         $this->$function();
440                         return true;
441                 }
442                 return false;
443         }
444
445         /**
446          * If there is no action found then display an error to the user.
447          */
448         protected function no_action(){
449                 sugar_die($GLOBALS['app_strings']['LBL_NO_ACTION']);
450         }
451
452         /**
453          * The default action handler for instances where we do not have access to process.
454          */
455         protected function no_access(){
456                 $this->view = 'noaccess';
457         }
458
459         ///////////////////////////////////////////////
460         /////// HELPER FUNCTIONS
461         ///////////////////////////////////////////////
462
463         /**
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
467          */
468         protected function hasFunction($function){
469                 return method_exists($this, $function);
470         }
471
472
473         /**
474          * Set the url to which we will want to redirect
475          *
476          * @param string url - the url to which we will want to redirect
477          */
478         protected function set_redirect($url){
479                 $this->redirect_url = $url;
480         }
481
482         /**
483          * Perform redirection based on the redirect_url
484          *
485          */
486         protected function redirect(){
487
488                 if(!empty($this->redirect_url))
489                         SugarApplication::redirect($this->redirect_url);
490         }
491
492         ////////////////////////////////////////////////////////
493         ////// DEFAULT ACTIONS
494         ///////////////////////////////////////////////////////
495
496         /*
497          * Save a bean
498          */
499
500         /**
501          * Do some processing before saving the bean to the database.
502          */
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;
506                 }
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]);
517                                         }
518                                         $_POST[$field] = encodeMultienumValue($_POST[$field]);
519                                 }
520                                 $this->bean->$field = $_POST[$field];
521                         } else if(!empty($properties['isMultiSelect']) && !isset($_POST[$field]) && isset($_POST[$field . '_multiselect'])) {
522                                 $this->bean->$field = '';
523                         }
524             if($sf != null){
525                 $sf->save($this->bean, $_POST, $field, $properties);
526             }
527                 }
528
529                 foreach($this->bean->relationship_fields as $field=>$link){
530                         if(!empty($_POST[$field])){
531                                 $this->bean->$field = $_POST[$field];
532                         }
533                 }
534                 if(!$this->bean->ACLAccess('save')){
535                         ACLController::displayNoAccess(true);
536                         sugar_cleanup(true);
537                 }
538                 $this->bean->unformat_all_fields();
539         }
540
541         /**
542          * Perform the actual save
543          */
544         public function action_save(){
545                 $this->bean->save(!empty($this->bean->notify_on_save));
546         }
547
548
549     public function action_spot()
550     {
551             $this->view = 'spot';
552     }
553
554
555         /**
556          * Specify what happens after the save has occurred.
557          */
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);
562
563                 $url = "index.php?module=".$module."&action=".$action."&record=".$id;
564                 $this->set_redirect($url);
565         }
566
567         /*
568          * Delete a bean
569          */
570
571         /**
572          * Perform the actual deletion.
573          */
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);
580                                 sugar_cleanup(true);
581                         }
582                         $this->bean->mark_deleted($_REQUEST['record']);
583                 }else{
584                         sugar_die("A record number must be specified to delete");
585                 }
586         }
587
588         /**
589          * Specify what happens after the deletion has occurred.
590          */
591         protected function post_delete(){
592         if (empty($_REQUEST['return_url'])) {
593             $return_module = isset($_REQUEST['return_module']) ?
594                 $_REQUEST['return_module'] :
595                 $GLOBALS['sugar_config']['default_module'];
596             $return_action = isset($_REQUEST['return_action']) ?
597                 $_REQUEST['return_action'] :
598                 $GLOBALS['sugar_config']['default_action'];
599             $return_id = isset($_REQUEST['return_id']) ?
600                 $_REQUEST['return_id'] :
601                 '';
602             $url = "index.php?module=".$return_module."&action=".$return_action."&record=".$return_id;
603         } else {
604             $url = $_REQUEST['return_url'];
605         }
606
607                 //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.
608                 if(isset($_REQUEST['offset']) && empty($_REQUEST['duplicateSave'])) {
609                     $url .= "&offset=".$_REQUEST['offset'];
610                 }
611
612                 $this->set_redirect($url);
613         }
614         /**
615          * Perform the actual massupdate.
616          */
617         protected function action_massupdate(){
618                 if(!empty($_REQUEST['massupdate']) && $_REQUEST['massupdate'] == 'true' && (!empty($_REQUEST['uid']) || !empty($_REQUEST['entire']))){
619                         if(!empty($_REQUEST['Delete']) && $_REQUEST['Delete']=='true' && !$this->bean->ACLAccess('delete')
620                 || (empty($_REQUEST['Delete']) || $_REQUEST['Delete']!='true') && !$this->bean->ACLAccess('save')){
621                                 ACLController::displayNoAccess(true);
622                                 sugar_cleanup(true);
623                         }
624
625             set_time_limit(0);//I'm wondering if we will set it never goes timeout here.
626             // until we have more efficient way of handling MU, we have to disable the limit
627             $GLOBALS['db']->setQueryLimit(0);
628             require_once("include/MassUpdate.php");
629             require_once('modules/MySettings/StoreQuery.php');
630             $seed = loadBean($_REQUEST['module']);
631             $mass = new MassUpdate();
632             $mass->setSugarBean($seed);
633             if(isset($_REQUEST['entire']) && empty($_POST['mass'])) {
634                 $mass->generateSearchWhere($_REQUEST['module'], $_REQUEST['current_query_by_page']);
635             }
636             $mass->handleMassUpdate();
637             $storeQuery = new StoreQuery();//restore the current search. to solve bug 24722 for multi tabs massupdate.
638             $temp_req = array('current_query_by_page' => $_REQUEST['current_query_by_page'], 'return_module' => $_REQUEST['return_module'], 'return_action' => $_REQUEST['return_action']);
639             if($_REQUEST['return_module'] == 'Emails') {
640                 if(!empty($_REQUEST['type']) && !empty($_REQUEST['ie_assigned_user_id'])) {
641                     $this->req_for_email = array('type' => $_REQUEST['type'], 'ie_assigned_user_id' => $_REQUEST['ie_assigned_user_id']); // Specifically for My Achieves
642                 }
643             }
644             $_REQUEST = array();
645             $_REQUEST = unserialize(base64_decode($temp_req['current_query_by_page']));
646             unset($_REQUEST[$seed->module_dir.'2_'.strtoupper($seed->object_name).'_offset']);//after massupdate, the page should redirect to no offset page
647             $storeQuery->saveFromRequest($_REQUEST['module']);
648             $_REQUEST = array('return_module' => $temp_req['return_module'], 'return_action' => $temp_req['return_action']);//for post_massupdate, to go back to original page.
649                 }else{
650                         sugar_die("You must massupdate at least one record");
651                 }
652         }
653         /**
654          * Specify what happens after the massupdate has occurred.
655          */
656         protected function post_massupdate(){
657                 $return_module = isset($_REQUEST['return_module']) ?
658                         $_REQUEST['return_module'] :
659                         $GLOBALS['sugar_config']['default_module'];
660                 $return_action = isset($_REQUEST['return_action']) ?
661                         $_REQUEST['return_action'] :
662                         $GLOBALS['sugar_config']['default_action'];
663                 $url = "index.php?module=".$return_module."&action=".$return_action;
664                 if($return_module == 'Emails'){//specificly for My Achieves
665                         if(!empty($this->req_for_email['type']) && !empty($this->req_for_email['ie_assigned_user_id'])) {
666                                 $url = $url . "&type=".$this->req_for_email['type']."&assigned_user_id=".$this->req_for_email['ie_assigned_user_id'];
667                         }
668                 }
669                 $this->set_redirect($url);
670         }
671         /**
672          * Perform the listview action
673          */
674         protected function action_listview(){
675                 $this->view_object_map['bean'] = $this->bean;
676                 $this->view = 'list';
677         }
678
679 /*
680
681         //THIS IS HANDLED IN ACTION_REMAP WHERE INDEX IS SET TO LISTVIEW
682         function action_index(){
683         }
684 */
685
686         /**
687          * Action to handle when using a file as was done in previous versions of Sugar.
688          */
689         protected function action_default(){
690                 $this->view = 'classic';
691         }
692
693         /**
694          * this method id used within a Dashlet when performing an ajax call
695          */
696         protected function action_callmethoddashlet(){
697                 if(!empty($_REQUEST['id'])) {
698                     $id = $_REQUEST['id'];
699                     $requestedMethod = $_REQUEST['method'];
700                     $dashletDefs = $GLOBALS['current_user']->getPreference('dashlets', 'Home'); // load user's dashlets config
701                     if(!empty($dashletDefs[$id])) {
702                         require_once($dashletDefs[$id]['fileLocation']);
703
704                         $dashlet = new $dashletDefs[$id]['className']($id, (isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array()));
705
706                         if(method_exists($dashlet, $requestedMethod) || method_exists($dashlet, '__call')) {
707                             echo $dashlet->$requestedMethod();
708                         }
709                         else {
710                             echo 'no method';
711                         }
712                     }
713                 }
714         }
715
716         /**
717          * this method is used within a Dashlet when the options configuration is posted
718          */
719         protected function action_configuredashlet(){
720                 global $current_user, $mod_strings;
721
722                 if(!empty($_REQUEST['id'])) {
723                     $id = $_REQUEST['id'];
724                     $dashletDefs = $current_user->getPreference('dashlets', $_REQUEST['module']); // load user's dashlets config
725                     require_once($dashletDefs[$id]['fileLocation']);
726
727                     $dashlet = new $dashletDefs[$id]['className']($id, (isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array()));
728                     if(!empty($_REQUEST['configure']) && $_REQUEST['configure']) { // save settings
729                         $dashletDefs[$id]['options'] = $dashlet->saveOptions($_REQUEST);
730                         $current_user->setPreference('dashlets', $dashletDefs, 0, $_REQUEST['module']);
731                     }
732                     else { // display options
733                         $json = getJSONobj();
734                         return 'result = ' . $json->encode((array('header' => $dashlet->title . ' : ' . $mod_strings['LBL_OPTIONS'],
735                                                                  'body'  => $dashlet->displayOptions())));
736
737                     }
738                 }
739                 else {
740                     return '0';
741                 }
742         }
743
744     /**
745      * Global method to delete an attachment
746      *
747      * If the bean does not have a deleteAttachment method it will return 'false' as a string
748      *
749      * @return void
750      */
751     protected function action_deleteattachment()
752     {
753         $this->view = 'edit';
754         $GLOBALS['view'] = $this->view;
755         ob_clean();
756         $retval = false;
757
758         if(method_exists($this->bean, 'deleteAttachment')) {
759             $duplicate = "false";
760             if (isset($_REQUEST['isDuplicate']) && $_REQUEST['isDuplicate'] == "true") {
761                 $duplicate = "true";
762             }
763             if (isset($_REQUEST['duplicateSave']) && $_REQUEST['duplicateSave'] == "true") {
764                 $duplicate = "true";
765             }
766             $retval = $this->bean->deleteAttachment($duplicate);
767         }
768         echo json_encode($retval);
769         sugar_cleanup(true);
770     }
771
772         /**
773          * getActionFilename
774          */
775         public static function getActionFilename($action) {
776            if(isset(self::$action_case_file[$action])) {
777                   return self::$action_case_file[$action];
778            }
779            return $action;
780         }
781
782         /********************************************************************/
783         //                              PROCESS TASKS
784         /********************************************************************/
785
786         /**
787          * Given the module and action, determine whether the super/admin has prevented access
788          * to this url. In addition if any links specified for this module, load the links into
789          * GLOBALS
790          *
791          * @return true if we want to stop processing, false if processing should continue
792          */
793         private function blockFileAccess(){
794                 //check if the we have enabled file_access_control and if so then check the mappings on the request;
795                 if(!empty($GLOBALS['sugar_config']['admin_access_control']) && $GLOBALS['sugar_config']['admin_access_control']){
796                         $this->loadMapping('file_access_control_map');
797                         //since we have this turned on, check the mapping file
798                         $module = strtolower($this->module);
799                         $action = strtolower($this->do_action);
800                         if(!empty($this->file_access_control_map['modules'][$module]['links'])){
801                                 $GLOBALS['admin_access_control_links'] = $this->file_access_control_map['modules'][$module]['links'];
802                         }
803
804                         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]))){
805                                 //check params
806                                 if(!empty($this->file_access_control_map['modules'][$module]['actions'][$action]['params'])){
807                                         $block = true;
808                                         $params = $this->file_access_control_map['modules'][$module]['actions'][$action]['params'];
809                                         foreach($params as $param => $paramVals){
810                                                 if(!empty($_REQUEST[$param])){
811                                                         if(!in_array($_REQUEST[$param], $paramVals)){
812                                                                 $block = false;
813                                                                 break;
814                                                         }
815                                                 }
816                                         }
817                                         if($block){
818                                                 $this->_processed = true;
819                                                 $this->no_access();
820                                         }
821                                 }else{
822                                         $this->_processed = true;
823                                         $this->no_access();
824                                 }
825                         }
826                 }else
827                         $this->_processed = false;
828         }
829
830         /**
831          * This code is part of the entry points reworking. We have consolidated all
832          * entry points to go through index.php. Now in order to bring up an entry point
833          * it will follow the format:
834          * 'index.php?entryPoint=download'
835          * the download entry point is mapped in the following file: entry_point_registry.php
836          *
837          */
838         private function handleEntryPoint(){
839                 if(!empty($_REQUEST['entryPoint'])){
840                         $this->loadMapping('entry_point_registry');
841                         $entryPoint = $_REQUEST['entryPoint'];
842
843                         if(!empty($this->entry_point_registry[$entryPoint])){
844                                 require_once($this->entry_point_registry[$entryPoint]['file']);
845                                 $this->_processed = true;
846                                 $this->view = '';
847                         }
848                 }
849         }
850
851     /**
852      * Checks to see if the requested entry point requires auth
853      *
854      * @param  $entrypoint string name of the entrypoint
855      * @return bool true if auth is required, false if not
856      */
857     public function checkEntryPointRequiresAuth($entryPoint)
858     {
859         $this->loadMapping('entry_point_registry');
860
861         if ( isset($this->entry_point_registry[$entryPoint]['auth'])
862                 && !$this->entry_point_registry[$entryPoint]['auth'] )
863             return false;
864         return true;
865     }
866
867         /**
868          * Meant to handle old views e.g. DetailView.php.
869          *
870          */
871         protected function callLegacyCode()
872         {
873                 $file = self::getActionFilename($this->do_action);
874                 if ( isset($this->action_view_map[strtolower($this->do_action)]) ) {
875                 $action = $this->action_view_map[strtolower($this->do_action)];
876             }
877             else {
878                 $action = $this->do_action;
879             }
880             // index actions actually maps to the view.list.php view
881             if ( $action == 'index' ) {
882                 $action = 'list';
883             }
884
885                 if ((file_exists('modules/' . $this->module . '/'. $file . '.php')
886                 && !file_exists('modules/' . $this->module . '/views/view.'. $action . '.php'))
887             || (file_exists('custom/modules/' . $this->module . '/'. $file . '.php')
888                 && !file_exists('custom/modules/' . $this->module . '/views/view.'. $action . '.php'))
889             ) {
890                         // A 'classic' module, using the old pre-MVC display files
891                         // We should now discard the bean we just obtained for tracking as the pre-MVC module will instantiate its own
892                         unset($GLOBALS['FOCUS']);
893                         $GLOBALS['log']->debug('Module:' . $this->module . ' using file: '. $file);
894                         $this->action_default();
895                         $this->_processed = true;
896                 }
897         }
898
899         /**
900          * If the action has been remapped to a different action as defined in
901          * action_file_map.php or action_view_map.php load those maps here.
902          *
903          */
904         private function handleActionMaps(){
905                 if(!empty($this->action_file_map[strtolower($this->do_action)])){
906                         $this->view = '';
907                         $GLOBALS['log']->debug('Using Action File Map:' . $this->action_file_map[strtolower($this->do_action)]);
908                         require_once($this->action_file_map[strtolower($this->do_action)]);
909                         $this->_processed = true;
910                 }elseif(!empty($this->action_view_map[strtolower($this->do_action)])){
911                         $GLOBALS['log']->debug('Using Action View Map:' . $this->action_view_map[strtolower($this->do_action)]);
912                         $this->view = $this->action_view_map[strtolower($this->do_action)];
913                         $this->_processed = true;
914                 }else
915                         $this->no_action();
916         }
917
918         /**
919          * Actually remap the action if required.
920          *
921          */
922         protected function remapAction(){
923                 if(!empty($this->action_remap[$this->do_action])){
924                         $this->action = $this->action_remap[$this->do_action];
925                         $this->do_action = $this->action;
926                 }
927         }
928
929 }
930 ?>