3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
10 * Copyright © 2013-2014 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2013-2014 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.8 - 2013-01-09
37 namespace Galette\Core
;
40 use Laminas\Db\Adapter\Adapter
;
43 * Galette installation
48 * @author Johan Cwiklinski <johan@x-tnd.be>
49 * @copyright 2013-2014 The Galette Team
50 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
51 * @link http://galette.tuxfamily.org
52 * @since Available since 0.8 - 2013-01-09
59 const STEP_DB_CHECKS
= 3;
60 const STEP_VERSION
= 4; //only for update
61 const STEP_DB_UPGRADE
= 5;
62 const STEP_DB_INSTALL
= 6;
64 const STEP_GALETTE_INIT
= 8;
70 //db version/galette version mapper
71 private $versions_mapper = array(
82 private $_installed_version;
91 private $_db_connected;
94 private $_admin_login;
102 public function __construct()
104 $this->_step
= self
::STEP_CHECK
;
106 $this->_version
= str_replace('v', '', GALETTE_VERSION
);
107 $this->_db_connected
= false;
108 $this->_db_prefix
= null;
112 * Return current step title
116 public function getStepTitle()
119 switch ($this->_step
) {
120 case self
::STEP_CHECK
:
121 $step_title = _T("Checks");
123 case self
::STEP_TYPE
:
124 $step_title = _T("Installation mode");
127 $step_title = _T("Database");
129 case self
::STEP_DB_CHECKS
:
130 $step_title = _T("Database access and permissions");
132 case self
::STEP_VERSION
:
133 $step_title = _T("Previous version selection");
135 case self
::STEP_DB_UPGRADE
:
136 $step_title = _T("Datapase upgrade");
138 case self
::STEP_DB_INSTALL
:
139 $step_title = _T("Tables Creation");
141 case self
::STEP_ADMIN
:
142 $step_title = _T("Admin parameters");
144 case self
::STEP_GALETTE_INIT
:
145 $step_title = _T("Galette initialization");
148 $step_title = _T("End!");
155 * HTML validation image
157 * @param boolean $arg Argument
159 * @return html string
161 public function getValidationImage($arg)
163 $img_name = ($arg === true) ?
'valid' : 'invalid';
164 $src = GALETTE_THEME_DIR
. 'images/icon-' . $img_name . '.png';
165 $alt = ($arg === true) ?
_T("Ok") : _T("Ko");
166 $img = '<img src="' . $src . '" alt="' . $alt . '"/>';
175 public function getMode()
185 public function isInstall()
187 return $this->_mode
=== self
::INSTALL
;
195 public function isUpgrade()
197 return $this->_mode
=== self
::UPDATE
;
201 * Set installation mode
203 * @param char $mode Requested mode
207 public function setMode($mode)
209 if ($mode === self
::INSTALL ||
$mode === self
::UPDATE
) {
210 $this->_mode
= $mode;
212 throw new \
UnexpectedValueException('Unknown mode "' . $mode . '"');
217 * Go back to previous step
221 public function atPreviousStep()
223 if ($this->_step
> 0) {
225 $this->_step
- 1 !== self
::STEP_DB_INSTALL
226 && $this->_step
!== self
::STEP_END
228 if ($this->_step
=== self
::STEP_DB_INSTALL
) {
229 $this->_step
= self
::STEP_DB_CHECKS
;
231 if ($this->_step
=== self
::STEP_DB_UPGRADE
) {
232 $this->setInstalledVersion(null);
234 $this->_step
= $this->_step
- 1;
238 if ($this->_step
=== self
::STEP_END
) {
239 $msg = 'Ok man, install is finished already!';
241 $msg = 'It is forbidden to rerun database install!';
243 Analog
::log($msg, Analog
::WARNING
);
249 * Are we at check step?
253 public function isCheckStep()
255 return $this->_step
=== self
::STEP_CHECK
;
259 * Set step to type of installation
263 public function atTypeStep()
265 $this->_step
= self
::STEP_TYPE
;
269 * Are we at type step?
273 public function isTypeStep()
275 return $this->_step
=== self
::STEP_TYPE
;
279 * Set step to database information
283 public function atDbStep()
285 $this->_step
= self
::STEP_DB
;
289 * Are we at database step?
293 public function isDbStep()
295 return $this->_step
=== self
::STEP_DB
;
303 public function postCheckDb()
305 return $this->_step
> self
::STEP_DB_CHECKS
;
311 * @param string $type Database type
312 * @param array $errs Errors array
316 public function setDbType($type, &$errs)
321 $this->_db_type
= $type;
324 $errs[] = _T("Database type unknown");
333 public function getDbType()
335 return $this->_db_type
;
339 * Set connection information
341 * @param string $host Database host
342 * @param string $port Database port
343 * @param string $name Database name
344 * @param string $user Database user name
345 * @param string $pass Database user's password
349 public function setDsn($host, $port, $name, $user, $pass)
351 $this->_db_host
= $host;
352 $this->_db_port
= $port;
353 $this->_db_name
= $name;
354 $this->_db_user
= $user;
355 $this->_db_pass
= $pass;
361 * @param string $prefix Prefix
365 public function setTablesPrefix($prefix)
367 $this->_db_prefix
= $prefix;
371 * Retrieve database host
375 public function getDbHost()
377 return $this->_db_host
;
381 * Retrieve database port
385 public function getDbPort()
387 return $this->_db_port
;
391 * Retrieve database name
395 public function getDbName()
397 return $this->_db_name
;
401 * Retrieve database user
405 public function getDbUser()
407 return $this->_db_user
;
411 * Retrieve database password
415 public function getDbPass()
417 return $this->_db_pass
;
421 * Retrieve tables prefix
425 public function getTablesPrefix()
427 return $this->_db_prefix
;
431 * Set step to database checks
435 public function atDbCheckStep()
437 $this->_step
= self
::STEP_DB_CHECKS
;
441 * Are we at database check step?
445 public function isDbCheckStep()
447 return $this->_step
=== self
::STEP_DB_CHECKS
;
451 * Test database connection
453 * @return true|array true if connection was successfull,
454 * an array with some infos otherwise
456 public function testDbConnexion()
458 return Db
::testConnectivity(
469 * Is database connexion ok?
473 public function isDbConnected()
475 return $this->_db_connected
;
479 * Set step to version selection
483 public function atVersionSelection()
485 $this->_step
= self
::STEP_VERSION
;
489 * Are we at version selection step?
493 public function isVersionSelectionStep()
495 return $this->_step
=== self
::STEP_VERSION
;
499 * Set step to database installation
503 public function atDbInstallStep()
505 $this->_step
= self
::STEP_DB_INSTALL
;
509 * Are we at db installation step?
513 public function isDbinstallStep()
515 return $this->_step
=== self
::STEP_DB_INSTALL
;
519 * Set step to database upgrade
523 public function atDbUpgradeStep()
525 $this->_step
= self
::STEP_DB_UPGRADE
;
529 * Are we at db upgrade step?
533 public function isDbUpgradeStep()
535 return $this->_step
=== self
::STEP_DB_UPGRADE
;
540 * Install/Update SQL scripts
542 * @param string $path Path to scripts (defaults to core scripts)
546 public function getScripts($path = null)
548 if ($path === null) {
549 $path = GALETTE_ROOT
. '/install';
551 $update_scripts = array();
553 if ($this->isUpgrade()) {
554 $update_scripts = self
::getUpdateScripts(
557 $this->_installed_version
560 $update_scripts['current'] = $this->_db_type
. '.sql';
563 return $update_scripts;
567 * List updates scripts from given path
569 * @param string $path Scripts path
570 * @param string $db_type Database type
571 * @param string $version Previous version, defaults to null
573 * @return array If a previous version is provided, update scripts
574 * file path from this one to the latest will be returned.
575 * If no previous version is provided, that will return all
576 * updates versions known.
578 public static function getUpdateScripts(
583 $dh = opendir($path . '/scripts');
584 $php_update_scripts = array();
585 $sql_update_scripts = array();
587 while (($file = readdir($dh)) !== false) {
588 if (preg_match("/upgrade-to-(.*).php/", $file, $ver)) {
589 if ($version === null) {
590 $php_update_scripts[$ver[1]] = $ver[1];
592 if ($version <= $ver[1]) {
593 $php_update_scripts[$ver[1]] = $file;
599 "/upgrade-to-(.*)-" . $db_type . ".sql/",
604 if ($version === null) {
605 $sql_update_scripts[$ver[1]] = $ver[1];
607 if ($version <= $ver[1]) {
608 $sql_update_scripts[$ver[1]] = $file;
613 $update_scripts = array_merge($sql_update_scripts, $php_update_scripts);
615 ksort($update_scripts);
617 return $update_scripts;
621 * Execute SQL scripts
623 * @param Galette\Core\Db $zdb Database instance
627 public function executeScripts($zdb)
629 $queries_results = array();
630 $fatal_error = false;
631 $update_scripts = $this->getScripts();
633 $this->_report
= array();
634 $scripts_path = GALETTE_ROOT
. '/install/scripts/';
636 foreach ($update_scripts as $key => $val) {
637 if (substr($val, -strlen('.sql')) === '.sql') {
638 //just a SQL script, run it
639 $script = fopen($scripts_path . $val, 'r');
641 if ($script === false) {
642 throw new \
RuntimeException(
643 'Unable to read SQL script from ' . $scripts_path . $val
647 $sql_query .= @fread
(
649 @filesize
($scripts_path . $val)
652 //we got an update class
653 include_once $scripts_path . $val;
654 $className = '\Galette\Updates\UpgradeTo' .
655 str_replace('.', '', $key);
661 $updater = new $className();
662 if ($updater instanceof \Galette\Updater\AbstractUpdater
) {
663 $updater->run($zdb, $this);
664 $ret = $updater->getReport();
665 $this->_report
= array_merge($this->_report
, $ret);
669 'Update class does not extends AbstractUpdater!',
674 $ret['message'] = str_replace(
677 _T("%version script has been successfully executed :)")
680 $this->_report
[] = $ret;
681 } catch (\RuntimeException
$e) {
686 $ret['message'] = str_replace(
689 _T("Unable to run %version update script :(")
692 $this->_report
[] = $ret;
697 if ($sql_query !== '') {
698 $sql_res = $this->executeSql($zdb, $sql_query);
699 $fatal_error = !$sql_res;
701 return !$fatal_error;
705 * Executes SQL queries
707 * @param Db $zdb Database instance
708 * @param string $sql_query SQL instructions
712 public function executeSql($zdb, $sql_query)
714 $fatal_error = false;
716 // begin : copyright (2002) the phpbb group (support@phpbb.com)
717 // load in the sql parser
718 include_once GALETTE_ROOT
. 'includes/sql_parse.php';
720 $sql_query = preg_replace('/galette_/', $this->_db_prefix
, $sql_query);
721 $sql_query = remove_remarks($sql_query);
723 $sql_query = split_sql_file($sql_query, ';');
725 $zdb->connection
->beginTransaction();
727 for ($i = 0; $i < sizeof($sql_query); $i++
) {
728 $query = trim($sql_query[$i]);
729 if ($query != '' && $query[0] != '-') {
739 Adapter
::QUERY_MODE_EXECUTE
742 } catch (\Exception
$e) {
743 $log_lvl = Analog
::WARNING
;
744 //if error are on drop, DROP, rename or RENAME we can continue
745 $parts = explode(' ', $query, 1);
747 (strcasecmp(trim($parts[0]), 'drop') != 0)
748 && (strcasecmp(trim($parts[0]), 'rename') != 0)
750 $log_lvl = Analog
::ERROR
;
751 $ret['debug'] = $e->getMessage();
752 $ret['query'] = $query;
759 'Error executing query | ' . $e->getMessage(),
764 $queries_results[] = $ret;
769 $zdb->connection
->rollBack();
771 $zdb->connection
->commit();
774 $this->_report
= array_merge($this->_report
, $queries_results);
775 return !$fatal_error;
779 * Retrieve database installation report
783 public function getDbInstallReport()
785 return $this->_report
;
789 * Reinitialize report array
793 public function reinitReport()
795 $this->_report
= array();
799 * Set step to super admin information
803 public function atAdminStep()
805 $this->_step
= self
::STEP_ADMIN
;
809 * Are we at super admin information step?
813 public function isAdminStep()
815 return $this->_step
=== self
::STEP_ADMIN
;
819 * Set super administrator information
821 * @param string $login Login
822 * @param string $pass Password
826 public function setAdminInfos($login, $pass)
828 $this->_admin_login
= $login;
829 $this->_admin_pass
= password_hash($pass, PASSWORD_BCRYPT
);
833 * Retrieve super admin login
837 public function getAdminLogin()
839 return $this->_admin_login
;
843 * Retrieve super admin password
847 public function getAdminPass()
849 return $this->_admin_pass
;
853 * Set step to Galette initialization
857 public function atGaletteInitStep()
859 $this->_step
= self
::STEP_GALETTE_INIT
;
863 * Are we at Galette initialization step?
867 public function isGaletteInitStep()
869 return $this->_step
=== self
::STEP_GALETTE_INIT
;
873 * Load existing config
875 * @param array $post_data Data posted
876 * @param array $error_detected Errors array
880 public function loadExistingConfig($post_data, &$error_detected)
882 if (file_exists(GALETTE_CONFIG_PATH
. 'config.inc.php')) {
883 $existing = $this->loadExistingConfigFile($post_data);
885 if ($existing['db_type'] !== null) {
886 $this->setDbType($existing['db_type'], $error_detected);
890 $existing['db_host'] !== null
891 ||
$existing['db_user'] !== null
892 ||
$existing['db_name'] !== null
895 $existing['db_host'],
896 $existing['db_port'],
897 $existing['db_name'],
898 $existing['db_user'],
903 if ($existing['prefix'] !== null) {
904 $this->setTablesPrefix(
912 * Load contents from existing config file
914 * @param array $post_data Data posted
915 * @param boolean $pass Retrieve password
919 private function loadExistingConfigFile($post_data = array(), $pass = false)
930 if (file_exists(GALETTE_CONFIG_PATH
. 'config.inc.php')) {
931 $conf = file_get_contents(GALETTE_CONFIG_PATH
. 'config.inc.php');
932 if ($conf !== false) {
933 if (!isset($post_data['install_dbtype'])) {
935 '/TYPE_DB["\'], ["\'](.*)["\']\);/',
939 if (isset($matches[1])) {
940 $existing['db_type'] = $matches[1];
943 if (!isset($post_data['install_dbhost'])) {
945 '/HOST_DB["\'], ["\'](.*)["\']\);/',
949 if (isset($matches[1])) {
950 $existing['db_host'] = $matches[1];
953 if (!isset($post_data['install_dbport'])) {
955 '/PORT_DB["\'], ["\'](.*)["\']\);/',
959 if (isset($matches[1])) {
960 $existing['db_port'] = $matches[1];
963 if (!isset($post_data['install_dbuser'])) {
965 '/USER_DB["\'], ["\'](.*)["\']\);/',
969 if (isset($matches[1])) {
970 $existing['db_user'] = $matches[1];
973 if (!isset($post_data['install_dbname'])) {
975 '/NAME_DB["\'], ["\'](.*)["\']\);/',
979 if (isset($matches[1])) {
980 $existing['db_name'] = $matches[1];
985 if (!isset($post_data['install_dbprefix'])) {
987 '/PREFIX_DB["\'], ["\'](.*)["\']\);/',
991 if (isset($matches[1])) {
992 $existing['prefix'] = $matches[1];
996 if ($pass === true) {
998 '/PWD_DB["\'], ["\'](.*)["\']\);/',
1002 if (isset($matches[1])) {
1003 $existing['pwd_db'] = $matches[1];
1013 * Write configuration file to disk
1017 public function writeConfFile()
1021 'message' => _T("Write configuration file"),
1025 //if config file is already up-to-date, nothing to write
1026 $existing = $this->loadExistingConfigFile(array(), true);
1029 isset($existing['db_type'])
1030 && $existing['db_type'] == $this->_db_type
1031 && isset($existing['db_host'])
1032 && $existing['db_host'] == $this->_db_host
1033 && isset($existing['db_port'])
1034 && $existing['db_port'] == $this->_db_port
1035 && isset($existing['db_user'])
1036 && $existing['db_user'] == $this->_db_user
1037 && isset($existing['pwd_db'])
1038 && $existing['pwd_db'] == $this->_db_pass
1039 && isset($existing['db_name'])
1040 && $existing['db_name'] == $this->_db_name
1041 && isset($existing['prefix'])
1042 && $existing['prefix'] == $this->_db_prefix
1045 'Config file is already up-to-date, nothing to do.',
1049 $this->_report
[] = array(
1050 'message' => _T("Config file already exists and is up to date"),
1056 $conffile = GALETTE_CONFIG_PATH
. 'config.inc.php';
1058 is_writable(GALETTE_CONFIG_PATH
)
1059 && (!file_exists($conffile) ||
file_exists($conffile) && is_writable($conffile))
1060 && $fd = @fopen
($conffile, 'w')
1063 define('TYPE_DB', '" . $this->_db_type
. "');
1064 define('HOST_DB', '" . $this->_db_host
. "');
1065 define('PORT_DB', '" . $this->_db_port
. "');
1066 define('USER_DB', '" . $this->_db_user
. "');
1067 define('PWD_DB', '" . $this->_db_pass
. "');
1068 define('NAME_DB', '" . $this->_db_name
. "');
1069 define('PREFIX_DB', '" . $this->_db_prefix
. "');
1074 Analog
::log('Configuration file written on disk', Analog
::INFO
);
1079 _T("Unable to create configuration file (%path)")
1081 Analog
::log($str, Analog
::WARNING
);
1082 $ret['error'] = $str;
1085 $this->_report
[] = $ret;
1090 * Initialize Galette relevant objects
1092 * @param I18n $i18n I18n
1093 * @param Db $zdb Database instance
1094 * @param Login $login Loged in instance
1098 public function initObjects(I18n
$i18n, Db
$zdb, Login
$login)
1100 if ($this->isInstall()) {
1101 $preferences = new Preferences($zdb, false);
1102 $ct = new \Galette\Entity\
ContributionsTypes($zdb);
1103 $status = new \Galette\Entity\
Status($zdb);
1104 include_once '../includes/fields_defs/members_fields.php';
1105 include_once '../includes/fields_defs/members_fields_cats.php';
1106 $fc = new \Galette\Entity\
FieldsConfig(
1108 \Galette\Entity\Adherent
::TABLE
,
1110 $members_fields_cats,
1113 //$fc = new \Galette\Entity\FieldsCategories();
1114 $texts = new \Galette\Entity\
Texts($preferences);
1115 $titles = new \Galette\Repository\
Titles();
1117 $models = new \Galette\Repository\
PdfModels($zdb, $preferences, $login);
1119 $this->_error
= false;
1121 //Install preferences
1122 $res = $preferences->installInit(
1124 $this->getAdminLogin(),
1125 $this->getAdminPass()
1127 $this->proceedReport(_T("Preferences"), $res);
1129 //Install contributions types
1130 $res = $ct->installInit();
1131 $this->proceedReport(_T("Contributions types"), $res);
1134 $res = $status->installInit();
1135 $this->proceedReport(_T("Status"), $res);
1137 //Install fields configuration and categories
1138 $res = $fc->installInit();
1139 $this->proceedReport(_T("Fields config and categories"), $res);
1142 $res = $texts->installInit(false);
1143 $this->proceedReport(_T("Mails texts"), $res);
1146 $res = $titles->installInit($zdb);
1147 $this->proceedReport(_T("Titles"), $res);
1149 //Install PDF models
1150 $res = $models->installInit(false);
1151 $this->proceedReport(_T("PDF Models"), $res);
1153 return !$this->_error
;
1154 } elseif ($this->isUpgrade()) {
1155 $preferences = new Preferences($zdb);
1156 $preferences->store();
1157 $this->proceedReport(_T("Update preferences"), true);
1159 $models = new \Galette\Repository\
PdfModels($zdb, $preferences, new Login($zdb, $i18n, new \RKA\
Session()));
1160 $res = $models->installInit(true);
1161 $this->proceedReport(_T("Update models"), true);
1163 $texts = new \Galette\Entity\
Texts($preferences);
1164 $res = $texts->installInit(true);
1165 $this->proceedReport(_T("Mails texts"), true);
1172 * Proceed installation report for each Entity/Repository
1174 * @param string $msg Report message title
1175 * @param mixed $res Initialialization result
1179 private function proceedReport($msg, $res)
1186 if ($res instanceof \Exception
) {
1187 $ret['debug'] = $res->getMessage();
1188 $this->_error
= true;
1192 $this->_report
[] = $ret;
1195 * Retrieve galette initialization report
1199 public function getInitializationReport()
1201 return $this->_report
;
1205 * Set step to database installation
1209 public function atEndStep()
1211 $this->_step
= self
::STEP_END
;
1215 * Are we at end step?
1219 public function isEndStep()
1221 return $this->_step
=== self
::STEP_END
;
1225 * Set installed version if we're upgrading
1227 * @param string $version Installed version
1231 public function setInstalledVersion($version)
1233 $this->_installed_version
= $version;
1237 * Current Galette installed version, according to database
1239 * @param Db $zdb Database instance
1243 public function getCurrentVersion($zdb)
1246 $db_ver = $zdb->getDbVersion(true);
1247 if (isset($this->versions_mapper
[$db_ver])) {
1248 return $this->versions_mapper
[$db_ver];
1252 } catch (\LogicException
$e) {
1258 * Check if step is passed
1260 * @param int $step Step
1264 public function isStepPassed($step)
1266 return $this->_step
> $step;