]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/MB/MBPackage.php
Release 6.5.8
[Github/sugarcrm.git] / modules / ModuleBuilder / MB / MBPackage.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  * 
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
16  * details.
17  * 
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  * 
23  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
25  * 
26  * The interactive user interfaces in modified source and object code versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  * 
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the "Powered by
32  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33  * technical reasons, the Appropriate Legal Notices must display the words
34  * "Powered by SugarCRM".
35  ********************************************************************************/
36
37 require_once('modules/ModuleBuilder/MB/MBModule.php');
38
39 class MBPackage{
40     var $name;
41     var $is_uninstallable = true;
42     var $description = '';
43     var $has_images = true;
44     var $modules = array();
45     var $date_modified = '';
46     var $author = '';
47     var $key = '';
48     var $readme='';
49     function MBPackage($name){
50         $this->name = $name;
51         $this->load();
52         
53     }
54     function loadModules($force=false){
55         if(!file_exists(MB_PACKAGE_PATH . '/' . $this->name .'/modules'))return;
56         $d = dir(MB_PACKAGE_PATH . '/' . $this->name .'/modules');
57         while($e = $d->read()){
58             if(substr($e, 0, 1) != '.' && is_dir(MB_PACKAGE_PATH . '/'. $this->name. '/modules/' . $e)){
59                 $this->getModule($e, $force);
60             }
61         }
62     }
63     
64     /**
65      * Loads the translated module titles from the selected language into.
66      * Will override currently loaded string to reflect undeployed label changes.
67      * $app_list_strings
68      * @return 
69      * @param $languge String language identifyer
70      */
71     function loadModuleTitles($languge = '') 
72     {
73         if (empty($language))
74         {
75             $language = $GLOBALS['current_language'];
76         }
77         global $app_list_strings;
78         $packLangFilePath = $this->getPackageDir() . "/language/application/" . $language . ".lang.php";
79         if (file_exists($packLangFilePath))
80         {
81             
82             require($packLangFilePath);
83         }
84     }
85     
86     /**
87      * @param $name
88      * @param bool $force
89      * @return MBModule
90      */
91     function getModule($name, $force=true){
92         if(!$force && !empty($this->modules[$name]))
93             return $this->modules[$name];
94
95         $path = $this->getPackageDir();
96         $this->modules[$name] = new MBModule($name, $path, $this->name, $this->key);
97
98         return $this->modules[$name];
99     }
100
101     /**
102      * Returns an MBModule by the given full name (package key + module name)
103      * if it exists in this package
104      *
105      * @param string $name
106      * @return MBModule
107      */
108     public function getModuleByFullName($name){
109         foreach($this->modules as $mname => $module) {
110             if ($this->key . "_" . $mname == $name)
111                 return $module;
112         }
113     }
114     
115     function deleteModule($name){
116         $this->modules[$name]->delete();
117         unset($this->modules[$name]);
118     }
119     
120 function getManifest($version_specific = false, $for_export = false){
121     //If we are exporting the package, we must ensure a different install key
122     $pre = $for_export ? MB_EXPORTPREPEND : "";
123     $date = TimeDate::getInstance()->nowDb();
124     $time = time();
125     $this->description = to_html($this->description);
126     $is_uninstallable = ($this->is_uninstallable ? true : false);
127     if($GLOBALS['sugar_flavor'] == 'CE') {
128         $flavors = array('CE','PRO','ENT');
129     } else {
130         $flavors = array($GLOBALS['sugar_flavor']);
131     }
132     $version = (!empty($version_specific))?$GLOBALS['sugar_version']:'';
133
134     // Build an array and use var_export to build this file
135     $manifest = array(
136         array('acceptable_sugar_versions' => array($version)),
137         array('acceptable_sugar_flavors' => $flavors),
138         'readme' => $this->readme,
139         'key' => $this->key,
140         'author' => $this->author,
141         'description' => $this->description,
142         'icon' => '',
143         'is_uninstallable' => $is_uninstallable,
144         'name' => $pre.$this->name,
145         'published_date' => $date,
146         'type' => 'module',
147         'version' => $time,
148         'remove_tables' => 'prompt',
149     );
150         
151               
152
153     $header = file_get_contents('modules/ModuleBuilder/MB/header.php');
154     
155     return $header."\n// THIS CONTENT IS GENERATED BY MBPackage.php\n".'$manifest = '.var_export_helper($manifest).";\n\n";
156 /*
157     return  <<<EOQ
158     $header
159     \$manifest = array (
160          'acceptable_sugar_versions' => 
161           array (
162             $version
163           ),
164           'acceptable_sugar_flavors' =>
165           array(
166             $flavor
167           ),
168           'readme'=>'$this->readme',
169           'key'=>'$this->key',
170           'author' => '$this->author',
171           'description' => '$this->description',
172           'icon' => '',
173           'is_uninstallable' => $is_uninstallable,
174           'name' => '$pre$this->name',
175           'published_date' => '$date',
176           'type' => 'module',
177           'version' => '$time',
178           'remove_tables' => 'prompt',
179           );
180 EOQ;
181 */
182 }
183     
184 function buildInstall($path){
185     $installdefs = array ('id' => $this->name,
186         'beans'=>array(),
187         'layoutdefs'=>array(),
188         'relationships'=>array(),
189     );
190     if($this->has_images){
191         $installdefs['image_dir'] = '<basepath>/icons'; 
192     }
193     foreach(array_keys($this->modules) as $module){
194         $this->modules[$module]->build($path);
195         $this->modules[$module]->addInstallDefs($installdefs);
196     }
197     $this->path = $this->getPackageDir(); 
198     if(file_exists($this->path . '/language')){
199         $d= dir($this->path . '/language');
200         while($e = $d->read()){
201             $lang_path = $this->path .'/language/' . $e;
202             if(substr($e, 0, 1) != '.' && is_dir($lang_path)){
203                 $f = dir($lang_path);
204                 while($g = $f->read()){
205                     if(substr($g, 0, 1) != '.' && is_file($lang_path.'/'. $g)){
206                         $lang = substr($g, 0, strpos($g, '.'));
207                         $installdefs['language'][] = array(
208                         'from'=> '<basepath>/SugarModules/language/'.$e . '/'. $g,
209                         'to_module'=> $e,
210                         'language'=> $lang  
211                         );
212                     }
213                 }
214             }
215         }
216             
217         copy_recursive( $this->path . '/language/', $path . '/language/');
218         $icon_path = $path . '/../icons/default/images/';
219         mkdir_recursive($icon_path);
220         copy_recursive($this->path . '/icons/', $icon_path);
221     }
222     return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
223
224 }
225     
226     function getPackageDir(){
227         return MB_PACKAGE_PATH . '/' . $this->name;
228     }
229     
230     function getBuildDir(){
231         return MB_PACKAGE_BUILD . DIRECTORY_SEPARATOR . $this->name;
232     }
233     
234     function getZipDir(){
235         return $this->getPackageDir() . '/zips';
236     }
237     
238     
239     function load(){
240         $path = $this->getPackageDir();
241         if(file_exists($path .'/manifest.php')){
242             require($path . '/manifest.php');
243             if(!empty($manifest)){
244                 $this->date_modified = $manifest['published_date'];
245                 $this->is_uninstallable = $manifest['is_uninstallable'];
246                 $this->author = $manifest['author'];
247                 $this->key = $manifest['key'];
248                 $this->description = $manifest['description'];
249                 if(!empty($manifest['readme']))
250                     $this->readme = $manifest['readme'];
251             }
252         }
253         $this->loadModules(true);
254     }
255
256     function save(){
257         $path = $this->getPackageDir();
258         if(mkdir_recursive($path)){
259             //Save all the modules when we save a package
260             $this->updateModulesMetaData(true);
261             sugar_file_put_contents_atomic($path .'/manifest.php', $this->getManifest());
262         }
263     }
264     
265     function build($export=true, $clean = false){
266         $this->loadModules();
267         require_once('include/utils/zip_utils.php');
268         $package_path = $this->getPackageDir();
269         $path = $this->getBuildDir() . '/SugarModules';
270         if($clean && file_exists($path))rmdir_recursive($path);
271         if(mkdir_recursive($path)){
272             
273             $manifest = $this->getManifest().$this->buildInstall($path);
274             $fp = sugar_fopen($this->getBuildDir() .'/manifest.php', 'w');
275             fwrite($fp, $manifest);
276             fclose($fp);
277             
278         }
279         if(file_exists('modules/ModuleBuilder/MB/LICENSE.txt')){
280             copy('modules/ModuleBuilder/MB/LICENSE.txt', $this->getBuildDir() . '/LICENSE.txt');
281         }else if(file_exists('LICENSE.txt')){
282             copy('LICENSE.txt', $this->getBuildDir() . '/LICENSE.txt');
283         }
284         $package_dir = $this->getPackageDir();
285         $date = date('Y_m_d_His');
286         $zipDir = $this->getZipDir();
287         if(!file_exists($zipDir))mkdir_recursive($zipDir);
288         $cwd = getcwd();
289         chdir($this->getBuildDir());
290         zip_dir('.',$cwd . '/'. $zipDir. '/'. $this->name. $date. '.zip');
291         chdir($cwd);
292         if($export){
293             header('Location:' . $zipDir. '/'. $this->name. $date. '.zip');
294         }
295         return array(
296             'zip'=>$zipDir. '/'. $this->name. $date. '.zip',
297             'manifest'=>$this->getBuildDir(). '/manifest.php',
298             'name'=>$this->name. $date,
299             );
300     }
301     
302     
303     function getNodes(){
304         $this->loadModules();
305         $node = array('name'=>$this->name, 'action'=>'module=ModuleBuilder&action=package&package=' . $this->name, 'children'=>array());
306         foreach(array_keys($this->modules) as $module){
307             $node['children'][] = $this->modules[$module]->getNodes();
308         }
309         return $node;
310     }
311     
312     function populateFromPost(){
313         $this->description = trim($_REQUEST['description']);
314         $this->author = trim($_REQUEST['author']);
315         $this->key = trim($_REQUEST['key']);
316         $this->readme = trim($_REQUEST['readme']);
317     }
318     
319     function rename($new_name){
320         $old= $this->getPackageDir();
321         $this->name = $new_name;
322         $new = $this->getPackageDir();
323         if(file_exists($new)){
324             return false;   
325         }
326         if(rename($old, $new)){
327             return true;
328         }
329             
330         return false;
331     }
332     
333     function updateModulesMetaData($save=false){
334             foreach(array_keys($this->modules) as $module){
335                 $old_name = $this->modules[$module]->key_name;
336                 $this->modules[$module]->key_name = $this->key . '_' . $this->modules[$module]->name;
337                 $this->modules[$module]->renameMetaData($this->modules[$module]->getModuleDir(), $old_name);
338                 $this->modules[$module]->renameLanguageFiles($this->modules[$module]->getModuleDir());
339                 if($save)$this->modules[$module]->save();
340             }
341         
342     }
343     
344     function copy($new_name){
345         $old= $this->getPackageDir();
346         
347         $count = 0;
348         $this->name = $new_name;
349         $new= $this->getPackageDir();
350         while(file_exists($new)){
351             $count++;
352             $this->name = $new_name . $count;
353             $new= $this->getPackageDir();
354         }
355         
356         $new = $this->getPackageDir();
357         if(copy_recursive($old, $new)){
358             $this->updateModulesMetaData();
359             return true;
360         }
361         return false;
362         
363     }
364     
365     function delete(){
366         return rmdir_recursive($this->getPackageDir());
367     }
368     
369     
370         //creation of the installdefs[] array for the manifest when exporting customizations
371     function customBuildInstall($modules, $path, $extensions = array()){
372         $columns=$this->getColumnsName();
373         $installdefs = array ('id' => $this->name, 'relationships' => array());
374         $include_path="$path/SugarModules/include/language";
375         if(file_exists($include_path) && is_dir($include_path)){
376             $dd= dir($include_path);
377             while($gg = $dd->read()){
378                 if(substr($gg, 0, 1) != '.' && is_file($include_path . '/' . $gg)){
379                     $lang = substr($gg, 0, strpos($gg, '.'));
380                     $installdefs['language'][] = array(
381                     'from'=> '<basepath>/SugarModules/include/language/'. $gg,
382                     'to_module'=> 'application',
383                     'language'=>$lang    
384                     );
385                 }
386             }
387         }
388         
389         foreach($modules as $value){
390             $custom_module = $this->getCustomModules($value);
391             foreach($custom_module as $va){
392                 if ($va == 'language'){
393                     $this->getLanguageManifestForModule($value, $installdefs);
394                     $this->getCustomFieldsManifestForModule($value, $installdefs);
395                 }//fi
396                 if($va == 'metadata'){
397                     $this->getCustomMetadataManifestForModule($value, $installdefs);
398                 }//fi
399             }//foreach
400             $relationshipsMetaFiles = $this->getCustomRelationshipsMetaFilesByModuleName($value, true, true,$modules);
401             if($relationshipsMetaFiles)
402             {
403                 foreach ($relationshipsMetaFiles as $file)
404                 {
405                     $installdefs['relationships'][] = array('meta_data' => str_replace('custom', '<basepath>', $file)); 
406                 }
407             }
408         }//foreach
409         if (is_dir($path . DIRECTORY_SEPARATOR . 'Extension'))
410         {
411             $this->getExtensionsManifestForPackage($path, $installdefs);
412         }
413         return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
414     }
415     
416     private function getLanguageManifestForModule($module, &$installdefs)
417     {
418         $lang_path = 'custom' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'language';
419         foreach(scandir($lang_path) as $langFile)
420         {
421                 if(substr($langFile, 0, 1) != '.' && is_file($lang_path . DIRECTORY_SEPARATOR . $langFile)){
422                     $lang = substr($langFile, 0, strpos($langFile, '.'));
423                     $installdefs['language'][] = array(
424                         'from'=> '<basepath>/SugarModules/modules/' . $module . '/language/'. $langFile,
425                         'to_module'=> $module,
426                         'language'=>$lang
427                     );
428                 }
429         }  
430     }
431     
432     private function getCustomFieldsManifestForModule($module, &$installdefs)
433     {
434         $db = DBManagerFactory::getInstance();
435         $result=$db->query("SELECT *  FROM fields_meta_data where custom_module='$module'");
436         while($row = $db->fetchByAssoc($result)){
437                 $name = $row['id'];
438                 foreach($row as $col=>$res){
439                         switch ($col) {
440                                 case 'custom_module':
441                                         $installdefs['custom_fields'][$name]['module'] = $res;
442                                         break;
443                                 case 'required':
444                                         $installdefs['custom_fields'][$name]['require_option'] = $res;
445                                         break;
446                                 case 'vname':
447                                         $installdefs['custom_fields'][$name]['label'] = $res;
448                                         break;
449                                 case 'required':
450                                         $installdefs['custom_fields'][$name]['require_option'] = $res;
451                                         break;
452                                 case 'massupdate':
453                                         $installdefs['custom_fields'][$name]['mass_update'] = $res;
454                                         break;
455                                 case 'comments':
456                                         $installdefs['custom_fields'][$name]['comments'] = $res;
457                                         break;
458                                 case 'help':
459                                         $installdefs['custom_fields'][$name]['help'] = $res;
460                                         break;
461                                 case 'len':
462                                         $installdefs['custom_fields'][$name]['max_size'] = $res;
463                                         break;
464                                 default:
465                                         $installdefs['custom_fields'][$name][$col] = $res;
466                         }//switch
467                 }//foreach
468         }//while
469     }
470     
471     private function getCustomMetadataManifestForModule($module, &$installdefs)
472     {
473         $meta_path = 'custom/modules/' . $module . '/metadata';
474         foreach(scandir($meta_path) as $meta_file)
475         {
476                 if(substr($meta_file, 0, 1) != '.' && is_file($meta_path . '/' . $meta_file)){
477                         if($meta_file == 'listviewdefs.php'){
478                                 $installdefs['copy'][] = array(
479                                 'from'=> '<basepath>/SugarModules/modules/'. $module . '/metadata/'. $meta_file,
480                                 'to'=> 'custom/modules/'. $module . '/metadata/' . $meta_file,   
481                                 );
482                         }
483                         else{
484                                 $installdefs['copy'][] = array(
485                                 'from'=> '<basepath>/SugarModules/modules/'. $module . '/metadata/'. $meta_file,
486                                 'to'=> 'custom/modules/'. $module . '/metadata/' . $meta_file,   
487                                 );
488                                 $installdefs['copy'][] = array(
489                                 'from'=> '<basepath>/SugarModules/modules/'. $module . '/metadata/'. $meta_file,
490                                 'to'=> 'custom/working/modules/'. $module . '/metadata/' . $meta_file,   
491                                 );
492                         }
493                 }
494         }
495     }
496
497     /**
498      * @todo private changed protected for testing purposes.
499      * 
500      * @param string $path
501      * @param array $installdefs link
502      */
503     protected function getExtensionsManifestForPackage($path, &$installdefs)
504     {
505         if(empty($installdefs['copy']))
506         {
507             $installdefs['copy']= array();
508         }
509         $generalPath = DIRECTORY_SEPARATOR . 'Extension' . DIRECTORY_SEPARATOR . 'modules';
510
511         //do not process if path is not a valid directory, or recursiveIterator will break.
512         if(!is_dir($path.$generalPath))
513         {
514             return;
515         }
516         
517         $recursiveIterator = new RecursiveIteratorIterator(
518                 new RecursiveDirectoryIterator($path . $generalPath),
519                 RecursiveIteratorIterator::SELF_FIRST
520         );
521
522         /* @var $fInfo SplFileInfo */
523         foreach (new RegexIterator($recursiveIterator, "/\.php$/i") as $fInfo)
524         {
525
526             $newPath = substr($fInfo->getPathname(), strrpos($fInfo->getPathname(), $generalPath));
527
528             $installdefs['copy'][] = array(
529                 'from' => '<basepath>' . $newPath,
530                 'to' => 'custom' . $newPath
531             );
532         }
533     }
534
535     //return an array which contain the name of fields_meta_data table's columns 
536     function getColumnsName(){
537          
538         $meta = new FieldsMetaData();
539         $arr = array(); 
540          foreach($meta->getFieldDefinitions() as $key=>$value) {
541             $arr[] = $key;
542         }
543         return $arr;
544     }
545
546
547     //creation of the custom fields ZIP file (use getmanifest() and customBuildInstall() )  
548     function exportCustom($modules, $export=true, $clean = true){
549         
550         $relationshipFiles = array();
551
552         $path = $this->getBuildDir();
553         if ($clean && file_exists($path)) {
554             rmdir_recursive($path);
555         }
556         //Copy the custom files to the build dir
557         foreach ($modules as $module) {
558             $pathmod = "$path/SugarModules/modules/$module";
559             if (mkdir_recursive($pathmod)) {
560                 if (file_exists("custom/modules/$module")) {
561                     copy_recursive("custom/modules/$module", "$pathmod");
562                     //Don't include cached extension files
563                     if (is_dir("$pathmod/Ext"))
564                         rmdir_recursive("$pathmod/Ext");
565                 }
566                 //Convert modstring files to extension compatible arrays
567                 $this->convertLangFilesToExtensions("$pathmod/language");
568             }
569
570             $extensions = $this->getExtensionsList($module, $modules);
571             $relMetaFiles = $this->getCustomRelationshipsMetaFilesByModuleName($module, true,false,$modules);
572             $extensions = array_merge($extensions, $relMetaFiles);
573
574             foreach ($extensions as $file) {
575                 $fileInfo = new SplFileInfo($file);
576                 $trimmedPath = ltrim($fileInfo->getPath(), 'custom');
577
578                 sugar_mkdir($path . $trimmedPath, NULL, true);
579                 copy($file, $path . $trimmedPath . '/' . $fileInfo->getFilename());
580             }
581         }
582
583         $this->copyCustomDropdownValuesForModules($modules,$path);
584         if(file_exists($path)){
585             $manifest = $this->getManifest(true).$this->customBuildInstall($modules,$path);
586             sugar_file_put_contents($path .'/manifest.php', $manifest);
587         }
588         if(file_exists('modules/ModuleBuilder/MB/LICENSE.txt')){
589             copy('modules/ModuleBuilder/MB/LICENSE.txt', $path . '/LICENSE.txt');
590         }
591         else if(file_exists('LICENSE.txt')){
592             copy('LICENSE.txt', $path . '/LICENSE.txt');
593         }
594         require_once('include/utils/zip_utils.php');
595         $date = date('Y_m_d_His');
596         $zipDir = $this->getZipDir();
597         if(!file_exists($zipDir))mkdir_recursive($zipDir);
598         $cwd = getcwd();
599         chdir($this->getBuildDir());
600         zip_dir('.',$cwd . '/'. $zipDir. '/'. $this->name. $date. '.zip');
601         chdir($cwd);
602         if($clean && file_exists($this->getBuildDir()))rmdir_recursive($this->getBuildDir());
603         if($export){
604             header('Location:' . $zipDir. '/'. $this->name. $date. '.zip');
605         }
606         return $zipDir. '/'. $this->name. $date. '.zip';
607     }
608     
609     private function convertLangFilesToExtensions($langDir)
610     {
611         if (is_dir($langDir))
612         {
613             foreach(scandir($langDir) as $langFile)
614             {
615                 $mod_strings = array();
616                 if (strcasecmp(substr($langFile, -4), ".php") != 0)
617                     continue;
618                 include("$langDir/$langFile");
619                 $out = "<?php \n // created: " . date('Y-m-d H:i:s') . "\n";
620                 foreach($mod_strings as $lbl_key => $lbl_val ) 
621                 {
622                     $out .= override_value_to_string("mod_strings", $lbl_key, $lbl_val) . "\n";
623                 }
624                 $out .= "\n?>\n";
625                 sugar_file_put_contents("$langDir/$langFile", $out);
626             }
627         }
628     }
629     private function copyCustomDropdownValuesForModules($modules, $path)
630     {
631         if(file_exists("custom/include/language")){
632             if(mkdir_recursive("$path/SugarModules/include")){
633                 global $app_list_strings;
634                 $backStrings = $app_list_strings;
635                 foreach(scandir("custom/include/language") as $langFile)
636                 {
637                     $app_list_strings = array();
638                     if (strcasecmp(substr($langFile, -4), ".php") != 0)
639                        continue;
640                     include("custom/include/language/$langFile");
641                     $out = "<?php \n";
642                     $lang = substr($langFile, 0, -9);
643                     $options = $this->getCustomDropDownStringsForModules($modules, $app_list_strings); 
644                     foreach($options as $name => $arr) {
645                         $out .= override_value_to_string('app_list_strings', $name, $arr);
646                     }
647                     mkdir_recursive("$path/SugarModules/include/language/");
648                     sugar_file_put_contents("$path/SugarModules/include/language/$lang.$this->name.php", $out);
649                 }
650                 $app_list_strings = $backStrings;
651             }
652         }
653     }
654     
655     function getCustomDropDownStringsForModules($modules, $list_strings) {
656         global $beanList, $beanFiles;
657         $options = array();
658         foreach($modules as $module)
659         {
660             if (!empty($beanList[$module]))
661             {
662                 require_once($beanFiles[$beanList[$module]]);
663                 $bean = new $beanList[$module]();
664                 foreach($bean->field_defs as $field => $def) 
665                 {
666                     if (isset($def['options']) && isset($list_strings[$def['options']]))
667                     {
668                         $options[$def['options']] = $list_strings[$def['options']];
669                     }
670                 }
671             }
672         }
673         return $options;
674     }
675
676
677
678     //if $module=false : return an array with custom module and there customizations.
679     //if $module=!false : return an array with the directories of custom/module/$module.
680     function getCustomModules($module=false){
681         global $mod_strings;
682         $path='custom/modules/';
683                 $extPath = 'custom/Extension/modules/';
684         if(!file_exists($path) || !is_dir($path)){
685             return array($mod_strings['LBL_EC_NOCUSTOM'] => "");
686         }
687         else{
688             if ($module != false ){
689                 $path=$path . $module . '/';
690             }
691             $scanlisting = scandir($path);
692             $dirlisting = array();
693             foreach ($scanlisting as $value){
694                 if(is_dir($path . $value) == true && $value != '.' && $value != '..') {
695                     $dirlisting[] = $value;
696                 }
697             }
698                         if(empty($dirlisting)){
699                 return array($mod_strings['LBL_EC_NOCUSTOM'] => "");
700             }
701             if ($module == false ){
702                 foreach ($dirlisting as $value){
703                         if(!file_exists('modules/' . $value . '/metadata/studio.php'))
704                                 continue;
705                     $custommodules[$value]=$this->getCustomModules($value);
706                     foreach ($custommodules[$value] as $va){
707                         switch ($va) {
708                         case 'language':
709                                 $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMFIELD'];
710                             break;
711                         case 'metadata':
712                             $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMLAYOUT'];
713                             break;
714                         case 'Ext':
715                             
716                                                         $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMFIELD'];
717                             break;
718                         case '':
719                             $return[$value . " " . $mod_strings['LBL_EC_EMPTYCUSTOM']] = "";
720                             break;
721                         default:
722                             $return[$value][$va] = $mod_strings['LBL_UNDEFINED'];
723                         }
724                     }
725                 }
726                 return $return;
727             }
728             else{
729                 return $dirlisting;
730             }
731         }
732     }
733         
734     /**
735      * Get _custom_ extensions for module.
736      * Default path - custom/Extension/modules/$module/Ext.
737      * 
738      * @param array $module Name.
739      * @param mixed $includeRelationships ARRAY - relationships files between $module and names in array;
740      * TRUE - with all relationships files; 
741      * @return array Paths.
742      */
743     protected function getExtensionsList($module, $includeRelationships = true)
744     {
745         if (BeanFactory::getBeanName($module) === false)
746         {
747             return array();
748         }
749         
750         $result = array();
751         $includeMask = false;
752         $extPath = sprintf('custom%1$sExtension%1$smodules%1$s' . $module . '%1$sExt', DIRECTORY_SEPARATOR);
753
754         //do not process if path is not a valid directory, or recursiveIterator will break.
755         if(!is_dir($extPath))
756         {
757             return $result;
758         }
759
760
761         if (is_array($includeRelationships))
762         {
763             $includeMask = array();
764             $customRels = $this->getCustomRelationshipsByModuleName($module);
765
766             $includeRelationships[] = $module;
767
768             foreach ($customRels as $k => $v)
769             {
770                 if (
771                     in_array($v->getLhsModule(), $includeRelationships) &&
772                     in_array($v->getRhsModule(), $includeRelationships)
773                 )
774                 {
775                     $includeMask[] = $k;
776                 }
777             }
778         }
779
780         $recursiveIterator = new RecursiveIteratorIterator(
781                 new RecursiveDirectoryIterator($extPath),
782                 RecursiveIteratorIterator::SELF_FIRST
783         );
784
785         /* @var $fileInfo SplFileInfo */
786         foreach ($recursiveIterator as $fileInfo)
787         {
788
789             if ($fileInfo->isFile() && !in_array($fileInfo->getPathname(), $result))
790             {
791                 //get the filename in lowercase for easier comparison
792                 $fn = $fileInfo->getFilename();
793                 if(!empty($fn))
794                 {
795                     $fn = strtolower($fn);
796                 }
797
798                 if ($this->filterExportedRelationshipFile($fn,$module,$includeRelationships) ){
799                     $result[] = $fileInfo->getPathname();
800                 }
801             }
802         }
803
804         return $result;
805     }
806
807     /**
808      * Processes the name of the file and compares against the passed in module names to
809      * evaluate whether the file should be included for export.  Returns true or false
810      *
811      * @param $fn (file name that is being evaluated)
812      * @param $module (name of current module being evaluated)
813      * @param $includeRelationship (list of related modules that are also being exported, to be used as filters)
814      * @return boolean true or false
815      */
816     function filterExportedRelationshipFile($fn,$module,$includeRelationships)
817     {
818         $shouldExport = false;
819         if(empty($fn) || !is_array($includeRelationships) || empty($includeRelationships))
820         {
821             return $shouldExport;
822         }
823
824         //if file name does not contain the current module name then it is not a relationship file,
825         //or if the module has the current module name twice seperated with an underscore, then this is a relationship within itself
826         //in both cases set the shouldExport flag to true
827         $lc_mod = strtolower($module);
828         $fn = strtolower($fn);
829         if ((strpos($fn, $lc_mod) === false) || (strpos($fn, $lc_mod.'_'.$lc_mod) !== false))
830         {
831             $shouldExport = true;
832         }else{
833
834             //grab only rels that have both modules in the export list
835             foreach ($includeRelationships as $relatedModule)
836             {
837                 //skip if the related module is empty
838                 if(empty($relatedModule))
839                 {
840                     continue;
841                 }
842
843                 //if the filename also has the related module name, then add the relationship file
844                 //strip the current module,as we have already checked for existance of module name and dont want any false positives
845                 $fn = str_replace($lc_mod,'',$fn);
846                 if (strpos($fn, strtolower($relatedModule)) !== false)
847                 {
848                     //both modules exist in the filename lets include in the results array
849                     $shouldExport = true;
850                     break;
851                 }
852             }
853         }
854
855         return $shouldExport;
856     }
857
858     /**
859      * Returns a set of field defs for fields that will exist when this package is deployed
860      * based on the relationships in all of its modules.
861      * 
862      * @param $moduleName (module must be from whithin this package)
863      * @return array Field defs
864      */
865     function getRelationshipsForModule($moduleName) {
866         $ret = array();
867         if (isset($this->modules[$moduleName])) {
868                 $keyName = $this->modules[$moduleName]->key_name;
869                 foreach($this->modules as $mName => $module) {
870                         $rels = $module->getRelationships();
871                         $relList = $rels->getRelationshipList();
872                         foreach($relList as $rName ) {
873                             $rel = $rels->get ( $rName ) ;
874                              if ($rel->lhs_module == $keyName || $rel->rhs_module == $keyName) {
875                         $ret[$rName] =  $rel;
876                              }
877                         }
878                 }
879         }
880         return $ret; 
881     }
882     
883
884     
885     function exportProjectInstall($package, $for_export){
886         $pre = $for_export ? MB_EXPORTPREPEND : "";
887         $installdefs = array ('id' => $pre . $this->name);
888         $installdefs['copy'][] = array(
889             'from'=> '<basepath>/' . $this->name,
890             'to'=> 'custom/modulebuilder/packages/'. $this->name,   
891         );
892         return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
893
894     }
895     
896     
897     
898     function exportProject($package, $export=true, $clean = true){
899         $tmppath="custom/modulebuilder/projectTMP/";
900         if(file_exists($this->getPackageDir())){
901             if(mkdir_recursive($tmppath)){
902                 copy_recursive($this->getPackageDir(), $tmppath ."/". $this->name);
903                 $manifest = $this->getManifest(true, $export).$this->exportProjectInstall($package, $export);
904                 $fp = sugar_fopen($tmppath .'/manifest.php', 'w');
905                 fwrite($fp, $manifest);
906                 fclose($fp);
907                 if(file_exists('modules/ModuleBuilder/MB/LICENSE.txt')){
908                     copy('modules/ModuleBuilder/MB/LICENSE.txt', $tmppath . '/LICENSE.txt');
909                 }
910                 else if(file_exists('LICENSE.txt')){
911                     copy('LICENSE.txt', $tmppath . '/LICENSE.txt');
912                 }
913                 $readme_contents = $this->readme;
914                 $readmefp = sugar_fopen($tmppath.'/README.txt','w');
915                 fwrite($readmefp, $readme_contents);
916                 fclose($readmefp);
917             }
918         }
919         require_once('include/utils/zip_utils.php');
920         $date = date('Y_m_d_His');
921         $zipDir = "custom/modulebuilder/packages/ExportProjectZips";
922         if(!file_exists($zipDir))mkdir_recursive($zipDir);
923         $cwd = getcwd();
924         chdir($tmppath);
925         zip_dir('.',$cwd . '/'. $zipDir. '/project_'. $this->name. $date. '.zip');
926         chdir($cwd);
927         if($clean && file_exists($tmppath))rmdir_recursive($tmppath);
928         if($export){
929             header('Location:' . $zipDir. '/project_'. $this->name. $date. '.zip');
930         }
931         return $zipDir. '/project_'. $this->name. $date. '.zip';
932     }
933     
934     /**
935      * This returns an UNFILTERED list of custom relationships by module name.  You will have to filter the relationships
936      * by the modules being exported after calling this method
937      * @param string $moduleName
938      * @param bool $lhs Return relationships where $moduleName - left module in join.
939      * @return mixed Array or false when module name is wrong.
940      */
941     protected function getCustomRelationshipsByModuleName($moduleName, $lhs = false)
942     {
943         if (BeanFactory::getBeanName($moduleName) === false)
944         {
945             return false;
946         }
947         
948         $result = array();
949         $relation = null;
950         $module = new StudioModule($moduleName);
951
952         /* @var $rel DeployedRelationships */
953         $rel = $module->getRelationships();
954
955         $relList = $rel->getRelationshipList();
956
957         foreach ($relList as $relationshipName)
958         {
959             $relation = $rel->get($relationshipName);
960
961             if ($relation->getFromStudio())
962             {
963                 if ($lhs && $relation->getLhsModule() != $moduleName)
964                 {
965                     continue;
966                 }
967                 $result[$relationshipName] = $relation;
968             }
969         }
970
971         return $result;
972     }
973     
974     /**
975      * @param string $moduleName
976      * @param bool $lhs Return relationships where $moduleName - left module in join.
977      * @param bool $metadataOnly Return only relationships metadata file.
978      * @param $includeRelationship (list of related modules that are also being exported)
979      * @return array (array of relationships filtered to only include relationships to modules being exported)
980      */
981     protected function getCustomRelationshipsMetaFilesByModuleName($moduleName, $lhs = false, $metadataOnly = false,$exportedModulesFilter=array())
982     {
983         
984         $path = $metadataOnly ?
985                 'custom' . DIRECTORY_SEPARATOR . 'metadata' . DIRECTORY_SEPARATOR :
986                 'custom' . DIRECTORY_SEPARATOR;
987         $result = array();
988
989
990         //do not process if path is not a valid directory, or recursiveIterator will break.
991         if(!is_dir($path))
992         {
993             return $result;
994         }
995
996         $relationships = $this->getCustomRelationshipsByModuleName($moduleName, $lhs);
997         
998         if (!$relationships)
999         {
1000             return array();
1001         }
1002         
1003         $recursiveIterator = new RecursiveIteratorIterator(
1004                 new RecursiveDirectoryIterator($path),
1005                 RecursiveIteratorIterator::SELF_FIRST
1006         );
1007
1008         /**
1009          * @var $fileInfo SplFileInfo 
1010          */
1011         foreach ($recursiveIterator as $fileInfo)
1012         {
1013             if ($fileInfo->isFile() && !in_array($fileInfo->getPathname(), $result))
1014             {
1015                 foreach ($relationships as $k => $v)
1016                 {
1017
1018                     if (strpos($fileInfo->getFilename(), $k) !== false)
1019                     {   //filter by modules being exported
1020                         if ($this->filterExportedRelationshipFile($fileInfo->getFilename(),$moduleName,$exportedModulesFilter) ){
1021                             $result[] = $fileInfo->getPathname();
1022                             break;
1023                         }
1024                     }
1025                 }
1026             }
1027         }
1028
1029         return $result;
1030     }
1031
1032     public function deleteBuild()
1033     {
1034         return rmdir_recursive($this->getBuildDir());
1035     }
1036
1037 }
1038
1039 ?>