2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
4 /*********************************************************************************
5 * SugarCRM Community Edition is a customer relationship management program developed by
6 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Affero General Public License version 3 as published by the
10 * Free Software Foundation with the addition of the following permission added
11 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
12 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
13 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
20 * You should have received a copy of the GNU Affero General Public License along with
21 * this program; if not, see http://www.gnu.org/licenses or write to the Free
22 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
26 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28 * The interactive user interfaces in modified source and object code versions
29 * of this program must display Appropriate Legal Notices, as required under
30 * Section 5 of the GNU Affero General Public License version 3.
32 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
33 * these Appropriate Legal Notices must retain the display of the "Powered by
34 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
35 * technical reasons, the Appropriate Legal Notices must display the words
36 * "Powered by SugarCRM".
37 ********************************************************************************/
39 require_once('include/connectors/sources/SourceFactory.php');
40 require_once('include/connectors/ConnectorFactory.php');
41 require_once('include/MVC/Controller/SugarController.php');
43 class ConnectorsController extends SugarController {
45 var $admin_actions = array('ConnectorSettings', 'DisplayProperties', 'MappingProperties', 'ModifyMapping', 'ModifyDisplay', 'ModifyProperties',
46 'ModifySearch', 'SearchProperties', 'SourceProperties',
47 'SavedModifyDisplay', 'SaveModifyProperties', 'SaveModifySearch');
51 if(!is_admin($GLOBALS['current_user']) && in_array($this->action, $this->admin_actions)) {
52 $this->hasAccess = false;
59 * When the user clicks the Search button, the form is posted back here and this action sets the
60 * search parameters in the session. Once this call returns, the tabs will then call RetrieveSource to load
61 * the data that was saved in the session.
64 function action_SetSearch(){
65 if(empty($_REQUEST)) {
70 require_once('include/connectors/utils/ConnectorUtils.php');
71 $searchdefs = ConnectorUtils::getSearchDefs();
72 $merge_module = $_REQUEST['merge_module'];
73 $record_id = $_REQUEST['record'];
74 $searchDefs = isset($searchdefs) ? $searchdefs : array();
75 unset($_SESSION['searchDefs'][$merge_module][$record_id]);
78 $search_source = $_REQUEST['source_id'];
79 $source_instance = ConnectorFactory::getInstance($search_source);
80 $source_map = $source_instance->getModuleMapping($merge_module);
81 $module_fields = array();
82 foreach($_REQUEST as $search_term => $val){
83 if(!empty($source_map[$search_term])){
84 $module_fields[$source_map[$search_term]] = $val;
88 foreach($module_fields as $search_term => $val){
89 foreach($searchDefs as $source => $modules){
90 if(empty($sMap[$source])){
91 $instance = ConnectorFactory::getInstance($source);
92 $sMap[$source] = array_flip($instance->getModuleMapping($merge_module));
95 if(!empty($sMap[$source][$search_term])){
96 $source_key = $sMap[$source][$search_term];
97 $_SESSION['searchDefs'][$merge_module][$record_id][$source][$source_key] = $val;
104 * This action it meant to handle the hover action on the listview.
107 function action_RetrieveSourceDetails() {
108 $this->view = 'ajax';
109 $source_id = $_REQUEST['source_id'];
110 $record_id = $_REQUEST['record_id'];
112 if(empty($source_id) || empty($record_id)) {
116 $source = ConnectorFactory::getInstance($source_id);
117 $module = $_SESSION['merge_module'];
119 $result = $source->fillBean(array('id' => $record_id), $module);
120 require_once('include/connectors/utils/ConnectorUtils.php');
121 $connector_strings = ConnectorUtils::getConnectorStrings($source_id);
123 $fields = $source->getModuleMapping($module);
124 $fieldDefs = $source->getFieldDefs();
127 foreach($fields as $key=>$field){
130 if(isset($fieldDefs[$key])) {
131 $label = isset($connector_strings[$fieldDefs[$key]['vname']]) ? $connector_strings[$fieldDefs[$key]['vname']] : $label;
134 $val = $result->$field;
136 if(strlen($val) > 50) {
137 $val = substr($val, 0, 47) . '...';
139 $str .= $label . ': ' . $val.'<br/>';
143 $json = getJSONobj();
146 $retArray['body'] = !empty($str) ? str_replace(array("\rn", "\r", "\n"), array('','','<br />'), $str) : $GLOBALS['mod_strings']['ERROR_NO_ADDITIONAL_DETAIL'];
147 $retArray['caption'] = "<div style='float:left'>{$GLOBALS['app_strings']['LBL_ADDITIONAL_DETAILS']}</div>";
148 $retArray['width'] = (empty($results['width']) ? '300' : $results['width']);
149 $retArray['theme'] = $theme;
150 echo 'result = ' . $json->encode($retArray);
154 function action_GetSearchForm(){
155 $this->view = 'ajax';
156 if(!empty($_REQUEST['source_id'])){
157 //get the search fields and return the search form
159 $ss = new Sugar_Smarty();
160 require_once('include/connectors/utils/ConnectorUtils.php');
161 $searchdefs = ConnectorUtils::getSearchDefs();
162 $merge_module = $_REQUEST['merge_module'];
163 $seed = loadBean($merge_module);
164 $_searchDefs = isset($searchdefs) ? $searchdefs : array();
165 $_trueFields = array();
166 $source = $_REQUEST['source_id'];
168 $searchLabels = ConnectorUtils::getConnectorStrings($source);
169 $record = $_REQUEST['record'];
170 $sourceObj = SourceFactory::getSource($source);
171 $field_defs = $sourceObj->getFieldDefs();
173 if(!empty($_searchDefs[$source][$merge_module])) {
174 foreach($_searchDefs[$source][$merge_module] as $key) {
175 if(!empty($_SESSION['searchDefs'][$merge_module][$record][$source][$key])){
176 $_trueFields[$key]['value'] = $_SESSION['searchDefs'][$merge_module][$record][$source][$key];
178 $_trueFields[$key]['value'] = '';
180 if(!empty($field_defs[$key]) && isset($searchLabels[$field_defs[$key]['vname']])){
181 $_trueFields[$key]['label'] = $searchLabels[$field_defs[$key]['vname']];
183 $_trueFields[$key]['label'] = $key;
188 $ss->assign('mod', $GLOBALS['mod_strings']);
189 $ss->assign('search_fields', $_trueFields);
190 $ss->assign('source_id', $source);
191 $ss->assign('fields', $seed->field_defs);
192 $ss->assign('module', $merge_module);
193 $ss->assign('RECORD', $record);
194 $ss->assign('APP', $GLOBALS['app_strings']);
195 $ss->assign('MOD', $GLOBALS['mod_strings']);
196 echo $ss->fetch('modules/Connectors/tpls/search_form.tpl');
201 function pre_save(){}
202 function post_save(){}
205 function action_CallRest() {
206 $this->view = 'ajax';
208 if(false === ($result=@file_get_contents($_REQUEST['url']))) {
210 } else if(!empty($_REQUEST['xml'])){
212 $p = xml_parser_create();
213 xml_parse_into_struct($p, $result, $values);
215 $json = getJSONobj();
216 echo $json->encode($values);
222 function action_CallSoap() {
223 $this->view = 'ajax';
224 $source_id = $_REQUEST['source_id'];
225 $module = $_REQUEST['module_id'];
226 $return_params = explode(',', $_REQUEST['fields']);
227 require_once('include/connectors/ConnectorFactory.php');
228 $component = ConnectorFactory::getInstance($source_id);
229 $beans = $component->fillBeans($_REQUEST, $module);
230 if(!empty($beans) && !empty($return_params)) {
233 foreach($beans as $bean) {
234 foreach($return_params as $field) {
235 $results[$count][$field] = $bean->$field;
239 $json = getJSONobj();
240 echo $json->encode($results);
247 function action_DefaultSoapPopup() {
248 $this->view = 'ajax';
249 $source_id = $_REQUEST['source_id'];
250 $module = $_REQUEST['module_id'];
251 $id = $_REQUEST['record_id'];
252 $mapping = $_REQUEST['mapping'];
254 $mapping = explode(',', $mapping);
258 $bean = loadBean($module);
259 $bean->retrieve($id);
261 require_once('include/connectors/ConnectorFactory.php');
262 $component = ConnectorFactory::getInstance($source_id);
265 $field_defs = $bean->getFieldDefinitions();
266 foreach($field_defs as $id=>$field) {
267 if(!empty($bean->$id)) {
268 $args[$id] = $bean->$id;
272 $beans = $component->fillBeans($args, $module);
273 if(!empty($beans) && !empty($mapping)) {
276 foreach($beans as $bean) {
277 foreach($mapping as $field) {
278 $results[$count][$field] = $bean->$field;
282 $json = getJSONobj();
283 echo $json->encode($results);
285 $GLOBALS['log']->error($GLOBALS['app_strings']['ERR_MISSING_MAPPING_ENTRY_FORM_MODULE']);
290 function action_SaveModifyProperties() {
291 require_once('include/connectors/sources/SourceFactory.php');
293 $properties = array();
294 foreach($_REQUEST as $name=>$value) {
295 if(preg_match("/^source[0-9]+$/", $name, $matches)) {
297 $properties = array();
298 foreach($_REQUEST as $arg=>$val) {
299 if(preg_match("/^{$source_id}_(.*?)$/", $arg, $matches2)) {
300 $properties[$matches2[1]] = $val;
303 $source = SourceFactory::getSource($source_id);
304 if(!empty($properties)) {
305 $source->setProperties($properties);
306 $source->saveConfig();
311 require_once('include/connectors/utils/ConnectorUtils.php');
312 ConnectorUtils::updateMetaDataFiles();
314 if(empty($_REQUEST['from_unit_test'])) {
316 header("Location: index.php?action=ConnectorSettings&module=Connectors");
322 function action_SaveModifyDisplay() {
323 if(empty($_REQUEST['display_sources'])) {
327 require_once('include/connectors/utils/ConnectorUtils.php');
328 require_once('include/connectors/sources/SourceFactory.php');
330 $connectors = ConnectorUtils::getConnectors();
331 $connector_keys = array_keys($connectors);
333 $modules_sources = ConnectorUtils::getDisplayConfig();
334 if ( !is_array($modules_sources) ) {
335 $modules_sources = (array) $modules_sources;
340 $new_modules_sources = array();
342 if(!empty($_REQUEST['display_values'])) {
343 $display_values = explode(',', $_REQUEST['display_values']);
344 foreach($display_values as $value) {
345 $entry = explode(':', $value);
346 $new_modules_sources[$entry[1]][$entry[0]] = $entry[0];
350 //These are the sources that were modified.
351 //We only update entries for these sources that have been changed
352 $display_sources = explode(',', $_REQUEST['display_sources']);
353 foreach($display_sources as $source) {
354 $sources[$source] = $source;
357 $removedModules = array();
359 //Unset entries that have all sources removed
360 foreach($modules_sources as $module=>$source_entries) {
361 foreach($source_entries as $source_id) {
362 if(!empty($sources[$source_id]) && empty($new_modules_sources[$module][$source_id])) {
363 unset($modules_sources[$module][$source_id]);
364 $removedModules[$module] = true;
368 $removedModules = array_keys($removedModules);
369 foreach($removedModules as $key){
370 if(empty($new_modules_sources[$key])){
371 ConnectorUtils::cleanMetaDataFile($key);
375 //Update based on new_modules_sources
376 foreach($new_modules_sources as $module=>$enabled_sources) {
377 //If the module is not in $modules_sources add it there
378 if(empty($modules_sources[$module])) {
379 $modules_sources[$module] = $enabled_sources;
381 foreach($enabled_sources as $source_id) {
382 if(empty($modules_sources[$module][$source_id])) {
383 $modules_sources[$module][$source_id] = $source_id;
389 //Should we just remove entries where all sources are disabled?
390 $unset_modules = array();
391 foreach($modules_sources as $module=>$mapping) {
392 if(empty($mapping)) {
393 $unset_modules[] = $module;
397 foreach($unset_modules as $mod) {
398 unset($modules_sources[$mod]);
401 if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
402 //Log error and return empty array
403 $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
406 $sources_modules = array();
407 foreach($modules_sources as $module=>$source_entries) {
408 foreach($source_entries as $id) {
409 $sources_modules[$id][$module] = $module;
414 //Now update the searchdefs and field mapping entries accordingly
415 require('modules/Connectors/metadata/searchdefs.php');
416 $originalSearchDefs = $searchdefs;
417 $connectorSearchDefs = ConnectorUtils::getSearchDefs();
419 $searchdefs = array();
420 foreach($sources_modules as $source_id=>$modules) {
421 foreach($modules as $module) {
422 $searchdefs[$source_id][$module] = !empty($connectorSearchDefs[$source_id][$module]) ? $connectorSearchDefs[$source_id][$module] : (!empty($originalSearchDefs[$source_id][$module]) ? $originalSearchDefs[$source_id][$module] : array());
426 //Write the new searchdefs out
427 if(!write_array_to_file('searchdefs', $searchdefs, 'custom/modules/Connectors/metadata/searchdefs.php')) {
428 $GLOBALS['log']->fatal("Cannot write file custom/modules/Connectors/metadata/searchdefs.php");
431 //Unset the $_SESSION['searchDefs'] variable
432 if (isset($_SESSION['searchDefs'])) {
433 unset($_SESSION['searchDefs']);
438 //Clear mapping file if needed (this happens when all modules are removed from a source
439 foreach($sources as $id) {
440 if(empty($sources_modules[$source])) {
441 //Now write the new mapping entry to the custom folder
442 $dir = $connectors[$id]['directory'];
443 if(!preg_match('/^custom\//', $dir)) {
444 $dir = 'custom/' . $dir;
447 if(!file_exists("{$dir}")) {
448 mkdir_recursive("{$dir}");
451 if(!write_array_to_file('mapping', array('beans'=>array()), "{$dir}/mapping.php")) {
452 $GLOBALS['log']->fatal("Cannot write file {$dir}/mapping.php");
457 //Now update the field mapping entries
458 foreach($sources_modules as $id=>$modules) {
459 $source = SourceFactory::getSource($id);
460 $mapping = $source->getMapping();
461 $mapped_modules = array_keys($mapping['beans']);
463 foreach($mapped_modules as $module) {
464 if(empty($sources_modules[$id][$module])) {
465 unset($mapping['beans'][$module]);
469 //Remove modules from the mapping entries
470 foreach($modules as $module) {
471 if(empty($mapping['beans'][$module])) {
472 $originalMapping = $source->getOriginalMapping();
473 if(empty($originalMapping['beans'][$module])) {
474 $defs = $source->getFieldDefs();
475 $keys = array_keys($defs);
476 $new_mapping_entry = array();
477 foreach($keys as $key) {
478 $new_mapping_entry[$key] = '';
480 $mapping['beans'][$module] = $new_mapping_entry;
482 $mapping['beans'][$module] = $originalMapping['beans'][$module];
488 //Now write the new mapping entry to the custom folder
489 $dir = $connectors[$id]['directory'];
490 if(!preg_match('/^custom\//', $dir)) {
491 $dir = 'custom/' . $dir;
494 if(!file_exists("{$dir}")) {
495 mkdir_recursive("{$dir}");
498 if(!write_array_to_file('mapping', $mapping, "{$dir}/mapping.php")) {
499 $GLOBALS['log']->fatal("Cannot write file {$dir}/mapping.php");
505 foreach($connectors as $connector_name => $data) {
506 if(isset($sources[$connector_name]) && !empty($data["eapm"])) {
507 // if we touched it AND it has EAPM data
508 $connectors[$connector_name]["eapm"]["enabled"] = !empty($_REQUEST[$connector_name."_external"]);
511 ConnectorUtils::saveConnectors($connectors);
513 ConnectorUtils::updateMetaDataFiles();
515 if(empty($_REQUEST['from_unit_test'])) {
517 header("Location: index.php?action=ConnectorSettings&module=Connectors");
527 * action_SaveModifyMapping
529 function action_SaveModifyMapping() {
530 $mapping_sources = !empty($_REQUEST['mapping_sources']) ? explode(',', $_REQUEST['mapping_sources']) : array();
531 $mapping_values = !empty($_REQUEST['mapping_values']) ? explode(',', $_REQUEST['mapping_values']) : array();
533 //Build the source->module->fields mapping
534 $source_modules_fields = array();
535 foreach($mapping_values as $id) {
536 $parts = explode(':', $id);
537 $key_vals = explode('=', $parts[2]);
538 //Note the strtolwer call... we are lowercasing the key values
539 $source_modules_fields[$parts[0]][$parts[1]][strtolower($key_vals[0])] = $key_vals[1];
542 foreach($mapping_sources as $source_id) {
543 if(empty($source_modules_fields[$source_id])) {
544 $source = SourceFactory::getSource($source_id);
545 $mapping = $source->getMapping();
546 foreach($mapping['beans'] as $module=>$entry) {
547 $source_modules_fields[$source_id][$module] = array();
555 require_once('include/connectors/utils/ConnectorUtils.php');
556 $source_entries = ConnectorUtils::getConnectors();
558 require_once('include/connectors/sources/SourceFactory.php');
559 foreach($source_modules_fields as $id=>$mapping_entry) {
560 //Insert the id mapping
561 foreach($mapping_entry as $module=>$entry) {
562 $mapping_entry[$module]['id'] = 'id';
565 $source = SourceFactory::getSource($id);
566 $mapping = $source->getMapping();
567 $mapping['beans'] = $mapping_entry;
569 //Now write the new mapping entry to the custom folder
570 $dir = $source_entries[$id]['directory'];
571 if(!preg_match('/^custom\//', $dir)) {
572 $dir = 'custom/' . $dir;
575 if(!file_exists("{$dir}")) {
576 mkdir_recursive("{$dir}");
579 if(!write_array_to_file('mapping', $mapping, "{$dir}/mapping.php")) {
580 $GLOBALS['log']->fatal("Cannot write file {$dir}/mapping.php");
584 //Rewrite the metadata files
585 ConnectorUtils::updateMetaDataFiles();
588 if(empty($_REQUEST['from_unit_test'])) {
590 header("Location: index.php?action=ConnectorSettings&module=Connectors");
597 function action_RunTest() {
598 $this->view = 'ajax';
599 $source_id = $_REQUEST['source_id'];
600 $source = SourceFactory::getSource($source_id);
601 $properties = array();
602 foreach($_REQUEST as $name=>$value) {
603 if(preg_match("/^{$source_id}_(.*?)$/", $name, $matches)) {
604 $properties[$matches[1]] = $value;
607 $source->setProperties($properties);
608 $source->saveConfig();
610 //Call again and call init
611 $source = SourceFactory::getSource($source_id);
617 if($source->isRequiredConfigFieldsForButtonSet() && $source->test()) {
618 echo $mod_strings['LBL_TEST_SOURCE_SUCCESS'];
620 echo $mod_strings['LBL_TEST_SOURCE_FAILED'];
622 } catch (Exception $ex) {
623 $GLOBALS['log']->fatal($ex->getMessage());
624 echo $ex->getMessage();
630 * action_RetrieveSources
631 * Returns a JSON encoded format of the Connectors that are configured for the system
634 function action_RetrieveSources() {
635 require_once('include/connectors/utils/ConnectorUtils.php');
636 $this->view = 'ajax';
637 $sources = ConnectorUtils:: getConnectors();
639 foreach($sources as $id=>$entry) {
640 $results[$id] = !empty($entry['name']) ? $entry['name'] : $id;
642 $json = getJSONobj();
643 echo $json->encode($results);