2020-02-09 23:02:19 +01:00
< ? php
2022-11-18 18:06:46 +01:00
namespace Core\Documents {
2020-04-03 15:56:04 +02:00
use Documents\Install\InstallBody ;
use Documents\Install\InstallHead ;
2022-11-18 18:06:46 +01:00
use Core\Elements\HtmlDocument ;
use Core\Objects\Router\Router ;
2020-04-03 15:56:04 +02:00
2021-12-08 16:53:43 +01:00
class Install extends HtmlDocument {
2022-06-01 12:28:50 +02:00
public function __construct ( Router $router ) {
parent :: __construct ( $router , InstallHead :: class , InstallBody :: class );
2020-04-02 21:39:02 +02:00
$this -> databaseRequired = false ;
2020-02-09 23:02:19 +01:00
}
}
}
namespace Documents\Install {
2022-11-18 18:06:46 +01:00
use Core\Configuration\Configuration ;
use Core\Configuration\CreateDatabase ;
2023-01-05 22:47:17 +01:00
use Core\Driver\SQL\Expression\Count ;
2022-11-18 18:06:46 +01:00
use Core\Driver\SQL\Query\Commit ;
use Core\Driver\SQL\Query\StartTransaction ;
use Core\Driver\SQL\SQL ;
use Core\Elements\Body ;
use Core\Elements\Head ;
use Core\Elements\Link ;
use Core\Elements\Script ;
use Core\External\PHPMailer\Exception ;
use Core\External\PHPMailer\PHPMailer ;
use Core\Objects\ConnectionData ;
2022-11-20 17:13:53 +01:00
use Core\Objects\DatabaseEntity\Group ;
2020-04-03 15:56:04 +02:00
class InstallHead extends Head {
2020-02-09 23:02:19 +01:00
public function __construct ( $document ) {
parent :: __construct ( $document );
}
protected function initSources () {
$this -> loadJQuery ();
$this -> loadBootstrap ();
$this -> loadFontawesome ();
2020-04-03 15:56:04 +02:00
$this -> addJS ( Script :: CORE );
$this -> addCSS ( Link :: CORE );
$this -> addJS ( Script :: INSTALL );
2020-02-09 23:02:19 +01:00
}
2021-04-02 21:58:06 +02:00
protected function initMetas () : array {
2020-02-09 23:02:19 +01:00
return array (
array ( 'name' => 'viewport' , 'content' => 'width=device-width, initial-scale=1.0' ),
array ( 'name' => 'format-detection' , 'content' => 'telephone=yes' ),
array ( 'charset' => 'utf-8' ),
array ( " http-equiv " => 'expires' , 'content' => '0' ),
array ( " name " => 'robots' , 'content' => 'noarchive' ),
);
}
2021-04-02 21:58:06 +02:00
protected function initRawFields () : array {
2020-02-09 23:02:19 +01:00
return array ();
}
2021-04-02 21:58:06 +02:00
protected function initTitle () : string {
2020-02-09 23:02:19 +01:00
return " WebBase - Installation " ;
}
}
2020-04-03 15:56:04 +02:00
class InstallBody extends Body {
2020-02-09 23:02:19 +01:00
// Status enum
const NOT_STARTED = 0 ;
const PENDING = 1 ;
2020-04-03 15:56:04 +02:00
const SUCCESSFUL = 2 ;
2020-02-09 23:02:19 +01:00
const ERROR = 3 ;
// Step enum
2020-04-03 15:56:04 +02:00
const CHECKING_REQUIREMENTS = 1 ;
2022-02-20 23:17:17 +01:00
const INSTALL_DEPENDENCIES = 2 ;
const DATABASE_CONFIGURATION = 3 ;
const CREATE_USER = 4 ;
const ADD_MAIL_SERVICE = 5 ;
const FINISH_INSTALLATION = 6 ;
2020-02-09 23:02:19 +01:00
//
2020-04-03 15:56:04 +02:00
private string $errorString ;
private int $currentStep ;
private array $steps ;
2020-02-09 23:02:19 +01:00
function __construct ( $document ) {
parent :: __construct ( $document );
2020-02-09 23:30:26 +01:00
$this -> errorString = " " ;
2020-04-03 15:56:04 +02:00
$this -> currentStep = InstallBody :: CHECKING_REQUIREMENTS ;
$this -> steps = array ();
2020-02-09 23:02:19 +01:00
}
2021-04-07 19:43:22 +02:00
function isDocker () : bool {
return file_exists ( " /.dockerenv " );
}
2021-04-02 21:58:06 +02:00
private function getParameter ( $name ) : ? string {
if ( isset ( $_REQUEST [ $name ]) && is_string ( $_REQUEST [ $name ])) {
2020-02-09 23:02:19 +01:00
return trim ( $_REQUEST [ $name ]);
}
return NULL ;
}
2022-12-02 13:52:24 +01:00
private function yarnInstall ( string $reactDir ) : array {
$fds = [
" 1 " => [ " pipe " , " w " ],
" 2 " => [ " pipe " , " w " ],
];
$proc = proc_open ( " yarn install --frozen-lockfile --non-interactive " , $fds , $pipes , $reactDir );
$output = stream_get_contents ( $pipes [ 1 ]) . stream_get_contents ( $pipes [ 2 ]);
$status = proc_close ( $proc );
return [ $status , $output ];
}
private function yarnBuild ( string $reactDir ) : array {
$fds = [
" 1 " => [ " pipe " , " w " ],
" 2 " => [ " pipe " , " w " ],
];
$proc = proc_open ( " yarn run build " , $fds , $pipes , $reactDir );
$output = stream_get_contents ( $pipes [ 1 ]) . stream_get_contents ( $pipes [ 2 ]);
$status = proc_close ( $proc );
return [ $status , $output ];
}
2022-02-21 00:32:40 +01:00
private function composerInstall ( bool $dryRun = false ) : array {
$command = " composer install " ;
2022-02-20 23:17:17 +01:00
if ( $dryRun ) {
$command .= " --dry-run " ;
}
$fds = [
" 1 " => [ " pipe " , " w " ],
" 2 " => [ " pipe " , " w " ],
];
$dir = $this -> getExternalDirectory ();
$env = null ;
if ( ! getenv ( " HOME " )) {
$env = [ " COMPOSER_HOME " => $dir ];
}
$proc = proc_open ( $command , $fds , $pipes , $dir , $env );
$output = stream_get_contents ( $pipes [ 1 ]) . stream_get_contents ( $pipes [ 2 ]);
$status = proc_close ( $proc );
return [ $status , $output ];
}
2022-02-21 00:32:40 +01:00
private function getExternalDirectory ( bool $absolute = true ) : string {
if ( $absolute ) {
2022-11-18 18:06:46 +01:00
return implode ( DIRECTORY_SEPARATOR , [ WEBROOT , " Core " , " External " ]);
2022-02-21 00:32:40 +01:00
} else {
2022-11-18 18:06:46 +01:00
return implode ( DIRECTORY_SEPARATOR , [ " Core " , " External " ]);
2022-02-21 00:32:40 +01:00
}
2022-02-20 23:17:17 +01:00
}
2021-04-02 21:58:06 +02:00
private function getCurrentStep () : int {
2020-02-09 23:02:19 +01:00
2021-04-02 21:58:06 +02:00
if ( ! $this -> checkRequirements ()[ " success " ]) {
2020-04-03 15:56:04 +02:00
return self :: CHECKING_REQUIREMENTS ;
2020-02-09 23:02:19 +01:00
}
2022-02-20 23:17:17 +01:00
$externalDir = $this -> getExternalDirectory ();
2022-02-21 00:32:40 +01:00
$autoload = implode ( DIRECTORY_SEPARATOR , [ $externalDir , " vendor " , " autoload.php " ]);
if ( ! is_file ( $autoload )) {
2022-02-20 23:17:17 +01:00
return self :: INSTALL_DEPENDENCIES ;
} else {
2022-02-21 00:32:40 +01:00
list ( $status , $output ) = $this -> composerInstall ( true );
2022-02-20 23:17:17 +01:00
if ( $status !== 0 ) {
2022-02-21 00:32:40 +01:00
$this -> errorString = " Error executing 'composer install --dry-run'. Please verify that the command succeeds locally and then try again. Status Code: $status , Output: $output " ;
2022-02-20 23:17:17 +01:00
return self :: CHECKING_REQUIREMENTS ;
} else {
2022-02-21 00:32:40 +01:00
if ( ! contains ( $output , " Nothing to install, update or remove " )) {
2022-02-20 23:17:17 +01:00
return self :: INSTALL_DEPENDENCIES ;
}
}
}
2022-12-02 13:52:24 +01:00
// TODO: check
2022-06-20 19:52:31 +02:00
$context = $this -> getDocument () -> getContext ();
$config = $context -> getConfig ();
2020-02-09 23:02:19 +01:00
// Check if database configuration exists
2021-04-02 21:58:06 +02:00
if ( ! $config -> getDatabase ()) {
2020-02-09 23:02:19 +01:00
return self :: DATABASE_CONFIGURATION ;
}
2022-06-20 19:52:31 +02:00
$sql = $context -> getSQL ();
2021-04-02 21:58:06 +02:00
if ( ! $sql || ! $sql -> isConnected ()) {
2020-06-25 16:54:58 +02:00
return self :: DATABASE_CONFIGURATION ;
}
2023-01-05 22:47:17 +01:00
$res = $sql -> select ( new Count ()) -> from ( " User " ) -> execute ();
2020-04-02 00:02:51 +02:00
if ( $res === FALSE ) {
return self :: DATABASE_CONFIGURATION ;
} else {
if ( $res [ 0 ][ " count " ] > 0 ) {
2020-02-10 00:52:25 +01:00
$step = self :: ADD_MAIL_SERVICE ;
} else {
return self :: CREATE_USER ;
2020-02-09 23:02:19 +01:00
}
}
2020-06-25 16:54:58 +02:00
if ( $step === self :: ADD_MAIL_SERVICE ) {
2022-11-18 18:06:46 +01:00
$req = new \Core\API\Settings\Get ( $context );
2020-06-25 16:54:58 +02:00
$success = $req -> execute ( array ( " key " => " ^mail_enabled $ " ));
if ( ! $success ) {
$this -> errorString = $req -> getLastError ();
return self :: DATABASE_CONFIGURATION ;
} else if ( isset ( $req -> getResult ()[ " settings " ][ " mail_enabled " ])) {
$step = self :: FINISH_INSTALLATION ;
2022-11-18 18:06:46 +01:00
$req = new \Core\API\Settings\Set ( $context );
2020-06-25 16:54:58 +02:00
$success = $req -> execute ( array ( " settings " => array ( " installation_completed " => " 1 " )));
2020-04-02 21:19:06 +02:00
if ( ! $success ) {
$this -> errorString = $req -> getLastError ();
}
2020-02-09 23:30:26 +01:00
}
2020-02-09 23:02:19 +01:00
}
return $step ;
}
2022-02-20 23:17:17 +01:00
private function command_exist ( string $cmd ) : bool {
$return = shell_exec ( sprintf ( " which %s 2>/dev/null " , escapeshellarg ( $cmd )));
return ! empty ( $return );
}
2021-04-02 21:58:06 +02:00
private function checkRequirements () : array {
2020-02-09 23:02:19 +01:00
2020-02-09 23:30:26 +01:00
$msg = $this -> errorString ;
2020-02-09 23:02:19 +01:00
$success = true ;
$failedRequirements = array ();
2022-02-21 00:32:40 +01:00
if ( ! is_writeable ( WEBROOT )) {
$failedRequirements [] = sprintf ( " <b>%s</b> is not writeable. Try running <b>chmod 700 %s</b> " , WEBROOT , WEBROOT );
2020-04-02 21:57:06 +02:00
$success = false ;
2020-02-09 23:02:19 +01:00
}
2020-04-02 22:02:44 +02:00
if ( function_exists ( " posix_getuid " )) {
$userId = posix_getuid ();
2022-02-21 00:32:40 +01:00
if ( fileowner ( WEBROOT ) !== $userId ) {
2020-04-02 22:02:44 +02:00
$username = posix_getpwuid ( $userId )[ 'name' ];
2022-02-21 00:32:40 +01:00
$failedRequirements [] = sprintf ( " <b>%s</b> is not owned by current user: $username ( $userId ). " .
" Try running <b>chown -R $userId %s</b> or give the required directories write permissions: " .
2022-12-04 13:21:40 +01:00
" <b>Site/Configuration</b>, <b>Site/Cache</b>, <b>Site/Logs</b>, <b>Core/External</b> " ,
2022-02-21 00:32:40 +01:00
WEBROOT , WEBROOT );
2020-04-02 22:02:44 +02:00
$success = false ;
}
}
2022-02-20 23:17:17 +01:00
if ( ! function_exists ( " yaml_emit " )) {
$failedRequirements [] = " <b>YAML</b> extension is not installed. " ;
$success = false ;
}
2022-08-20 22:04:09 +02:00
$requiredVersion = '8.0' ;
if ( version_compare ( PHP_VERSION , $requiredVersion , '<' )) {
$failedRequirements [] = " PHP Version <b>>= $requiredVersion </b> is required. Got: <b> " . PHP_VERSION . " </b> " ;
2021-04-02 21:58:06 +02:00
$success = false ;
2020-02-09 23:02:19 +01:00
}
2022-02-20 23:17:17 +01:00
if ( ! $this -> command_exist ( " composer " )) {
$failedRequirements [] = " <b>Composer</b> is not installed or cannot be found. " ;
$success = false ;
}
2022-12-02 13:52:24 +01:00
if ( ! $this -> command_exist ( " yarn " )) {
$failedRequirements [] = " <b>Yarn</b> is not installed or cannot be found. " ;
$success = false ;
}
2021-04-02 21:58:06 +02:00
if ( ! $success ) {
2020-02-09 23:02:19 +01:00
$msg = " The following requirements failed the check:<br> " .
$this -> createUnorderedList ( $failedRequirements );
2020-04-02 22:25:13 +02:00
$this -> errorString = $msg ;
2020-02-09 23:02:19 +01:00
}
return array ( " success " => $success , " msg " => $msg );
}
2022-02-20 23:17:17 +01:00
private function installDependencies () : array {
2022-02-21 00:32:40 +01:00
list ( $status , $output ) = $this -> composerInstall ();
2022-12-02 13:52:24 +01:00
if ( $status === 0 ) {
$reactDir = implode ( DIRECTORY_SEPARATOR , [ WEBROOT , " react " ]);
list ( $status , $output ) = $this -> yarnInstall ( $reactDir );
if ( $status === 0 ) {
list ( $status , $output ) = $this -> yarnBuild ( $reactDir );
}
}
2022-02-20 23:17:17 +01:00
return [ " success " => $status === 0 , " msg " => $output ];
}
2021-04-02 21:58:06 +02:00
private function databaseConfiguration () : array {
2020-02-09 23:02:19 +01:00
$host = $this -> getParameter ( " host " );
$port = $this -> getParameter ( " port " );
$username = $this -> getParameter ( " username " );
$password = $this -> getParameter ( " password " );
$database = $this -> getParameter ( " database " );
2020-04-02 00:02:51 +02:00
$type = $this -> getParameter ( " type " );
2022-02-20 23:17:17 +01:00
$encoding = $this -> getParameter ( " encoding " ) ? ? " UTF8 " ;
2020-02-09 23:02:19 +01:00
$success = true ;
$missingInputs = array ();
2022-02-20 23:17:17 +01:00
if ( empty ( $host )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Host " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $port )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Port " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $username )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Username " ;
}
2021-04-02 21:58:06 +02:00
if ( is_null ( $password )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Password " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $database )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Database " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $type )) {
2020-04-02 00:02:51 +02:00
$success = false ;
$missingInputs [] = " Type " ;
}
2021-03-31 13:59:02 +02:00
$supportedTypes = array ( " mysql " , " postgres " );
2021-04-02 21:58:06 +02:00
if ( ! $success ) {
2020-02-09 23:02:19 +01:00
$msg = " Please fill out the following inputs:<br> " .
$this -> createUnorderedList ( $missingInputs );
2021-04-02 21:58:06 +02:00
} else if ( ! is_numeric ( $port ) || ( $port = intval ( $port )) < 1 || $port > 65535 ) {
2020-02-09 23:02:19 +01:00
$msg = " Port must be in range of 1-65535. " ;
$success = false ;
2021-04-02 21:58:06 +02:00
} else if ( ! in_array ( $type , $supportedTypes )) {
2020-04-02 00:02:51 +02:00
$msg = " Unsupported database type. Must be one of: " . implode ( " , " , $supportedTypes );
$success = false ;
2020-02-09 23:02:19 +01:00
} else {
2020-04-03 15:56:04 +02:00
$connectionData = new ConnectionData ( $host , $port , $username , $password );
2020-02-09 23:02:19 +01:00
$connectionData -> setProperty ( 'database' , $database );
2020-04-02 00:02:51 +02:00
$connectionData -> setProperty ( 'encoding' , $encoding );
$connectionData -> setProperty ( 'type' , $type );
2022-02-21 13:01:03 +01:00
$connectionData -> setProperty ( 'isDocker' , $this -> isDocker ());
2020-04-03 15:56:04 +02:00
$sql = SQL :: createConnection ( $connectionData );
2020-04-02 00:02:51 +02:00
$success = false ;
2021-04-02 21:58:06 +02:00
if ( is_string ( $sql )) {
2020-04-03 15:56:04 +02:00
$msg = " Error connecting to database: $sql " ;
2021-04-02 21:58:06 +02:00
} else if ( ! $sql -> isConnected ()) {
2020-04-04 15:11:38 +02:00
if ( ! $sql -> checkRequirements ()) {
2020-04-02 01:48:46 +02:00
$driverName = $sql -> getDriverName ();
$installLink = " https://www.php.net/manual/en/ $driverName .setup.php " ;
$link = $this -> createExternalLink ( $installLink );
$msg = " $driverName is not enabled yet. See: $link " ;
} else {
$msg = " Error connecting to database:<br> " . $sql -> getLastError ();
}
2020-02-09 23:02:19 +01:00
} else {
2020-04-02 00:02:51 +02:00
$msg = " " ;
$success = true ;
2020-04-03 15:56:04 +02:00
$queries = CreateDatabase :: createQueries ( $sql );
2022-02-20 18:31:54 +01:00
array_unshift ( $queries , new StartTransaction ( $sql ));
$queries [] = new Commit ( $sql );
2021-04-02 21:58:06 +02:00
foreach ( $queries as $query ) {
2022-02-20 18:31:54 +01:00
try {
if ( ! $query -> execute ()) {
$msg = " Error creating tables: " . $sql -> getLastError ();
$success = false ;
}
} finally {
if ( ! $success ) {
2022-11-27 15:58:44 +01:00
$sql -> rollback ();
2022-02-20 18:31:54 +01:00
}
}
if ( ! $success ) {
2020-04-02 00:02:51 +02:00
break ;
2020-02-09 23:02:19 +01:00
}
}
2022-06-20 19:52:31 +02:00
if ( $success ) {
$context = $this -> getDocument () -> getContext ();
$config = $context -> getConfig ();
2022-11-18 18:06:46 +01:00
if ( Configuration :: create ( \Site\Configuration\Database :: class , $connectionData ) === false ) {
2022-05-31 16:14:49 +02:00
$success = false ;
2022-06-20 19:52:31 +02:00
$msg = " Unable to write database file " ;
2022-05-31 16:14:49 +02:00
} else {
2022-06-20 19:52:31 +02:00
$config -> setDatabase ( $connectionData );
if ( ! $context -> initSQL ()) {
2022-05-31 16:14:49 +02:00
$success = false ;
2022-06-20 19:52:31 +02:00
$msg = " Unable to verify database connection after installation " ;
} else {
2022-11-18 18:06:46 +01:00
$req = new \Core\API\Routes\GenerateCache ( $context );
2022-06-20 19:52:31 +02:00
if ( ! $req -> execute ()) {
$success = false ;
$msg = " Unable to write route file: " . $req -> getLastError ();
}
2022-05-31 16:14:49 +02:00
}
}
2020-02-09 23:02:19 +01:00
}
2020-04-02 00:02:51 +02:00
$sql -> close ();
}
2020-02-09 23:02:19 +01:00
}
return array ( " success " => $success , " msg " => $msg );
}
2021-04-02 21:58:06 +02:00
private function createUser () : array {
2020-02-09 23:02:19 +01:00
2022-06-20 19:52:31 +02:00
$context = $this -> getDocument () -> getContext ();
2021-04-02 21:58:06 +02:00
if ( $this -> getParameter ( " prev " ) === " true " ) {
2022-06-20 19:52:31 +02:00
$success = $context -> getConfig () -> delete ( " Database " );
2020-02-09 23:02:19 +01:00
$msg = $success ? " " : error_get_last ();
return array ( " success " => $success , " msg " => $msg );
}
$username = $this -> getParameter ( " username " );
$password = $this -> getParameter ( " password " );
$confirmPassword = $this -> getParameter ( " confirmPassword " );
2020-04-04 01:15:59 +02:00
$email = $this -> getParameter ( " email " ) ? ? " " ;
2020-02-09 23:02:19 +01:00
$success = true ;
$missingInputs = array ();
2022-02-20 23:17:17 +01:00
if ( empty ( $username )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Username " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $password )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Password " ;
}
2022-02-20 23:17:17 +01:00
if ( empty ( $confirmPassword )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Confirm Password " ;
}
2021-04-02 21:58:06 +02:00
if ( ! $success ) {
2020-02-09 23:02:19 +01:00
$msg = " Please fill out the following inputs:<br> " .
$this -> createUnorderedList ( $missingInputs );
} else {
2022-11-18 18:06:46 +01:00
$req = new \Core\API\User\Create ( $context );
2020-06-23 18:40:43 +02:00
$success = $req -> execute ( array (
'username' => $username ,
'email' => $email ,
'password' => $password ,
'confirmPassword' => $confirmPassword ,
2022-11-20 17:13:53 +01:00
'groups' => [ Group :: ADMIN ]
2020-06-23 18:40:43 +02:00
));
$msg = $req -> getLastError ();
2020-02-09 23:02:19 +01:00
}
return array ( " msg " => $msg , " success " => $success );
}
2021-04-02 21:58:06 +02:00
private function addMailService () : array {
2020-02-09 23:02:19 +01:00
2022-06-20 19:52:31 +02:00
$context = $this -> getDocument () -> getContext ();
2021-04-02 21:58:06 +02:00
if ( $this -> getParameter ( " prev " ) === " true " ) {
2022-06-20 19:52:31 +02:00
$sql = $context -> getSQL ();
2020-04-02 00:02:51 +02:00
$success = $sql -> delete ( " User " ) -> execute ();
$msg = $sql -> getLastError ();
2020-02-09 23:02:19 +01:00
return array ( " success " => $success , " msg " => $msg );
}
2021-04-02 21:58:06 +02:00
if ( $this -> getParameter ( " skip " ) === " true " ) {
2022-11-18 18:06:46 +01:00
$req = new \Core\API\Settings\Set ( $context );
2021-04-02 21:58:06 +02:00
$success = $req -> execute ( array ( " settings " => array ( " mail_enabled " => " 0 " )));
2020-06-25 16:54:58 +02:00
$msg = $req -> getLastError ();
2020-02-09 23:02:19 +01:00
} else {
$address = $this -> getParameter ( " address " );
$port = $this -> getParameter ( " port " );
$username = $this -> getParameter ( " username " );
$password = $this -> getParameter ( " password " );
$success = true ;
$missingInputs = array ();
2022-06-20 19:52:31 +02:00
if ( empty ( $address )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " SMTP Address " ;
}
2022-06-20 19:52:31 +02:00
if ( empty ( $port )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Port " ;
}
2022-06-20 19:52:31 +02:00
if ( empty ( $username )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Username " ;
}
2021-04-02 21:58:06 +02:00
if ( is_null ( $password )) {
2020-02-09 23:02:19 +01:00
$success = false ;
$missingInputs [] = " Password " ;
}
2021-04-02 21:58:06 +02:00
if ( ! $success ) {
2020-02-09 23:02:19 +01:00
$msg = " Please fill out the following inputs:<br> " .
$this -> createUnorderedList ( $missingInputs );
2021-04-02 21:58:06 +02:00
} else if ( ! is_numeric ( $port ) || ( $port = intval ( $port )) < 1 || $port > 65535 ) {
2020-02-09 23:02:19 +01:00
$msg = " Port must be in range of 1-65535. " ;
$success = false ;
} else {
$success = false ;
2020-04-03 15:56:04 +02:00
$mail = new PHPMailer ( true );
2020-02-09 23:02:19 +01:00
$mail -> IsSMTP ();
$mail -> SMTPAuth = true ;
$mail -> Username = $username ;
$mail -> Password = $password ;
$mail -> Host = $address ;
$mail -> Port = $port ;
$mail -> SMTPSecure = 'tls' ;
$mail -> Timeout = 10 ;
try {
$success = $mail -> SmtpConnect ();
2021-04-02 21:58:06 +02:00
if ( ! $success ) {
2020-02-09 23:02:19 +01:00
$error = empty ( $mail -> ErrorInfo ) ? " Unknown Error " : $mail -> ErrorInfo ;
$msg = " Could not connect to SMTP Server: $error " ;
} else {
$success = true ;
$msg = " " ;
$mail -> smtpClose ();
}
2021-04-02 21:58:06 +02:00
} catch ( Exception $error ) {
2020-02-09 23:02:19 +01:00
$msg = " Could not connect to SMTP Server: " . $error -> errorMessage ();
}
2021-04-02 21:58:06 +02:00
if ( $success ) {
2022-11-18 18:06:46 +01:00
$req = new \Core\API\Settings\Set ( $context );
2020-06-25 16:54:58 +02:00
$success = $req -> execute ( array ( " settings " => array (
" mail_enabled " => " 1 " ,
" mail_host " => " $address " ,
" mail_port " => " $port " ,
" mail_username " => " $username " ,
" mail_password " => " $password " ,
)));
$msg = $req -> getLastError ();
2020-02-09 23:02:19 +01:00
}
}
}
return array ( " success " => $success , " msg " => $msg );
}
2021-04-02 21:58:06 +02:00
private function performStep () : array {
2020-02-09 23:02:19 +01:00
2021-04-02 21:58:06 +02:00
switch ( $this -> currentStep ) {
2020-02-09 23:02:19 +01:00
2020-04-03 15:56:04 +02:00
case self :: CHECKING_REQUIREMENTS :
2020-02-09 23:02:19 +01:00
return $this -> checkRequirements ();
2022-02-20 23:17:17 +01:00
case self :: INSTALL_DEPENDENCIES :
return $this -> installDependencies ();
2020-02-09 23:02:19 +01:00
case self :: DATABASE_CONFIGURATION :
return $this -> databaseConfiguration ();
case self :: CREATE_USER :
return $this -> createUser ();
case self :: ADD_MAIL_SERVICE :
return $this -> addMailService ();
default :
return array (
" success " => false ,
" msg " => " Invalid step number "
);
}
}
2021-04-02 21:58:06 +02:00
private function createProgressSidebar () : string {
2020-02-09 23:02:19 +01:00
$items = array ();
2021-04-02 21:58:06 +02:00
foreach ( $this -> steps as $num => $step ) {
2020-02-09 23:02:19 +01:00
$title = $step [ " title " ];
$status = $step [ " status " ];
$currentStep = ( $num == $this -> currentStep ) ? " id= \" currentStep \" " : " " ;
2021-04-02 21:58:06 +02:00
switch ( $status ) {
2020-02-09 23:02:19 +01:00
case self :: PENDING :
2021-04-02 21:58:06 +02:00
$statusIcon = $this -> createIcon ( " spinner " );
$statusText = " Loading… " ;
2020-02-09 23:02:19 +01:00
$statusColor = " muted " ;
break ;
2020-04-03 15:56:04 +02:00
case self :: SUCCESSFUL :
2021-04-02 21:58:06 +02:00
$statusIcon = $this -> createIcon ( " check-circle " );
$statusText = " Successful " ;
2020-02-09 23:02:19 +01:00
$statusColor = " success " ;
break ;
case self :: ERROR :
2021-04-02 21:58:06 +02:00
$statusIcon = $this -> createIcon ( " times-circle " );
$statusText = " Failed " ;
2020-02-09 23:02:19 +01:00
$statusColor = " danger " ;
break ;
case self :: NOT_STARTED :
default :
2020-06-25 16:54:58 +02:00
$statusIcon = $this -> createIcon ( " circle " , " far " );
2020-02-09 23:02:19 +01:00
$statusText = " Pending " ;
$statusColor = " muted " ;
break ;
}
$items [] = "
< li class = \ " list-group-item d-flex justify-content-between lh-condensed \" $currentStep >
< div >
< h6 class = \ " my-0 \" > $title </h6>
< small class = \ " text- $statusColor\ " > $statusText </ small >
</ div >
< span class = \ " text- $statusColor\ " > $statusIcon </ span >
</ li > " ;
}
return implode ( " " , $items );
}
2021-04-02 21:58:06 +02:00
private function createFormItem ( $formItem , $inline = false ) : string {
2020-02-09 23:02:19 +01:00
$title = $formItem [ " title " ];
2021-04-02 21:58:06 +02:00
$name = $formItem [ " name " ];
$type = $formItem [ " type " ];
2020-02-09 23:02:19 +01:00
$attributes = array (
" name " => $name ,
" id " => $name ,
2020-04-02 00:02:51 +02:00
" class " => " form-control "
2020-02-09 23:02:19 +01:00
);
2021-04-02 21:58:06 +02:00
if ( isset ( $formItem [ " required " ]) && $formItem [ " required " ]) {
2020-02-09 23:02:19 +01:00
$attributes [ " required " ] = " " ;
}
2020-04-02 00:02:51 +02:00
if ( $type !== " select " ) {
$attributes [ " type " ] = $type ;
2021-04-02 21:58:06 +02:00
if ( isset ( $formItem [ " value " ]) && $formItem [ " value " ]) {
2020-04-02 00:02:51 +02:00
$attributes [ " value " ] = $formItem [ " value " ];
}
2021-04-02 21:58:06 +02:00
if ( $type === " number " ) {
if ( isset ( $formItem [ " min " ]) && is_numeric ( $formItem [ " min " ]))
2020-04-02 00:02:51 +02:00
$attributes [ " min " ] = $formItem [ " min " ];
2021-04-02 21:58:06 +02:00
if ( isset ( $formItem [ " max " ]) && is_numeric ( $formItem [ " max " ]))
2020-04-02 00:02:51 +02:00
$attributes [ " max " ] = $formItem [ " max " ];
2021-04-02 21:58:06 +02:00
if ( isset ( $formItem [ " step " ]) && is_numeric ( $formItem [ " step " ]))
2020-04-02 00:02:51 +02:00
$attributes [ " step " ] = $formItem [ " step " ];
2021-04-07 19:43:22 +02:00
} else {
if ( isset ( $formItem [ " default " ])) {
$attributes [ " value " ] = $formItem [ " default " ];
}
2020-04-02 00:02:51 +02:00
}
2020-02-09 23:02:19 +01:00
}
2022-06-20 19:52:31 +02:00
// $attributes = html_attributes($attributes);
2020-04-02 00:02:51 +02:00
if ( $type === " select " ) {
$items = $formItem [ " items " ] ? ? array ();
2022-06-20 19:52:31 +02:00
$options = [];
2021-04-02 21:58:06 +02:00
foreach ( $items as $key => $val ) {
2022-06-20 19:52:31 +02:00
$options [] = html_tag_ex ( " option " , [ " value " => $key ], $val , true , false );
2020-04-02 00:02:51 +02:00
}
2020-02-09 23:02:19 +01:00
2022-06-20 19:52:31 +02:00
$element = html_tag_ex ( " select " , $attributes , $options , false );
2020-02-09 23:02:19 +01:00
} else {
2022-06-20 19:52:31 +02:00
$element = html_tag_short ( " input " , $attributes );
2020-02-09 23:02:19 +01:00
}
2022-06-20 19:52:31 +02:00
$label = html_tag_ex ( " label " , [ " for " => $name ], $title , true , false );
$className = ( $inline ? " col-md-6 mb-3 " : " d-block my-3 " );
return html_tag_ex ( " div " , [ " class " => $className ], $label . $element , false );
2020-02-09 23:02:19 +01:00
}
2022-06-20 19:52:31 +02:00
private function createProgressMainview () : string {
2020-02-09 23:02:19 +01:00
2021-04-07 19:43:22 +02:00
$isDocker = $this -> isDocker ();
2021-04-08 18:29:47 +02:00
$defaultHost = ( $isDocker ? " db " : " localhost " );
2021-04-07 19:43:22 +02:00
$defaultUsername = ( $isDocker ? " root " : " " );
$defaultPassword = ( $isDocker ? " webbasedb " : " " );
$defaultDatabase = ( $isDocker ? " webbase " : " " );
2020-02-09 23:02:19 +01:00
$views = array (
2020-04-03 15:56:04 +02:00
self :: CHECKING_REQUIREMENTS => array (
2020-02-09 23:02:19 +01:00
" title " => " Application Requirements " ,
" progressText " => " Checking requirements, please wait a moment… "
),
2022-02-20 23:17:17 +01:00
self :: INSTALL_DEPENDENCIES => array (
" title " => " Installing Dependencies " ,
" progressText " => " Please wait while required dependencies are being installed… " ,
),
2020-02-09 23:02:19 +01:00
self :: DATABASE_CONFIGURATION => array (
" title " => " Database configuration " ,
" form " => array (
2020-04-02 00:02:51 +02:00
array ( " title " => " Database Type " , " name " => " type " , " type " => " select " , " required " => true , " items " => array (
2021-03-31 13:59:02 +02:00
" mysql " => " MySQL " , " postgres " => " PostgreSQL "
2020-04-02 00:02:51 +02:00
)),
2021-04-07 19:43:22 +02:00
array ( " title " => " Username " , " name " => " username " , " type " => " text " , " required " => true , " default " => $defaultUsername ),
array ( " title " => " Password " , " name " => " password " , " type " => " password " , " default " => $defaultPassword ),
array ( " title " => " Database " , " name " => " database " , " type " => " text " , " required " => true , " default " => $defaultDatabase ),
2020-02-09 23:02:19 +01:00
array ( " type " => " row " , " items " => array (
array (
2021-04-02 21:58:06 +02:00
" title " => " Address " , " name " => " host " , " type " => " text " , " required " => true ,
2021-04-07 19:43:22 +02:00
" value " => " localhost " , " row " => true , " default " => $defaultHost
2020-02-09 23:02:19 +01:00
),
array (
2021-04-02 21:58:06 +02:00
" title " => " Port " , " name " => " port " , " type " => " number " , " required " => true ,
2020-02-09 23:02:19 +01:00
" value " => " 3306 " , " min " => " 1 " , " max " => " 65535 " , " row " => true
)
)),
2020-04-02 00:02:51 +02:00
array (
2021-04-02 21:58:06 +02:00
" title " => " Encoding " , " name " => " encoding " , " type " => " text " , " required " => false ,
2022-02-20 18:31:54 +01:00
" value " => " UTF8 "
2020-04-02 00:02:51 +02:00
),
2020-02-09 23:02:19 +01:00
)
),
self :: CREATE_USER => array (
" title " => " Create a User " ,
" form " => array (
2021-04-02 21:58:06 +02:00
array ( " title " => " Username " , " name " => " username " , " type " => " text " , " required " => true ),
array ( " title " => " Email " , " name " => " email " , " type " => " text " ),
array ( " title " => " Password " , " name " => " password " , " type " => " password " , " required " => true ),
array ( " title " => " Confirm Password " , " name " => " confirmPassword " , " type " => " password " , " required " => true ),
2020-02-10 00:52:25 +01:00
),
" previousButton " => true
2020-02-09 23:02:19 +01:00
),
self :: ADD_MAIL_SERVICE => array (
" title " => " Optional: Add Mail Service " ,
" form " => array (
2021-04-02 21:58:06 +02:00
array ( " title " => " Username " , " name " => " username " , " type " => " text " , " required " => true ),
array ( " title " => " Password " , " name " => " password " , " type " => " password " ),
2020-02-09 23:02:19 +01:00
array ( " type " => " row " , " items " => array (
array (
2021-04-02 21:58:06 +02:00
" title " => " SMTP Address " , " name " => " address " , " type " => " text " , " required " => true ,
2020-02-09 23:02:19 +01:00
" value " => " localhost " , " row " => true
),
array (
2021-04-02 21:58:06 +02:00
" title " => " Port " , " name " => " port " , " type " => " number " , " required " => true ,
2020-02-09 23:02:19 +01:00
" value " => " 587 " , " min " => " 1 " , " max " => " 65535 " , " row " => true
)
)),
),
2020-02-10 00:52:25 +01:00
" skip " => true ,
" previousButton " => true
2020-02-09 23:02:19 +01:00
),
2020-02-09 23:30:26 +01:00
self :: FINISH_INSTALLATION => array (
" title " => " Finish Installation " ,
" text " => " Installation finished, you can now customize your own website, check the source code and stuff. "
2020-02-09 23:02:19 +01:00
)
);
2021-04-02 21:58:06 +02:00
if ( ! isset ( $views [ $this -> currentStep ])) {
2020-02-09 23:02:19 +01:00
return " " ;
}
$currentView = $views [ $this -> currentStep ];
2020-02-10 00:52:25 +01:00
$prevDisabled = ! isset ( $currentView [ " previousButton " ]) || ! $currentView [ " previousButton " ];
2020-02-09 23:02:19 +01:00
$spinnerIcon = $this -> createIcon ( " spinner " );
$title = $currentView [ " title " ];
2022-06-20 19:52:31 +02:00
$html = " <h4 class= \" mb-3 \" > $title </h4><hr class= \" mb-4 \" /> " ;
2020-02-09 23:02:19 +01:00
2021-04-02 21:58:06 +02:00
if ( isset ( $currentView [ " text " ])) {
2020-02-09 23:30:26 +01:00
$text = $currentView [ " text " ];
$html .= " <div class= \" my-3 \" > $text </i></div> " ;
}
2021-04-02 21:58:06 +02:00
if ( isset ( $currentView [ " progressText " ])) {
2020-02-09 23:02:19 +01:00
$progressText = $currentView [ " progressText " ];
2022-02-20 23:17:17 +01:00
$hidden = ( ! in_array ( $this -> currentStep , [ self :: CHECKING_REQUIREMENTS , self :: INSTALL_DEPENDENCIES ]))
? " hidden " : " " ;
$html .= " <div id= \" progressText \" class= \" my-3 $hidden\ " > $progressText $spinnerIcon </ i ></ div > " ;
2020-02-09 23:02:19 +01:00
}
2021-04-02 21:58:06 +02:00
if ( isset ( $currentView [ " form " ])) {
2020-02-09 23:02:19 +01:00
$html .= " <form id= \" installForm \" > " ;
2021-04-02 21:58:06 +02:00
foreach ( $currentView [ " form " ] as $formItem ) {
2020-02-09 23:02:19 +01:00
2021-04-02 21:58:06 +02:00
if ( $formItem [ " type " ] === " row " ) {
2020-02-09 23:02:19 +01:00
$html .= " <div class= \" row \" > " ;
2021-04-02 21:58:06 +02:00
foreach ( $formItem [ " items " ] as $item ) {
2020-02-09 23:02:19 +01:00
$html .= $this -> createFormItem ( $item , true );
}
$html .= " </div> " ;
} else {
$html .= $this -> createFormItem ( $formItem );
}
}
2022-02-20 23:17:17 +01:00
$html .= " </form> " ;
2020-02-09 23:02:19 +01:00
}
$buttons = array (
2020-02-09 23:30:26 +01:00
array ( " title " => " Go Back " , " type " => " info " , " id " => " btnPrev " , " float " => " left " , " disabled " => $prevDisabled )
2020-02-09 23:02:19 +01:00
);
2021-04-02 21:58:06 +02:00
if ( $this -> currentStep != self :: FINISH_INSTALLATION ) {
2022-02-20 23:17:17 +01:00
if ( in_array ( $this -> currentStep , [ self :: CHECKING_REQUIREMENTS , self :: INSTALL_DEPENDENCIES ])) {
$buttons [] = array ( " title " => " Retry " , " type " => " success " , " id " => " btnRetry " , " float " => " right " , " hidden " => true );
2020-04-02 22:25:13 +02:00
} else {
$buttons [] = array ( " title " => " Submit " , " type " => " success " , " id " => " btnSubmit " , " float " => " right " );
}
2020-02-09 23:30:26 +01:00
} else {
$buttons [] = array ( " title " => " Finish " , " type " => " success " , " id " => " btnFinish " , " float " => " right " );
}
2021-04-02 21:58:06 +02:00
if ( isset ( $currentView [ " skip " ])) {
2020-02-09 23:02:19 +01:00
$buttons [] = array ( " title " => " Skip " , " type " => " secondary " , " id " => " btnSkip " , " float " => " right " );
}
$buttonsLeft = " " ;
$buttonsRight = " " ;
2021-04-02 21:58:06 +02:00
foreach ( $buttons as $button ) {
2020-02-09 23:02:19 +01:00
$title = $button [ " title " ];
$type = $button [ " type " ];
$id = $button [ " id " ];
$float = $button [ " float " ];
$disabled = ( isset ( $button [ " disabled " ]) && $button [ " disabled " ]) ? " disabled " : " " ;
2022-02-20 23:17:17 +01:00
$hidden = ( isset ( $button [ " hidden " ]) && $button [ " hidden " ]) ? " hidden " : " " ;
$button = " <button type= \" button \" id= \" $id\ " class = \ " btn btn- $type m-1 $hidden\ " $disabled > $title </ button > " ;
2020-02-09 23:02:19 +01:00
2021-04-02 21:58:06 +02:00
if ( $float === " left " ) {
2020-02-09 23:02:19 +01:00
$buttonsLeft .= $button ;
} else {
$buttonsRight .= $button ;
}
}
$html .=
" <div class= \" row \" >
< div class = \ " col-6 float-left text-left \" > $buttonsLeft </div>
< div class = \ " col-6 float-right text-right \" > $buttonsRight </div>
</ div > " ;
return $html ;
}
2021-04-02 21:58:06 +02:00
function getCode () : string {
2020-02-09 23:02:19 +01:00
$html = parent :: getCode ();
$this -> steps = array (
2020-04-03 15:56:04 +02:00
self :: CHECKING_REQUIREMENTS => array (
2020-02-09 23:02:19 +01:00
" title " => " Checking requirements " ,
2020-04-02 22:25:13 +02:00
" status " => self :: ERROR
2020-02-09 23:02:19 +01:00
),
2022-02-20 23:17:17 +01:00
self :: INSTALL_DEPENDENCIES => array (
" title " => " Install dependencies " ,
" status " => self :: NOT_STARTED
),
2020-02-09 23:02:19 +01:00
self :: DATABASE_CONFIGURATION => array (
" title " => " Database configuration " ,
" status " => self :: NOT_STARTED
),
self :: CREATE_USER => array (
" title " => " Create User " ,
" status " => self :: NOT_STARTED
),
self :: ADD_MAIL_SERVICE => array (
" title " => " Add Mail Service " ,
" status " => self :: NOT_STARTED
),
2020-02-09 23:30:26 +01:00
self :: FINISH_INSTALLATION => array (
" title " => " Finish Installation " ,
2020-02-09 23:02:19 +01:00
" status " => self :: NOT_STARTED
),
);
$this -> currentStep = $this -> getCurrentStep ();
// set status
2021-04-02 21:58:06 +02:00
for ( $step = self :: CHECKING_REQUIREMENTS ; $step < $this -> currentStep ; $step ++ ) {
2020-04-03 15:56:04 +02:00
$this -> steps [ $step ][ " status " ] = self :: SUCCESSFUL ;
2020-02-09 23:02:19 +01:00
}
2021-04-02 21:58:06 +02:00
if ( $this -> currentStep == self :: FINISH_INSTALLATION ) {
2020-04-03 15:56:04 +02:00
$this -> steps [ $this -> currentStep ][ " status " ] = self :: SUCCESSFUL ;
2020-02-09 23:30:26 +01:00
}
2020-02-09 23:02:19 +01:00
// POST
2021-04-02 21:58:06 +02:00
if ( $_SERVER [ 'REQUEST_METHOD' ] == 'POST' ) {
2022-02-20 23:17:17 +01:00
if ( ! isset ( $_REQUEST [ 'status' ])) {
$response = $this -> performStep ();
} else {
$response = [ " error " => $this -> errorString ];
}
2020-04-02 22:25:13 +02:00
$response [ " step " ] = $this -> currentStep ;
2020-02-09 23:02:19 +01:00
die ( json_encode ( $response ));
}
$progressSidebar = $this -> createProgressSidebar ();
2022-06-20 19:52:31 +02:00
$progressMainView = $this -> createProgressMainview ();
2020-06-25 21:53:33 +02:00
2020-02-09 23:30:26 +01:00
$errorStyle = ( $this -> errorString ? '' : ' style="display:none"' );
2020-02-10 00:52:25 +01:00
$errorClass = ( $this -> errorString ? ' alert-danger' : '' );
2020-02-09 23:02:19 +01:00
$html .= "
< body class = \ " bg-light \" >
< div class = \ " container \" >
< div class = \ " py-5 text-center \" >
< h2 > WebBase - Installation </ h2 >
< p class = \ " lead \" >
Process the following steps and fill out the required forms to install your WebBase - Installation .
</ p >
</ div >
< div class = \ " row \" >
< div class = \ " col-md-4 order-md-2 mb-4 \" >
< h4 class = \ " d-flex justify-content-between align-items-center mb-3 \" >
< span class = \ " text-muted \" >Progress</span>
</ h4 >
< ul class = \ " list-group mb-3 \" >
$progressSidebar
</ ul >
</ div >
< div class = \ " col-md-8 order-md-1 \" >
2022-06-20 19:52:31 +02:00
$progressMainView
2020-04-03 22:10:21 +02:00
< div class = \ " alert $errorClass mt-4 \" id= \" status \" $errorStyle > $this->errorString </div>
2020-02-09 23:02:19 +01:00
</ div >
</ div >
</ div >
</ body > " ;
return $html ;
}
}
2020-04-04 15:11:38 +02:00
}