mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 08:17:18 +08:00
5a66316965 SuiteCRM 7.12.3 Release 1cfaf51831 Fix #9454 - Avoid duplicate results in basic search 7eb52791f1 Fix #9455 - Popup metadata override removed when filtered a21463bee1 Fix #8155 - Removed Unused PDF Settings 7f3c59f382 Fix #8948 - Make Project Tasks Importable ac4a1e79a7 Fix #9191 - Add utf normalization repair command bcb784eef7 Fix #9434 - Cron notion unit test fails 2e4aa8f594 Fix #8309 - Delete button showing for users with no delete access bf5fe39148 Fix #8525 - Bulk Action label not showing for users without delete access 1a45a93b5c Fix #9398 - Consistently store dropdowns in $app_list_strings rather than $GLOBALS 664b2d5c74 Fix #9406 - Validation displayed static message isn't correct de192fa845 Fix #9271 - Primary Email property is kept after ading an extra Email address 206db24eaa Fix #9378 - Filter by Email1 Field Through the API 6c39d73580 Fix #9312 - Declaring object within StudioClass to remove Strict Warnings be2044b341 Fix #9387 - Correct Variable Names 7e3c96d5b4 Fix #9387 - Clean Cron Historic and Failed Jobs b3e0556482 Fix #9387 - Display Actual Job Result in Subpanel 90b4e0423a Fix issue #9380 where date action in workflow fails to save Add check on if value is array, if is array don't attempt date formatting 864d614945 Fix #9408 - adding fix for deleting emails 7b442f10ca Merge next into suite 8 b522a5acf3 [Legacy] Meeting Module Styling Fixes 4e4d4e7d86 [Legacy] Admin Import Styling Fixes e320acd63c [Legacy] Popup Selector Styling Fixes 60acfc36f5 [Legacy] Backup Modules Styling Fixes 73cc52ce14 [Legacy] Jotpad Dashlet Styling Fixes 0128df2e63 [Legacy] User Name Longtext styling fixes f12c72e8f6 [Legacy] Diagnostic Tool Whitespace Styling Fixes 841b16e033 [Legacy] Dashboard Dashlet Styling Fixes e274de5a58 [Legacy] Scheduler Styling Fixes 0c330bc280 [Legacy] Reports Subpanels Styling Fixes 0730d433b2 [Legacy] Meeting Popup Selector Styling Fixes a22626df09 [Legacy] Bump version to 8.0.0 ebde6fb8cc [Legacy] Apply set-timezone styling ot the the users set-timezone page 55f1e360df [Legacy] Add name mapping for SetTimezone action 44fa660c65 [Legacy] Define summary templates for contacts, leads, prospects f363b741f9 [Legacy] Change Contacts detailviewdefs to use full name 8afde55218 [Legacy] Fix .htaccess build to take the full CRM url into account 8373995c26 [Legacy] Admin diagnostic tool styling fixes 03593b259c [Legacy] Disable upgrade wizard in admin 66c9fe4d8f [Legacy] Remove lucene and AOD_index 21f9844286 [Legacy] Fix event invite mod_strings handling 04bdfd8975 [Legacy] Forgot Password Page Styling Fixes ba459af407 [Legacy] fix redirect from projects subpanel 0f19a141d1 [Legacy] Add Default Link Item Mapper - add relate module injection on backend bean field_defs efc2083eaf [Legacy] Email Body Styling Fixes 648a1d9350 Merge next into suite 8 a0cfd72ab3 [Legacy] Remove deprecated spots module 103fd1076f [Legacy] Fix capitalized url on user profile calendar options 97fca254bd [Legacy] Re-add user module reset password modal 864c35c4ad [Legacy] Remove echo on user save 169a15a6c9 [Legacy] Close user action dropdown on password reset e8c400ab24 [Legacy] Fix user triggered password reset e38f48c6d4 [Legacy] Fix password recovery templates setup on install 927969c9a3 [Legacy] Bump version to 8.0.0-rc.2 b27584577d [Legacy] UserProfile Settings Modal Styling Fixes 2aba88dfe8 [Legacy] Meetings Styling Fixes 13d743e598 [Legacy] Password & System Settings Action Button Fixes cfb0bcc86d [Legacy] User Setup Modal Styling Fixes d05f3aa4f5 [Legacy] Fix legacy widget acl definitions e469e337f8 [Legacy] Suite 8 extensions to legacy plugins 41e84b1d17 [Legacy] Override server variables when doing kernel bypass 7f5d03f280 Merge commit '75ea167930
' into suite7merge c5f43d6c88 [Legacy] Popup Selector Styling Fixes 0445c1c0ab [Legacy] update install language label eb3fe0472b [Legacy] Dashlet Settings Modal Styling Fixes 6f0918b73d [Legacy] User Profile Mobile Styling Fixes 5af790f7fa [Legacy] Add acls calculation on legacy list data calls 096a59a7cf [Legacy] Get list of visible modules from module name mapper c2d618e631 Fix valid module check aa79de3a31 [Legacy] add widget acl metadata - update acl config for top widget 315b554b7e [Legacy] Subpanels Action Button Styling Fixes c87e66a262 remove photo widgets for contacts and leads 433940a72f [Legacy] Admin Release Styling Fixes c0a1378873 Suite8 ChangeLog ModalPopup Styling Fixes aa484f3f9e [Legacy] Add close menu label 8f81b47c65 [Legacy] Add not module selected label 36a0b05eb2 [Legacy] Add parent type ApiBeanMapper 7e02d1d23c [Legacy] Fix username display for unauthorized users 2ea8f210a1 [Legacy] DetailView Header Title Overlaps fixes 2a25edd6c4 [Legacy] Fix htaccess generation 34500ba791 Fix Date Filed for Targets 211ce6bfbb [Legacy] Remove Button Hover Fixes 223adf26bc [Legacy] Reports footer button removed fixes c567777afe [Legacy] Reports Styling Fixes 6f71ed31c2 [Legacy] Maps Button Position Fixes c5d5936c22 [Legacy] Add Tab Dashlet Sapcing Fixes 018c11c562 [Legacy] Dashlet Setting Modal Label Fix & Button Fixes 9645c68ca6 [Legacy] Activity Stream Post Button Hover color fixes 445ab52dae [Legacy] Bump version to 8.0.0-rc e10b872b21 [Legacy] Remove license from install app strings e5c21ca68c Merge commit 'dd455a1c2d
' into legacy_update_2 3886e3cfa6 Fix ignore system checks valitation 415cdd57b0 [Legacy] Add install labels 70ae186de5 [Legacy] add new install labels 7e05e69580 [Legacy] add install validator class 4c4ed83f3d [Legacy] Add Label for Subpanel Edit Line Action 17d8f4e7e9 [Legacy] Dashboard Mobile Styling Fixes abd914702a [Legacy] History Subpanel Button Styling Fixes 34a0dac449 [Legacy] Workflow Stying Fixes 32c4827895 [Legacy] Convert Lead Styling Fixes f1ca14c009 [Legacy] Roles Styling Fixes 3dbdce8055 [Legacy] Admin Modules label font fixes 36a38a1c44 [Legacy] HomePage Mobile Fixes a5b451ab12 update legacy handlers to apply new logic considering - base actions - action resolvers - fix url navigation issue in classic view 87e6e239ce [Legacy] Module framework - move suite 8 module config to legacy ee973e74f6 [Legacy] Add support for more metadata on listviewdefs ad312c45fd [Legacy] Add Support for all modules with parent css classes a409d81e2b [Legacy] Security Group Styling Fixes 5171ae319f [Legacy] Password Management Mobile Fixes d0eca8193d [Leagcy] Email Settings Fixes 766af5f4e6 [Legacy] Products Modules Buttons fixes 99ec9e30f9 [Legacy] OAUTH2 Clients and tokens fixes 54c962a532 [Legacy] adapt legacy install scripts to work with Suite8 install d6f20e38e6 [Legacy] add labels for S8 Installer 83b00b6a94 [Legacy] Add widget not found label 3ac807db61 Merge commit '114b895b6d
' into merge_develop 1feeb409f8 [Legacy] Email Action DropDown Styling Fixes git-subtree-dir: public/legacy git-subtree-split: 5a663169652b57bb561de74a7e3a8051c9330a83
1146 lines
38 KiB
PHP
Executable file
1146 lines
38 KiB
PHP
Executable file
<?php
|
|
/**
|
|
*
|
|
* SugarCRM Community Edition is a customer relationship management program developed by
|
|
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
|
|
*
|
|
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
|
|
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation with the addition of the following permission added
|
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
|
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
|
|
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
* this program; if not, see http://www.gnu.org/licenses or write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*
|
|
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
|
|
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of this program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU Affero General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "Powered by
|
|
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
|
|
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
|
|
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
|
|
*/
|
|
|
|
if (!defined('sugarEntry') || !sugarEntry) {
|
|
die('Not A Valid Entry Point');
|
|
}
|
|
|
|
require_once('include/MVC/View/SugarView.php');
|
|
|
|
/**
|
|
* Main SugarCRM controller
|
|
* @api
|
|
*/
|
|
class SugarController
|
|
{
|
|
/**
|
|
* @var array $action_remap
|
|
* remap actions in here
|
|
* e.g. make all detail views go to edit views
|
|
* $action_remap = array('detailview'=>'editview');
|
|
*/
|
|
protected $action_remap = array('index' => 'listview');
|
|
|
|
/**
|
|
* @var string $module
|
|
* The name of the current module.
|
|
*/
|
|
public $module = 'Home';
|
|
|
|
/**
|
|
* @var string|null
|
|
* The name of the target module.
|
|
*/
|
|
public $target_module = null;
|
|
|
|
/**
|
|
* @var string $action
|
|
* The name of the current action.
|
|
*/
|
|
public $action = 'index';
|
|
|
|
/**
|
|
* @var string $record
|
|
* The id of the current record.
|
|
*/
|
|
public $record = '';
|
|
|
|
/**
|
|
* @var string|null $return_module
|
|
* The name of the return module.
|
|
*/
|
|
public $return_module = null;
|
|
|
|
/**
|
|
* @var string|null $return_action
|
|
* The name of the return action.
|
|
*/
|
|
public $return_action = null;
|
|
|
|
/**
|
|
* @var string|null $return_id uuid
|
|
* The id of the return record.
|
|
*/
|
|
public $return_id = null;
|
|
|
|
/**
|
|
* @var string $do_action
|
|
* If the action was remapped it will be set to do_action and then we will just
|
|
* use do_action for the actual action to perform.
|
|
*/
|
|
protected $do_action = 'index';
|
|
|
|
/**
|
|
* @var SugarBean|null $bean
|
|
* If a bean is present that set it.*
|
|
*/
|
|
public $bean = null;
|
|
|
|
/**
|
|
* @var string $redirect_url
|
|
* url to redirect to
|
|
*/
|
|
public $redirect_url = '';
|
|
|
|
/**
|
|
* @var string $view
|
|
* any subcontroller can modify this to change the view
|
|
*/
|
|
public $view = 'classic';
|
|
|
|
/**
|
|
* @var array $view_object_map
|
|
* this array will hold the mappings between a key and an object for use within the view.
|
|
*/
|
|
public $view_object_map = array();
|
|
|
|
/**
|
|
* This array holds the methods that handleAction() will invoke, in sequence.
|
|
*/
|
|
protected $tasks = array(
|
|
'pre_action',
|
|
'do_action',
|
|
'post_action'
|
|
);
|
|
/**
|
|
* @var array $process_tasks
|
|
* List of options to run through within the process() method.
|
|
* This list is meant to easily allow additions for new functionality as well as
|
|
* the ability to add a controller's own handling.
|
|
*/
|
|
public $process_tasks = array(
|
|
'blockFileAccess',
|
|
'handleEntryPoint',
|
|
'callLegacyCode',
|
|
'remapAction',
|
|
'handle_action',
|
|
'handleActionMaps',
|
|
);
|
|
/**
|
|
* Whether or not the action has been handled by $process_tasks
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $_processed = false;
|
|
/**
|
|
* Map an action directly to a file
|
|
*/
|
|
/**
|
|
* Map an action directly to a file. This will be loaded from action_file_map.php
|
|
*/
|
|
protected $action_file_map = array();
|
|
/**
|
|
* Map an action directly to a view
|
|
*/
|
|
/**
|
|
* Map an action directly to a view. This will be loaded from action_view_map.php
|
|
*/
|
|
protected $action_view_map = array();
|
|
|
|
/**
|
|
* This can be set from the application to tell us whether we have authorization to
|
|
* process the action. If this is set we will default to the noaccess view.
|
|
*@var bool
|
|
*/
|
|
public $hasAccess ;
|
|
|
|
/**
|
|
* Map case sensitive filenames to action. This is used for linux/unix systems
|
|
* where filenames are case sensitive
|
|
*/
|
|
public static $action_case_file = array(
|
|
'editview' => 'EditView',
|
|
'detailview' => 'DetailView',
|
|
'listview' => 'ListView'
|
|
);
|
|
|
|
/**
|
|
* Constructor. This ie meant to load up the module, action, record as well
|
|
* as the mapping arrays.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->hasAccess = true;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Called from SugarApplication and is meant to perform the setup operations
|
|
* on the controller.
|
|
*
|
|
*/
|
|
public function setup($module = '')
|
|
{
|
|
if (empty($module) && !empty($_REQUEST['module'])) {
|
|
$module = $_REQUEST['module'];
|
|
}
|
|
//set the module
|
|
if (!empty($module)) {
|
|
$this->setModule($module);
|
|
}
|
|
|
|
if (!empty($_REQUEST['target_module']) && $_REQUEST['target_module'] != 'undefined') {
|
|
$this->target_module = $_REQUEST['target_module'];
|
|
}
|
|
//set properties on the controller from the $_REQUEST
|
|
$this->loadPropertiesFromRequest();
|
|
//load the mapping files
|
|
$this->loadMappings();
|
|
/**
|
|
* @see SugarController::allowAction()
|
|
*/
|
|
$this->allowAction($this->action);
|
|
}
|
|
|
|
/**
|
|
* Set the module on the Controller
|
|
*
|
|
* @param object $module
|
|
*/
|
|
public function setModule($module)
|
|
{
|
|
$this->module = $module;
|
|
}
|
|
|
|
/**
|
|
* Set properties on the Controller from the $_REQUEST
|
|
*
|
|
*/
|
|
private function loadPropertiesFromRequest()
|
|
{
|
|
if (!empty($_REQUEST['action'])) {
|
|
$this->action = $_REQUEST['action'];
|
|
}
|
|
if (!empty($_REQUEST['record'])) {
|
|
$this->record = $_REQUEST['record'];
|
|
}
|
|
if (!empty($_REQUEST['view'])) {
|
|
$this->view = $_REQUEST['view'];
|
|
}
|
|
if (!empty($_REQUEST['return_module'])) {
|
|
$this->return_module = $_REQUEST['return_module'];
|
|
}
|
|
if (!empty($_REQUEST['return_action'])) {
|
|
$this->return_action = $_REQUEST['return_action'];
|
|
}
|
|
if (!empty($_REQUEST['return_id'])) {
|
|
$this->return_id = $_REQUEST['return_id'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load map files for use within the Controller
|
|
*
|
|
*/
|
|
private function loadMappings()
|
|
{
|
|
$this->loadMapping('action_view_map');
|
|
$this->loadMapping('action_file_map');
|
|
$this->loadMapping('action_remap', true);
|
|
}
|
|
|
|
/**
|
|
* Allows action to be pass XSS protection check provide that the action exists in the SugarController
|
|
*
|
|
* @param string $action
|
|
*/
|
|
protected function allowAction($action)
|
|
{
|
|
if ($this->hasFunction($this->getActionMethodName())) {
|
|
$GLOBALS['sugar_config']['http_referer']['actions'][] = $action;
|
|
} else {
|
|
$GLOBALS['log']->debug("Unable to find SugarController:: $action");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a record id load the bean. This bean is accessible from any sub controllers.
|
|
*/
|
|
public function loadBean()
|
|
{
|
|
if (!empty($GLOBALS['beanList'][$this->module])) {
|
|
$class = $GLOBALS['beanList'][$this->module];
|
|
if (!empty($GLOBALS['beanFiles'][$class])) {
|
|
require_once($GLOBALS['beanFiles'][$class]);
|
|
$this->bean = new $class();
|
|
if (!empty($this->record)) {
|
|
$this->bean->retrieve($this->record);
|
|
if ($this->bean) {
|
|
$GLOBALS['FOCUS'] = $this->bean;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generic load method to load mapping arrays.
|
|
*/
|
|
private function loadMapping($var, $merge = false)
|
|
{
|
|
$$var = sugar_cache_retrieve("CONTROLLER_" . $var . "_" . $this->module);
|
|
if (!$$var) {
|
|
if ($merge && !empty($this->$var)) {
|
|
$$var = $this->$var;
|
|
} else {
|
|
$$var = array();
|
|
}
|
|
if (file_exists('include/MVC/Controller/' . $var . '.php')) {
|
|
require('include/MVC/Controller/' . $var . '.php');
|
|
}
|
|
if (file_exists('modules/' . $this->module . '/' . $var . '.php')) {
|
|
require('modules/' . $this->module . '/' . $var . '.php');
|
|
}
|
|
if (file_exists('custom/modules/' . $this->module . '/' . $var . '.php')) {
|
|
require('custom/modules/' . $this->module . '/' . $var . '.php');
|
|
}
|
|
if (file_exists('custom/include/MVC/Controller/' . $var . '.php')) {
|
|
require('custom/include/MVC/Controller/' . $var . '.php');
|
|
}
|
|
|
|
// entry_point_registry -> EntryPointRegistry
|
|
|
|
$varname = str_replace(" ", "", ucwords(str_replace("_", " ", $var)));
|
|
if (file_exists("custom/application/Ext/$varname/$var.ext.php")) {
|
|
require("custom/application/Ext/$varname/$var.ext.php");
|
|
}
|
|
if (file_exists("custom/modules/{$this->module}/Ext/$varname/$var.ext.php")) {
|
|
require("custom/modules/{$this->module}/Ext/$varname/$var.ext.php");
|
|
}
|
|
|
|
sugar_cache_put("CONTROLLER_" . $var . "_" . $this->module, $$var);
|
|
}
|
|
$this->$var = $$var;
|
|
}
|
|
|
|
/**
|
|
* This method is called from SugarApplication->execute and it will bootstrap the entire controller process
|
|
*/
|
|
final public function execute()
|
|
{
|
|
try {
|
|
$this->process();
|
|
if (!empty($this->view)) {
|
|
$this->processView();
|
|
} elseif (!empty($this->redirect_url)) {
|
|
$this->redirect();
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->handleException($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param Exception $e
|
|
*/
|
|
protected function showException(Exception $e)
|
|
{
|
|
global $sugar_config;
|
|
|
|
LoggerManager::getLogger()->fatal('Exception in Controller: ' . $e->getMessage());
|
|
|
|
if ($sugar_config['stackTrace']) {
|
|
LoggerManager::getLogger()->fatal("backtrace:\n" . $e->getTraceAsString());
|
|
}
|
|
|
|
if ($prev = $e->getPrevious()) {
|
|
LoggerManager::getLogger()->fatal("Previous:\n");
|
|
$this->showException($prev);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle exception
|
|
* @param Exception $e
|
|
*/
|
|
protected function handleException(Exception $e)
|
|
{
|
|
$GLOBALS['log']->fatal("Exception handling in " . __FILE__ . ':' . __LINE__);
|
|
$this->showException($e);
|
|
$logicHook = new LogicHook();
|
|
|
|
if (isset($this->bean)) {
|
|
$logicHook->setBean($this->bean);
|
|
$logicHook->call_custom_logic($this->bean->module_dir, "handle_exception", $e);
|
|
} else {
|
|
$logicHook->call_custom_logic('', "handle_exception", $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the appropriate view.
|
|
*/
|
|
private function processView()
|
|
{
|
|
if (!isset($this->view_object_map['remap_action']) && isset($this->action_view_map[strtolower($this->action)])) {
|
|
$this->view_object_map['remap_action'] = $this->action_view_map[strtolower($this->action)];
|
|
}
|
|
$view = ViewFactory::loadView(
|
|
$this->view,
|
|
$this->module,
|
|
$this->bean,
|
|
$this->view_object_map,
|
|
$this->target_module
|
|
);
|
|
$GLOBALS['current_view'] = $view;
|
|
if (!empty($this->bean) && !$this->bean->ACLAccess($view->type) && $view->type != 'list') {
|
|
ACLController::displayNoAccess(true);
|
|
sugar_cleanup(true);
|
|
}
|
|
if (isset($this->errors)) {
|
|
$view->errors = $this->errors;
|
|
}
|
|
$view->process();
|
|
}
|
|
|
|
/**
|
|
* Meant to be overridden by a subclass and allows for specific functionality to be
|
|
* injected prior to the process() method being called.
|
|
*/
|
|
public function preProcess()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* if we have a function to support the action use it otherwise use the default action
|
|
*
|
|
* 1) check for file
|
|
* 2) check for action
|
|
*/
|
|
public function process()
|
|
{
|
|
$GLOBALS['action'] = $this->action;
|
|
$GLOBALS['module'] = $this->module;
|
|
|
|
//check to ensure we have access to the module.
|
|
if ($this->hasAccess) {
|
|
$this->do_action = $this->action;
|
|
|
|
$file = self::getActionFilename($this->do_action);
|
|
|
|
$this->loadBean();
|
|
|
|
$processed = false;
|
|
if (!$this->_processed) {
|
|
foreach ($this->process_tasks as $process) {
|
|
$this->$process();
|
|
if ($this->_processed) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->redirect();
|
|
} else {
|
|
$this->no_access();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called from the process method. I could also be called within an action_* method.
|
|
* It allows a developer to override any one of these methods contained within,
|
|
* or if the developer so chooses they can override the entire action_* method.
|
|
*
|
|
* @return true if any one of the pre_, do_, or post_ methods have been defined,
|
|
* false otherwise. This is important b/c if none of these methods exists, then we will run the
|
|
* action_default() method.
|
|
*/
|
|
protected function handle_action()
|
|
{
|
|
$processed = false;
|
|
foreach ($this->tasks as $task) {
|
|
$processed = ($this->$task() || $processed);
|
|
}
|
|
$this->_processed = $processed;
|
|
}
|
|
|
|
/**
|
|
* Perform an action prior to the specified action.
|
|
* This can be overridde in a sub-class
|
|
*/
|
|
private function pre_action()
|
|
{
|
|
$function = $this->getPreActionMethodName();
|
|
if ($this->hasFunction($function)) {
|
|
$GLOBALS['log']->debug('Performing pre_action');
|
|
$this->$function();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Perform the specified action.
|
|
* This can be overridde in a sub-class
|
|
*/
|
|
private function do_action()
|
|
{
|
|
$function = $this->getActionMethodName();
|
|
if ($this->hasFunction($function)) {
|
|
$GLOBALS['log']->debug('Performing action: ' . $function . ' MODULE: ' . $this->module);
|
|
$this->$function();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Perform an action after to the specified action has occurred.
|
|
* This can be overridde in a sub-class
|
|
*/
|
|
private function post_action()
|
|
{
|
|
$function = $this->getPostActionMethodName();
|
|
if ($this->hasFunction($function)) {
|
|
$GLOBALS['log']->debug('Performing post_action');
|
|
$this->$function();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* If there is no action found then display an error to the user.
|
|
*/
|
|
protected function no_action()
|
|
{
|
|
sugar_die(sprintf($GLOBALS['app_strings']['LBL_NO_ACTION'], $this->do_action));
|
|
}
|
|
|
|
/**
|
|
* The default action handler for instances where we do not have access to process.
|
|
*/
|
|
protected function no_access()
|
|
{
|
|
$this->view = 'noaccess';
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
/////// HELPER FUNCTIONS
|
|
///////////////////////////////////////////////
|
|
|
|
/**
|
|
* Determine if a given function exists on the objects
|
|
* @param function - the function to check
|
|
* @return true if the method exists on the object, false otherwise
|
|
*/
|
|
protected function hasFunction($function)
|
|
{
|
|
return method_exists($this, $function);
|
|
}
|
|
|
|
/**
|
|
* @param $action
|
|
* @return string
|
|
*/
|
|
protected function getPreActionMethodName()
|
|
{
|
|
return 'pre_' . $this->action;
|
|
}
|
|
|
|
/**
|
|
* @param $action
|
|
* @return string
|
|
*/
|
|
protected function getActionMethodName()
|
|
{
|
|
return 'action_' . strtolower($this->do_action);
|
|
}
|
|
|
|
/**
|
|
* @param $action
|
|
* @return string
|
|
*/
|
|
protected function getPostActionMethodName()
|
|
{
|
|
return 'post_' . strtolower($this->action);
|
|
}
|
|
|
|
/**
|
|
* Set the url to which we will want to redirect
|
|
*
|
|
* @param string url - the url to which we will want to redirect
|
|
*/
|
|
protected function set_redirect($url)
|
|
{
|
|
$this->redirect_url = $url;
|
|
}
|
|
|
|
/**
|
|
* Perform redirection based on the redirect_url
|
|
*
|
|
*/
|
|
protected function redirect()
|
|
{
|
|
if (!empty($this->redirect_url)) {
|
|
SugarApplication::redirect($this->redirect_url);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////
|
|
////// DEFAULT ACTIONS
|
|
///////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* Save a bean
|
|
*/
|
|
|
|
/**
|
|
* Do some processing before saving the bean to the database.
|
|
*/
|
|
public function pre_save()
|
|
{
|
|
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])) {
|
|
$this->bean->notify_on_save = true;
|
|
}
|
|
$GLOBALS['log']->debug("SugarController:: performing pre_save.");
|
|
require_once('include/SugarFields/SugarFieldHandler.php');
|
|
$sfh = new SugarFieldHandler();
|
|
foreach ($this->bean->field_defs as $field => $properties) {
|
|
$type = !empty($properties['custom_type']) ? $properties['custom_type'] : $properties['type'];
|
|
$sf = $sfh::getSugarField(ucfirst($type), true);
|
|
if (isset($_POST[$field])) {
|
|
if (is_array($_POST[$field]) && !empty($properties['isMultiSelect'])) {
|
|
if (empty($_POST[$field][0])) {
|
|
unset($_POST[$field][0]);
|
|
}
|
|
$_POST[$field] = encodeMultienumValue($_POST[$field]);
|
|
}
|
|
$this->bean->$field = $_POST[$field];
|
|
} else {
|
|
if (!empty($properties['isMultiSelect']) && !isset($_POST[$field]) && isset($_POST[$field . '_multiselect'])) {
|
|
$this->bean->$field = '';
|
|
}
|
|
}
|
|
if ($sf != null) {
|
|
$sf->save($this->bean, isset($_POST) ? $_POST : null, $field, $properties);
|
|
}
|
|
}
|
|
|
|
foreach ($this->bean->relationship_fields as $field => $link) {
|
|
if (!empty($_POST[$field])) {
|
|
$this->bean->$field = $_POST[$field];
|
|
}
|
|
}
|
|
if (!$this->bean->ACLAccess('save')) {
|
|
ACLController::displayNoAccess(true);
|
|
sugar_cleanup(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform the actual save
|
|
*/
|
|
public function action_save()
|
|
{
|
|
$this->bean->save(!empty($this->bean->notify_on_save));
|
|
}
|
|
|
|
/**
|
|
* Specify what happens after the save has occurred.
|
|
*/
|
|
protected function post_save()
|
|
{
|
|
$module = (!empty($this->return_module) ? $this->return_module : $this->module);
|
|
$action = (!empty($this->return_action) ? $this->return_action : 'DetailView');
|
|
$id = (!empty($this->return_id) ? $this->return_id : $this->bean->id);
|
|
|
|
$url = "index.php?module=" . $module . "&action=" . $action . "&record=" . $id;
|
|
$this->set_redirect($url);
|
|
}
|
|
|
|
/*
|
|
* Delete a bean
|
|
*/
|
|
|
|
/**
|
|
* Perform the actual deletion.
|
|
*/
|
|
protected function action_delete()
|
|
{
|
|
//do any pre delete processing
|
|
//if there is some custom logic for deletion.
|
|
if (!empty($_REQUEST['record'])) {
|
|
if (!$this->bean->ACLAccess('Delete')) {
|
|
ACLController::displayNoAccess(true);
|
|
sugar_cleanup(true);
|
|
}
|
|
$this->bean->mark_deleted($_REQUEST['record']);
|
|
} else {
|
|
sugar_die("A record number must be specified to delete");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Specify what happens after the deletion has occurred.
|
|
*/
|
|
protected function post_delete()
|
|
{
|
|
if (empty($_REQUEST['return_url'])) {
|
|
$return_module = isset($_REQUEST['return_module']) ?
|
|
$_REQUEST['return_module'] :
|
|
$GLOBALS['sugar_config']['default_module'];
|
|
$return_action = isset($_REQUEST['return_action']) ?
|
|
$_REQUEST['return_action'] :
|
|
$GLOBALS['sugar_config']['default_action'];
|
|
$return_id = isset($_REQUEST['return_id']) ?
|
|
$_REQUEST['return_id'] :
|
|
'';
|
|
$url = "index.php?module=" . $return_module . "&action=" . $return_action . "&record=" . $return_id;
|
|
} else {
|
|
$url = $_REQUEST['return_url'];
|
|
}
|
|
|
|
//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.
|
|
if (isset($_REQUEST['offset']) && empty($_REQUEST['duplicateSave'])) {
|
|
$url .= "&offset=" . $_REQUEST['offset'];
|
|
}
|
|
|
|
$this->set_redirect($url);
|
|
}
|
|
|
|
/**
|
|
* Perform the actual massupdate.
|
|
*/
|
|
protected function action_massupdate()
|
|
{
|
|
if (!empty($_REQUEST['massupdate']) && $_REQUEST['massupdate'] == 'true' && (!empty($_REQUEST['uid']) || !empty($_REQUEST['entire']))) {
|
|
if (!empty($_REQUEST['Delete']) && $_REQUEST['Delete'] == 'true' && !$this->bean->ACLAccess('delete')
|
|
|| (empty($_REQUEST['Delete']) || $_REQUEST['Delete'] != 'true') && !$this->bean->ACLAccess('save')
|
|
) {
|
|
ACLController::displayNoAccess(true);
|
|
sugar_cleanup(true);
|
|
}
|
|
|
|
set_time_limit(0);//I'm wondering if we will set it never goes timeout here.
|
|
// until we have more efficient way of handling MU, we have to disable the limit
|
|
DBManagerFactory::getInstance()->setQueryLimit(0);
|
|
require_once("include/MassUpdate.php");
|
|
require_once('modules/MySettings/StoreQuery.php');
|
|
$seed = loadBean($_REQUEST['module']);
|
|
$mass = new MassUpdate();
|
|
$mass->setSugarBean($seed);
|
|
if (isset($_REQUEST['entire']) && empty($_POST['mass'])) {
|
|
$mass->generateSearchWhere($_REQUEST['module'], $_REQUEST['current_query_by_page']);
|
|
}
|
|
$mass->handleMassUpdate();
|
|
$storeQuery = new StoreQuery();//restore the current search. to solve bug 24722 for multi tabs massupdate.
|
|
$temp_req = array(
|
|
'current_query_by_page' => $_REQUEST['current_query_by_page'],
|
|
'return_module' => $_REQUEST['return_module'],
|
|
'return_action' => $_REQUEST['return_action']
|
|
);
|
|
if ($_REQUEST['return_module'] == 'Emails') {
|
|
if (!empty($_REQUEST['type']) && !empty($_REQUEST['ie_assigned_user_id'])) {
|
|
$this->req_for_email = array(
|
|
'type' => $_REQUEST['type'],
|
|
'ie_assigned_user_id' => $_REQUEST['ie_assigned_user_id']
|
|
); // Specifically for My Achieves
|
|
}
|
|
}
|
|
$_REQUEST = array();
|
|
$_REQUEST = json_decode(html_entity_decode($temp_req['current_query_by_page']), true);
|
|
unset($_REQUEST[$seed->module_dir . '2_' . strtoupper($seed->object_name) . '_offset']);//after massupdate, the page should redirect to no offset page
|
|
$storeQuery->saveFromRequest($_REQUEST['module']);
|
|
$_REQUEST = array(
|
|
'return_module' => $temp_req['return_module'],
|
|
'return_action' => $temp_req['return_action']
|
|
);//for post_massupdate, to go back to original page.
|
|
} else {
|
|
sugar_die("You must massupdate at least one record");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Specify what happens after the massupdate has occurred.
|
|
*/
|
|
protected function post_massupdate()
|
|
{
|
|
$return_module = isset($_REQUEST['return_module']) ?
|
|
$_REQUEST['return_module'] :
|
|
$GLOBALS['sugar_config']['default_module'];
|
|
$return_action = isset($_REQUEST['return_action']) ?
|
|
$_REQUEST['return_action'] :
|
|
$GLOBALS['sugar_config']['default_action'];
|
|
$url = "index.php?module=" . $return_module . "&action=" . $return_action;
|
|
if ($return_module == 'Emails') {//specificly for My Achieves
|
|
if (!empty($this->req_for_email['type']) && !empty($this->req_for_email['ie_assigned_user_id'])) {
|
|
$url = $url . "&type=" . $this->req_for_email['type'] . "&assigned_user_id=" . $this->req_for_email['ie_assigned_user_id'];
|
|
}
|
|
}
|
|
$this->set_redirect($url);
|
|
}
|
|
|
|
/**
|
|
* Perform the listview action
|
|
*/
|
|
protected function action_listview()
|
|
{
|
|
$this->view_object_map['bean'] = $this->bean;
|
|
$this->view = 'list';
|
|
}
|
|
|
|
/*
|
|
|
|
//THIS IS HANDLED IN ACTION_REMAP WHERE INDEX IS SET TO LISTVIEW
|
|
function action_index(){
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Action to handle when using a file as was done in previous versions of Sugar.
|
|
*/
|
|
protected function action_default()
|
|
{
|
|
$this->view = 'classic';
|
|
}
|
|
|
|
/**
|
|
* this method id used within a Dashlet when performing an ajax call
|
|
*/
|
|
protected function action_callmethoddashlet()
|
|
{
|
|
if (!empty($_REQUEST['id'])) {
|
|
$id = $_REQUEST['id'];
|
|
$requestedMethod = $_REQUEST['method'];
|
|
$dashletDefs = $GLOBALS['current_user']->getPreference('dashlets', 'Home'); // load user's dashlets config
|
|
if (!empty($dashletDefs[$id])) {
|
|
require_once($dashletDefs[$id]['fileLocation']);
|
|
|
|
$dashlet = new $dashletDefs[$id]['className'](
|
|
$id,
|
|
(isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array())
|
|
);
|
|
|
|
if (method_exists($dashlet, $requestedMethod) || method_exists($dashlet, '__call')) {
|
|
echo $dashlet->$requestedMethod();
|
|
} else {
|
|
echo 'no method';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* this method is used within a Dashlet when the options configuration is posted
|
|
*/
|
|
protected function action_configuredashlet()
|
|
{
|
|
global $current_user, $mod_strings;
|
|
|
|
if (!empty($_REQUEST['id'])) {
|
|
$id = $_REQUEST['id'];
|
|
$dashletDefs = $current_user->getPreference('dashlets', $_REQUEST['module']); // load user's dashlets config
|
|
require_once($dashletDefs[$id]['fileLocation']);
|
|
|
|
$dashlet = new $dashletDefs[$id]['className'](
|
|
$id,
|
|
(isset($dashletDefs[$id]['options']) ? $dashletDefs[$id]['options'] : array())
|
|
);
|
|
if (!empty($_REQUEST['configure']) && $_REQUEST['configure']) { // save settings
|
|
$dashletDefs[$id]['options'] = $dashlet->saveOptions($_REQUEST);
|
|
$current_user->setPreference('dashlets', $dashletDefs, 0, $_REQUEST['module']);
|
|
} else { // display options
|
|
$json = getJSONobj();
|
|
|
|
return 'result = ' . $json->encode((array(
|
|
'header' => $dashlet->title . ' : ' . $mod_strings['LBL_OPTIONS'],
|
|
'body' => $dashlet->displayOptions()
|
|
)));
|
|
}
|
|
} else {
|
|
return '0';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Global method to delete an attachment
|
|
*
|
|
* If the bean does not have a deleteAttachment method it will return 'false' as a string
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function action_deleteattachment()
|
|
{
|
|
$this->view = 'edit';
|
|
$GLOBALS['view'] = $this->view;
|
|
ob_clean();
|
|
$retval = false;
|
|
|
|
if (method_exists($this->bean, 'deleteAttachment')) {
|
|
$duplicate = "false";
|
|
if (isset($_REQUEST['isDuplicate']) && $_REQUEST['isDuplicate'] == "true") {
|
|
$duplicate = "true";
|
|
}
|
|
if (isset($_REQUEST['duplicateSave']) && $_REQUEST['duplicateSave'] == "true") {
|
|
$duplicate = "true";
|
|
}
|
|
$retval = $this->bean->deleteAttachment($duplicate);
|
|
}
|
|
echo json_encode($retval);
|
|
sugar_cleanup(true);
|
|
}
|
|
|
|
/**
|
|
* getActionFilename
|
|
*/
|
|
public static function getActionFilename($action)
|
|
{
|
|
if (isset(self::$action_case_file[$action])) {
|
|
return self::$action_case_file[$action];
|
|
}
|
|
|
|
return $action;
|
|
}
|
|
|
|
/********************************************************************/
|
|
// PROCESS TASKS
|
|
/********************************************************************/
|
|
|
|
/**
|
|
* Given the module and action, determine whether the super/admin has prevented access
|
|
* to this url. In addition if any links specified for this module, load the links into
|
|
* GLOBALS
|
|
*
|
|
* @return true if we want to stop processing, false if processing should continue
|
|
*/
|
|
private function blockFileAccess()
|
|
{
|
|
//check if the we have enabled file_access_control and if so then check the mappings on the request;
|
|
if (!empty($GLOBALS['sugar_config']['admin_access_control']) && $GLOBALS['sugar_config']['admin_access_control']) {
|
|
$this->loadMapping('file_access_control_map');
|
|
//since we have this turned on, check the mapping file
|
|
$module = strtolower($this->module);
|
|
$action = strtolower($this->do_action);
|
|
if (!empty($this->file_access_control_map['modules'][$module]['links'])) {
|
|
$GLOBALS['admin_access_control_links'] = $this->file_access_control_map['modules'][$module]['links'];
|
|
}
|
|
|
|
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]))
|
|
) {
|
|
//check params
|
|
if (!empty($this->file_access_control_map['modules'][$module]['actions'][$action]['params'])) {
|
|
$block = true;
|
|
$params = $this->file_access_control_map['modules'][$module]['actions'][$action]['params'];
|
|
foreach ($params as $param => $paramVals) {
|
|
if (!empty($_REQUEST[$param])) {
|
|
if (!in_array($_REQUEST[$param], $paramVals)) {
|
|
$block = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ($block) {
|
|
$this->_processed = true;
|
|
$this->no_access();
|
|
}
|
|
} else {
|
|
$this->_processed = true;
|
|
$this->no_access();
|
|
}
|
|
}
|
|
} else {
|
|
$this->_processed = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This code is part of the entry points reworking. We have consolidated all
|
|
* entry points to go through index.php. Now in order to bring up an entry point
|
|
* it will follow the format:
|
|
* 'index.php?entryPoint=download'
|
|
* the download entry point is mapped in the following file: entry_point_registry.php
|
|
*
|
|
*/
|
|
private function handleEntryPoint()
|
|
{
|
|
if (!empty($_REQUEST['entryPoint'])) {
|
|
$this->loadMapping('entry_point_registry');
|
|
$entryPoint = $_REQUEST['entryPoint'];
|
|
|
|
if (!empty($this->entry_point_registry[$entryPoint])) {
|
|
require_once($this->entry_point_registry[$entryPoint]['file']);
|
|
$this->_processed = true;
|
|
$this->view = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks to see if the requested entry point requires auth
|
|
*
|
|
* @param $entrypoint string name of the entrypoint
|
|
* @return bool true if auth is required, false if not
|
|
*/
|
|
public function checkEntryPointRequiresAuth($entryPoint)
|
|
{
|
|
$this->loadMapping('entry_point_registry');
|
|
|
|
if (isset($this->entry_point_registry[$entryPoint]['auth'])
|
|
&& !$this->entry_point_registry[$entryPoint]['auth']
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Meant to handle old views e.g. DetailView.php.
|
|
*
|
|
*/
|
|
protected function callLegacyCode()
|
|
{
|
|
$file = self::getActionFilename($this->do_action);
|
|
if (isset($this->action_view_map[strtolower($this->do_action)])) {
|
|
$action = $this->action_view_map[strtolower($this->do_action)];
|
|
} else {
|
|
$action = $this->do_action;
|
|
}
|
|
// index actions actually maps to the view.list.php view
|
|
if ($action == 'index') {
|
|
$action = 'list';
|
|
}
|
|
|
|
if ((file_exists('modules/' . $this->module . '/' . $file . '.php')
|
|
&& !file_exists('modules/' . $this->module . '/views/view.' . $action . '.php'))
|
|
|| (file_exists('custom/modules/' . $this->module . '/' . $file . '.php')
|
|
&& !file_exists('custom/modules/' . $this->module . '/views/view.' . $action . '.php'))
|
|
) {
|
|
// A 'classic' module, using the old pre-MVC display files
|
|
// We should now discard the bean we just obtained for tracking as the pre-MVC module will instantiate its own
|
|
unset($GLOBALS['FOCUS']);
|
|
$GLOBALS['log']->debug('Module:' . $this->module . ' using file: ' . $file);
|
|
$this->action_default();
|
|
$this->_processed = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the action has been remapped to a different action as defined in
|
|
* action_file_map.php or action_view_map.php load those maps here.
|
|
*
|
|
*/
|
|
private function handleActionMaps()
|
|
{
|
|
if (!empty($this->action_file_map[strtolower($this->do_action)])) {
|
|
$this->view = '';
|
|
$GLOBALS['log']->debug('Using Action File Map:' . $this->action_file_map[strtolower($this->do_action)]);
|
|
require_once($this->action_file_map[strtolower($this->do_action)]);
|
|
$this->_processed = true;
|
|
} elseif (!empty($this->action_view_map[strtolower($this->do_action)])) {
|
|
$GLOBALS['log']->debug('Using Action View Map:' . $this->action_view_map[strtolower($this->do_action)]);
|
|
$this->view = $this->action_view_map[strtolower($this->do_action)];
|
|
$this->_processed = true;
|
|
} else {
|
|
$this->no_action();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Actually remap the action if required.
|
|
*
|
|
*/
|
|
protected function remapAction()
|
|
{
|
|
if (!empty($this->action_remap[$this->do_action])) {
|
|
$this->action = $this->action_remap[$this->do_action];
|
|
$this->do_action = $this->action;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* action: Send Confirm Opt In Email to Contact/Lead/Account/Prospect
|
|
*
|
|
* @global array $app_strings using for user messages about error/success status of action
|
|
*/
|
|
public function action_sendConfirmOptInEmail()
|
|
{
|
|
global $app_strings;
|
|
|
|
if (!($this->bean instanceof Company || $this->bean instanceof Person)) {
|
|
$msg = $app_strings['LBL_CONFIRM_OPT_IN_ONLY_FOR_PERSON'];
|
|
SugarApplication::appendErrorMessage($msg);
|
|
} else {
|
|
$configurator = new Configurator();
|
|
$confirmOptInEnabled = $configurator->isConfirmOptInEnabled();
|
|
if (!$confirmOptInEnabled) {
|
|
$msg = $app_strings['LBL_CONFIRM_OPT_IN_IS_DISABLED'];
|
|
SugarApplication::appendErrorMessage($msg);
|
|
} else {
|
|
$emailAddressStringCaps = strtoupper($this->bean->email1);
|
|
if ($emailAddressStringCaps) {
|
|
$emailAddress = BeanFactory::newBean('EmailAddresses');
|
|
$emailAddress->retrieve_by_string_fields(array(
|
|
'email_address_caps' => $emailAddressStringCaps,
|
|
));
|
|
|
|
$emailMan = BeanFactory::newBean('EmailMan');
|
|
|
|
$success = $emailMan->sendOptInEmail($emailAddress, $this->bean->module_name, $this->bean->id);
|
|
|
|
if (!$success) {
|
|
$msg = $app_strings['LBL_CONFIRM_EMAIL_SENDING_FAILED'];
|
|
SugarApplication::appendErrorMessage($msg);
|
|
} else {
|
|
$msg = $app_strings['LBL_CONFIRM_EMAIL_SENT'];
|
|
SugarApplication::appendSuccessMessage($msg);
|
|
}
|
|
} else {
|
|
$msg = $app_strings['LBL_CONTACT_HAS_NO_PRIMARY_EMAIL'];
|
|
SugarApplication::appendErrorMessage($msg);
|
|
}
|
|
}
|
|
}
|
|
$this->view = 'detail';
|
|
}
|
|
}
|