464 lines
16 KiB
PHP
464 lines
16 KiB
PHP
<?php
|
|
|
|
use ProcessMaker\Util\Common;
|
|
use ProcessMaker\Core\System;
|
|
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
global $translation;
|
|
|
|
include PATH_LANGUAGECONT . "translation." . SYS_LANG;
|
|
|
|
class InstallerModule extends Controller
|
|
{
|
|
const MYSQL_VERSION_MAXIMUM_SUPPORTED = "5.7";
|
|
const PHP_VERSION_MINIMUM_SUPPORTED = "8.1";
|
|
const PHP_VERSION_NOT_SUPPORTED = "8.4";
|
|
public $path_config;
|
|
public $path_languages;
|
|
public $path_plugins;
|
|
public $path_xmlforms;
|
|
public $path_shared;
|
|
public $path_sep;
|
|
public $systemName;
|
|
|
|
public $link; #resource for database connection
|
|
|
|
/**
|
|
* Default name for connection
|
|
*
|
|
* @var string
|
|
*/
|
|
const CONNECTION_INSTALL = 'install';
|
|
const CONNECTION_TEST_INSTALL = 'testInstall';
|
|
|
|
/**
|
|
* Constructor
|
|
* We defined the paths for the installer
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->path_config = PATH_CORE . 'config/';
|
|
$this->path_languages = PATH_CORE . 'content/languages/';
|
|
$this->path_plugins = PATH_CORE . 'plugins/';
|
|
$this->path_xmlforms = PATH_CORE . 'xmlform/';
|
|
$this->path_public = PATH_HOME . 'public_html/index.html';
|
|
$this->path_shared = PATH_TRUNK . 'shared/';
|
|
$this->path_sep = PATH_SEP;
|
|
$this->systemName = '';
|
|
$this->path_translations = PATH_CORE . 'js/labels/';
|
|
$this->path_translationsMafe = PATH_HOME . 'public_html/translations/';
|
|
}
|
|
|
|
public function index($httpData)
|
|
{
|
|
if (file_exists(FILE_PATHS_INSTALLED)) {
|
|
$this->setJSVar('messageError', G::LoadTranslation('ID_PROCESSMAKER_ALREADY_INSTALLED'));
|
|
$this->includeExtJS('installer/stopInstall');
|
|
$this->setView('installer/mainStopInstall');
|
|
G::RenderPage('publish', 'extJs');
|
|
return;
|
|
}
|
|
if ((strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') && (file_exists($this->path_shared . 'partner.info'))) {
|
|
$this->setJSVar('messageError', G::LoadTranslation('ID_NO_INSTALL'));
|
|
$this->includeExtJS('installer/stopInstall');
|
|
$this->setView('installer/mainStopInstall');
|
|
G::RenderPage('publish', 'extJs');
|
|
return;
|
|
}
|
|
|
|
$licenseContent = file_get_contents(PATH_TRUNK . 'LICENSE.txt');
|
|
|
|
$this->includeExtJS('installer/CardLayout', false);
|
|
$this->includeExtJS('installer/Wizard', false);
|
|
$this->includeExtJS('installer/Header', false);
|
|
$this->includeExtJS('installer/Card', false);
|
|
|
|
$this->includeExtJS('installer/installer_cards');
|
|
$this->includeExtJS('installer/main', false);
|
|
|
|
$this->setView('installer/main');
|
|
G::RenderPage('publish', 'extJs');
|
|
}
|
|
|
|
/**
|
|
* Set config connection
|
|
*
|
|
* @param string $nameConnection name Connection
|
|
* @param string $host
|
|
* @param string $user
|
|
* @param string $pass
|
|
* @param string $database
|
|
* @param int $port
|
|
* @param array $options
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public static function setNewConnection($nameConnection, $host, $user, $pass, $database, $port, $options = [])
|
|
{
|
|
try {
|
|
if (empty($port)) {
|
|
$dbHost = explode(':', $host);
|
|
$port = 3306;
|
|
if (count($dbHost) > 1) {
|
|
$port = $dbHost[1];
|
|
}
|
|
$host = $dbHost[0];
|
|
}
|
|
config(['database.connections.' . $nameConnection => [
|
|
'driver' => 'mysql',
|
|
'host' => $host,
|
|
'port' => $port,
|
|
'database' => $database,
|
|
'username' => $user,
|
|
'password' => $pass,
|
|
'unix_socket' => '',
|
|
'charset' => 'utf8mb4',
|
|
'collation' => 'utf8mb4_0900_ai_ci',
|
|
'prefix' => '',
|
|
'strict' => false,
|
|
'engine' => 'InnoDB',
|
|
'options' => $options
|
|
]]);
|
|
DB::connection($nameConnection)->getPdo();
|
|
} catch (Exception $e) {
|
|
throw new Exception(G::LoadTranslation('ID_MYSQL_CREDENTIALS_WRONG'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get system information for review the requirements to install ProcessMaker
|
|
*
|
|
* @return object
|
|
*/
|
|
public function getSystemInfo()
|
|
{
|
|
$this->setResponseType('json');
|
|
|
|
// PHP info and verification
|
|
$phpVer = phpversion();
|
|
preg_match('/[0-9\.]+/', $phpVer, $match);
|
|
$phpVerNum = (float)$match[0];
|
|
|
|
$info = new stdclass();
|
|
$info->php = new stdclass();
|
|
$info->mysql = new stdclass();
|
|
$info->openssl = new stdclass();
|
|
$info->curl = new stdclass();
|
|
$info->dom = new stdclass();
|
|
$info->gd = new stdclass();
|
|
$info->multibyte = new stdclass();
|
|
$info->soap = new stdclass();
|
|
$info->ldap = new stdclass();
|
|
$info->memory = new stdclass();
|
|
|
|
$info->php->version = $phpVer;
|
|
$info->php->result = (
|
|
version_compare(phpversion(), self::PHP_VERSION_MINIMUM_SUPPORTED, '>=') &&
|
|
version_compare(phpversion(), self::PHP_VERSION_NOT_SUPPORTED, '<')) ? true : false;
|
|
|
|
// MYSQL info and verification
|
|
$info->mysql->result = false;
|
|
if (function_exists('mysqli_query')) {
|
|
$mysqlVer = mysqli_get_client_info();
|
|
preg_match('/[0-9\.]+/', $mysqlVer, $match);
|
|
$mysqlNum = (float)$match[0];
|
|
$info->mysql->version = 'Client API version ' . $mysqlVer;
|
|
$info->mysql->result = $mysqlNum >= 5.0;
|
|
}
|
|
|
|
// OpenSSL info
|
|
$info->openssl->result = false;
|
|
$info->openssl->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (function_exists('openssl_open')) {
|
|
$info->openssl->result = true;
|
|
$info->openssl->version = G::LoadTranslation('ID_ENABLED');
|
|
}
|
|
|
|
// Curl info
|
|
$info->curl->result = false;
|
|
$info->curl->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (function_exists('curl_version')) {
|
|
$info->curl->result = true;
|
|
$version = curl_version();
|
|
$info->curl->version = 'cURL ' . $version['version'];
|
|
$info->openssl->version = $version['ssl_version'];
|
|
}
|
|
|
|
// DOMDocument info
|
|
$info->dom->result = false;
|
|
$info->dom->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (class_exists('DOMDocument')) {
|
|
$info->dom->result = true;
|
|
$info->dom->version = G::LoadTranslation('ID_ENABLED');
|
|
}
|
|
|
|
// GD info
|
|
$info->gd->result = false;
|
|
$info->gd->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (function_exists('gd_info')) {
|
|
$info->gd->result = true;
|
|
$gdinfo = gd_info();
|
|
$info->gd->version = $gdinfo['GD Version'];
|
|
}
|
|
|
|
// Multibyte info
|
|
$info->multibyte->result = false;
|
|
$info->multibyte->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (function_exists('mb_check_encoding')) {
|
|
$info->multibyte->result = true;
|
|
$info->multibyte->version = G::LoadTranslation('ID_ENABLED');
|
|
}
|
|
|
|
// soap info
|
|
$info->soap->result = false;
|
|
$info->soap->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (class_exists('SoapClient')) {
|
|
$info->soap->result = true;
|
|
$info->soap->version = G::LoadTranslation('ID_ENABLED');
|
|
}
|
|
|
|
// ldap info
|
|
$info->ldap->result = false;
|
|
$info->ldap->version = G::LoadTranslation('ID_NOT_ENABLED');
|
|
if (function_exists('ldap_connect')) {
|
|
$info->ldap->result = true;
|
|
$info->ldap->version = G::LoadTranslation('ID_ENABLED');
|
|
}
|
|
|
|
// memory limit verification
|
|
$memory = (int)ini_get('memory_limit');
|
|
$info->memory->version = changeAbbreviationOfDirectives(ini_get('memory_limit'));
|
|
$info->memory->result = $memory > 255;
|
|
|
|
return $info;
|
|
}
|
|
|
|
public function is_dir_writable($path)
|
|
{
|
|
return G::is_writable_r($path);
|
|
}
|
|
|
|
/**
|
|
* Test db connection
|
|
*
|
|
* @return StdClass
|
|
*/
|
|
public function testConnection()
|
|
{
|
|
$this->setResponseType('json');
|
|
$info = new StdClass();
|
|
try {
|
|
$info = $this->testMySQLConnection();
|
|
} catch (Exception $e) {
|
|
$info->result = false;
|
|
$info->message = G::LoadTranslation('DBCONNECTIONS_MSGA');
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* log the queries and other information to install.log,
|
|
* the install.log files should be placed in shared/logs
|
|
* for that reason we are using the $_REQUEST of pathShared
|
|
*/
|
|
private function installLog($text)
|
|
{
|
|
//if this function is called outside the createWorkspace, just returns and do nothing
|
|
if (!isset($_REQUEST['pathShared'])) {
|
|
return;
|
|
}
|
|
//log file is in shared/logs
|
|
$pathShared = trim($_REQUEST['pathShared']);
|
|
if (substr($pathShared, -1) !== '/') {
|
|
$pathShared .= '/';
|
|
}
|
|
$pathSharedLog = $pathShared . 'logs/';
|
|
G::verifyPath($pathSharedLog, true);
|
|
$logFile = $pathSharedLog . 'install.log';
|
|
|
|
if (!is_file($logFile)) {
|
|
G::mk_dir(dirname($pathShared));
|
|
$fpt = fopen($logFile, 'w');
|
|
if ($fpt !== null) {
|
|
fwrite($fpt, sprintf("%s %s\n", date('Y:m:d H:i:s'), '----- ' . G::LoadTranslation('ID_STARTING_LOG_FILE') . ' ------'));
|
|
fclose($fpt);
|
|
} else {
|
|
throw new Exception(G::LoadTranslation('ID_FILE_NOT_WRITEABLE', SYS_LANG, [$logFile]));
|
|
return $false;
|
|
}
|
|
}
|
|
|
|
$filter = new InputFilter();
|
|
$logFile = $filter->validateInput($logFile, 'path');
|
|
|
|
$fpt = fopen($logFile, 'a');
|
|
fwrite($fpt, sprintf("%s %s\n", date('Y:m:d H:i:s'), trim($text)));
|
|
fclose($fpt);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Display an error when processMaker is already installed
|
|
*
|
|
* @return void
|
|
*/
|
|
private function displayError()
|
|
{
|
|
$this->setJSVar('messageError', G::LoadTranslation('ID_PROCESSMAKER_ALREADY_INSTALLED'));
|
|
$this->includeExtJS('installer/stopInstall');
|
|
$this->setView('installer/mainStopInstall');
|
|
G::RenderPage('publish', 'extJs');
|
|
}
|
|
|
|
public function forceTogenerateTranslationsFiles($url)
|
|
{
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, G::browserCacheFilesUrl(System::getServerProtocolHost() . "/js/ext/translation.en.js?r=" . rand(1, 10000)));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
|
|
curl_exec($ch);
|
|
curl_close($ch);
|
|
}
|
|
|
|
/**
|
|
* query_sql_file send many statements to server
|
|
*
|
|
* @param string $file
|
|
* @param string $connection
|
|
* @return array $report
|
|
*/
|
|
public function mysqlFileQuery($file)
|
|
{
|
|
if (!is_file($file)) {
|
|
throw new Exception(G::LoadTranslation('ID_SQL_FILE_INVALID', SYS_LANG, [$file]));
|
|
return $false;
|
|
}
|
|
$this->installLog(G::LoadTranslation('ID_PROCESING', SYS_LANG, [$file]));
|
|
$startTime = microtime(true);
|
|
//New Update, to support more complex queries
|
|
|
|
$lines = file($file);
|
|
$previous = null;
|
|
DB::connection(self::CONNECTION_INSTALL)
|
|
->statement("SET NAMES 'utf8'");
|
|
foreach ($lines as $j => $line) {
|
|
$line = trim($line); // Remove comments from the script
|
|
|
|
if (strpos($line, '--') === 0) {
|
|
$line = substr($line, 0, strpos($line, '--'));
|
|
}
|
|
|
|
if (empty($line)) {
|
|
continue;
|
|
}
|
|
|
|
if (strpos($line, '#') === 0) {
|
|
$line = substr($line, 0, strpos($line, '#'));
|
|
}
|
|
|
|
if (empty($line)) {
|
|
continue;
|
|
}
|
|
|
|
// Concatenate the previous line, if any, with the current
|
|
if ($previous) {
|
|
$line = $previous . ' ' . $line;
|
|
}
|
|
$previous = null;
|
|
|
|
// If the current line doesnt end with ; then put this line together
|
|
// with the next one, thus supporting multi-line statements.
|
|
if (strrpos($line, ';') !== strlen($line) - 1) {
|
|
$previous = $line;
|
|
continue;
|
|
}
|
|
|
|
$line = substr($line, 0, strrpos($line, ';'));
|
|
DB::connection(self::CONNECTION_INSTALL)
|
|
->statement($line);
|
|
}
|
|
|
|
$endTime = microtime(true);
|
|
$this->installLog(G::LoadTranslation('ID_FILE_PROCESSED', SYS_LANG, [basename($file), $endTime - $startTime]));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Privates functions section, non callable by http request
|
|
*/
|
|
|
|
private function testMySQLConnection()
|
|
{
|
|
try {
|
|
$filter = new InputFilter();
|
|
$info = new StdClass();
|
|
$info->result = false;
|
|
$info->message = '';
|
|
if (!function_exists('mysqli_connect')) {
|
|
$info->message = G::LoadTranslation('ID_PHP_MYSQLI_NOT_INSTALL');
|
|
return $info;
|
|
}
|
|
$dataRequest = $_REQUEST;
|
|
$db_hostname = $filter->validateInput($dataRequest['db_hostname']);
|
|
$db_port = $filter->validateInput($dataRequest['db_port']);
|
|
$db_username = $filter->validateInput($dataRequest['db_username']);
|
|
$db_password = urlencode($dataRequest['db_password']);
|
|
$db_password = urldecode($filter->validateInput($db_password));
|
|
$fp = @fsockopen($db_hostname, $db_port, $errno, $errstr, 30);
|
|
if (!$fp) {
|
|
$info->message .= G::LoadTranslation('ID_CONNECTION_ERROR', SYS_LANG, ["$errstr ($errno)"]);
|
|
return $info;
|
|
}
|
|
|
|
$db_username = $filter->validateInput($db_username, 'nosql');
|
|
$db_hostname = $filter->validateInput($db_hostname, 'nosql');
|
|
|
|
self::setNewConnection(self::CONNECTION_TEST_INSTALL, $db_hostname, $db_username, $db_password, '', $db_port);
|
|
$query = "SELECT * FROM `information_schema`.`USER_PRIVILEGES` where (GRANTEE = \"'$db_username'@'$db_hostname'\" OR GRANTEE = \"'$db_username'@'%%'\") ";
|
|
|
|
$response = DB::connection(self::CONNECTION_TEST_INSTALL)->select($query);
|
|
|
|
if (!is_array($response)) {
|
|
$info->message .= G::LoadTranslation('ID_CONNECTION_ERROR_PRIVILEGE', SYS_LANG, [$db_username]);
|
|
return $info;
|
|
}
|
|
$info->message .= G::LoadTranslation('ID_MYSQL_SUCCESS_CONNECT');
|
|
$info->result = true;
|
|
} catch (Exception $e) {
|
|
$info->result = false;
|
|
$info->message = G::LoadTranslation('ID_MYSQL_CREDENTIALS_WRONG');
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Verify/create framework shared directory structure
|
|
*
|
|
*/
|
|
private function verifySharedFrameworkPaths($sharedPath)
|
|
{
|
|
// Check if $sharedPath ends with a slash and append if necessary
|
|
if (substr($sharedPath, -1) !== DIRECTORY_SEPARATOR) {
|
|
$sharedPath .= DIRECTORY_SEPARATOR;
|
|
}
|
|
$paths = [
|
|
$sharedPath . 'fonts' => 0770,
|
|
$sharedPath . 'framework' => 0770,
|
|
$sharedPath . 'framework' . DIRECTORY_SEPARATOR . 'cache' => 0770,
|
|
];
|
|
foreach ($paths as $path => $permission) {
|
|
if (!file_exists($path)) {
|
|
G::mk_dir($path, $permission);
|
|
}
|
|
if (!file_exists($path)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|