Files
thetempusproject/install.php
Joey Kimsey 32a9711ade wip from ATB
2025-01-21 19:19:06 -05:00

421 lines
17 KiB
PHP

<?php
/**
* install.php
*
* This is the install controller for the application.
* After completion: YOU SHOULD DELETE THIS FILE.
*
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject\Controllers;
require_once 'bin/autoload.php';
use TheTempusProject\TheTempusProject;
use TheTempusProject\Classes\Controller;
use TheTempusProject\Classes\Plugin;
use TheTempusProject\Classes\Installer;
use TheTempusProject\Classes\Forms;
use TheTempusProject\Models\User;
use TheTempusProject\Classes\Email;
use TheTempusProject\Bedrock\Functions\Code;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Cookie;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Upload;
use TheTempusProject\Bedrock\Functions\Hash;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Hermes\Functions\Route;
use TheTempusProject\Canary\Bin\Canary as Debug;
class Install extends Controller {
private $installer;
private $location = false;
private $steps = [
'Welcome',
'Terms',
'Verify',
'Configure',
'Routing',
'Models',
'Plugins',
'Install',
'Resources',
'User',
'Complete',
];
/**
* This is the main builder for the rest of the controller. It mostly handle template variables used on all pages.
*/
public function __construct() {
parent::__construct();
self::$title = 'TP Installer';
self::$pageDescription = 'This is the install script for The Tempus Project.';
$this->installer = new Installer();
Template::noIndex();
Template::noFollow();
foreach ( $this->steps as $step ) {
Components::set( 'menu-' . $step, 'disabled' );
}
if ( $this->checkSession() !== false ) {
$this->location = $this->getStep();
Components::set( 'menu-' . ucfirst( $this->location ), 'active' );
} else {
Components::set( 'menu-Welcome', 'active' );
}
Components::set( 'installer-nav', Views::simpleView( 'install.nav' ) );
}
/**
* This method will reset the install hash, set the saved install step, update the session and cookie, and refresh if required.
*
* @param string $page
* @param boolean $redirect
* @return void
*/
public function nextStep( $page, $redirect = false ) {
$newHash = Code::genInstall();
$this->installer->setNode( 'installHash', $newHash, true );
$this->installer->setNode( 'installStep', $page, true );
Session::put( 'installHash', $newHash );
Cookie::put( 'installHash', $newHash );
if ( $redirect === true ) {
Redirect::reload();
}
return true;
}
public function checkSession() {
if ( empty( $this->installer->getNode('installHash') ) ) {
Debug::error( 'install hash not found on file.' );
return false;
}
$session = Session::get( 'installHash' );
$cookie = Cookie::get( 'installHash' );
$file = $this->installer->getNode('installHash');
if ( ! $session && ! $cookie ) {
Debug::error( 'install hash not found in session or cookie.' );
return false;
}
if ( $cookie && ! $session ) {
if ( $cookie !== $file ) {
Debug::error( 'install cookie did not match install file.' );
Cookie::delete( 'installHash' );
return false;
}
Debug::error( 'cookie matches file, using as session' );
Session::put( 'installHash', $cookie );
return true;
}
if ( $session !== $file ) {
Debug::error( 'session did not match file, deleting session' );
Session::delete( 'installHash' );
return false;
}
return true;
}
public function getStep() {
if ( !empty( $this->installer->getNode('installStep') ) ) {
return $this->installer->getNode('installStep');
}
Debug::error( 'install status not found.' );
return false;
}
/**
* The index method is called on the first request and all requests thereafter and is responsible for routing
* the current request to the appropriate installer step/method.
*/
public function index() {
if ( false === $this->location ) {
return $this->welcome();
}
// this seems dumb, i could probably do this better
$location = $this->location;
return $this->$location();
}
/**
* The welcome method is is just a page to submit a form and save the install.json for the first time.
*/
public function welcome() {
if ( Forms::Check( 'installStart' ) ) {
return $this->nextStep( 'terms', true );
}
if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with the Installation.' => Check::userErrors()] );
}
Views::view( 'install.welcome' );
}
/**
* The terms step is pretty straight forward. You simply need to continue to the next step, understanding
* that you agree to these terms when you continue the installation.
*/
public function terms() {
if ( Forms::Check( 'installAgreement' ) ) {
return $this->nextStep( 'verify', true );
}
if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', [ 'There was an error with the Installation.' => Check::userErrors() ] );
}
Components::set( 'TERMS', Views::simpleView( 'terms' ) );
Views::view( 'install.terms' );
}
/**
* There is a small list a of requirements for the application to run properly. These are things like sessions, emails, cookies, etc.
* This step verifies that all of these features are working as expected.
*/
public function verify() {
if ( Forms::Check( 'installCheck' ) ) {
return $this->nextStep( 'configure', true );
}
if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with the Installation.' => array_merge( Check::userErrors(), Check::systemErrors() )] );
}
Views::view( 'install.verify' );
}
/**
* One of the most important steps for installation, is the configuration. In this step, we will define some very core settings
* for the app including the app's name and database credentials.
*/
public function configure() {
if ( Forms::Check( 'installConfigure' ) ) {
$logo = 'images/logo.png';
if ( Input::exists( 'logo' ) && Upload::image( 'logo', 'System' ) ) {
$logo = 'Uploads/Images/System/' . Upload::last();
}
TheTempusProject::$activeConfig->load( BEDROCK_CONFIG_JSON );
$baseConfig = TheTempusProject::$configMatrix;
$baseConfig['main']['logo']['value'] = $logo;
$baseConfig['main']['name']['value'] = Input::postNull( 'siteName' );
$baseConfig['main']['template']['value'] = $baseConfig['main']['template']['default'];
$baseConfig['main']['tokenEnabled']['value'] = $baseConfig['main']['tokenEnabled']['default'];
$baseConfig['main']['loginLimit']['value'] = $baseConfig['main']['loginLimit']['default'];
$baseConfig['database']['dbEnabled']['value'] = $baseConfig['database']['dbEnabled']['default'];
$baseConfig['database']['dbHost']['value'] = Input::postNull( 'dbHost' );
$baseConfig['database']['dbMaxQuery']['value'] = $baseConfig['database']['dbMaxQuery']['default'];
$baseConfig['database']['dbName']['value'] = Input::postNull( 'dbName' );
$baseConfig['database']['dbPassword']['value'] = Input::postNull( 'dbPassword' );
$baseConfig['database']['dbPrefix']['value'] = Input::postNull( 'dbPrefix' );
$baseConfig['database']['dbUsername']['value'] = Input::postNull( 'dbUsername' );
if ( ! TheTempusProject::$activeConfig->generate( CONFIG_JSON, $baseConfig ) ) {
return Issues::add( 'error', 'Config file already exists so the installer has been halted. If there was an error with installation, please delete app/config/config.json manually and try again. The installer should automatically bring you back to this step.' );
}
Session::flash( 'success', 'Config saved successfully.' );
return $this->nextStep( 'routing', true );
}
if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
}
Views::view( 'install.configure' );
}
/**
* For the application to function properly on nginx or apache, the web servers must be configured correctly.
* Depending on which server you use, this step will help you set up and test the routing required for the
* application to function as expected.
*/
public function routing() {
if ( Input::exists( 'submit' ) && Forms::Check( 'installRouting' ) ) {
// if its Apache, attempt to generate the htaccess file before testing
if ( Check::isApache() ) {
if ( !$this->installer->checkHtaccess() ) {
if ( !$this->installer->saveHtaccess() ) {
Issues::add( 'error', 'There was an unexpected error when generating your htaccess file. Please see the error logs for more information.' );
}
}
}
// Apache should have the htaccess now, and Nginx should have been configured this way out of the box
if ( Route::testRouting() ) {
Session::flash( 'success', 'Routing is working as expected.' );
return $this->nextStep( 'models', true );
} else {
Issues::add( 'error', 'Could not verify url routing' );
}
// routing is busted, if its Apache, we already have the error from htaccess generation
// so Nginx is the only one that needs more info
if ( Check::isNginx() ) {
Issues::add( 'error', 'There appears to be an issue with your configuration. Certain urls are not being routed as expected.' );
}
} elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
}
Views::view( 'install.routing' );
}
/**
* Since models are required for the proper function of the app, this step is required and has no selection to make.
* This step will install all the required models excluding resources.
*/
public function models() {
$errors = [];
$options = [ 'installResources' => false ];
$models = $this->installer->getModelList( MODEL_DIRECTORY );
if ( Input::exists( 'submit' ) && Forms::Check( 'installModels' ) ) {
$error = false;
foreach ( $models as $model ) {
$result = $this->installer->installModel( $model, $options );
if ( $result === false ) {
$error = true;
continue;
}
}
if ( $error ) {
Issues::add( 'error', [ 'There was an error with the Installation.' => $this->installer->getErrors() ] );
} else {
Session::flash( 'success', [ 'Models Have been installed successfully.' => $this->installer->getErrors() ] );
return $this->nextStep( 'plugins', true );
}
} elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
}
Views::view( 'install.models', $models );
}
/**
* This step will allow the user to install any plugins currently available for installing excluding resources.
*/
public function plugins() {
$errors = [];
$options = [ 'resources_installed' => false ];
$plugins = $this->installer->getAvailablePlugins();
$selected_plugins = Input::post( 'P_' );
if ( Input::exists( 'submit' ) && Forms::Check( 'installPlugins' ) ) {
$error = false;
foreach ( $plugins as $plugin ) {
if ( ! in_array( $plugin->name, $selected_plugins ) ) {
continue;
}
$result = $this->installer->installPlugin( $plugin, $options );
if ( !$result ) {
$error = true;
continue;
}
Plugin::enable( $plugin->name, true );
}
if ( $error ) {
Issues::add( 'error', ['There was an error with the Installation.' => $this->installer->getErrors()] );
} else {
Session::flash( 'success', [ 'Plugins Have been installed successfully.' => $this->installer->getErrors() ] );
return $this->nextStep( 'resources', true );
}
} elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
}
Views::view( 'install.plugins', $plugins );
}
/**
* The resource step will cycle through the partially installed models and install any missing resources.
*/
public function resources() {
$errors = [];
if ( Input::exists( 'submit' ) && Forms::Check( 'installResources' ) ) {
$error = false;
$allModules = $this->installer->getModules(true);
foreach ( $allModules as $name => $module ) {
if ( empty( $module ) || 'unknown' === $name || empty( $name ) || empty( $module['installedVersion'] ) ) {
continue;
}
if ( 'plugin' == $module['type'] ) {
$installResult = $this->installer->installPlugin( (object) $module, [ 'resources_installed' => true ], false );
} else {
$installResult = $this->installer->installModel( (object) $module, [ 'installResources' => true ], false );
}
if ( !$installResult ) {
$error = true;
}
}
if ( $error ) {
Issues::add( 'error', ['There was an error with the Installation.' => $this->installer->getErrors()] );
} else {
Session::flash( 'success', ['Resources have been installed successfully.' => $this->installer->getErrors()] );
return $this->nextStep( 'user', true );
}
} elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
}
Views::view( 'install.resources' );
}
/**
* This is the registration step; allowing the installer to create the super admin account.
*/
public function user() {
if ( Input::exists( 'submit' ) && Forms::Check( 'installAdminUser' ) ) {
$user = new User();
if ( !$user->create( [
'username' => Input::post( 'newUsername' ),
'password' => Hash::make( Input::post( 'userPassword' ) ),
'email' => Input::post( 'userEmail' ),
'lastLogin' => time(),
'registered' => time(),
'confirmed' => 1,
'terms' => 1,
'userGroup' => 1,
] ) ) {
Issues::add( 'error', 'There was an error creating the admin user.' );
return;
}
$this->nextStep( 'complete' );
return $this->complete( true );
} elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
}
Views::view( 'install.user' );
}
/**
* This is the final step of installation. On first load it will send an email and show the final view.
* It will then redirect to the index controller and prompt the user to delete this file on any subsequent loads.
*
* @param bool $sendEmail
*/
public function complete( $sendEmail = false ) {
if ( $sendEmail ) {
Issues::add( 'success', 'The Tempus Project has been installed successfully.' );
Email::send( Input::post( 'email' ), 'install', null, [ 'template' => true ] );
return Views::view( 'install.complete' );
}
Session::flash( 'notice', 'Installation has been completed. Updates and installation can be managed in the admin panel. Please delete the install.php file.' );
Redirect::to( 'home/index' );
}
}
$app = new TheTempusProject();
if ( CANARY_ENABLED ) {
// ini_set( 'display_errors', '1' );
// ini_set( 'display_startup_errors', '1' );
// error_reporting( E_ALL );
// $app->printDebug();
}
$app->setUrl( 'install/index' );
$app->load();
if ( CANARY_DEBUG_TO_CONSOLE ) {
Components::set( 'DEBUGGING_LOG', Debug::dump() );
register_shutdown_function( [ $app::class, 'handle_shutdown' ] );
}
exit;