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