VvebOIDC/install/controller/index.php

432 lines
13 KiB
PHP

<?php
/**
* Vvveb
*
* Copyright (C) 2022 Ziadin Givan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Vvveb\Controller;
use function Vvveb\__;
use function Vvveb\installedLanguages;
use function Vvveb\session as sess;
use function Vvveb\setLanguage;
use Vvveb\Sql\LanguageSQL;
use Vvveb\Sql\menuSQL;
use Vvveb\Sql\RoleSQL;
use Vvveb\Sql\SiteSQL;
use Vvveb\System\Core\View;
use Vvveb\System\Extensions\Plugins;
use Vvveb\System\Extensions\Themes as ThemesList;
use Vvveb\System\Functions\Str;
use Vvveb\System\Media\Image;
use Vvveb\System\User\Admin;
use function Vvveb\userPreferedLanguage;
define('REQUIRED_EXTENSIONS', ['mysqli', 'mysqlnd', 'xml', 'libxml', 'pcre', 'zip', 'dom', 'curl', 'gettext']);
define('WRITABLE_FOLDERS', ['storage', 'storage/cache', 'storage/model', 'storage/compiled-templates', 'config', 'config/sites.php', 'public/media/', 'public/themes', 'public/image-cache', 'plugins']);
define('MIN_PHP_VERSION', '7.4.0');
define('DEFAULT_LANG', 'en_US');
#[\AllowDynamicProperties]
class Index extends Base {
private $config = ['engine' => 'mysqli', 'host' => '127.0.0.1', 'database' => 'vvveb', 'user' => 'root', 'password' => '', 'port' => null, 'prefix' => ''];
private $subdir = '';
function __construct() {
if (! ($lang = sess('language'))) {
$lang = userPreferedLanguage();
if ($lang) {
sess(['language' => $lang, 'language_id' => 1]);
}
}
if ($lang) {
setLanguage($lang);
}
$this->subdir = (V_SUBDIR_INSTALL ? V_SUBDIR_INSTALL : \Vvveb\detectSubDir());
if (\Vvveb\is_installed()) {
$admin = false;
$message = '';
try {
//check if super admin user is available
$admin = Admin::get(['role_id' => 1]);
} catch (\Exception $e) {
$this->view = View :: getInstance();
$message = __('Missing admin table or data, remove config/db.php to reinstall!') . "\n\n" . $e->getMessage();
$this->view->info[] = $message;
//die($message);
}
if ($admin && isset($admin['status']) && $admin['status'] == '1') {
$this->redirect($this->subdir . '/');
die(__('Already installed! To reinstall remove config/db.php') . "\n");
} else {
if (! $admin || ! isset($admin['role_id']) || $admin['role_id'] != '1') {
$message .= __('Invalid installation. No user with "super admin" role found!');
$this->view = View :: getInstance();
$this->view->info[] = $message;
}
}
}
}
private function checkRequirements() {
$notMet = [];
if (version_compare(PHP_VERSION, MIN_PHP_VERSION) < 0) {
$notMet[] = sprintf(__('You need at least PHP %s , your current version %s'), MIN_PHP_VERSION, PHP_VERSION);
}
foreach (REQUIRED_EXTENSIONS as $extension) {
if (! extension_loaded($extension)) {
$notMet[] = sprintf(__('PHP extension %s is not installed'), $extension);
}
}
foreach (WRITABLE_FOLDERS as $folder) {
$path = DIR_ROOT . $folder;
if (! is_writable($path) && ! @chmod($path, 0750)) {
$notMet[] = sprintf(__('"%s" is not writable'), $folder);
}
}
return $notMet;
}
function replaceInFile($file, $search, $replace) {
$content = file_get_contents($file);
if ($content) {
$content = str_replace($search, $replace, $content);
return file_put_contents($file, $content);
}
}
private function writeConfig($data) {
return \Vvveb\setConfig('db', $data);
$configFile = DIR_ROOT . 'config/db.php';
file_put_contents($configFile, "<?php\n return " . var_export($data, true) . ';');
clearstatcache(true, $configFile);
}
private function import($noimport = false) {
$config = $this->config;
array_walk($this->request->post, function ($value, $key) use (&$config) {
if (isset($config[$key])) {
$config[$key] = $value;
}
});
$config['engine'] = $config['engine'] ? $config['engine'] : 'mysqli';
if ($config['engine'] == 'sqlite') {
$config['host'] = DIR_STORAGE . 'sqlite/vvveb.db';
}
extract($config);
$prefix = $prefix ?? '';
$data['default'] = $config['engine'];
$data['connections'][$engine] = $config;
try {
if (! defined('DB_ENGINE')) {
define('DB_ENGINE', $engine);
define('DB_HOST', $host);
define('DB_USER', $user);
define('DB_PASS', $password);
define('DB_NAME', $database);
define('DB_PREFIX', $prefix);
define('DB_PORT', $port);
define('DIR_SQL', DIR_APP . 'sql/' . DB_ENGINE . '/');
}
$import = new \Vvveb\System\Import\Sql($engine, $host, $database, $user, $password, $port, $prefix);
//$import->createDb($database);
if ($engine == 'mysqli') {
$import->createDb($database);
}
$import->setPath(DIR_ROOT . "install/sql/$engine/schema/");
$import->createTables();
if ($noimport) {
$exclude = ['post*', 'product*', 'vendor*', 'manufacturer*', 'taxonomy_*', 'attribute*', 'option*', 'user.sql', 'comment*', 'digital_asset*'];
} else {
$exclude = [];
}
$import->setPath(DIR_ROOT . 'install/sql/insert/');
$import->insertData([], $exclude);
//$import->db->close();
$this->writeConfig($data);
$this->redirect(($_SERVER['REQUEST_URI'] ?? 'localhost') . '?action=install');
} catch (\Exception $e) {
$this->view->errors[] = sprintf(__('Db error: "%s" Error code: "%s"'), $e->getMessage(), $e->getCode());
}
}
function index() {
$requirements = $this->checkRequirements();
$noimport = $this->request->post['noimport'] ?? false;
if ($requirements) {
$this->view->requirements = $requirements;
}
\Vvveb\System\CacheManager::clearFrontend();
//get defaults from get parameters if passed or from env if available
foreach ($this->config as $key => &$value) {
$env = 'DB_' . strtoupper($key);
if (isset($_ENV[$env])) {
$value = $_ENV[$env];
}
if (isset($this->request->get[$key])) {
$value = $this->request->get[$key];
}
}
if ($this->request->post) {
if (isset($this->request->post['language'])) {
$lang = $this->request->post['language'];
sess(['language' => $lang, 'language_id' => 1]);
setLanguage($lang);
} else {
$this->import($noimport);
//if user data is provided (by CLI) run also step2
if (isset($this->request->post['admin'])) {
$this->install();
}
}
}
$installedLanguages = [DEFAULT_LANG => '0'] + array_flip(installedLanguages());
$languagesList = include DIR_SYSTEM . 'data/languages-list.php';
$languages = array_intersect_key($languagesList, $installedLanguages);
$this->view->config = $this->config;
if (! defined('CLI')) {
$this->view->languagesList = $languages;
$this->view->currentLanguage = sess('language') ?? DEFAULT_LANG;
}
}
function install() {
if (! defined('CLI')) {
$themes = ThemesList :: getList();
$this->view->themes = $themes;
$this->view->count = count($themes);
}
$isRootPublic = (constant('PUBLIC_PATH') == DIRECTORY_SEPARATOR) ? 'true' : 'false';
$this->view->isRootPublic = $isRootPublic;
$languagesList = include DIR_SYSTEM . 'data/languages-list.php';
if (! defined('CLI')) {
$this->view->languagesList = $languagesList;
$this->view->currentLanguage = sess('language') ?? DEFAULT_LANG;
}
if ($this->request->post) {
//set admin password
$user = $this->request->post['admin'] ?? [];
$settings = $this->request->post['settings'] ?? [];
$theme = $this->request->post['theme'] ?? 'landing';
$noecommerce = $this->request->post['noecommerce'] ?? false;
$hostname = $this->request->post['hostname'] ?? null;
$adminPath = $this->request->post['admin-path'] ?? false;
$language = $this->request->post['language'] ?? 'en_US';
$user['status'] = 1;
$result = Admin::update($user, ['username' => 'admin']);
$sites = new SiteSQL();
//$result = \Vvveb\setMultiSetting('site',$settings);
if ($noecommerce) {
Plugins::activate('hide-ecommerce', 1);
}
//set default website url
$sites = new SiteSQL();
$siteSettings = $sites->get(['site_id' => 1]);
$hasSiteSettings = false;
if (is_array($settings) && is_array($siteSettings)) {
$hasSiteSettings = true;
$data = json_decode($siteSettings['settings'], true) ?? [];
$settings = $settings + $data;
}
$settings['admin-email'] = $user['email'];
$settings['contact-email'] = $user['email'];
if (Image::formats('webp')) {
$settings['image_format'] = 'webp';
}
$site = [
'host' => $hostname ?? '*.*.*', //$_SERVER['HTTP_HOST']
'site_id' => 1,
'id' => 1,
'name' => 'Default',
'theme' => $theme,
'settings' => json_encode($settings),
];
if ($hasSiteSettings) {
$sites->edit(['site' => $site, 'site_id' => 1]);
} else {
//empty installation
$site['site_id'] = 1;
$site['key'] = '* * *';
$site['name'] = 'default';
$sites->add(['site' => $site]);
$role = new RoleSQL();
$role->add(['role' => [
'role_id' => 1,
'name' => 'super_admin',
'display_name' => 'Super Administrator',
'permissions' => '{"allow":["*"], "deny":[]}',
]]);
$languageModel = new LanguageSQL();
$languageModel->add(['language' => [
'language_id' => 1,
'name' => 'English',
'code' => 'en_US',
'locale' => 'en-us',
'slug' => 'en',
'status' => 1,
'default' => 1,
]]);
}
unset($site['settings']);
@\Vvveb\setConfig('sites.* * *', $site);
$lang = $language ?? sess('language') ?? 'en_US';
//set default language
if ($lang && $lang != 'en_US') {
$languageModel = new LanguageSQL();
$language = $languagesList[$lang];
$language['name'] = preg_replace('/$(\w+).*/', '$1', $language['name']);
$language['locale'] = $language['code'];
$language['code'] = $lang;
$language['status'] = 1;
//$installed = $languageModel->get(['code' => $lang]);
$result = $languageModel->edit(['language' => $language, 'language_id' => 1]);
sess(['language' => $lang, 'language_id' => 1]);
setLanguage($lang);
}
$error = '';
//change admin login path
if ($isRootPublic && $adminPath &&
($adminPath != 'admin' && $adminPath != 'vadmin')) {
$from = DIR_PUBLIC . 'vadmin';
$to = DIR_PUBLIC . $adminPath;
if (@rename($from, $to)) {
@\Vvveb\setConfig('admin.path', $adminPath);
//if succesful remove failsafe /admin login option
@unlink(DIR_PUBLIC . 'admin' . DS . 'index.php');
} else {
$error = sprintf(__('Renaming admin login path from %s to %s failed! use /admin/index.php path to login'), 'vadmin', $adminPath);
}
}
@\Vvveb\setConfig('app.cronkey', Str::random(32));
@\Vvveb\setConfig('app.key', Str::random(32));
//set APCu memory cache if available instead of default file cache
$cacheDriver = (function_exists('apcu_cache_info') && ini_get('apc.enabled')) ? 'APCu' : null;
foreach (['app', 'admin', 'graphql', 'rest'] as $app) {
if ($cacheDriver) {
@\Vvveb\setConfig($app . '.cache.driver', $cacheDriver);
}
@\Vvveb\setConfig($app . '.cronkey', Str::random(32));
@\Vvveb\setConfig($app . '.key', Str::random(32));
}
$subdir = $this->request->post['subdir'] ?? $this->subdir ?? false;
if ($subdir) {
$subdir = '/' . trim($subdir, '/ ');
//add subdir path to menu links
$menus = new menuSQL();
foreach ([1, 5] as $menu_id) { //main menu and footer menu id's
$menuItems = $menus->get(['menu_id' => $menu_id, 'language_id' => 1])['menu'] ?? [];
foreach ($menuItems as $menuItem) {
$data = ['url' => $this->subdir . $menuItem['url'], 'menu_item_content' => []];
$menus->editMenuItem(['menu_item' => $data, 'menu_item_id' => $menuItem['menu_item_id']]);
}
}
//try to set subdir in env.php and .htaccess
$this->replaceInFile(DIR_ROOT . 'env.php', "define('V_SUBDIR_INSTALL', false" , "define('V_SUBDIR_INSTALL', '{$subdir}'");
$this->replaceInFile(DIR_ROOT . '.htaccess', 'RewriteRule ^ index.php [L]' , "RewriteRule ^ {$subdir}/index.php [L]");
}
if ($error) {
$this->view->error[] = $error;
}
//admin auto login
if (! defined('CLI')) {
$adminInfo = Admin::get(['admin_id' => 1]);
if ($adminInfo) {
\Vvveb\session(['admin' => $adminInfo]);
}
}
$success = __('Installation succesful!');
$this->view->success[] = $success;
$admin_path = \Vvveb\adminPath();
$admin_path = str_replace($this->subdir, '', $admin_path);
$location = preg_replace('@/install.*$@', $admin_path . "/index.php?module=settings/site&site_id=1&success=$success&errors=$error#description", ($_SERVER['REQUEST_URI'] ?? ''));
$this->redirect($location);
}
$this->view->template('install.html');
}
}