]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/connectors/utils/ConnectorUtils.php
Release 6.5.0
[Github/sugarcrm.git] / include / connectors / utils / ConnectorUtils.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 define('CONNECTOR_DISPLAY_CONFIG_FILE', 'custom/modules/Connectors/metadata/display_config.php');
39 require_once('include/connectors/ConnectorFactory.php');
40
41 /**
42  * Source sorting by order value
43  * @internal
44  */
45 function sources_sort_function($a, $b) {
46         if(isset($a['order']) && isset($b['order'])) {
47            if($a['order'] == $b['order']) {
48                   return 0;
49            }
50
51            return ($a['order'] < $b['order']) ? -1 : 1;
52         }
53
54         return 0;
55 }
56
57 /**
58  * Connector utilities
59  * @api
60  */
61 class ConnectorUtils
62 {
63     /**
64      * Cached connectors data
65      * @var array
66      */
67     protected static $connectors_cache;
68
69     /**
70      * Get connector data by ID
71      * @param string $id
72      * @param bool $refresh
73      * @return null|array Connector data
74      */
75     public static function getConnector(
76         $id,
77         $refresh = false
78         )
79     {
80         $s = self::getConnectors($refresh);
81         return !empty($s[$id]) ? $s[$id] : null;
82     }
83
84     /**
85      * Check if external accounts are enabled for this connector
86      * @param string $id
87      */
88     public static function eapmEnabled($id, $refresh = false)
89     {
90         $data = self::getConnector($id, $refresh);
91         if(!$data || !isset($data["eapm"])) {
92             // TODO: if we don't know this connector, should we decide it's enabled or disabled?
93             return true;
94         }
95         return !empty($data["eapm"]["enabled"]);
96     }
97
98     /**
99      * getSearchDefs
100      * Returns an Array of the search field defintions Connector module to
101      * search entries from the connector.  If the searchdefs.php file in the custom
102      * directory is not found, it defaults to using the mapping.php file entries to
103      * create a default version of the file.
104      *
105      * @param boolean $refresh boolean value to manually refresh the search definitions
106      * @return mixed $searchdefs Array of the search definitions
107      */
108     public static function getSearchDefs(
109         $refresh = false
110         )
111     {
112         if($refresh || !file_exists('custom/modules/Connectors/metadata/searchdefs.php')) {
113
114             require('modules/Connectors/metadata/searchdefs.php');
115
116             if(!file_exists('custom/modules/Connectors/metadata')) {
117                mkdir_recursive('custom/modules/Connectors/metadata');
118             }
119
120             if(!write_array_to_file('searchdefs', $searchdefs, 'custom/modules/Connectors/metadata/searchdefs.php')) {
121                $GLOBALS['log']->fatal("Cannot write file custom/modules/Connectors/metadata/searchdefs.php");
122                return array();
123             }
124         }
125
126         require('custom/modules/Connectors/metadata/searchdefs.php');
127         return $searchdefs;
128     }
129
130
131     /**
132      * getViewDefs
133      * Returns an Array of the merge definitions used by the Connector module to
134      * merge values into the bean instance
135      *
136      * @param mixed $filter_sources Array optional Array value of sources to only use
137      * @return mixed $mergedefs Array of the merge definitions
138      */
139     public static function getViewDefs(
140         $filter_sources = array()
141         )
142     {
143         //Go through all connectors and get their mapping keys and merge them across each module
144         $connectors = self::getConnectors();
145         $modules_sources = self::getDisplayConfig();
146         $view_defs = array();
147         foreach($connectors as $id=>$ds) {
148
149            if(!empty($filter_sources) && !isset($filter_sources[$id])) {
150               continue;
151            }
152
153            if(file_exists('custom/' . $ds['directory'] . '/mapping.php')) {
154              require('custom/' . $ds['directory'] . '/mapping.php');
155            } else if(file_exists($ds['directory'] . '/mapping.php')) {
156              require($ds['directory'] . '/mapping.php');
157            }
158
159            if(!empty($mapping['beans'])) {
160               foreach($mapping['beans'] as $module=>$map) {
161                  if(!empty($modules_sources[$module][$id])) {
162                      if(!empty($view_defs['Connector']['MergeView'][$module])) {
163                         $view_defs['Connector']['MergeView'][$module] = array_merge($view_defs['Connector']['MergeView'][$module], array_flip($map));
164                      } else {
165                         $view_defs['Connector']['MergeView'][$module] = array_flip($map);
166                      }
167                  }
168               }
169            }
170         }
171
172         if(!empty($view_defs['Connector']['MergeView'])) {
173             foreach($view_defs['Connector']['MergeView'] as $module=>$map) {
174                 $view_defs['Connector']['MergeView'][$module] = array_keys($view_defs['Connector']['MergeView'][$module]);
175             }
176         }
177
178         return $view_defs;
179     }
180
181
182     /**
183      * getMergeViewDefs
184      * Returns an Array of the merge definitions used by the Connector module to
185      * merge values into the bean instance
186      *
187      * @deprecated This method has been replaced by getViewDefs
188      * @param boolean $refresh boolean value to manually refresh the mergeview definitions
189      * @return mixed $mergedefs Array of the merge definitions
190      */
191     public static function getMergeViewDefs(
192         $refresh = false
193         )
194     {
195         if($refresh || !file_exists('custom/modules/Connectors/metadata/mergeviewdefs.php')) {
196
197             //Go through all connectors and get their mapping keys and merge them across each module
198             $connectors = self::getConnectors($refresh);
199             $modules_sources = self::getDisplayConfig();
200             $view_defs = array();
201             foreach($connectors as $id=>$ds) {
202
203                if(file_exists('custom/' . $ds['directory'] . '/mapping.php')) {
204                  require('custom/' . $ds['directory'] . '/mapping.php');
205                } else if(file_exists($ds['directory'] . '/mapping.php')) {
206                  require($ds['directory'] . '/mapping.php');
207                }
208
209                if(!empty($mapping['beans'])) {
210                   foreach($mapping['beans'] as $module=>$map) {
211                      if(!empty($modules_sources[$module][$id])) {
212                          if(!empty($view_defs['Connector']['MergeView'][$module])) {
213                             $view_defs['Connector']['MergeView'][$module] = array_merge($view_defs['Connector']['MergeView'][$module], array_flip($map));
214                          } else {
215                             $view_defs['Connector']['MergeView'][$module] = array_flip($map);
216                          }
217                      }
218                   }
219                }
220             }
221
222             if(!empty($view_defs['Connector']['MergeView'])) {
223                 foreach($view_defs['Connector']['MergeView'] as $module=>$map) {
224                     $view_defs['Connector']['MergeView'][$module] = array_keys($view_defs['Connector']['MergeView'][$module]);
225                 }
226             }
227
228             if(!file_exists('custom/modules/Connectors/metadata')) {
229                mkdir_recursive('custom/modules/Connectors/metadata');
230             }
231
232             if(!write_array_to_file('viewdefs', $view_defs, 'custom/modules/Connectors/metadata/mergeviewdefs.php')) {
233                $GLOBALS['log']->fatal("Cannot write file custom/modules/Connectors/metadata/mergeviewdefs.php");
234                return array();
235             }
236         }
237
238         require('custom/modules/Connectors/metadata/mergeviewdefs.php');
239         return $viewdefs;
240     }
241
242
243     /**
244      * getConnectors
245      * Returns an Array of the connectors that have been loaded into the system
246      * along with attributes pertaining to each connector.
247      *
248      * @param boolean $refresh boolean flag indicating whether or not to force rewriting the file; defaults to false
249      * @returns mixed $connectors Array of the connector entries found
250      */
251     public static function getConnectors(
252         $refresh = false
253         )
254     {
255         if (inDeveloperMode()) {
256             $refresh = true;
257         }
258
259         if(!empty(self::$connectors_cache) && !$refresh) {
260             return self::$connectors_cache;
261         }
262         //define paths
263         $src1 = 'modules/Connectors/connectors/sources';
264         $src2 = 'custom/modules/Connectors/connectors/sources';
265         $src3 = 'custom/modules/Connectors/metadata';
266         $src4 = 'custom/modules/Connectors/metadata/connectors.php';
267
268         //if this is a templated environment, then use utilities to get the proper paths
269         if(defined('TEMPLATE_URL')){
270             $src1 = SugarTemplateUtilities::getFilePath($src1);
271             $src2 = SugarTemplateUtilities::getFilePath($src2);
272             $src3 = SugarTemplateUtilities::getFilePath($src3);
273             $src4 = SugarTemplateUtilities::getFilePath($src4);
274         }
275
276         if($refresh || !file_exists($src4)) {
277
278           $sources = array_merge(self::getSources($src1), self::getSources($src2));
279           if(!file_exists($src3)) {
280              mkdir_recursive($src3);
281           }
282           if(file_exists($src4)) {
283               require($src4);
284
285             //define connectors if it doesn't exist or is not an array
286             if (!isset($connectors) || !is_array($connectors)){
287                 $connectors = array();
288                 $err_str = string_format($GLOBALS['app_strings']['ERR_CONNECTOR_NOT_ARRAY'],array($src4));
289                 $GLOBALS['log']->error($err_str);
290             }
291
292             $sources = array_merge($sources, $connectors);
293           }
294
295           if(!self::saveConnectors($sources, $src4)) {
296              return array();
297           }
298         } //if
299
300         require($src4);
301         self::$connectors_cache = $connectors;
302         return $connectors;
303     }
304
305     /**
306      * Save connectors array to file
307      * @param array $connectors Source data to write
308      * @param string $toFile filename to use
309      * @return bool success
310      */
311     public static function saveConnectors($connectors, $toFile = '')
312     {
313         if(empty($toFile)) {
314             $toFile = 'custom/modules/Connectors/metadata/connectors.php';
315             if(defined('TEMPLATE_URL')) {
316                 $toFile = SugarTemplateUtilities::getFilePath($toFile);
317             }
318         }
319
320         if(!is_array($connectors))
321         {
322             $connectors = array();
323         }
324
325         if(!write_array_to_file('connectors', $connectors, $toFile)) {
326            //Log error and return empty array
327            $GLOBALS['log']->fatal("Cannot write sources to file");
328            return false;
329         }
330         self::$connectors_cache = $connectors;
331         return true;
332     }
333
334     /**
335      * getSources
336      * Returns an Array of source entries found under the given directory
337      * @param String $directory The directory to search
338      * @return mixed $sources An Array of source entries
339      */
340     private static function getSources(
341         $directory = 'modules/Connectors/connectors/sources'
342         )
343     {
344           if(file_exists($directory)) {
345
346               $files = array();
347               $files = findAllFiles($directory, $files, false, 'config\.php');
348               $start = strrpos($directory, '/') == strlen($directory)-1 ? strlen($directory) : strlen($directory) + 1;
349               $sources = array();
350               $sources_ordering = array();
351               foreach($files as $file) {
352                       require($file);
353                       $end = strrpos($file, '/') - $start;
354                       $source = array();
355                       $source['id'] = str_replace('/', '_', substr($file, $start, $end));
356                       $source['name'] = !empty($config['name']) ? $config['name'] : $source['id'];
357                       $source['enabled'] = true;
358                       $source['directory'] = $directory . '/' . str_replace('_', '/', $source['id']);
359                       $order = isset($config['order']) ? $config['order'] : 99; //default to end using 99 if no order set
360
361                       $instance = ConnectorFactory::getInstance($source['id']);
362                       $source['eapm'] = empty($config['eapm'])?false:$config['eapm'];
363                       $mapping = $instance->getMapping();
364                       $modules = array();
365                       if(!empty($mapping['beans'])) {
366                          foreach($mapping['beans'] as $module=>$mapping_entry) {
367                              $modules[]=$module;
368                          }
369                       }
370                       $source['modules'] = $modules;
371                       $sources_ordering[$source['id']] = array('order'=>$order, 'source'=>$source);
372               }
373
374               usort($sources_ordering, 'sources_sort_function');
375               foreach($sources_ordering as $entry) {
376                  $sources[$entry['source']['id']] = $entry['source'];
377               }
378               return $sources;
379           }
380           return array();
381     }
382
383
384     /**
385      * getDisplayConfig
386      *
387      */
388     public static function getDisplayConfig(
389         $refresh = false
390         )
391     {
392         if(!file_exists(CONNECTOR_DISPLAY_CONFIG_FILE) || $refresh) {
393             $sources = self::getConnectors();
394             $modules_sources = array();
395
396             //Make the directory for the config file
397             if(!file_exists('custom/modules/Connectors/metadata')) {
398                 mkdir_recursive('custom/modules/Connectors/metadata');
399             }
400
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);
404             }
405
406         }
407
408         require(CONNECTOR_DISPLAY_CONFIG_FILE);
409         return $modules_sources;
410     }
411
412
413     /**
414      * getModuleConnectors
415      *
416      * @param String $module the module to get the connectors for
417      * @param mixed $connectors Array of connectors mapped to the module or empty if none
418      * @return array
419      */
420     public static function getModuleConnectors(
421         $module
422         )
423     {
424         $modules_sources = self::getDisplayConfig();
425         if(!empty($modules_sources) && !empty($modules_sources[$module])){
426             $sources = array();
427             foreach($modules_sources[$module] as $index => $id){
428                 $sources[$id] = self::getConnector($id);
429             }
430             return $sources;
431         }else{
432             return array();
433         }
434     }
435
436     /**
437      * isModuleEnabled
438      * Given a module name, checks to see if the module is enabled to be serviced by the connector module
439      * @param String $module String name of the module
440      * @return boolean $enabled boolean value indicating whether or not the module is enabled to be serviced by the connector module
441      */
442     public static function isModuleEnabled(
443         $module
444         )
445     {
446         $modules_sources = self::getDisplayConfig();
447         return !empty($modules_sources) && !empty($modules_sources[$module]) ? true : false;
448     }
449
450
451     /**
452      * isSourceEnabled
453      * Given a source id, checks to see if the source is enabled for at least one module
454      * @param String $source String name of the source
455      * @return boolean $enabled boolean value indicating whether or not the source is displayed in at least one module
456      */
457     public static function isSourceEnabled(
458         $source
459         )
460     {
461         $modules_sources = self::getDisplayConfig();
462         foreach($modules_sources as $module=>$mapping) {
463                 foreach($mapping as $s) {
464                         if($s == $source) {
465                            return true;
466                         }
467                 }
468         }
469         return false;
470     }
471
472     /**
473      * When a module has all of the sources removed from it we do not properly remove it from the viewdefs. This function
474      * will handle that.
475      *
476      * @param String $module     - the module in question
477      */
478     public static function cleanMetaDataFile(
479         $module
480         )
481     {
482         $metadata_file = file_exists("custom/modules/{$module}/metadata/detailviewdefs.php") ? "custom/modules/{$module}/metadata/detailviewdefs.php" : "modules/{$module}/metadata/detailviewdefs.php";
483         require($metadata_file);
484
485         $insertConnectorButton = true;
486
487
488
489
490        self::removeHoverField($viewdefs, $module);
491
492         //Make the directory for the metadata file
493         if(!file_exists("custom/modules/{$module}/metadata")) {
494             mkdir_recursive("custom/modules/{$module}/metadata");
495         }
496
497         if(!write_array_to_file('viewdefs', $viewdefs,  "custom/modules/{$module}/metadata/detailviewdefs.php")) {
498             $GLOBALS['log']->fatal("Cannot update file custom/modules/{$module}/metadata/detailviewdefs.php");
499             return false;
500         }
501
502         if(file_exists($cachedfile = sugar_cached("modules/{$module}/DetailView.tpl")) && !unlink($cachedfile)) {
503             $GLOBALS['log']->fatal("Cannot delete file $cachedfile");
504             return false;
505         }
506     }
507
508
509     /**
510      * updateMetaDataFiles
511      * This method updates the metadata files (detailviewdefs.php) according to the settings in display_config.php
512      * @return $result boolean value indicating whether or not the method successfully completed.
513      */
514     public static function updateMetaDataFiles()
515     {
516         if(file_exists(CONNECTOR_DISPLAY_CONFIG_FILE)) {
517            $modules_sources = array();
518
519            require(CONNECTOR_DISPLAY_CONFIG_FILE);
520
521            $GLOBALS['log']->debug(var_export($modules_sources, true));
522            if(!empty($modules_sources)) {
523               foreach($modules_sources as $module=>$mapping) {
524                      $metadata_file = file_exists("custom/modules/{$module}/metadata/detailviewdefs.php") ? "custom/modules/{$module}/metadata/detailviewdefs.php" : "modules/{$module}/metadata/detailviewdefs.php";
525
526
527                      $viewdefs = array();
528                      if( !file_exists($metadata_file) )
529                      {
530                         $GLOBALS['log']->info("Unable to update metadata file for module: {$module}");
531                          continue;
532                      }
533                      else
534                         require($metadata_file);
535
536                      $insertConnectorButton = true;
537
538
539
540
541                      self::removeHoverField($viewdefs, $module);
542
543                      //Insert the hover field if available
544                      if(!empty($mapping)) {
545
546                         require_once('include/connectors/sources/SourceFactory.php');
547                         require_once('include/connectors/formatters/FormatterFactory.php');
548                         $shown_formatters = array();
549                         foreach($mapping as $id) {
550                                 $source = SourceFactory::getSource($id, false);
551                                 if($source->isEnabledInHover() && $source->isRequiredConfigFieldsForButtonSet()) {
552                                    $shown_formatters[$id] = FormatterFactory::getInstance($id);
553                                 }
554                         }
555
556                         //Now we have to decide which field to put it on... use the first one for now
557                         if(!empty($shown_formatters)) {
558
559                            foreach($shown_formatters as $id=>$formatter) {
560                                $added_field = false;
561                                $formatter_mapping = $formatter->getSourceMapping();
562
563                                $source = $formatter->getComponent()->getSource();
564                                //go through the mapping and add the hover to every field define in the mapping
565                                //1) check for hover fields
566                                $hover_fields = $source->getFieldsWithParams('hover', true);
567
568                                foreach($hover_fields as $key => $def){
569                                     if(!empty($formatter_mapping['beans'][$module][$key])){
570                                         $added_field = self::setHoverField($viewdefs, $module, $formatter_mapping['beans'][$module][$key], $id);
571                                     }
572                                }
573
574                                //2) check for first mapping field
575                                if(!$added_field && !empty($formatter_mapping['beans'][$module])) {
576                                     foreach($formatter_mapping['beans'][$module] as $key => $val){
577                                         $added_field = self::setHoverField($viewdefs, $module, $val, $id);
578                                         if($added_field){
579                                             break;
580                                         }
581                                     }
582                                }
583                            } //foreach
584
585
586
587                            //Log an error message
588                            if(!$added_field) {
589                               $GLOBALS['log']->fatal("Unable to place hover field link on metadata for module {$module}");
590                            }
591                         }
592
593                      }
594
595
596                      //Make the directory for the metadata file
597                      if(!file_exists("custom/modules/{$module}/metadata")) {
598                         mkdir_recursive("custom/modules/{$module}/metadata");
599                      }
600
601                      if(!write_array_to_file('viewdefs', $viewdefs,  "custom/modules/{$module}/metadata/detailviewdefs.php")) {
602                         $GLOBALS['log']->fatal("Cannot update file custom/modules/{$module}/metadata/detailviewdefs.php");
603                         return false;
604                      }
605
606                      if(file_exists($cachedfile = sugar_cached("modules/{$module}/DetailView.tpl")) && !unlink($cachedfile)) {
607                          $GLOBALS['log']->fatal("Cannot delete file $cachedfile");
608                          return false;
609                      }
610               }
611            }
612         }
613         return true;
614     }
615
616     public function removeHoverField(
617         &$viewdefs,
618         $module
619         )
620     {
621         require_once('include/SugarFields/Parsers/MetaParser.php');
622         $metaParser = new MetaParser();
623         if(!$metaParser->hasMultiplePanels($viewdefs[$module]['DetailView']['panels'])) {
624             $keys = array_keys($viewdefs[$module]['DetailView']['panels']);
625             if(!empty($keys) && count($keys) != 1) {
626                $viewdefs[$module]['DetailView']['panels'] = array('default'=>$viewdefs[$module]['DetailView']['panels']);
627             }
628         }
629
630         foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
631           foreach($panel as $row_id=>$row) {
632               foreach($row as $field_id=>$field) {
633                   if(is_array($field) && !empty($field['displayParams']['enableConnectors'])) {
634
635                      unset($field['displayParams']['enableConnectors']);
636                      unset($field['displayParams']['module']);
637                      unset($field['displayParams']['connectors']);
638                      $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
639                   }
640               } //foreach
641           } //foreach
642         } //foreach
643         return false;
644     }
645
646     public function setHoverField(
647         &$viewdefs,
648         $module,
649         $hover_field,
650         $source_id
651         )
652     {
653        //Check for metadata files that aren't correctly created
654        require_once('include/SugarFields/Parsers/MetaParser.php');
655        $metaParser = new MetaParser();
656        if(!$metaParser->hasMultiplePanels($viewdefs[$module]['DetailView']['panels'])) {
657             $keys = array_keys($viewdefs[$module]['DetailView']['panels']);
658             if(!empty($keys) && count($keys) != 1) {
659                $viewdefs[$module]['DetailView']['panels'] = array('default'=>$viewdefs[$module]['DetailView']['panels']);
660             }
661        }
662
663        foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
664           foreach($panel as $row_id=>$row) {
665               foreach($row as $field_id=>$field) {
666                   $name = is_array($field) ? $field['name'] : $field;
667                   if($name == $hover_field) {
668                       if(is_array($field)) {
669                          if(!empty($viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'])) {
670                             $newDisplayParam = $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'];
671                             $newDisplayParam['module'] = $module;
672                             $newDisplayParam['enableConnectors'] = true;
673                             if(!is_null($source_id) && !in_array($source_id, $newDisplayParam['connectors'])){
674                                 $newDisplayParam['connectors'][] = $source_id;
675                             }
676                             $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'] = $newDisplayParam;
677                          } else {
678                             $field['displayParams'] = array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id));
679                             $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
680                          }
681
682                       } else {
683                          $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = array ('name'=>$field, 'displayParams'=>array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id)));
684                       }
685                       return true;
686                   }
687               }
688           }
689        }
690        return false;
691     }
692
693     /**
694      * setDefaultHoverField
695      * Sets the hover field to the first element in the detailview screen
696      *
697      * @param Array $viewdefs the metadata of the detailview
698      * @param String $module the Module to which the hover field should be added to
699      * @return boolean True if field was added; false otherwise
700      */
701     private function setDefaultHoverField(
702         &$viewdefs,
703         $module,
704         $source_id
705         )
706     {
707       foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
708           foreach($panel as $row_id=>$row) {
709               foreach($row as $field_id=>$field) {
710                   if(is_array($field)) {
711                      if(!empty($viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'])) {
712                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['enableConnectors'] = true;
713                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['module'] = $module;
714                         if(!is_null($source_id) && !in_array($source_id, $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['connectors'])){
715                                 $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['connectors'][] = $source_id;
716                         }
717                      } else {
718                         $field['displayParams'] = array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id));
719                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
720                      }
721                   } else {
722                      $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = array ('name'=>$field, 'displayParams'=>array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id)));
723                   }
724                   return true;
725               } //foreach
726           } //foreach
727       } //foreach
728       return false;
729     }
730
731
732     /**
733      * getConnectorButtonScript
734      * This method builds the HTML code for the hover link field
735      *
736      * @param mixed $displayParams Array value of display parameters passed from the SugarField code
737      * @param mixed $smarty The Smarty object from the calling smarty code
738      * @return String $code The HTML code for the hover link
739      */
740     public static function getConnectorButtonScript(
741         $displayParams,
742         $smarty
743         )
744     {
745         $module = $displayParams['module'];
746         require_once('include/connectors/utils/ConnectorUtils.php');
747         $modules_sources = self::getDisplayConfig();
748         global $current_language, $app_strings;
749         $mod_strings = return_module_language($current_language, 'Connectors');
750         $menuParams = 'var menuParams = "';
751         $shown_sources = array();
752         if(!empty($module) && !empty($displayParams['connectors'])) {
753             foreach($displayParams['connectors'] as $id) {
754                 if(!empty($modules_sources[$module]) && in_array($id, $modules_sources[$module])){
755                     $shown_sources[] = $id;
756                 }
757               }
758
759               if(empty($shown_sources)) {
760                     return '';
761               }
762
763               require_once('include/connectors/formatters/FormatterFactory.php');
764               $code = '';
765
766               //If there is only one source, just show the icon or some standalone view
767               if(count($shown_sources) == 1) {
768                   $formatter = FormatterFactory::getInstance($shown_sources[0]);
769                   $formatter->setModule($module);
770                   $formatter->setSmarty($smarty);
771                   $formatter_code = $formatter->getDetailViewFormat();
772                   if(!empty($formatter_code)) {
773                       $iconFilePath = $formatter->getIconFilePath();
774                       $iconFilePath = empty($iconFilePath) ? 'themes/default/images/MoreDetail.png' : $iconFilePath;
775
776                       $code = '<!--not_in_theme!--><img id="dswidget_img" border="0" src="' . $iconFilePath .'" alt="' . $shown_sources[0] .'" onmouseover="show_' . $shown_sources[0] . '(event);">';
777
778                       $code .= "<script type='text/javascript' src='{sugar_getjspath file='include/connectors/formatters/default/company_detail.js'}'></script>";
779                       $code .= $formatter->getDetailViewFormat();
780                       $code .= $formatter_code;
781                   }
782                   return $code;
783               } else {
784
785                   $formatterCode = '';
786                   $sourcesDisplayed = 0;
787                   $singleIcon = '';
788                   foreach($shown_sources as $id) {
789                       $formatter = FormatterFactory::getInstance($id);
790                       $formatter->setModule($module);
791                       $formatter->setSmarty($smarty);
792                       $buttonCode = $formatter->getDetailViewFormat();
793                       if(!empty($buttonCode)) {
794                           $sourcesDisplayed++;
795                           $singleIcon = $formatter->getIconFilePath();
796                           $source = SourceFactory::getSource($id);
797                           $config = $source->getConfig();
798                           $name = !empty($config['name']) ? $config['name'] : $id;
799                           //Create the menu item to call show_[source id] method in javascript
800                           $menuParams .= '<a href=\'#\' style=\'width:150px\' class=\'menuItem\' onmouseover=\'hiliteItem(this,\"yes\");\' onmouseout=\'unhiliteItem(this);\' onclick=\'show_' . $id . '(event);\'>' . $name . '</a>';
801                           $formatterCode .= $buttonCode;
802                       }
803                   } //for
804
805                   if(!empty($formatterCode)) {
806                       if($sourcesDisplayed > 1) {
807                         $dswidget_img = SugarThemeRegistry::current()->getImageURL('MoreDetail.png');
808                         $code = '<!--not_in_theme!--><img id="dswidget_img" src="'.$dswidget_img.'" width="11" height="7" border="0" alt="'.$app_strings['LBL_CONNECTORS_POPUPS'].'" onclick="return showConnectorMenu2(this);">';
809
810                       } else {
811                           $dswidget_img = SugarThemeRegistry::current()->getImageURL('MoreDetail.png');
812                           $singleIcon = empty($singleIcon) ? $dswidget_img : $singleIcon;
813                           $code = '<!--not_in_theme!--><img id="dswidget_img" border="0" src="' . $singleIcon . '" alt="'.$app_strings['LBL_CONNECTORS_POPUPS'].'" onclick="return showConnectorMenu2(this);">';
814
815                       }
816                       $code .= "<script type='text/javascript' src='{sugar_getjspath file='include/connectors/formatters/default/company_detail.js'}'></script>\n";
817                       $code .= "<script type='text/javascript'>\n";
818                       $code .= "function showConnectorMenu2(el) {literal} { {/literal}\n";
819
820                       $menuParams .= '";';
821                       $code .= $menuParams . "\n";
822                       $code .= "return SUGAR.util.showHelpTips(el,menuParams);\n";
823                       $code .= "{literal} } {/literal}\n";
824                       $code .= "</script>\n";
825                       $code .= $formatterCode;
826                   }
827                   return $code;
828               } //if-else
829         } //if
830     }
831
832
833     /**
834      * getConnectorStrings
835      * This method returns the language Strings for a given connector instance
836      *
837      * @param String $source_id String value of the connector id to retrive language strings for
838      * @param String $language optional String value for the language to use (defaults to $GLOBALS['current_language'])
839      */
840     public static function getConnectorStrings(
841         $source_id,
842         $language = ''
843         )
844     {
845         $lang = empty($language) ? $GLOBALS['current_language'] : $language;
846         $lang .= '.lang.php';
847         $dir = str_replace('_', '/', $source_id);
848         if(file_exists("custom/modules/Connectors/connectors/sources/{$dir}/language/{$lang}")) {
849             require("custom/modules/Connectors/connectors/sources/{$dir}/language/{$lang}");
850             return !empty($connector_strings) ? $connector_strings : array();
851         } else if(file_exists("modules/Connectors/connectors/sources/{$dir}/language/{$lang}")){
852             require("modules/Connectors/connectors/sources/{$dir}/language/{$lang}");
853             return !empty($connector_strings) ? $connector_strings : array();
854         } else {
855             $GLOBALS['log']->error("Unable to locate language string file for source {$source_id}");
856             return array();
857         }
858     }
859
860
861     /**
862      * installSource
863      * Install the name of the source (called from ModuleInstaller.php).  Modifies the files in the custom
864      * directory to add the new source in.
865      *
866      * @param String $source String value of the id of the connector to install
867      * @return boolean $result boolean value indicating whether or not connector was installed
868      */
869     public static function installSource(
870         $source
871         )
872     {
873         if(empty($source)) {
874            return false;
875         }
876         //Add the source to the connectors.php file
877         self::getConnectors(true);
878
879         //Get the display config file
880         self::getDisplayConfig();
881         //Update the display_config.php file to show this new source
882         $modules_sources = array();
883         require(CONNECTOR_DISPLAY_CONFIG_FILE);
884         foreach($modules_sources as $module=>$mapping) {
885
886             foreach($mapping as $id=>$src) {
887               if($src == $source) {
888                  unset($modules_sources[$module][$id]);
889                  break;
890               }
891             }
892         }
893
894         //Make the directory for the config file
895         if(!file_exists('custom/modules/Connectors/metadata')) {
896            mkdir_recursive('custom/modules/Connectors/metadata');
897         }
898
899         if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
900            //Log error and return empty array
901            $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
902         }
903         return true;
904     }
905
906
907     /**
908      * uninstallSource
909      *
910      * @param String $source String value of the id of the connector to un-install
911      * @return boolean $result boolean value indicating whether or not connector was un-installed
912      */
913     public static function uninstallSource(
914         $source
915         )
916     {
917         if(empty($source)) {
918            return false;
919         }
920
921         //Remove the source from the connectors.php file
922         self::getConnectors(true);
923
924         //Update the display_config.php file to remove this source
925         $modules_sources = array();
926         require(CONNECTOR_DISPLAY_CONFIG_FILE);
927         foreach($modules_sources as $module=>$mapping) {
928             foreach($mapping as $id=>$src) {
929                 if($src == $source) {
930                    unset($modules_sources[$module][$id]);
931                 }
932             }
933         }
934
935         //Make the directory for the config file
936         if(!file_exists('custom/modules/Connectors/metadata')) {
937            mkdir_recursive('custom/modules/Connectors/metadata');
938         }
939
940         if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
941            //Log error and return empty array
942            $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
943            return false;
944         }
945
946
947
948         return true;
949     }
950
951     /**
952      * hasWizardSourceEnabledForModule
953      * This is a private method that returns a boolean value indicating whether or not at least one
954      * source is enabled for a given module.  By enabled we mean that the source has the neccessary
955      * configuration properties set as determined by the isRequiredConfigFieldsForButtonSet method.  In
956      * addition, a check is made to ensure that it is a source that has been enabled for the wizard.
957      *
958      * @param String $module String value of module to check
959      * @return boolean $enabled boolean value indicating whether or not module has at least one source enabled
960      */
961     private static function hasWizardSourceEnabledForModule(
962         $module = ''
963         )
964     {
965         if(file_exists(CONNECTOR_DISPLAY_CONFIG_FILE)) {
966            require_once('include/connectors/sources/SourceFactory.php');
967            require(CONNECTOR_DISPLAY_CONFIG_FILE);
968            if(!empty($modules_sources) && !empty($modules_sources[$module])) {
969               foreach($modules_sources[$module] as $id) {
970                    $source = SourceFactory::getSource($id, false);
971                    if(!is_null($source) && $source->isEnabledInWizard() && $source->isRequiredConfigFieldsForButtonSet()) {
972                       return true;
973                    }
974               }
975            }
976            return false;
977         }
978         return false;
979     }
980 }