/**
  * Installs or upgrades a module from it's directory
  * Checks dependencies, and enables
  * @param string   The name of the module to install
  * @param bool     If true, skips status and dependency checks
  * @return mixed   True if succesful, array of error messages if not succesful
  */
 function install($modulename, $force = false)
 {
     $this->modDepends = array();
     $this->notFound = false;
     global $db, $amp_conf;
     set_time_limit($this->maxTimeLimit);
     $modules = $this->getinfo($modulename);
     // make sure we have a directory, to begin with
     $dir = $amp_conf['AMPWEBROOT'] . '/admin/modules/' . $modulename;
     if (!is_dir($dir)) {
         $this->notFound = true;
         return array(_("Cannot find module"));
     }
     // read the module.xml file
     $modules = $this->getinfo($modulename);
     if (!isset($modules[$modulename])) {
         return array(_("Could not read module.xml"));
     }
     // don't force this bit - we can't install a broken module (missing files)
     if ($modules[$modulename]['status'] == MODULE_STATUS_BROKEN) {
         return array(_("Module " . $modules[$modulename]['rawname'] . " is broken and cannot be installed. You should try to download it again."));
     }
     $mod = FreePBX::GPG()->verifyModule($modulename);
     $revoked = $mod['status'] & GPG::STATE_REVOKED;
     if ($revoked) {
         return array(_("Module " . $modulename . " has a revoked signature and cannot be installed"));
     }
     if (!$force) {
         if (!in_array($modules[$modulename]['status'], array(MODULE_STATUS_ENABLED, MODULE_STATUS_NOTINSTALLED, MODULE_STATUS_NEEDUPGRADE))) {
             //return array(_("This module is already installed."));
             // This isn't really an error, we just exit
             return true;
         }
         // check dependencies
         if (is_array($errors = $this->checkdepends($modules[$modulename]))) {
             return $errors;
         }
     }
     // Check if another module wants this install to be rejected
     // The module must have a callback: [modulename]_module_install_check_callback() that takes
     // a single modules array from module_getinfo() about the module to be installed
     // and it must pass back boolean true if the installation can proceed, or a message
     // indicating why the installation must fail
     //
     $rejects = array();
     //We need to include developer files before the callback happens during an install
     if (!$this->_runscripts_include($modules, 'install')) {
         return array(_("Failed to run installation scripts"));
     }
     foreach (mod_func_iterator('module_install_check_callback', $modules) as $mod => $res) {
         if ($res !== true) {
             $rejects[] = $res;
         }
     }
     if (!empty($rejects)) {
         return $rejects;
     }
     //Developer mode, remind them they need to run install_amp manually
     //run this before the install scripts below because they end up removing install.php...yup
     if ($modulename == 'framework' && !file_exists($dir . '/install.php')) {
         out(_("Framework has been detected as being in Developer mode, Please make sure to run './install_amp --update-links' manually so that any database or system settings can be updated"));
     }
     // run the scripts
     if (!$this->_runscripts($modulename, 'install', $modules)) {
         return array(_("Failed to run installation scripts"));
     }
     if ($modules[$modulename]['status'] == MODULE_STATUS_NOTINSTALLED) {
         // customize INSERT query
         $sql = "INSERT INTO modules (modulename, version, enabled) values ('" . $db->escapeSimple($modules[$modulename]['rawname']) . "','" . $db->escapeSimple($modules[$modulename]['version']) . "', 1);";
     } else {
         // just need to update the version
         $sql = "UPDATE modules SET version='" . $db->escapeSimple($modules[$modulename]['version']) . "' WHERE modulename = '" . $db->escapeSimple($modules[$modulename]['rawname']) . "'";
     }
     // run query
     $results = $db->query($sql);
     if (DB::IsError($results)) {
         return array(sprintf(_("Error updating database. Command was: %s; error was: %s "), $sql, $results->getMessage()));
     }
     // If module is framework then update the framework version
     // normally this is done inside of the funky upgrade script runner but we are changing this now as
     // framework and freepbx versions are the same
     if ($modulename == 'framework' && !empty($modules[$modulename]['version']) && getVersion() != $modules[$modulename]['version']) {
         out(sprintf(_("Framework Detected, Setting FreePBX Version to %s"), $modules[$modulename]['version']));
         $sql = "UPDATE admin SET value = '" . $db->escapeSimple($modules[$modulename]['version']) . "' WHERE variable = 'version'";
         $result = $db->query($sql);
         if (DB::IsError($result)) {
             die($result->getMessage());
         }
         if (getVersion() != $modules[$modulename]['version']) {
             die(_('Internal Error. Function getVersion did not match the Framework version, even after it was suppose to be applied'));
         }
     }
     // module is now installed & enabled, invalidate the modulelist class since it is now stale
     $modulelist =& modulelist::create($db);
     $modulelist->invalidate();
     // edit the notification table to list any remaining upgrades available or clear
     // it if none are left. It requres a copy of the most recent module_xml to compare
     // against the installed modules.
     //
     $sql = 'SELECT data FROM module_xml WHERE id = "xml"';
     $data = sql($sql, "getOne");
     $parser = new xml2ModuleArray($data);
     $xmlarray = $parser->parseAdvanced($data);
     $new_modules = array();
     if (count($xmlarray)) {
         foreach ($xmlarray['xml']['module'] as $mod) {
             $new_modules[$mod['rawname']] = $mod;
         }
     }
     $this->upgrade_notifications($new_modules, 'PASSIVE');
     needreload();
     FreePBX::Config()->update("SIGNATURECHECK", true);
     $db->query("DELETE FROM admin WHERE variable = 'unsigned' LIMIT 1");
     //Generate LESS on install
     //http://issues.freepbx.org/browse/FREEPBX-8287
     outn(_("Generating CSS..."));
     try {
         if ($modulename == 'framework') {
             FreePBX::Less()->generateMainStyles();
         } else {
             FreePBX::Less()->generateModuleStyles($modulename);
         }
     } catch (\Exception $e) {
     }
     out(_("Done"));
     return true;
 }