]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/connectors/utils/ConnectorUtils.php
Release 6.4.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-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 /**
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 ( isset($GLOBALS['sugar_config']['developerMode']) ) {
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               $sources = array_merge($sources, $connectors);
285           }
286
287           if(!self::saveConnectors($sources, $src4)) {
288              return array();
289           }
290         } //if
291
292         require($src4);
293         self::$connectors_cache = $connectors;
294         return $connectors;
295     }
296
297     /**
298      * Save connectors array to file
299      * @param array $connectors Source data to write
300      * @param string $toFile filename to use
301      * @return bool success
302      */
303     public static function saveConnectors($connectors, $toFile = '')
304     {
305         if(empty($toFile)) {
306             $toFile = 'custom/modules/Connectors/metadata/connectors.php';
307             if(defined('TEMPLATE_URL')) {
308                 $toFile = SugarTemplateUtilities::getFilePath($toFile);
309             }
310         }
311
312         if(!write_array_to_file('connectors', $connectors, $toFile)) {
313            //Log error and return empty array
314            $GLOBALS['log']->fatal("Cannot write sources to file");
315            return false;
316         }
317         self::$connectors_cache = $connectors;
318         return true;
319     }
320
321     /**
322      * getSources
323      * Returns an Array of source entries found under the given directory
324      * @param String $directory The directory to search
325      * @return mixed $sources An Array of source entries
326      */
327     private static function getSources(
328         $directory = 'modules/Connectors/connectors/sources'
329         )
330     {
331           if(file_exists($directory)) {
332
333               $files = array();
334               $files = findAllFiles($directory, $files, false, 'config\.php');
335               $start = strrpos($directory, '/') == strlen($directory)-1 ? strlen($directory) : strlen($directory) + 1;
336               $sources = array();
337               $sources_ordering = array();
338               foreach($files as $file) {
339                       require($file);
340                       $end = strrpos($file, '/') - $start;
341                       $source = array();
342                       $source['id'] = str_replace('/', '_', substr($file, $start, $end));
343                       $source['name'] = !empty($config['name']) ? $config['name'] : $source['id'];
344                       $source['enabled'] = true;
345                       $source['directory'] = $directory . '/' . str_replace('_', '/', $source['id']);
346                       $order = isset($config['order']) ? $config['order'] : 99; //default to end using 99 if no order set
347
348                       $instance = ConnectorFactory::getInstance($source['id']);
349                       $source['eapm'] = empty($config['eapm'])?false:$config['eapm'];
350                       $mapping = $instance->getMapping();
351                       $modules = array();
352                       if(!empty($mapping['beans'])) {
353                          foreach($mapping['beans'] as $module=>$mapping_entry) {
354                              $modules[]=$module;
355                          }
356                       }
357                       $source['modules'] = $modules;
358                       $sources_ordering[$source['id']] = array('order'=>$order, 'source'=>$source);
359               }
360
361               usort($sources_ordering, 'sources_sort_function');
362               foreach($sources_ordering as $entry) {
363                  $sources[$entry['source']['id']] = $entry['source'];
364               }
365               return $sources;
366           }
367           return array();
368     }
369
370
371     /**
372      * getDisplayConfig
373      *
374      */
375     public static function getDisplayConfig(
376         $refresh = false
377         )
378     {
379         if(!file_exists(CONNECTOR_DISPLAY_CONFIG_FILE) || $refresh) {
380             $sources = self::getConnectors();
381             $modules_sources = array();
382
383             //Make the directory for the config file
384             if(!file_exists('custom/modules/Connectors/metadata')) {
385                 mkdir_recursive('custom/modules/Connectors/metadata');
386             }
387
388             if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
389                 //Log error and return empty array
390                 $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
391             }
392
393         }
394
395         require(CONNECTOR_DISPLAY_CONFIG_FILE);
396         return $modules_sources;
397     }
398
399
400     /**
401      * getModuleConnectors
402      *
403      * @param String $module the module to get the connectors for
404      * @param mixed $connectors Array of connectors mapped to the module or empty if none
405      * @return array
406      */
407     public static function getModuleConnectors(
408         $module
409         )
410     {
411         $modules_sources = self::getDisplayConfig();
412         if(!empty($modules_sources) && !empty($modules_sources[$module])){
413             $sources = array();
414             foreach($modules_sources[$module] as $index => $id){
415                 $sources[$id] = self::getConnector($id);
416             }
417             return $sources;
418         }else{
419             return array();
420         }
421     }
422
423     /**
424      * isModuleEnabled
425      * Given a module name, checks to see if the module is enabled to be serviced by the connector module
426      * @param String $module String name of the module
427      * @return boolean $enabled boolean value indicating whether or not the module is enabled to be serviced by the connector module
428      */
429     public static function isModuleEnabled(
430         $module
431         )
432     {
433         $modules_sources = self::getDisplayConfig();
434         return !empty($modules_sources) && !empty($modules_sources[$module]) ? true : false;
435     }
436
437
438     /**
439      * isSourceEnabled
440      * Given a source id, checks to see if the source is enabled for at least one module
441      * @param String $source String name of the source
442      * @return boolean $enabled boolean value indicating whether or not the source is displayed in at least one module
443      */
444     public static function isSourceEnabled(
445         $source
446         )
447     {
448         $modules_sources = self::getDisplayConfig();
449         foreach($modules_sources as $module=>$mapping) {
450                 foreach($mapping as $s) {
451                         if($s == $source) {
452                            return true;
453                         }
454                 }
455         }
456         return false;
457     }
458
459     /**
460      * When a module has all of the sources removed from it we do not properly remove it from the viewdefs. This function
461      * will handle that.
462      *
463      * @param String $module     - the module in question
464      */
465     public static function cleanMetaDataFile(
466         $module
467         )
468     {
469         $metadata_file = file_exists("custom/modules/{$module}/metadata/detailviewdefs.php") ? "custom/modules/{$module}/metadata/detailviewdefs.php" : "modules/{$module}/metadata/detailviewdefs.php";
470         require($metadata_file);
471
472         $insertConnectorButton = true;
473
474
475
476
477        self::removeHoverField($viewdefs, $module);
478
479         //Make the directory for the metadata file
480         if(!file_exists("custom/modules/{$module}/metadata")) {
481             mkdir_recursive("custom/modules/{$module}/metadata");
482         }
483
484         if(!write_array_to_file('viewdefs', $viewdefs,  "custom/modules/{$module}/metadata/detailviewdefs.php")) {
485             $GLOBALS['log']->fatal("Cannot update file custom/modules/{$module}/metadata/detailviewdefs.php");
486             return false;
487         }
488
489         if(file_exists($cachedfile = sugar_cached("modules/{$module}/DetailView.tpl")) && !unlink($cachedfile)) {
490             $GLOBALS['log']->fatal("Cannot delete file $cachedfile");
491             return false;
492         }
493     }
494
495
496     /**
497      * updateMetaDataFiles
498      * This method updates the metadata files (detailviewdefs.php) according to the settings in display_config.php
499      * @return $result boolean value indicating whether or not the method successfully completed.
500      */
501     public static function updateMetaDataFiles()
502     {
503         if(file_exists(CONNECTOR_DISPLAY_CONFIG_FILE)) {
504            $modules_sources = array();
505
506            require(CONNECTOR_DISPLAY_CONFIG_FILE);
507
508            $GLOBALS['log']->debug(var_export($modules_sources, true));
509            if(!empty($modules_sources)) {
510               foreach($modules_sources as $module=>$mapping) {
511                      $metadata_file = file_exists("custom/modules/{$module}/metadata/detailviewdefs.php") ? "custom/modules/{$module}/metadata/detailviewdefs.php" : "modules/{$module}/metadata/detailviewdefs.php";
512
513
514                      $viewdefs = array();
515                      if( !file_exists($metadata_file) )
516                      {
517                         $GLOBALS['log']->info("Unable to update metadata file for module: {$module}");
518                          continue;
519                      }
520                      else
521                         require($metadata_file);
522
523                      $insertConnectorButton = true;
524
525
526
527
528                      self::removeHoverField($viewdefs, $module);
529
530                      //Insert the hover field if available
531                      if(!empty($mapping)) {
532
533                         require_once('include/connectors/sources/SourceFactory.php');
534                         require_once('include/connectors/formatters/FormatterFactory.php');
535                         $shown_formatters = array();
536                         foreach($mapping as $id) {
537                                 $source = SourceFactory::getSource($id, false);
538                                 if($source->isEnabledInHover() && $source->isRequiredConfigFieldsForButtonSet()) {
539                                    $shown_formatters[$id] = FormatterFactory::getInstance($id);
540                                 }
541                         }
542
543                         //Now we have to decide which field to put it on... use the first one for now
544                         if(!empty($shown_formatters)) {
545
546                            foreach($shown_formatters as $id=>$formatter) {
547                                $added_field = false;
548                                $formatter_mapping = $formatter->getSourceMapping();
549
550                                $source = $formatter->getComponent()->getSource();
551                                //go through the mapping and add the hover to every field define in the mapping
552                                //1) check for hover fields
553                                $hover_fields = $source->getFieldsWithParams('hover', true);
554
555                                foreach($hover_fields as $key => $def){
556                                     if(!empty($formatter_mapping['beans'][$module][$key])){
557                                         $added_field = self::setHoverField($viewdefs, $module, $formatter_mapping['beans'][$module][$key], $id);
558                                     }
559                                }
560
561                                //2) check for first mapping field
562                                if(!$added_field && !empty($formatter_mapping['beans'][$module])) {
563                                     foreach($formatter_mapping['beans'][$module] as $key => $val){
564                                         $added_field = self::setHoverField($viewdefs, $module, $val, $id);
565                                         if($added_field){
566                                             break;
567                                         }
568                                     }
569                                }
570                            } //foreach
571
572
573
574                            //Log an error message
575                            if(!$added_field) {
576                               $GLOBALS['log']->fatal("Unable to place hover field link on metadata for module {$module}");
577                            }
578                         }
579
580                      }
581
582
583                      //Make the directory for the metadata file
584                      if(!file_exists("custom/modules/{$module}/metadata")) {
585                         mkdir_recursive("custom/modules/{$module}/metadata");
586                      }
587
588                      if(!write_array_to_file('viewdefs', $viewdefs,  "custom/modules/{$module}/metadata/detailviewdefs.php")) {
589                         $GLOBALS['log']->fatal("Cannot update file custom/modules/{$module}/metadata/detailviewdefs.php");
590                         return false;
591                      }
592
593                      if(file_exists($cachedfile = sugar_cached("modules/{$module}/DetailView.tpl")) && !unlink($cachedfile)) {
594                          $GLOBALS['log']->fatal("Cannot delete file $cachedfile");
595                          return false;
596                      }
597               }
598            }
599         }
600         return true;
601     }
602
603     public function removeHoverField(
604         &$viewdefs,
605         $module
606         )
607     {
608         require_once('include/SugarFields/Parsers/MetaParser.php');
609         $metaParser = new MetaParser();
610         if(!$metaParser->hasMultiplePanels($viewdefs[$module]['DetailView']['panels'])) {
611             $keys = array_keys($viewdefs[$module]['DetailView']['panels']);
612             if(!empty($keys) && count($keys) != 1) {
613                $viewdefs[$module]['DetailView']['panels'] = array('default'=>$viewdefs[$module]['DetailView']['panels']);
614             }
615         }
616
617         foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
618           foreach($panel as $row_id=>$row) {
619               foreach($row as $field_id=>$field) {
620                   if(is_array($field) && !empty($field['displayParams']['enableConnectors'])) {
621
622                      unset($field['displayParams']['enableConnectors']);
623                      unset($field['displayParams']['module']);
624                      unset($field['displayParams']['connectors']);
625                      $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
626                   }
627               } //foreach
628           } //foreach
629         } //foreach
630         return false;
631     }
632
633     public function setHoverField(
634         &$viewdefs,
635         $module,
636         $hover_field,
637         $source_id
638         )
639     {
640        //Check for metadata files that aren't correctly created
641        require_once('include/SugarFields/Parsers/MetaParser.php');
642        $metaParser = new MetaParser();
643        if(!$metaParser->hasMultiplePanels($viewdefs[$module]['DetailView']['panels'])) {
644             $keys = array_keys($viewdefs[$module]['DetailView']['panels']);
645             if(!empty($keys) && count($keys) != 1) {
646                $viewdefs[$module]['DetailView']['panels'] = array('default'=>$viewdefs[$module]['DetailView']['panels']);
647             }
648        }
649
650        foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
651           foreach($panel as $row_id=>$row) {
652               foreach($row as $field_id=>$field) {
653                   $name = is_array($field) ? $field['name'] : $field;
654                   if($name == $hover_field) {
655                       if(is_array($field)) {
656                          if(!empty($viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'])) {
657                             $newDisplayParam = $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'];
658                             $newDisplayParam['module'] = $module;
659                             $newDisplayParam['enableConnectors'] = true;
660                             if(!is_null($source_id) && !in_array($source_id, $newDisplayParam['connectors'])){
661                                 $newDisplayParam['connectors'][] = $source_id;
662                             }
663                             $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'] = $newDisplayParam;
664                          } else {
665                             $field['displayParams'] = array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id));
666                             $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
667                          }
668
669                       } else {
670                          $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = array ('name'=>$field, 'displayParams'=>array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id)));
671                       }
672                       return true;
673                   }
674               }
675           }
676        }
677        return false;
678     }
679
680     /**
681      * setDefaultHoverField
682      * Sets the hover field to the first element in the detailview screen
683      *
684      * @param Array $viewdefs the metadata of the detailview
685      * @param String $module the Module to which the hover field should be added to
686      * @return boolean True if field was added; false otherwise
687      */
688     private function setDefaultHoverField(
689         &$viewdefs,
690         $module,
691         $source_id
692         )
693     {
694       foreach($viewdefs[$module]['DetailView']['panels'] as $panel_id=>$panel) {
695           foreach($panel as $row_id=>$row) {
696               foreach($row as $field_id=>$field) {
697                   if(is_array($field)) {
698                      if(!empty($viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams'])) {
699                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['enableConnectors'] = true;
700                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['module'] = $module;
701                         if(!is_null($source_id) && !in_array($source_id, $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['connectors'])){
702                                 $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id]['displayParams']['connectors'][] = $source_id;
703                         }
704                      } else {
705                         $field['displayParams'] = array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id));
706                         $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = $field;
707                      }
708                   } else {
709                      $viewdefs[$module]['DetailView']['panels'][$panel_id][$row_id][$field_id] = array ('name'=>$field, 'displayParams'=>array('enableConnectors'=>true, 'module'=>$module, 'connectors' => array(0 => $source_id)));
710                   }
711                   return true;
712               } //foreach
713           } //foreach
714       } //foreach
715       return false;
716     }
717
718
719     /**
720      * getConnectorButtonScript
721      * This method builds the HTML code for the hover link field
722      *
723      * @param mixed $displayParams Array value of display parameters passed from the SugarField code
724      * @param mixed $smarty The Smarty object from the calling smarty code
725      * @return String $code The HTML code for the hover link
726      */
727     public static function getConnectorButtonScript(
728         $displayParams,
729         $smarty
730         )
731     {
732         $module = $displayParams['module'];
733         require_once('include/connectors/utils/ConnectorUtils.php');
734         $modules_sources = self::getDisplayConfig();
735         global $current_language, $app_strings;
736         $mod_strings = return_module_language($current_language, 'Connectors');
737         $menuParams = 'var menuParams = "';
738         $shown_sources = array();
739         if(!empty($module) && !empty($displayParams['connectors'])) {
740             foreach($displayParams['connectors'] as $id) {
741                 if(!empty($modules_sources[$module]) && in_array($id, $modules_sources[$module])){
742                     $shown_sources[] = $id;
743                 }
744               }
745
746               if(empty($shown_sources)) {
747                     return '';
748               }
749
750               require_once('include/connectors/formatters/FormatterFactory.php');
751               $code = '';
752
753               //If there is only one source, just show the icon or some standalone view
754               if(count($shown_sources) == 1) {
755                   $formatter = FormatterFactory::getInstance($shown_sources[0]);
756                   $formatter->setModule($module);
757                   $formatter->setSmarty($smarty);
758                   $formatter_code = $formatter->getDetailViewFormat();
759                   if(!empty($formatter_code)) {
760                       $iconFilePath = $formatter->getIconFilePath();
761                       $iconFilePath = empty($iconFilePath) ? 'themes/default/images/MoreDetail.png' : $iconFilePath;
762
763                       $code = '<!--not_in_theme!--><img id="dswidget_img" border="0" src="' . $iconFilePath .'" alt="' . $shown_sources[0] .'" onmouseover="show_' . $shown_sources[0] . '(event);">';
764
765                       $code .= "<script type='text/javascript' src='{sugar_getjspath file='include/connectors/formatters/default/company_detail.js'}'></script>";
766                       $code .= $formatter->getDetailViewFormat();
767                       $code .= $formatter_code;
768                   }
769                   return $code;
770               } else {
771
772                   $formatterCode = '';
773                   $sourcesDisplayed = 0;
774                   $singleIcon = '';
775                   foreach($shown_sources as $id) {
776                       $formatter = FormatterFactory::getInstance($id);
777                       $formatter->setModule($module);
778                       $formatter->setSmarty($smarty);
779                       $buttonCode = $formatter->getDetailViewFormat();
780                       if(!empty($buttonCode)) {
781                           $sourcesDisplayed++;
782                           $singleIcon = $formatter->getIconFilePath();
783                           $source = SourceFactory::getSource($id);
784                           $config = $source->getConfig();
785                           $name = !empty($config['name']) ? $config['name'] : $id;
786                           //Create the menu item to call show_[source id] method in javascript
787                           $menuParams .= '<a href=\'#\' style=\'width:150px\' class=\'menuItem\' onmouseover=\'hiliteItem(this,\"yes\");\' onmouseout=\'unhiliteItem(this);\' onclick=\'show_' . $id . '(event);\'>' . $name . '</a>';
788                           $formatterCode .= $buttonCode;
789                       }
790                   } //for
791
792                   if(!empty($formatterCode)) {
793                       if($sourcesDisplayed > 1) {
794                         $dswidget_img = SugarThemeRegistry::current()->getImageURL('MoreDetail.png');
795                         $code = '<!--not_in_theme!--><img id="dswidget_img" src="'.$dswidget_img.'" width="11" height="7" border="0" alt="'.$app_strings['LBL_CONNECTORS_POPUPS'].'" onmouseover="return showConnectorMenu2();" onmouseout="return nd(1000);">';
796
797                       } else {
798                           $dswidget_img = SugarThemeRegistry::current()->getImageURL('MoreDetail.png');
799                           $singleIcon = empty($singleIcon) ? $dswidget_img : $singleIcon;
800                           $code = '<!--not_in_theme!--><img id="dswidget_img" border="0" src="' . $singleIcon . '" alt="'.$app_strings['LBL_CONNECTORS_POPUPS'].'" onmouseover="return showConnectorMenu2();" onmouseout="return nd(1000);">';
801
802                       }
803                       $code .= "{overlib_includes}\n";
804                       $code .= "<script type='text/javascript' src='{sugar_getjspath file='include/connectors/formatters/default/company_detail.js'}'></script>\n";
805                       $code .= "<script type='text/javascript'>\n";
806                       $code .= "function showConnectorMenu2() {literal} { {/literal}\n";
807
808                       $menuParams .= '";';
809                       $code .= $menuParams . "\n";
810                       $code .= "return overlib(menuParams, CENTER, STICKY, MOUSEOFF, 3000, WIDTH, 110, FGCLASS, 'olOptionsFgClass', CGCLASS, 'olOptionsCgClass', BGCLASS, 'olBgClass', TEXTFONTCLASS, 'olFontClass', CAPTIONFONTCLASS, 'olOptionsCapFontClass', CLOSEFONTCLASS, 'olOptionsCloseFontClass');\n";
811                       $code .= "{literal} } {/literal}\n";
812                       $code .= "</script>\n";
813                       $code .= $formatterCode;
814                   }
815                   return $code;
816               } //if-else
817         } //if
818     }
819
820
821     /**
822      * getConnectorStrings
823      * This method returns the language Strings for a given connector instance
824      *
825      * @param String $source_id String value of the connector id to retrive language strings for
826      * @param String $language optional String value for the language to use (defaults to $GLOBALS['current_language'])
827      */
828     public static function getConnectorStrings(
829         $source_id,
830         $language = ''
831         )
832     {
833         $lang = empty($language) ? $GLOBALS['current_language'] : $language;
834         $lang .= '.lang.php';
835         $dir = str_replace('_', '/', $source_id);
836         if(file_exists("custom/modules/Connectors/connectors/sources/{$dir}/language/{$lang}")) {
837             require("custom/modules/Connectors/connectors/sources/{$dir}/language/{$lang}");
838             return !empty($connector_strings) ? $connector_strings : array();
839         } else if(file_exists("modules/Connectors/connectors/sources/{$dir}/language/{$lang}")){
840             require("modules/Connectors/connectors/sources/{$dir}/language/{$lang}");
841             return !empty($connector_strings) ? $connector_strings : array();
842         } else {
843             $GLOBALS['log']->error("Unable to locate language string file for source {$source_id}");
844             return array();
845         }
846     }
847
848
849     /**
850      * installSource
851      * Install the name of the source (called from ModuleInstaller.php).  Modifies the files in the custom
852      * directory to add the new source in.
853      *
854      * @param String $source String value of the id of the connector to install
855      * @return boolean $result boolean value indicating whether or not connector was installed
856      */
857     public static function installSource(
858         $source
859         )
860     {
861         if(empty($source)) {
862            return false;
863         }
864         //Add the source to the connectors.php file
865         self::getConnectors(true);
866
867         //Get the display config file
868         self::getDisplayConfig();
869         //Update the display_config.php file to show this new source
870         $modules_sources = array();
871         require(CONNECTOR_DISPLAY_CONFIG_FILE);
872         foreach($modules_sources as $module=>$mapping) {
873
874             foreach($mapping as $id=>$src) {
875               if($src == $source) {
876                  unset($modules_sources[$module][$id]);
877                  break;
878               }
879             }
880         }
881
882         //Make the directory for the config file
883         if(!file_exists('custom/modules/Connectors/metadata')) {
884            mkdir_recursive('custom/modules/Connectors/metadata');
885         }
886
887         if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
888            //Log error and return empty array
889            $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
890         }
891         return true;
892     }
893
894
895     /**
896      * uninstallSource
897      *
898      * @param String $source String value of the id of the connector to un-install
899      * @return boolean $result boolean value indicating whether or not connector was un-installed
900      */
901     public static function uninstallSource(
902         $source
903         )
904     {
905         if(empty($source)) {
906            return false;
907         }
908
909         //Remove the source from the connectors.php file
910         self::getConnectors(true);
911
912         //Update the display_config.php file to remove this source
913         $modules_sources = array();
914         require(CONNECTOR_DISPLAY_CONFIG_FILE);
915         foreach($modules_sources as $module=>$mapping) {
916             foreach($mapping as $id=>$src) {
917                 if($src == $source) {
918                    unset($modules_sources[$module][$id]);
919                 }
920             }
921         }
922
923         //Make the directory for the config file
924         if(!file_exists('custom/modules/Connectors/metadata')) {
925            mkdir_recursive('custom/modules/Connectors/metadata');
926         }
927
928         if(!write_array_to_file('modules_sources', $modules_sources, CONNECTOR_DISPLAY_CONFIG_FILE)) {
929            //Log error and return empty array
930            $GLOBALS['log']->fatal("Cannot write \$modules_sources to " . CONNECTOR_DISPLAY_CONFIG_FILE);
931            return false;
932         }
933
934
935
936         return true;
937     }
938
939     /**
940      * hasWizardSourceEnabledForModule
941      * This is a private method that returns a boolean value indicating whether or not at least one
942      * source is enabled for a given module.  By enabled we mean that the source has the neccessary
943      * configuration properties set as determined by the isRequiredConfigFieldsForButtonSet method.  In
944      * addition, a check is made to ensure that it is a source that has been enabled for the wizard.
945      *
946      * @param String $module String value of module to check
947      * @return boolean $enabled boolean value indicating whether or not module has at least one source enabled
948      */
949     private static function hasWizardSourceEnabledForModule(
950         $module = ''
951         )
952     {
953         if(file_exists(CONNECTOR_DISPLAY_CONFIG_FILE)) {
954            require_once('include/connectors/sources/SourceFactory.php');
955            require(CONNECTOR_DISPLAY_CONFIG_FILE);
956            if(!empty($modules_sources) && !empty($modules_sources[$module])) {
957               foreach($modules_sources[$module] as $id) {
958                    $source = SourceFactory::getSource($id, false);
959                    if(!is_null($source) && $source->isEnabledInWizard() && $source->isRequiredConfigFieldsForButtonSet()) {
960                       return true;
961                    }
962               }
963            }
964            return false;
965         }
966         return false;
967     }
968 }