2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
37 require_once('modules/ModuleBuilder/MB/MBModule.php');
41 var $is_uninstallable = true;
42 var $description = '';
43 var $has_images = true;
44 var $modules = array();
45 var $date_modified = '';
49 function MBPackage($name){
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);
65 * Loads the translated module titles from the selected language into.
66 * Will override currently loaded string to reflect undeployed label changes.
69 * @param $languge String language identifyer
71 function loadModuleTitles($languge = '')
75 $language = $GLOBALS['current_language'];
77 global $app_list_strings;
78 $packLangFilePath = $this->getPackageDir() . "/language/application/" . $language . ".lang.php";
79 if (file_exists($packLangFilePath))
82 require($packLangFilePath);
91 function getModule($name, $force=true){
92 if(!$force && !empty($this->modules[$name]))
93 return $this->modules[$name];
95 $path = $this->getPackageDir();
96 $this->modules[$name] = new MBModule($name, $path, $this->name, $this->key);
98 return $this->modules[$name];
102 * Returns an MBModule by the given full name (package key + module name)
103 * if it exists in this package
105 * @param string $name
108 public function getModuleByFullName($name){
109 foreach($this->modules as $mname => $module) {
110 if ($this->key . "_" . $mname == $name)
115 function deleteModule($name){
116 $this->modules[$name]->delete();
117 unset($this->modules[$name]);
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();
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');
130 $flavors = array($GLOBALS['sugar_flavor']);
132 $version = (!empty($version_specific))?$GLOBALS['sugar_version']:'';
134 // Build an array and use var_export to build this file
136 array('acceptable_sugar_versions' => array($version)),
137 array('acceptable_sugar_flavors' => $flavors),
138 'readme' => $this->readme,
140 'author' => $this->author,
141 'description' => $this->description,
143 'is_uninstallable' => $is_uninstallable,
144 'name' => $pre.$this->name,
145 'published_date' => $date,
148 'remove_tables' => 'prompt',
153 $header = file_get_contents('modules/ModuleBuilder/MB/header.php');
155 return $header."\n// THIS CONTENT IS GENERATED BY MBPackage.php\n".'$manifest = '.var_export_helper($manifest).";\n\n";
160 'acceptable_sugar_versions' =>
164 'acceptable_sugar_flavors' =>
168 'readme'=>'$this->readme',
170 'author' => '$this->author',
171 'description' => '$this->description',
173 'is_uninstallable' => $is_uninstallable,
174 'name' => '$pre$this->name',
175 'published_date' => '$date',
177 'version' => '$time',
178 'remove_tables' => 'prompt',
184 function buildInstall($path){
185 $installdefs = array ('id' => $this->name,
187 'layoutdefs'=>array(),
188 'relationships'=>array(),
190 if($this->has_images){
191 $installdefs['image_dir'] = '<basepath>/icons';
193 foreach(array_keys($this->modules) as $module){
194 $this->modules[$module]->build($path);
195 $this->modules[$module]->addInstallDefs($installdefs);
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,
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);
222 return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
226 function getPackageDir(){
227 return MB_PACKAGE_PATH . '/' . $this->name;
230 function getBuildDir(){
231 return MB_PACKAGE_BUILD . DIRECTORY_SEPARATOR . $this->name;
234 function getZipDir(){
235 return $this->getPackageDir() . '/zips';
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'];
253 $this->loadModules(true);
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());
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)){
273 $manifest = $this->getManifest().$this->buildInstall($path);
274 $fp = sugar_fopen($this->getBuildDir() .'/manifest.php', 'w');
275 fwrite($fp, $manifest);
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');
284 $package_dir = $this->getPackageDir();
285 $date = date('Y_m_d_His');
286 $zipDir = $this->getZipDir();
287 if(!file_exists($zipDir))mkdir_recursive($zipDir);
289 chdir($this->getBuildDir());
290 zip_dir('.',$cwd . '/'. $zipDir. '/'. $this->name. $date. '.zip');
293 header('Location:' . $zipDir. '/'. $this->name. $date. '.zip');
296 'zip'=>$zipDir. '/'. $this->name. $date. '.zip',
297 'manifest'=>$this->getBuildDir(). '/manifest.php',
298 'name'=>$this->name. $date,
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();
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']);
319 function rename($new_name){
320 $old= $this->getPackageDir();
321 $this->name = $new_name;
322 $new = $this->getPackageDir();
323 if(file_exists($new)){
326 if(rename($old, $new)){
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();
344 function copy($new_name){
345 $old= $this->getPackageDir();
348 $this->name = $new_name;
349 $new= $this->getPackageDir();
350 while(file_exists($new)){
352 $this->name = $new_name . $count;
353 $new= $this->getPackageDir();
356 $new = $this->getPackageDir();
357 if(copy_recursive($old, $new)){
358 $this->updateModulesMetaData();
366 return rmdir_recursive($this->getPackageDir());
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',
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);
396 if($va == 'metadata'){
397 $this->getCustomMetadataManifestForModule($value, $installdefs);
400 $relationshipsMetaFiles = $this->getCustomRelationshipsMetaFilesByModuleName($value, true, true,$modules);
401 if($relationshipsMetaFiles)
403 foreach ($relationshipsMetaFiles as $file)
405 $installdefs['relationships'][] = array('meta_data' => str_replace('custom', '<basepath>', $file));
409 if (is_dir($path . DIRECTORY_SEPARATOR . 'Extension'))
411 $this->getExtensionsManifestForPackage($path, $installdefs);
413 return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
416 private function getLanguageManifestForModule($module, &$installdefs)
418 $lang_path = 'custom' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'language';
419 foreach(scandir($lang_path) as $langFile)
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,
432 private function getCustomFieldsManifestForModule($module, &$installdefs)
434 $db = DBManagerFactory::getInstance();
435 $result=$db->query("SELECT * FROM fields_meta_data where custom_module='$module'");
436 while($row = $db->fetchByAssoc($result)){
438 foreach($row as $col=>$res){
440 case 'custom_module':
441 $installdefs['custom_fields'][$name]['module'] = $res;
444 $installdefs['custom_fields'][$name]['require_option'] = $res;
447 $installdefs['custom_fields'][$name]['label'] = $res;
450 $installdefs['custom_fields'][$name]['require_option'] = $res;
453 $installdefs['custom_fields'][$name]['mass_update'] = $res;
456 $installdefs['custom_fields'][$name]['comments'] = $res;
459 $installdefs['custom_fields'][$name]['help'] = $res;
462 $installdefs['custom_fields'][$name]['max_size'] = $res;
465 $installdefs['custom_fields'][$name][$col] = $res;
471 private function getCustomMetadataManifestForModule($module, &$installdefs)
473 $meta_path = 'custom/modules/' . $module . '/metadata';
474 foreach(scandir($meta_path) as $meta_file)
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,
484 $installdefs['copy'][] = array(
485 'from'=> '<basepath>/SugarModules/modules/'. $module . '/metadata/'. $meta_file,
486 'to'=> 'custom/modules/'. $module . '/metadata/' . $meta_file,
488 $installdefs['copy'][] = array(
489 'from'=> '<basepath>/SugarModules/modules/'. $module . '/metadata/'. $meta_file,
490 'to'=> 'custom/working/modules/'. $module . '/metadata/' . $meta_file,
498 * @todo private changed protected for testing purposes.
500 * @param string $path
501 * @param array $installdefs link
503 protected function getExtensionsManifestForPackage($path, &$installdefs)
505 if(empty($installdefs['copy']))
507 $installdefs['copy']= array();
509 $generalPath = DIRECTORY_SEPARATOR . 'Extension' . DIRECTORY_SEPARATOR . 'modules';
511 //do not process if path is not a valid directory, or recursiveIterator will break.
512 if(!is_dir($path.$generalPath))
517 $recursiveIterator = new RecursiveIteratorIterator(
518 new RecursiveDirectoryIterator($path . $generalPath),
519 RecursiveIteratorIterator::SELF_FIRST
522 /* @var $fInfo SplFileInfo */
523 foreach (new RegexIterator($recursiveIterator, "/\.php$/i") as $fInfo)
526 $newPath = substr($fInfo->getPathname(), strrpos($fInfo->getPathname(), $generalPath));
528 $installdefs['copy'][] = array(
529 'from' => '<basepath>' . $newPath,
530 'to' => 'custom' . $newPath
535 //return an array which contain the name of fields_meta_data table's columns
536 function getColumnsName(){
538 $meta = new FieldsMetaData();
540 foreach($meta->getFieldDefinitions() as $key=>$value) {
547 //creation of the custom fields ZIP file (use getmanifest() and customBuildInstall() )
548 function exportCustom($modules, $export=true, $clean = true){
550 $relationshipFiles = array();
552 $path = $this->getBuildDir();
553 if ($clean && file_exists($path)) {
554 rmdir_recursive($path);
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");
566 //Convert modstring files to extension compatible arrays
567 $this->convertLangFilesToExtensions("$pathmod/language");
570 $extensions = $this->getExtensionsList($module, $modules);
571 $relMetaFiles = $this->getCustomRelationshipsMetaFilesByModuleName($module, true,false,$modules);
572 $extensions = array_merge($extensions, $relMetaFiles);
574 foreach ($extensions as $file) {
575 $fileInfo = new SplFileInfo($file);
576 $trimmedPath = ltrim($fileInfo->getPath(), 'custom');
578 sugar_mkdir($path . $trimmedPath, NULL, true);
579 copy($file, $path . $trimmedPath . '/' . $fileInfo->getFilename());
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);
588 if(file_exists('modules/ModuleBuilder/MB/LICENSE.txt')){
589 copy('modules/ModuleBuilder/MB/LICENSE.txt', $path . '/LICENSE.txt');
591 else if(file_exists('LICENSE.txt')){
592 copy('LICENSE.txt', $path . '/LICENSE.txt');
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);
599 chdir($this->getBuildDir());
600 zip_dir('.',$cwd . '/'. $zipDir. '/'. $this->name. $date. '.zip');
602 if($clean && file_exists($this->getBuildDir()))rmdir_recursive($this->getBuildDir());
604 header('Location:' . $zipDir. '/'. $this->name. $date. '.zip');
606 return $zipDir. '/'. $this->name. $date. '.zip';
609 private function convertLangFilesToExtensions($langDir)
611 if (is_dir($langDir))
613 foreach(scandir($langDir) as $langFile)
615 $mod_strings = array();
616 if (strcasecmp(substr($langFile, -4), ".php") != 0)
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 )
622 $out .= override_value_to_string("mod_strings", $lbl_key, $lbl_val) . "\n";
625 sugar_file_put_contents("$langDir/$langFile", $out);
629 private function copyCustomDropdownValuesForModules($modules, $path)
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)
637 $app_list_strings = array();
638 if (strcasecmp(substr($langFile, -4), ".php") != 0)
640 include("custom/include/language/$langFile");
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);
647 mkdir_recursive("$path/SugarModules/include/language/");
648 sugar_file_put_contents("$path/SugarModules/include/language/$lang.$this->name.php", $out);
650 $app_list_strings = $backStrings;
655 function getCustomDropDownStringsForModules($modules, $list_strings) {
656 global $beanList, $beanFiles;
658 foreach($modules as $module)
660 if (!empty($beanList[$module]))
662 require_once($beanFiles[$beanList[$module]]);
663 $bean = new $beanList[$module]();
664 foreach($bean->field_defs as $field => $def)
666 if (isset($def['options']) && isset($list_strings[$def['options']]))
668 $options[$def['options']] = $list_strings[$def['options']];
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){
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'] => "");
688 if ($module != false ){
689 $path=$path . $module . '/';
691 $scanlisting = scandir($path);
692 $dirlisting = array();
693 foreach ($scanlisting as $value){
694 if(is_dir($path . $value) == true && $value != '.' && $value != '..') {
695 $dirlisting[] = $value;
698 if(empty($dirlisting)){
699 return array($mod_strings['LBL_EC_NOCUSTOM'] => "");
701 if ($module == false ){
702 foreach ($dirlisting as $value){
703 if(!file_exists('modules/' . $value . '/metadata/studio.php'))
705 $custommodules[$value]=$this->getCustomModules($value);
706 foreach ($custommodules[$value] as $va){
709 $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMFIELD'];
712 $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMLAYOUT'];
716 $return[$value][$va] = $mod_strings['LBL_EC_CUSTOMFIELD'];
719 $return[$value . " " . $mod_strings['LBL_EC_EMPTYCUSTOM']] = "";
722 $return[$value][$va] = $mod_strings['LBL_UNDEFINED'];
735 * Get _custom_ extensions for module.
736 * Default path - custom/Extension/modules/$module/Ext.
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.
743 protected function getExtensionsList($module, $includeRelationships = true)
745 if (BeanFactory::getBeanName($module) === false)
751 $includeMask = false;
752 $extPath = sprintf('custom%1$sExtension%1$smodules%1$s' . $module . '%1$sExt', DIRECTORY_SEPARATOR);
754 //do not process if path is not a valid directory, or recursiveIterator will break.
755 if(!is_dir($extPath))
761 if (is_array($includeRelationships))
763 $includeMask = array();
764 $customRels = $this->getCustomRelationshipsByModuleName($module);
766 $includeRelationships[] = $module;
768 foreach ($customRels as $k => $v)
771 in_array($v->getLhsModule(), $includeRelationships) &&
772 in_array($v->getRhsModule(), $includeRelationships)
780 $recursiveIterator = new RecursiveIteratorIterator(
781 new RecursiveDirectoryIterator($extPath),
782 RecursiveIteratorIterator::SELF_FIRST
785 /* @var $fileInfo SplFileInfo */
786 foreach ($recursiveIterator as $fileInfo)
789 if ($fileInfo->isFile() && !in_array($fileInfo->getPathname(), $result))
791 //get the filename in lowercase for easier comparison
792 $fn = $fileInfo->getFilename();
795 $fn = strtolower($fn);
798 if ($this->filterExportedRelationshipFile($fn,$module,$includeRelationships) ){
799 $result[] = $fileInfo->getPathname();
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
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
816 function filterExportedRelationshipFile($fn,$module,$includeRelationships)
818 $shouldExport = false;
819 if(empty($fn) || !is_array($includeRelationships) || empty($includeRelationships))
821 return $shouldExport;
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))
831 $shouldExport = true;
834 //grab only rels that have both modules in the export list
835 foreach ($includeRelationships as $relatedModule)
837 //skip if the related module is empty
838 if(empty($relatedModule))
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)
848 //both modules exist in the filename lets include in the results array
849 $shouldExport = true;
855 return $shouldExport;
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.
862 * @param $moduleName (module must be from whithin this package)
863 * @return array Field defs
865 function getRelationshipsForModule($moduleName) {
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) {
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,
892 return "\n".'$installdefs = ' . var_export_helper($installdefs). ';';
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);
907 if(file_exists('modules/ModuleBuilder/MB/LICENSE.txt')){
908 copy('modules/ModuleBuilder/MB/LICENSE.txt', $tmppath . '/LICENSE.txt');
910 else if(file_exists('LICENSE.txt')){
911 copy('LICENSE.txt', $tmppath . '/LICENSE.txt');
913 $readme_contents = $this->readme;
914 $readmefp = sugar_fopen($tmppath.'/README.txt','w');
915 fwrite($readmefp, $readme_contents);
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);
925 zip_dir('.',$cwd . '/'. $zipDir. '/project_'. $this->name. $date. '.zip');
927 if($clean && file_exists($tmppath))rmdir_recursive($tmppath);
929 header('Location:' . $zipDir. '/project_'. $this->name. $date. '.zip');
931 return $zipDir. '/project_'. $this->name. $date. '.zip';
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.
941 protected function getCustomRelationshipsByModuleName($moduleName, $lhs = false)
943 if (BeanFactory::getBeanName($moduleName) === false)
950 $module = new StudioModule($moduleName);
952 /* @var $rel DeployedRelationships */
953 $rel = $module->getRelationships();
955 $relList = $rel->getRelationshipList();
957 foreach ($relList as $relationshipName)
959 $relation = $rel->get($relationshipName);
961 if ($relation->getFromStudio())
963 if ($lhs && $relation->getLhsModule() != $moduleName)
967 $result[$relationshipName] = $relation;
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)
981 protected function getCustomRelationshipsMetaFilesByModuleName($moduleName, $lhs = false, $metadataOnly = false,$exportedModulesFilter=array())
984 $path = $metadataOnly ?
985 'custom' . DIRECTORY_SEPARATOR . 'metadata' . DIRECTORY_SEPARATOR :
986 'custom' . DIRECTORY_SEPARATOR;
990 //do not process if path is not a valid directory, or recursiveIterator will break.
996 $relationships = $this->getCustomRelationshipsByModuleName($moduleName, $lhs);
1003 $recursiveIterator = new RecursiveIteratorIterator(
1004 new RecursiveDirectoryIterator($path),
1005 RecursiveIteratorIterator::SELF_FIRST
1009 * @var $fileInfo SplFileInfo
1011 foreach ($recursiveIterator as $fileInfo)
1013 if ($fileInfo->isFile() && !in_array($fileInfo->getPathname(), $result))
1015 foreach ($relationships as $k => $v)
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();
1032 public function deleteBuild()
1034 return rmdir_recursive($this->getBuildDir());