Exemple #1
0
 function Install($tmpfilepath, $updateOnce = false)
 {
     $result = FALSE;
     import('com.solarix.ampoliros.core.Ampoliros');
     $amp = Ampoliros::instance('Ampoliros');
     if ($amp->GetState() == Ampoliros::STATE_DEBUG) {
         $GLOBALS['gEnv']['runtime']['debug']['loadtime']->Mark('moduleinstallstart');
     }
     if (file_exists($tmpfilepath)) {
         import('carthag.io.archive.Archive');
         // Moves temp file to modules repository and extracts it
         //
         $fname = MODULE_PATH . basename($tmpfilepath);
         @copy($tmpfilepath, $fname);
         $basetmpdir = $tmpdir = TMP_PATH . 'modinst/' . md5(microtime());
         @mkdir($tmpdir, 0755);
         $olddir = getcwd();
         @chdir($tmpdir);
         //@system( escapeshellcmd( 'tar zxf '.$fname ) );
         $archive_format = ARCHIVE_TGZ;
         if (substr($fname, -4) == '.zip') {
             $archive_format = ARCHIVE_ZIP;
         }
         $mod_archive = new Archive($fname, $archive_format);
         $mod_archive->Extract($tmpdir);
         // Checks if the files are into a directory instead of the root
         //
         if (!@is_dir($tmpdir . '/defs')) {
             $dhandle = opendir($tmpdir);
             while (FALSE != ($file = readdir($dhandle))) {
                 if ($file != '.' && $file != '..' && is_dir($tmpdir . '/' . $file . '/defs')) {
                     $tmpdir = $tmpdir . '/' . $file;
                 }
             }
             closedir($dhandle);
         }
         $this->basedir = $tmpdir;
         // Checks for definition and structure files
         //
         if (file_exists($tmpdir . '/defs/' . Module::BUNDLEDEF_FILE)) {
             $modules_array = file($tmpdir . '/defs/' . Module::BUNDLEDEF_FILE);
             $result = TRUE;
             while (list(, $module) = each($modules_array)) {
                 $module = trim($module);
                 if (strlen($module) and file_exists($tmpdir . '/modules/' . $module)) {
                     $temp_module = new Module($this->ampdb);
                     if (!$temp_module->Install($tmpdir . '/modules/' . $module)) {
                         $result = FALSE;
                     }
                 }
             }
         } else {
             if (file_exists($tmpdir . '/defs/' . Module::STRUCTURE_FILE) and file_exists($tmpdir . '/defs/' . Module::GENERALDEF_FILE)) {
                 $genconfig = new ConfigFile($tmpdir . '/defs/' . Module::GENERALDEF_FILE);
                 $this->modname = $genconfig->Value('MODULEIDNAME');
                 // Checks if the module has been already installed
                 //
                 $tmpquery = $this->ampdb->Execute('SELECT id,modfile FROM modules WHERE modid=' . $this->ampdb->Format_Text($this->modname));
                 if (!$tmpquery->NumRows()) {
                     // Module is new, so it will be installed
                     //
                     // Dependencies check
                     //
                     $this->unmetdeps = array();
                     $this->unmetsuggs = array();
                     $moddeps = new ModuleDep($this->ampdb);
                     $deps = $moddeps->ExplodeDeps($genconfig->Value('MODULEDEPENDENCIES'));
                     $suggs = $moddeps->ExplodeDeps($genconfig->Value('MODULESUGGESTIONS'));
                     if ($deps != FALSE) {
                         $this->unmetdeps = $moddeps->CheckModuleDeps(0, '', $deps);
                     } else {
                         $this->unmetdeps == FALSE;
                     }
                     // Suggestions check
                     //
                     if ($suggs != FALSE) {
                         $unmetsuggs = $moddeps->CheckModuleDeps(0, '', $suggs);
                         if (is_array($unmetsuggs)) {
                             $this->unmetsuggs = $unmetsuggs;
                         }
                     }
                     // If dependencies are ok, go on
                     //
                     if ($this->unmetdeps == FALSE) {
                         // Gets serial number for the module
                         //
                         $this->serial = $this->ampdb->NextSeqValue('modules_id_seq');
                         $this->ampdb->Execute('INSERT INTO modules VALUES ( ' . $this->serial . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULEIDNAME')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULEVERSION')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULEDATE')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULEDESCRIPTION')) . ',' . $this->ampdb->Format_Text(basename($tmpfilepath)) . ',' . $this->ampdb->Format_Text($this->ampdb->fmtfalse) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR_EMAIL')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR_SITE')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_SUPPORT_EMAIL')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_BUGS_EMAIL')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_COPYRIGHT')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_LICENSE')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_LICENSE_FILE')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_MAINTAINER')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_MAINTAINER_EMAIL')) . ',' . $this->ampdb->Format_Text($genconfig->Value('MODULE_CATEGORY')) . ')');
                         // Module dir creation
                         //
                         @mkdir(MODULE_PATH . $genconfig->Value('MODULEIDNAME'), 0755);
                         // Defs files
                         //
                         if ($dhandle = @opendir($tmpdir . '/defs')) {
                             while (FALSE != ($file = readdir($dhandle))) {
                                 if ($file != '.' && $file != '..' && is_file($tmpdir . '/defs/' . $file)) {
                                     @copy($tmpdir . '/defs/' . $file, MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . $file);
                                 }
                             }
                             closedir($dhandle);
                         }
                         // Adds modules dependencies
                         //
                         $moddeps->AddDepsArray($genconfig->Value('MODULEIDNAME'), $deps, DEPTYPE_DEP);
                         $moddeps->AddDepsArray($genconfig->Value('MODULEIDNAME'), $suggs, DEPTYPE_SUGG);
                         $this->SetSubModules(explode(',', trim($genconfig->Value('MODULE_SUBMODULES'), ' ,')));
                         $this->HandleStructure($tmpdir . '/defs/' . Module::STRUCTURE_FILE, Module::INSTALL_MODE_INSTALL, $tmpdir);
                         if (strlen($genconfig->Value('MODULE_LICENSE_FILE')) and file_exists($tmpdir . '/' . $genconfig->Value('MODULE_LICENSE_FILE'))) {
                             @copy($tmpdir . '/' . $genconfig->Value('MODULE_LICENSE_FILE'), MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . $genconfig->Value('MODULE_LICENSE_FILE'));
                         }
                         // Checks if it is an extension module
                         //
                         $genconfig = new ConfigFile($tmpdir . '/defs/' . Module::GENERALDEF_FILE);
                         $ext = $this->ampdb->fmtfalse;
                         if ($genconfig->Value('ONLYEXTENSION') == 'y') {
                             $ext = $this->ampdb->fmttrue;
                             $this->onlyextension = TRUE;
                         } else {
                             if ($genconfig->Value('ONLYEXTENSION') == 'n') {
                                 $ext = $this->ampdb->fmtfalse;
                                 $this->onlyextension = FALSE;
                             } else {
                                 if ($this->onlyextension) {
                                     $ext = $this->ampdb->fmttrue;
                                 }
                             }
                         }
                         $this->ampdb->Execute('UPDATE modules SET onlyextension=' . $this->ampdb->Format_Text($ext) . ' WHERE modid=' . $this->ampdb->Format_Text($this->modname));
                         $result = TRUE;
                         if ($GLOBALS['gEnv']['core']['config']->Value('ALERT_ON_MODULE_OPERATION') == '1') {
                             import('com.solarix.ampoliros.security.SecurityLayer');
                             $amp_security = new SecurityLayer();
                             $amp_security->SendAlert('Module ' . $this->modname . ' has been installed');
                             unset($amp_security);
                         }
                         if ($result == true) {
                             if ($GLOBALS['gEnv']['core']['edition'] == AMP_EDITION_ENTERPRISE and $this->modname != 'ampoliros' and $ext != $this->ampdb->fmttrue) {
                                 $sites_query = $GLOBALS['gEnv']['root']['db']->Execute('SELECT id FROM sites');
                                 if ($sites_query->NumRows()) {
                                     $this->Enable($sites_query->Fields('id'));
                                 }
                             }
                             import('com.solarix.ampoliros.io.log.Logger');
                             $log = new Logger(AMP_LOG);
                             $log->LogEvent('Ampoliros', 'Installed module ' . $this->modname, LOGGER_NOTICE);
                         }
                     }
                 } else {
                     $moddata = $tmpquery->Fields();
                     $this->serial = $moddata['id'];
                     // Module will be updated
                     //
                     if ($this->serial) {
                         // Dependencies check
                         //
                         $this->unmetdeps = array();
                         $this->unmetsuggs = array();
                         $moddeps = new ModuleDep($this->ampdb);
                         $deps = $moddeps->ExplodeDeps($genconfig->Value('MODULEDEPENDENCIES'));
                         $suggs = $moddeps->ExplodeDeps($genconfig->Value('MODULESUGGESTIONS'));
                         if ($deps != FALSE) {
                             $this->unmetdeps = $moddeps->CheckModuleDeps(0, '', $deps);
                         } else {
                             $this->unmetdeps == FALSE;
                         }
                         // Suggestions check
                         //
                         if ($suggs != FALSE) {
                             $unmetsuggs = $moddeps->CheckModuleDeps(0, '', $suggs);
                             if (is_array($unmetsuggs)) {
                                 $this->unmetsuggs = $unmetsuggs;
                             }
                         }
                         // If dependencies are ok, go on
                         //
                         if ($this->unmetdeps == FALSE) {
                             // Creates lock file
                             //
                             touch(TMP_PATH . '.upgrading_system');
                             // :WARNING: evil 20020506: possible problems on Windows systems
                             // It has a 'permission denied'.
                             // Removes old module file
                             //
                             if (basename($fname) != $moddata['modfile'] and file_exists(MODULE_PATH . $moddata['modfile'])) {
                                 @unlink(MODULE_PATH . $moddata['modfile']);
                             }
                             // Updates modules table
                             //
                             $this->ampdb->Execute('UPDATE modules SET modversion=' . $this->ampdb->Format_Text($genconfig->Value('MODULEVERSION')) . ', moddate=' . $this->ampdb->Format_Text($genconfig->Value('MODULEDATE')) . ', moddesc=' . $this->ampdb->Format_Text($genconfig->Value('MODULEDESCRIPTION')) . ', modfile=' . $this->ampdb->Format_Text(basename($tmpfilepath)) . ', author=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR')) . ', authoremail=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR_EMAIL')) . ', authorsite=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_AUTHOR_SITE')) . ', supportemail=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_SUPPORT_EMAIL')) . ', bugsemail=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_BUGS_EMAIL')) . ', copyright=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_COPYRIGHT')) . ', license=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_LICENSE')) . ', licensefile=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_LICENSE_FILE')) . ', maintainer=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_MAINTAINER')) . ', maintaineremail=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_MAINTAINER_EMAIL')) . ', category=' . $this->ampdb->Format_Text($genconfig->Value('MODULE_CATEGORY')) . ' WHERE id=' . (int) $this->serial);
                             $genconfig = new ConfigFile($tmpdir . '/defs/' . Module::GENERALDEF_FILE);
                             // Script files - only before handlestructure
                             //
                             if ($dhandle = @opendir($tmpdir . '/defs')) {
                                 while (FALSE != ($file = readdir($dhandle))) {
                                     if ($file != '.' and $file != '..' and $file != Module::STRUCTURE_FILE and $file != Module::GENERALDEF_FILE and is_file($tmpdir . '/defs/' . $file)) {
                                         @copy($tmpdir . '/defs/' . $file, MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . $file);
                                     }
                                 }
                                 closedir($dhandle);
                             }
                             $this->HandleStructure($tmpdir . '/defs/' . Module::STRUCTURE_FILE, Module::INSTALL_MODE_UPDATE, $tmpdir);
                             if (strlen($genconfig->Value('MODULE_LICENSE_FILE')) and file_exists($tmpdir . '/' . $genconfig->Value('MODULE_LICENSE_FILE'))) {
                                 @copy($tmpdir . '/' . $genconfig->Value('MODULE_LICENSE_FILE'), MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . $genconfig->Value('MODULE_LICENSE_FILE'));
                             }
                             // Defs files - only after handlestructure
                             //
                             @copy($tmpdir . '/defs/' . Module::STRUCTURE_FILE, MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . Module::STRUCTURE_FILE);
                             @copy($tmpdir . '/defs/' . Module::GENERALDEF_FILE, MODULE_PATH . $genconfig->Value('MODULEIDNAME') . '/' . Module::GENERALDEF_FILE);
                             // Checks if it is an extension module
                             //
                             $ext = $this->ampdb->fmtfalse;
                             if ($genconfig->Value('ONLYEXTENSION') == 'y') {
                                 $ext = $this->ampdb->fmttrue;
                                 $this->onlyextension = TRUE;
                             } else {
                                 if ($genconfig->Value('ONLYEXTENSION') == 'n') {
                                     $ext = $this->ampdb->fmtfalse;
                                     $this->onlyextension = FALSE;
                                 } else {
                                     if ($this->onlyextension) {
                                         $ext = $this->ampdb->fmttrue;
                                     }
                                 }
                             }
                             $this->ampdb->Execute('UPDATE modules SET onlyextension=' . $this->ampdb->Format_Text($ext) . ' WHERE modid=' . $this->ampdb->Format_Text($this->modname));
                             $this->SetSubModules(explode(',', trim($genconfig->Value('MODULE_SUBMODULES'), ' ,')));
                             if ($this->modname != 'ampoliros') {
                                 // Remove old dependencies
                                 //
                                 $moddeps->RemAllDep($this->serial);
                                 // Adds new modules dependencies
                                 //
                                 $moddeps->AddDepsArray($genconfig->Value('MODULEIDNAME'), $deps, DEPTYPE_DEP);
                                 $moddeps->AddDepsArray($genconfig->Value('MODULEIDNAME'), $suggs, DEPTYPE_SUGG);
                             }
                             $result = TRUE;
                             if (function_exists('apc_reset_cache')) {
                                 apc_reset_cache();
                             }
                             if ($updateOnce == FALSE) {
                                 $this->Install($tmpfilepath, true);
                                 // Removes lock file
                                 //
                                 unlink(TMP_PATH . '.upgrading_system');
                                 if ($GLOBALS['gEnv']['core']['config']->Value('ALERT_ON_MODULE_OPERATION') == '1') {
                                     Carthag::import('com.solarix.ampoliros.security.SecurityLayer');
                                     $amp_security = new SecurityLayer();
                                     $amp_security->SendAlert('Module ' . $this->modname . ' has been updated');
                                     unset($amp_security);
                                 }
                                 if ($result == TRUE) {
                                     import('com.solarix.ampoliros.io.log.Logger');
                                     $log = new Logger(AMP_LOG);
                                     $log->LogEvent('Ampoliros', 'Updated module ' . $this->modname, LOGGER_NOTICE);
                                 }
                             }
                         }
                         /*
                         else $this->mLog->LogEvent( 'ampoliros.modules_library.modules_class.install',
                         'Structure definition file for module '.$this->modname.' does not exists', LOGGER_ERROR );
                         */
                     } else {
                         import('com.solarix.ampoliros.io.log.Logger');
                         $log = new Logger(AMP_LOG);
                         $log->LogEvent('ampoliros.modules_library.modules_class.install', 'Empty module serial', LOGGER_ERROR);
                     }
                 }
             } else {
                 import('com.solarix.ampoliros.io.log.Logger');
                 $log = new Logger(AMP_LOG);
                 if (!file_exists($tmpdir . '/defs/' . Module::STRUCTURE_FILE)) {
                     $log->LogEvent('ampoliros.modules_library.modules_class.install', 'Module structure file ' . $tmpdir . '/defs/' . Module::STRUCTURE_FILE . ' not found', LOGGER_ERROR);
                 }
                 if (!file_exists($tmpdir . '/defs/' . Module::GENERALDEF_FILE)) {
                     $log->LogEvent('ampoliros.modules_library.modules_class.install', 'Module definition file ' . $tmpdir . '/defs/' . Module::GENERALDEF_FILE . ' not found', LOGGER_ERROR);
                 }
             }
         }
         // Cleans up temp stuff
         //
         @chdir($olddir);
         RecRemoveDir($basetmpdir);
         if (file_exists($tmpfilepath)) {
             @unlink($tmpfilepath);
         }
     } else {
         if (!file_exists($tmpfilepath)) {
             import('com.solarix.ampoliros.io.log.Logger');
             $log = new Logger(AMP_LOG);
             $log->LogEvent('ampoliros.modules_library.modules_class.install', 'Temporary module file (' . $tmpfilepath . ') does not exists', LOGGER_ERROR);
         }
     }
     if ($amp->getState() == Ampoliros::STATE_DEBUG) {
         $GLOBALS['gEnv']['runtime']['debug']['loadtime']->Mark('moduleinstallend');
         import('com.solarix.ampoliros.io.log.Logger');
         $log = new Logger(AMP_LOG);
         $log->LogEvent('ampoliros.modules_library.module_class.install', 'Module installation load time: ' . $GLOBALS['gEnv']['runtime']['debug']['loadtime']->GetSectionLoad('moduleinstallend'), LOGGER_DEBUG);
     }
     return $result;
 }
function main_moduleversions($eventData)
{
    global $gEnv, $gLocale, $gXml_def, $gPage_title, $gToolbars;
    OpenLibrary('modules.library');
    $ac_remote = new AmpCentralRemoteServer($gEnv['root']['db'], $eventData['id']);
    $avail_reps = $ac_remote->ListAvailableRepositories(isset($eventData['refresh']) ? true : false);
    $avail_mods = $ac_remote->ListAvailableModules($eventData['repid'], isset($eventData['refresh']) ? true : false);
    $mod_versions = $ac_remote->ListAvailableModuleVersions($eventData['repid'], $eventData['moduleid'], isset($eventData['refresh']) ? true : false);
    $x_account = new XmlRpcAccount($gEnv['root']['db'], $ac_remote->mAccountId);
    $headers[0]['label'] = $gLocale->GetStr('version.header');
    $headers[1]['label'] = $gLocale->GetStr('dependencies.header');
    $headers[2]['label'] = $gLocale->GetStr('installed_version.header');
    $gXml_def = '<vertgroup><name>modules</name>
  <children>
    <label><name>title</name>
      <args>
        <bold>true</bold>
        <label type="encoded">' . urlencode($x_account->mName . ' - ' . $avail_reps[$eventData['repid']]['name'] . ' - ' . $avail_mods[$eventData['moduleid']]['modid']) . '</label>
      </args>
    </label>
    <table><name>modules</name>
      <args>
        <headers type="array">' . huixml_encode($headers) . '</headers>
        <rowsperpage>10</rowsperpage>
        <pagesactionfunction>repmodules_list_action_builder</pagesactionfunction>
        <pagenumber>' . (isset($eventData['pagenumber']) ? $eventData['pagenumber'] : '') . '</pagenumber>
        <sessionobjectusername>' . $eventData['id'] . '-' . $eventData['repid'] . '-' . $eventData['moduleid'] . '</sessionobjectusername>
      </args>
      <children>';
    $row = 0;
    $mod_query =& $gEnv['root']['db']->Execute('SELECT modversion ' . 'FROM modules ' . 'WHERE modid=' . $gEnv['root']['db']->Format_Text($avail_mods[$eventData['moduleid']]['modid']));
    while (list($version, $data) = each($mod_versions)) {
        if (strlen($data['dependencies'])) {
            $mod_deps = new ModuleDep($gEnv['root']['db']);
            $dep_check = $mod_deps->CheckModuleDeps(0, '', $mod_deps->ExplodeDeps($data['dependencies']));
        } else {
            $dep_check = false;
        }
        if ($mod_query->NumRows()) {
            $current_version = $mod_query->Fields('modversion');
        } else {
            $current_version = $gLocale->GetStr('none_version.label');
        }
        if ($dep_check == false) {
            $mod_installable = true;
            $missing_deps = '';
            if ($mod_query->NumRows()) {
                switch (CompareVersionNumbers($version, $current_version)) {
                    case AMPOLIROS_VERSIONCOMPARE_EQUAL:
                        $label = $gLocale->GetStr('reinstall_module.button');
                        $icon = 'reload';
                        break;
                    case AMPOLIROS_VERSIONCOMPARE_MORE:
                        $label = $gLocale->GetStr('update_module.button');
                        $icon = 'folder_new';
                        break;
                    case AMPOLIROS_VERSIONCOMPARE_LESS:
                        $label = $gLocale->GetStr('downgrade_module.button');
                        $icon = 'down';
                        break;
                }
            } else {
                $label = $gLocale->GetStr('install_module.button');
                $icon = 'folder';
            }
        } else {
            $mod_installable = false;
            $missing_deps = '<br><strong>' . $gLocale->GetStr('missing_deps.label') . '</strong>';
            while (list(, $dep) = each($dep_check)) {
                $missing_deps .= '<br>' . $dep;
            }
        }
        $toolbars = array();
        if ($mod_installable) {
            $toolbars['main']['install'] = array('label' => $label, 'themeimage' => $icon, 'horiz' => 'true', 'action' => build_events_call_string('', array(array('main', 'repositorymodules', array('id' => $eventData['id'], 'repid' => $eventData['repid'])), array('action', 'installmodule', array('id' => $eventData['id'], 'repid' => $eventData['repid'], 'moduleid' => $eventData['moduleid'], 'version' => $version)))));
        }
        $gXml_def .= '<label row="' . $row . '" col="0"><name>version</name>
  <args>
    <label type="encoded">' . urlencode($version) . '</label>
  </args>
</label>

<label row="' . $row . '" col="1"><name>dependencies</name>
  <args>
    <label type="encoded">' . urlencode(str_replace(',', '<br>', $data['dependencies']) . (strlen($data['suggestions']) ? '<br><br><strong>' . $gLocale->GetStr('suggestions.label') . '</strong><br>' . str_replace(',', '<br>', $data['suggestions']) . '<br>' : '') . $missing_deps) . '</label>
  </args>
</label>

<label row="' . $row . '" col="2"><name>current</name>
  <args>
    <label type="encoded">' . urlencode($current_version) . '</label>
  </args>
</label>

<amptoolbar row="' . $row . '" col="3"><name>tb</name>
  <args>
    <frame>false</frame>
    <toolbars type="array">' . huixml_encode($toolbars) . '</toolbars>
  </args>
</amptoolbar>';
        $row++;
    }
    $gXml_def .= '      </children>
    </table>
  </children>
</vertgroup>';
    $gToolbars['reptools'] = array('refresh' => array('label' => $gLocale->GetStr('refresh.button'), 'themeimage' => 'reload', 'horiz' => 'true', 'action' => build_events_call_string('', array(array('main', 'moduleversions', array('id' => $eventData['id'], 'repid' => $eventData['repid'], 'moduleid' => $eventData['moduleid'], 'refresh' => '1'))))));
    $gPage_title .= ' - ' . $gLocale->GetStr('moduleversions.title');
}