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