protected function DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks = false)
    {
        $aAllClasses = array();
        // flat list of classes
        // Determine the target modules for the MENUS
        //
        $aMenuNodes = array();
        $aMenusByModule = array();
        foreach ($this->oFactory->ListActiveChildNodes('menus', 'menu') as $oMenuNode) {
            $sMenuId = $oMenuNode->getAttribute('id');
            $aMenuNodes[$sMenuId] = $oMenuNode;
            $sModuleMenu = $oMenuNode->getAttribute('_created_in');
            $aMenusByModule[$sModuleMenu][] = $sMenuId;
        }
        // Determine the target module (exactly one!) for USER RIGHTS
        //
        $sUserRightsModule = '';
        $oUserRightsNode = $this->oFactory->GetNodes('user_rights')->item(0);
        if ($oUserRightsNode) {
            $sUserRightsModule = $oUserRightsNode->getAttribute('_created_in');
            $this->Log("User Rights module found: {$sUserRightsModule}");
        }
        // List root classes
        //
        $this->aRootClasses = array();
        foreach ($this->oFactory->ListRootClasses() as $oClass) {
            $this->Log("Root class (with child classes): " . $oClass->getAttribute('id'));
            $this->aRootClasses[$oClass->getAttribute('id')] = $oClass;
        }
        // Compile, module by module
        //
        $aModules = $this->oFactory->GetLoadedModules();
        foreach ($aModules as $foo => $oModule) {
            $sModuleName = $oModule->GetName();
            $sModuleVersion = $oModule->GetVersion();
            $sModuleRootDir = $oModule->GetRootDir();
            if ($sModuleRootDir != '') {
                $sModuleRootDir = realpath($sModuleRootDir);
                $sRelativeDir = basename($sModuleRootDir);
                // Push the other module files
                SetupUtils::copydir($sModuleRootDir, $sTempTargetDir . '/' . $sRelativeDir, $bUseSymbolicLinks);
            }
            $sCompiledCode = '';
            $oConstants = $this->oFactory->ListConstants($sModuleName);
            if ($oConstants->length > 0) {
                foreach ($oConstants as $oConstant) {
                    $sCompiledCode .= $this->CompileConstant($oConstant) . "\n";
                }
            }
            $oClasses = $this->oFactory->ListClasses($sModuleName);
            $iClassCount = $oClasses->length;
            if ($iClassCount == 0) {
                $this->Log("Found module without classes declared: {$sModuleName}");
            } else {
                foreach ($oClasses as $oClass) {
                    $sClass = $oClass->getAttribute("id");
                    $aAllClasses[] = $sClass;
                    try {
                        $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
                    } catch (DOMFormatException $e) {
                        throw new Exception("Failed to process class '{$sClass}', from '{$sModuleRootDir}': " . $e->getMessage());
                    }
                }
            }
            if (!array_key_exists($sModuleName, $aMenusByModule)) {
                $this->Log("Found module without menus declared: {$sModuleName}");
            } else {
                $sCompiledCode .= <<<EOF

//
// Menus
//

global \$__comp_menus__; // ensure that the global variable is indeed global !

EOF;
                // Preliminary: determine parent menus not defined within the current module
                $aMenusToLoad = array();
                $aParentMenus = array();
                foreach ($aMenusByModule[$sModuleName] as $sMenuId) {
                    $oMenuNode = $aMenuNodes[$sMenuId];
                    if ($sParent = $oMenuNode->GetChildText('parent', null)) {
                        $aMenusToLoad[] = $sParent;
                        $aParentMenus[] = $sParent;
                    }
                    // Note: the order matters: the parents must be defined BEFORE
                    $aMenusToLoad[] = $sMenuId;
                }
                $aMenusToLoad = array_unique($aMenusToLoad);
                foreach ($aMenusToLoad as $sMenuId) {
                    $oMenuNode = $aMenuNodes[$sMenuId];
                    if ($oMenuNode->getAttribute("xsi:type") == 'MenuGroup') {
                        // Note: this algorithm is wrong
                        // 1 - the module may appear empty in the current module, while children are defined in other modules
                        // 2 - check recursively that child nodes are not empty themselves
                        // Future algorithm:
                        // a- browse the modules and build the menu tree
                        // b- browse the tree and blacklist empty menus
                        // c- before compiling, discard if blacklisted
                        if (!in_array($oMenuNode->getAttribute("id"), $aParentMenus)) {
                            // Discard empty menu groups
                            continue;
                        }
                    }
                    try {
                        $sCompiledCode .= $this->CompileMenu($oMenuNode, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
                    } catch (DOMFormatException $e) {
                        throw new Exception("Failed to process menu '{$sMenuId}', from '{$sModuleRootDir}': " . $e->getMessage());
                    }
                }
            }
            // User rights
            //
            if ($sModuleName == $sUserRightsModule) {
                $sCompiledCode .= $this->CompileUserRights($oUserRightsNode);
            }
            // Create (overwrite if existing) the compiled file
            //
            if (strlen($sCompiledCode) > 0) {
                // We have compiled something: write the result file
                //
                $sResultFile = $sTempTargetDir . '/' . $sRelativeDir . '/model.' . $sModuleName . '.php';
                if (is_file($sResultFile)) {
                    $this->Log("Updating {$sResultFile} for module {$sModuleName} in version {$sModuleVersion} ({$iClassCount} classes)");
                } else {
                    $sResultDir = dirname($sResultFile);
                    if (!is_dir($sResultDir)) {
                        $this->Log("Creating directory {$sResultDir}");
                        mkdir($sResultDir, 0777, true);
                    }
                    $this->Log("Creating {$sResultFile} for module {$sModuleName} in version {$sModuleVersion} ({$iClassCount} classes)");
                }
                // Compile the module into a single file
                //
                $sId = $sModuleName;
                $sCurrDate = date(DATE_ISO8601);
                $sAuthor = 'iTop compiler';
                $sLicence = 'http://opensource.org/licenses/AGPL-3.0';
                $sFileHeader = <<<EOF
<?php
//
// File generated by ... on the {$sCurrDate}
// Please do not edit manually
//

/**
 * Classes and menus for {$sModuleName} (version {$sModuleVersion})
 *
 * @author      {$sAuthor}
 * @license     {$sLicence}
 */

EOF;
                $ret = file_put_contents($sResultFile, $sFileHeader . $sCompiledCode);
                if ($ret === false) {
                    $iLen = strlen($sFileHeader . $sCompiledCode);
                    $fFree = @disk_free_space(dirname($sResultFile));
                    $aErr = error_get_last();
                    throw new Exception("Failed to write '{$sResultFile}'. Last error: '{$aErr['message']}', content to write: {$iLen} bytes, available free space on disk: {$fFree}.");
                }
            } else {
                $this->Log("Compilation of module {$sModuleName} in version {$sModuleVersion} produced not code at all. No file written.");
            }
        }
        // foreach module
        // Compile the dictionaries -out of the modules
        //
        $sDictDir = $sTempTargetDir . '/dictionaries';
        if (!is_dir($sDictDir)) {
            $this->Log("Creating directory {$sDictDir}");
            mkdir($sDictDir, 0777, true);
        }
        $oDictionaries = $this->oFactory->ListActiveChildNodes('dictionaries', 'dictionary');
        foreach ($oDictionaries as $oDictionaryNode) {
            $this->CompileDictionary($oDictionaryNode, $sTempTargetDir, $sFinalTargetDir);
        }
        // Compile the branding
        //
        $oBrandingNode = $this->oFactory->GetNodes('branding')->item(0);
        $this->CompileBranding($oBrandingNode, $sTempTargetDir, $sFinalTargetDir);
    }
Example #2
0
    protected function DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks = false)
    {
        $aAllClasses = array();
        // flat list of classes
        // Determine the target modules for the MENUS
        //
        $aMenuNodes = array();
        $aMenusByModule = array();
        foreach ($this->oFactory->GetNodes('menus/menu') as $oMenuNode) {
            $sMenuId = $oMenuNode->getAttribute('id');
            $aMenuNodes[$sMenuId] = $oMenuNode;
            $sModuleMenu = $oMenuNode->getAttribute('_created_in');
            $aMenusByModule[$sModuleMenu][] = $sMenuId;
        }
        // Determine the target module (exactly one!) for USER RIGHTS
        // This used to be based solely on the module which created the user_rights node first
        // Unfortunately, our sample extension was delivered with the xml structure, resulting in the new module to be the recipient of the compilation
        // Then model.itop-profiles-itil would not exist... resulting in an error after the compilation (and the actual product of the compiler would never be included
        // The bullet proof implementation would be to compile in a separate directory as it has been done with the dictionaries... that's another story
        $aModules = $this->oFactory->GetLoadedModules();
        $sUserRightsModule = '';
        foreach ($aModules as $foo => $oModule) {
            if ($oModule->GetName() == 'itop-profiles-itil') {
                $sUserRightsModule = 'itop-profiles-itil';
                break;
            }
        }
        $oUserRightsNode = $this->oFactory->GetNodes('user_rights')->item(0);
        if ($oUserRightsNode && $sUserRightsModule == '') {
            // Legacy algorithm (itop <= 2.0.3)
            $sUserRightsModule = $oUserRightsNode->getAttribute('_created_in');
        }
        $this->Log("User Rights module found: '{$sUserRightsModule}'");
        // List root classes
        //
        $this->aRootClasses = array();
        foreach ($this->oFactory->ListRootClasses() as $oClass) {
            $this->Log("Root class (with child classes): " . $oClass->getAttribute('id'));
            $this->aRootClasses[$oClass->getAttribute('id')] = $oClass;
        }
        $this->LoadSnippets();
        // Compile, module by module
        //
        $aModules = $this->oFactory->GetLoadedModules();
        foreach ($aModules as $foo => $oModule) {
            $sModuleName = $oModule->GetName();
            $sModuleVersion = $oModule->GetVersion();
            $sModuleRootDir = $oModule->GetRootDir();
            if ($sModuleRootDir != '') {
                $sModuleRootDir = realpath($sModuleRootDir);
                $sRelativeDir = basename($sModuleRootDir);
                // Push the other module files
                SetupUtils::copydir($sModuleRootDir, $sTempTargetDir . '/' . $sRelativeDir, $bUseSymbolicLinks);
            }
            $sCompiledCode = '';
            $oConstants = $this->oFactory->ListConstants($sModuleName);
            if ($oConstants->length > 0) {
                foreach ($oConstants as $oConstant) {
                    $sCompiledCode .= $this->CompileConstant($oConstant) . "\n";
                }
            }
            if (array_key_exists($sModuleName, $this->aSnippets)) {
                foreach ($this->aSnippets[$sModuleName]['before'] as $aSnippet) {
                    $sCompiledCode .= "\n";
                    $sCompiledCode .= "/**\n";
                    $sCompiledCode .= " * Snippet: {$aSnippet['snippet_id']}\n";
                    $sCompiledCode .= " */\n";
                    $sCompiledCode .= $aSnippet['content'] . "\n";
                }
            }
            $oClasses = $this->oFactory->ListClasses($sModuleName);
            $iClassCount = $oClasses->length;
            if ($iClassCount == 0) {
                $this->Log("Found module without classes declared: {$sModuleName}");
            } else {
                foreach ($oClasses as $oClass) {
                    $sClass = $oClass->getAttribute("id");
                    $aAllClasses[] = $sClass;
                    try {
                        $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
                    } catch (DOMFormatException $e) {
                        throw new Exception("Failed to process class '{$sClass}', from '{$sModuleRootDir}': " . $e->getMessage());
                    }
                }
            }
            if (!array_key_exists($sModuleName, $aMenusByModule)) {
                $this->Log("Found module without menus declared: {$sModuleName}");
            } else {
                $sMenuCreationClass = 'MenuCreation_' . preg_replace('/[^A-Za-z0-9_]/', '_', $sModuleName);
                $sCompiledCode .= <<<EOF

//
// Menus
//
class {$sMenuCreationClass} extends ModuleHandlerAPI
{
\tpublic static function OnMenuCreation()
\t{
\t\tglobal \$__comp_menus__; // ensure that the global variable is indeed global !

EOF;
                // Preliminary: determine parent menus not defined within the current module
                $aMenusToLoad = array();
                $aParentMenus = array();
                foreach ($aMenusByModule[$sModuleName] as $sMenuId) {
                    $oMenuNode = $aMenuNodes[$sMenuId];
                    if ($sParent = $oMenuNode->GetChildText('parent', null)) {
                        $aMenusToLoad[] = $sParent;
                        $aParentMenus[] = $sParent;
                    }
                    // Note: the order matters: the parents must be defined BEFORE
                    $aMenusToLoad[] = $sMenuId;
                }
                $aMenusToLoad = array_unique($aMenusToLoad);
                $aMenusForAll = array();
                $aMenusForAdmins = array();
                foreach ($aMenusToLoad as $sMenuId) {
                    $oMenuNode = $aMenuNodes[$sMenuId];
                    if ($oMenuNode->getAttribute("xsi:type") == 'MenuGroup') {
                        // Note: this algorithm is wrong
                        // 1 - the module may appear empty in the current module, while children are defined in other modules
                        // 2 - check recursively that child nodes are not empty themselves
                        // Future algorithm:
                        // a- browse the modules and build the menu tree
                        // b- browse the tree and blacklist empty menus
                        // c- before compiling, discard if blacklisted
                        if (!in_array($oMenuNode->getAttribute("id"), $aParentMenus)) {
                            // Discard empty menu groups
                            continue;
                        }
                    }
                    try {
                        $aMenuLines = $this->CompileMenu($oMenuNode, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP);
                    } catch (DOMFormatException $e) {
                        throw new Exception("Failed to process menu '{$sMenuId}', from '{$sModuleRootDir}': " . $e->getMessage());
                    }
                    if ($oMenuNode->GetChildText('enable_admin_only') == '1') {
                        $aMenusForAdmins = array_merge($aMenusForAdmins, $aMenuLines);
                    } else {
                        $aMenusForAll = array_merge($aMenusForAll, $aMenuLines);
                    }
                }
                $sIndent = "\t\t";
                foreach ($aMenusForAll as $sPHPLine) {
                    $sCompiledCode .= $sIndent . $sPHPLine . "\n";
                }
                if (count($aMenusForAdmins) > 0) {
                    $sCompiledCode .= $sIndent . "if (UserRights::IsAdministrator())\n";
                    $sCompiledCode .= $sIndent . "{\n";
                    foreach ($aMenusForAdmins as $sPHPLine) {
                        $sCompiledCode .= $sIndent . "\t" . $sPHPLine . "\n";
                    }
                    $sCompiledCode .= $sIndent . "}\n";
                }
                $sCompiledCode .= <<<EOF
\t}
} // class {$sMenuCreationClass}

EOF;
            }
            // User rights
            //
            if ($sModuleName == $sUserRightsModule) {
                $sCompiledCode .= $this->CompileUserRights($oUserRightsNode);
            }
            if (array_key_exists($sModuleName, $this->aSnippets)) {
                foreach ($this->aSnippets[$sModuleName]['after'] as $aSnippet) {
                    $sCompiledCode .= "\n";
                    $sCompiledCode .= "/**\n";
                    $sCompiledCode .= " * Snippet: {$aSnippet['snippet_id']}\n";
                    $sCompiledCode .= " */\n";
                    $sCompiledCode .= $aSnippet['content'] . "\n";
                }
            }
            // Create (overwrite if existing) the compiled file
            //
            if (strlen($sCompiledCode) > 0) {
                // We have compiled something: write the result file
                //
                $sResultFile = $sTempTargetDir . '/' . $sRelativeDir . '/model.' . $sModuleName . '.php';
                if (is_file($sResultFile)) {
                    $this->Log("Updating {$sResultFile} for module {$sModuleName} in version {$sModuleVersion} ({$iClassCount} classes)");
                } else {
                    $sResultDir = dirname($sResultFile);
                    if (!is_dir($sResultDir)) {
                        $this->Log("Creating directory {$sResultDir}");
                        mkdir($sResultDir, 0777, true);
                    }
                    $this->Log("Creating {$sResultFile} for module {$sModuleName} in version {$sModuleVersion} ({$iClassCount} classes)");
                }
                // Compile the module into a single file
                //
                $sId = $sModuleName;
                $sCurrDate = date(DATE_ISO8601);
                $sAuthor = 'iTop compiler';
                $sLicence = 'http://opensource.org/licenses/AGPL-3.0';
                $sFileHeader = <<<EOF
<?php
//
// File generated by ... on the {$sCurrDate}
// Please do not edit manually
//

/**
 * Classes and menus for {$sModuleName} (version {$sModuleVersion})
 *
 * @author      {$sAuthor}
 * @license     {$sLicence}
 */

EOF;
                $ret = file_put_contents($sResultFile, $sFileHeader . $sCompiledCode);
                if ($ret === false) {
                    $iLen = strlen($sFileHeader . $sCompiledCode);
                    $fFree = @disk_free_space(dirname($sResultFile));
                    $aErr = error_get_last();
                    throw new Exception("Failed to write '{$sResultFile}'. Last error: '{$aErr['message']}', content to write: {$iLen} bytes, available free space on disk: {$fFree}.");
                }
            } else {
                $this->Log("Compilation of module {$sModuleName} in version {$sModuleVersion} produced not code at all. No file written.");
            }
        }
        // foreach module
        // Compile the dictionaries -out of the modules
        //
        $sDictDir = $sTempTargetDir . '/dictionaries';
        if (!is_dir($sDictDir)) {
            $this->Log("Creating directory {$sDictDir}");
            mkdir($sDictDir, 0777, true);
        }
        $oDictionaries = $this->oFactory->GetNodes('dictionaries/dictionary');
        foreach ($oDictionaries as $oDictionaryNode) {
            $this->CompileDictionary($oDictionaryNode, $sTempTargetDir, $sFinalTargetDir);
        }
        // Compile the branding
        //
        $oBrandingNode = $this->oFactory->GetNodes('branding')->item(0);
        $this->CompileBranding($oBrandingNode, $sTempTargetDir, $sFinalTargetDir);
        if (array_key_exists('_core_', $this->aSnippets)) {
            foreach ($this->aSnippets['_core_']['before'] as $aSnippet) {
                $this->sMainPHPCode .= "\n";
                $this->sMainPHPCode .= "/**\n";
                $this->sMainPHPCode .= " * Snippet: {$aSnippet['snippet_id']}\n";
                $this->sMainPHPCode .= " */\n";
                $this->sMainPHPCode .= $aSnippet['content'] . "\n";
            }
        }
        // Compile the portals
        $oPortalsNode = $this->oFactory->GetNodes('/itop_design/portals')->item(0);
        $this->CompilePortals($oPortalsNode, $sTempTargetDir, $sFinalTargetDir);
        // Create module design XML files
        $this->CompileModuleDesigns($sTempTargetDir, $sFinalTargetDir);
        // Compile the XML parameters
        $oParametersNode = $this->oFactory->GetNodes('/itop_design/module_parameters')->item(0);
        $this->CompileParameters($oParametersNode, $sTempTargetDir, $sFinalTargetDir);
        if (array_key_exists('_core_', $this->aSnippets)) {
            foreach ($this->aSnippets['_core_']['after'] as $aSnippet) {
                $this->sMainPHPCode .= "\n";
                $this->sMainPHPCode .= "/**\n";
                $this->sMainPHPCode .= " * Snippet: {$aSnippet['snippet_id']}\n";
                $this->sMainPHPCode .= " */\n";
                $this->sMainPHPCode .= $aSnippet['content'] . "\n";
            }
        }
        if (count($this->aRelations) > 0) {
            $this->sMainPHPCode .= "\n";
            $this->sMainPHPCode .= "/**\n";
            $this->sMainPHPCode .= " * Relations\n";
            $this->sMainPHPCode .= " */\n";
            foreach ($this->aRelations as $sRelationCode => $aData) {
                $sRelCodeSafe = addslashes($sRelationCode);
                $this->sMainPHPCode .= "MetaModel::RegisterRelation('{$sRelCodeSafe}');\n";
            }
        }
        // Write core/main.php
        SetupUtils::builddir($sTempTargetDir . '/core');
        $sPHPFile = $sTempTargetDir . '/core/main.php';
        file_put_contents($sPHPFile, $this->sMainPHPCode);
    }
 protected static function DoCopy($aCopies)
 {
     $aReports = array();
     foreach ($aCopies as $aCopy) {
         $sSource = $aCopy['source'];
         $sDestination = APPROOT . $aCopy['destination'];
         SetupUtils::builddir($sDestination);
         SetupUtils::tidydir($sDestination);
         SetupUtils::copydir($sSource, $sDestination);
         $aReports[] = "'{$aCopy['source']}' to '{$aCopy['destination']}' (OK)";
     }
     if (count($aReports) > 0) {
         $sReport = "Copies: " . count($aReports) . ': ' . implode('; ', $aReports);
     } else {
         $sReport = "No file copy";
     }
     return $sReport;
 }