]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Core/Install.php
Add telemetry step at install
[galette.git] / galette / lib / Galette / Core / Install.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette installation
7 *
8 * PHP version 5
9 *
10 * Copyright © 2013-2023 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
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.
18 *
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.
23 *
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/>.
26 *
27 * @category Core
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2013-2023 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
35 */
36
37 namespace Galette\Core;
38
39 use Throwable;
40 use Analog\Analog;
41 use Laminas\Db\Adapter\Adapter;
42
43 /**
44 * Galette installation
45 *
46 * @category Core
47 * @name Install
48 * @package Galette
49 * @author Johan Cwiklinski <johan@x-tnd.be>
50 * @copyright 2013-2023 The Galette Team
51 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
52 * @link http://galette.tuxfamily.org
53 * @since Available since 0.8 - 2013-01-09
54 */
55 class Install
56 {
57 public const STEP_CHECK = 0;
58 public const STEP_TYPE = 1;
59 public const STEP_DB = 2;
60 public const STEP_DB_CHECKS = 3;
61 public const STEP_VERSION = 4; //only for update
62 public const STEP_DB_UPGRADE = 5;
63 public const STEP_DB_INSTALL = 6;
64 public const STEP_ADMIN = 7;
65 public const STEP_TELEMETRY = 8;
66 public const STEP_GALETTE_INIT = 9;
67 public const STEP_END = 10;
68
69 public const INSTALL = 'i';
70 public const UPDATE = 'u';
71
72 //db version/galette version mapper
73 private $versions_mapper = array(
74 '0.700' => '0.70',
75 '0.701' => '0.71',
76 '0.702' => '0.74',
77 '0.703' => '0.75',
78 '0.704' => '0.76'
79 );
80
81 protected $_step;
82 private $_mode;
83 private $_version;
84 private $_installed_version;
85
86 private $_db_type;
87 private $_db_host;
88 private $_db_port;
89 private $_db_name;
90 private $_db_user;
91 private $_db_pass;
92 private $_db_prefix;
93
94 private $_db_connected;
95 private $_report;
96
97 private $_admin_login;
98 private $_admin_pass;
99
100 private $_error;
101
102 /**
103 * Main constructor
104 */
105 public function __construct()
106 {
107 $this->_step = self::STEP_CHECK;
108 $this->_mode = null;
109 $this->_version = str_replace('v', '', GALETTE_VERSION);
110 $this->_db_connected = false;
111 $this->_db_prefix = null;
112 }
113
114 /**
115 * Return current step title
116 *
117 * @return string
118 */
119 public function getStepTitle()
120 {
121 $step_title = null;
122 switch ($this->_step) {
123 case self::STEP_CHECK:
124 $step_title = _T("Checks");
125 break;
126 case self::STEP_TYPE:
127 $step_title = _T("Installation mode");
128 break;
129 case self::STEP_DB:
130 $step_title = _T("Database");
131 break;
132 case self::STEP_DB_CHECKS:
133 $step_title = _T("Database access and permissions");
134 break;
135 case self::STEP_VERSION:
136 $step_title = _T("Previous version selection");
137 break;
138 case self::STEP_DB_UPGRADE:
139 $step_title = _T("Datapase upgrade");
140 break;
141 case self::STEP_DB_INSTALL:
142 $step_title = _T("Tables Creation");
143 break;
144 case self::STEP_ADMIN:
145 $step_title = _T("Admin parameters");
146 break;
147 case self::STEP_TELEMETRY:
148 $step_title = _T("Telemetry");
149 break;
150 case self::STEP_GALETTE_INIT:
151 $step_title = _T("Galette initialization");
152 break;
153 case self::STEP_END:
154 $step_title = _T("End!");
155 break;
156 }
157 return $step_title;
158 }
159
160 /**
161 * HTML validation image
162 *
163 * @param bool $arg Argument
164 *
165 * @return string html string
166 */
167 public function getValidationImage($arg)
168 {
169 $img_name = ($arg === true) ? 'green check' : 'red times';
170 $alt = ($arg === true) ? _T("Ok") : _T("Ko");
171 $img = '<i class="ui ' . $img_name . ' icon"></i><span class="displaynone">' . $alt . '</span>';
172 return $img;
173 }
174
175 /**
176 * Get current mode
177 *
178 * @return string
179 */
180 public function getMode()
181 {
182 return $this->_mode;
183 }
184
185 /**
186 * Are we installing?
187 *
188 * @return boolean
189 */
190 public function isInstall()
191 {
192 return $this->_mode === self::INSTALL;
193 }
194
195 /**
196 * Are we upgrading?
197 *
198 * @return boolean
199 */
200 public function isUpgrade()
201 {
202 return $this->_mode === self::UPDATE;
203 }
204
205 /**
206 * Set installation mode
207 *
208 * @param string $mode Requested mode
209 *
210 * @return void
211 */
212 public function setMode($mode)
213 {
214 if ($mode === self::INSTALL || $mode === self::UPDATE) {
215 $this->_mode = $mode;
216 } else {
217 throw new \UnexpectedValueException('Unknown mode "' . $mode . '"');
218 }
219 }
220
221 /**
222 * Go back to previous step
223 *
224 * @return void
225 */
226 public function atPreviousStep()
227 {
228 if ($this->_step > 0) {
229 if (
230 $this->_step - 1 !== self::STEP_DB_INSTALL
231 && $this->_step !== self::STEP_END
232 ) {
233 if ($this->_step === self::STEP_DB_INSTALL) {
234 $this->_step = self::STEP_DB_CHECKS;
235 } else {
236 if ($this->_step === self::STEP_DB_UPGRADE) {
237 $this->setInstalledVersion(null);
238 }
239 $this->_step = $this->_step - 1;
240 }
241 } else {
242 $msg = null;
243 if ($this->_step === self::STEP_END) {
244 $msg = 'Ok man, install is finished already!';
245 } else {
246 $msg = 'It is forbidden to rerun database install!';
247 }
248 Analog::log($msg, Analog::WARNING);
249 }
250 }
251 }
252
253 /**
254 * Are we at check step?
255 *
256 * @return boolean
257 */
258 public function isCheckStep()
259 {
260 return $this->_step === self::STEP_CHECK;
261 }
262
263 /**
264 * Set step to type of installation
265 *
266 * @return void
267 */
268 public function atTypeStep()
269 {
270 $this->_step = self::STEP_TYPE;
271 }
272
273 /**
274 * Are we at type step?
275 *
276 * @return boolean
277 */
278 public function isTypeStep()
279 {
280 return $this->_step === self::STEP_TYPE;
281 }
282
283 /**
284 * Set step to database information
285 *
286 * @return void
287 */
288 public function atDbStep()
289 {
290 $this->_step = self::STEP_DB;
291 }
292
293 /**
294 * Are we at database step?
295 *
296 * @return boolean
297 */
298 public function isDbStep()
299 {
300 return $this->_step === self::STEP_DB;
301 }
302
303 /**
304 * Is DB step passed?
305 *
306 * @return boolean
307 */
308 public function postCheckDb()
309 {
310 return $this->_step >= self::STEP_DB_CHECKS;
311 }
312
313 /**
314 * Set database type
315 *
316 * @param string $type Database type
317 * @param array $errs Errors array
318 *
319 * @return Install
320 */
321 public function setDbType($type, &$errs)
322 {
323 switch ($type) {
324 case Db::MYSQL:
325 case Db::PGSQL:
326 $this->_db_type = $type;
327 break;
328 default:
329 $errs[] = _T("Database type unknown");
330 }
331 return $this;
332 }
333
334 /**
335 * Get database type
336 *
337 * @return string
338 */
339 public function getDbType()
340 {
341 return $this->_db_type;
342 }
343
344 /**
345 * Set connection information
346 *
347 * @param string $host Database host
348 * @param string $port Database port
349 * @param string $name Database name
350 * @param string $user Database user name
351 * @param string $pass Database user's password
352 *
353 * @return void
354 */
355 public function setDsn($host, $port, $name, $user, $pass)
356 {
357 $this->_db_host = $host;
358 $this->_db_port = $port;
359 $this->_db_name = $name;
360 $this->_db_user = $user;
361 $this->_db_pass = $pass;
362 }
363
364 /**
365 * Set tables prefix
366 *
367 * @param string $prefix Prefix
368 *
369 * @return void
370 */
371 public function setTablesPrefix($prefix)
372 {
373 $this->_db_prefix = $prefix;
374 }
375
376 /**
377 * Retrieve database host
378 *
379 * @return string
380 */
381 public function getDbHost()
382 {
383 return $this->_db_host;
384 }
385
386 /**
387 * Retrieve database port
388 *
389 * @return string
390 */
391 public function getDbPort()
392 {
393 return $this->_db_port;
394 }
395
396 /**
397 * Retrieve database name
398 *
399 * @return string
400 */
401 public function getDbName()
402 {
403 return $this->_db_name;
404 }
405
406 /**
407 * Retrieve database user
408 *
409 * @return string
410 */
411 public function getDbUser()
412 {
413 return $this->_db_user;
414 }
415
416 /**
417 * Retrieve database password
418 *
419 * @return string
420 */
421 public function getDbPass()
422 {
423 return $this->_db_pass;
424 }
425
426 /**
427 * Retrieve tables prefix
428 *
429 * @return string
430 */
431 public function getTablesPrefix()
432 {
433 return $this->_db_prefix;
434 }
435
436 /**
437 * Set step to database checks
438 *
439 * @return void
440 */
441 public function atDbCheckStep()
442 {
443 $this->_step = self::STEP_DB_CHECKS;
444 }
445
446 /**
447 * Are we at database check step?
448 *
449 * @return boolean
450 */
451 public function isDbCheckStep()
452 {
453 return $this->_step === self::STEP_DB_CHECKS;
454 }
455
456 /**
457 * Test database connection
458 *
459 * @return true
460 *
461 * @throws \Exception
462 */
463 public function testDbConnexion()
464 {
465 return Db::testConnectivity(
466 $this->_db_type,
467 $this->_db_user,
468 $this->_db_pass,
469 $this->_db_host,
470 $this->_db_port,
471 $this->_db_name
472 );
473 }
474
475 /**
476 * Is database connexion ok?
477 *
478 * @return boolean
479 */
480 public function isDbConnected()
481 {
482 return $this->_db_connected;
483 }
484
485 /**
486 * Set step to version selection
487 *
488 * @return void
489 */
490 public function atVersionSelection()
491 {
492 $this->_step = self::STEP_VERSION;
493 }
494
495 /**
496 * Are we at version selection step?
497 *
498 * @return boolean
499 */
500 public function isVersionSelectionStep()
501 {
502 return $this->_step === self::STEP_VERSION;
503 }
504
505 /**
506 * Set step to database installation
507 *
508 * @return void
509 */
510 public function atDbInstallStep()
511 {
512 $this->_step = self::STEP_DB_INSTALL;
513 }
514
515 /**
516 * Are we at db installation step?
517 *
518 * @return boolean
519 */
520 public function isDbinstallStep()
521 {
522 return $this->_step === self::STEP_DB_INSTALL;
523 }
524
525 /**
526 * Set step to database upgrade
527 *
528 * @return void
529 */
530 public function atDbUpgradeStep()
531 {
532 $this->_step = self::STEP_DB_UPGRADE;
533 }
534
535 /**
536 * Are we at db upgrade step?
537 *
538 * @return boolean
539 */
540 public function isDbUpgradeStep()
541 {
542 return $this->_step === self::STEP_DB_UPGRADE;
543 }
544
545
546 /**
547 * Install/Update SQL scripts
548 *
549 * @param string $path Path to scripts (defaults to core scripts)
550 *
551 * @return array
552 */
553 public function getScripts($path = null)
554 {
555 if ($path === null) {
556 $path = GALETTE_ROOT . '/install';
557 }
558 $update_scripts = array();
559
560 if ($this->isUpgrade()) {
561 $update_scripts = self::getUpdateScripts(
562 $path,
563 $this->_db_type,
564 $this->_installed_version
565 );
566 } else {
567 $update_scripts['current'] = $this->_db_type . '.sql';
568 }
569
570 return $update_scripts;
571 }
572
573 /**
574 * List updates scripts from given path
575 *
576 * @param string $path Scripts path
577 * @param string $db_type Database type
578 * @param string $version Previous version, defaults to null
579 *
580 * @return array If a previous version is provided, update scripts
581 * file path from this one to the latest will be returned.
582 * If no previous version is provided, that will return all
583 * updates versions known.
584 */
585 public static function getUpdateScripts(
586 $path,
587 $db_type = 'mysql',
588 $version = null
589 ) {
590 $dh = opendir($path . '/scripts');
591 $php_update_scripts = array();
592 $sql_update_scripts = array();
593 $update_scripts = [];
594 if ($dh !== false) {
595 while (($file = readdir($dh)) !== false) {
596 if (preg_match("/upgrade-to-(.*).php/", $file, $ver)) {
597 if ($version === null) {
598 $php_update_scripts[$ver[1]] = $ver[1];
599 } else {
600 if ($version < $ver[1]) {
601 $php_update_scripts[$ver[1]] = $file;
602 }
603 }
604 }
605 if (
606 preg_match(
607 "/upgrade-to-(.*)-" . $db_type . ".sql/",
608 $file,
609 $ver
610 )
611 ) {
612 if ($version === null) {
613 $sql_update_scripts[$ver[1]] = $ver[1];
614 } else {
615 if ($version < $ver[1]) {
616 $sql_update_scripts[$ver[1]] = $file;
617 }
618 }
619 }
620 }
621 $update_scripts = array_merge($sql_update_scripts, $php_update_scripts);
622 closedir($dh);
623 ksort($update_scripts);
624 }
625 return $update_scripts;
626 }
627
628 /**
629 * Execute SQL scripts
630 *
631 * @param Db $zdb Database instance
632 * @param string $spath Path to scripts
633 *
634 * @return bool
635 */
636 public function executeScripts(Db $zdb, $spath = null)
637 {
638 $fatal_error = false;
639 $update_scripts = $this->getScripts($spath);
640 $this->_report = array();
641 $scripts_path = ($spath ?? GALETTE_ROOT . '/install') . '/scripts/';
642
643 foreach ($update_scripts as $key => $val) {
644 if (substr($val, -strlen('.sql')) === '.sql') {
645 //just a SQL script, run it
646 $script = fopen($scripts_path . $val, 'r');
647
648 if ($script === false) {
649 throw new \RuntimeException(
650 'Unable to read SQL script from ' . $scripts_path . $val
651 );
652 }
653
654 $sql_query = @fread(
655 $script,
656 @filesize($scripts_path . $val)
657 ) . "\n";
658
659 $sql_res = $this->executeSql($zdb, $sql_query);
660 if (!$sql_res) {
661 $fatal_error = true;
662 }
663 } else {
664 //we got an update class
665 include_once $scripts_path . $val;
666 $className = '\Galette\Updates\UpgradeTo' .
667 str_replace('.', '', $key);
668 $ret = array(
669 'message' => null,
670 'res' => false
671 );
672 try {
673 $updater = new $className();
674 if ($updater instanceof \Galette\Updater\AbstractUpdater) {
675 $updater->run($zdb, $this);
676 $ret = $updater->getReport();
677 $this->_report = array_merge($this->_report, $ret);
678 } else {
679 $fatal_error = true;
680 Analog::log(
681 'Update class does not extends AbstractUpdater!',
682 Analog::ERROR
683 );
684 }
685
686 $ret['message'] = str_replace(
687 '%version',
688 $key,
689 _T("%version script has been successfully executed :)")
690 );
691 $ret['res'] = true;
692 $this->_report[] = $ret;
693 } catch (\RuntimeException $e) {
694 Analog::log(
695 $e->getMessage(),
696 Analog::ERROR
697 );
698 $ret['message'] = str_replace(
699 '%version',
700 $key,
701 _T("Unable to run %version update script :(")
702 );
703 $fatal_error = true;
704 $this->_report[] = $ret;
705 }
706 }
707
708 Analog::log(
709 str_replace('%s', $key, 'Upgrade to %s complete'),
710 Analog::INFO
711 );
712 }
713
714 return !$fatal_error;
715 }
716
717 /**
718 * Executes SQL queries
719 *
720 * @param Db $zdb Database instance
721 * @param string $sql_query SQL instructions
722 *
723 * @return boolean;
724 */
725 public function executeSql($zdb, $sql_query)
726 {
727 $queries_results = array();
728 $fatal_error = false;
729
730 // begin : copyright (2002) the phpbb group (support@phpbb.com)
731 // load in the sql parser
732 include_once GALETTE_ROOT . 'includes/sql_parse.php';
733
734 $sql_query = preg_replace('/galette_/', $this->_db_prefix, $sql_query);
735 $sql_query = remove_remarks($sql_query);
736
737 $sql_query = split_sql_file($sql_query, ';');
738
739 $zdb->connection->beginTransaction();
740
741 $sql_size = sizeof($sql_query);
742 for ($i = 0; $i < $sql_size; $i++) {
743 $query = trim($sql_query[$i]);
744 if ($query != '' && $query[0] != '-') {
745 //some output infos
746 $ret = array(
747 'message' => $query,
748 'res' => false
749 );
750
751 try {
752 $zdb->db->query(
753 $query,
754 Adapter::QUERY_MODE_EXECUTE
755 );
756 $ret['res'] = true;
757 } catch (Throwable $e) {
758 $log_lvl = Analog::WARNING;
759 //if error are on drop, DROP, rename or RENAME we can continue
760 $parts = explode(' ', $query, 1);
761 if (
762 (strcasecmp(trim($parts[0]), 'drop') != 0)
763 && (strcasecmp(trim($parts[0]), 'rename') != 0)
764 ) {
765 $log_lvl = Analog::ERROR;
766 $ret['debug'] = $e->getMessage();
767 $ret['query'] = $query;
768 $ret['res'] = false;
769 $fatal_error = true;
770 } else {
771 $ret['res'] = true;
772 }
773 Analog::log(
774 'Error executing query | ' . $e->getMessage(),
775 $log_lvl
776 );
777 }
778
779 $queries_results[] = $ret;
780 }
781 }
782
783 if ($fatal_error) {
784 try {
785 $zdb->connection->rollBack();
786 } catch (\PDOException $e) {
787 //to avoid php8/mysql autocommit issue
788 if ($zdb->isPostgres() || (!$zdb->isPostgres() && !str_contains($e->getMessage(), 'no active transaction'))) {
789 throw $e;
790 }
791 }
792 } else {
793 try {
794 $zdb->connection->commit();
795 } catch (\PDOException $e) {
796 //to avoid php8/mysql autocommit issue
797 if ($zdb->isPostgres() || (!$zdb->isPostgres() && !str_contains($e->getMessage(), 'no active transaction'))) {
798 throw $e;
799 }
800 }
801 }
802
803 $this->_report = array_merge($this->_report, $queries_results);
804 return !$fatal_error;
805 }
806
807 /**
808 * Retrieve database installation report
809 *
810 * @return array
811 */
812 public function getDbInstallReport()
813 {
814 return $this->_report;
815 }
816
817 /**
818 * Reinitialize report array
819 *
820 * @return void
821 */
822 public function reinitReport()
823 {
824 $this->_report = array();
825 }
826
827 /**
828 * Set step to super admin information
829 *
830 * @return void
831 */
832 public function atAdminStep()
833 {
834 $this->_step = self::STEP_ADMIN;
835 }
836
837 /**
838 * Are we at super admin information step?
839 *
840 * @return boolean
841 */
842 public function isAdminStep()
843 {
844 return $this->_step === self::STEP_ADMIN;
845 }
846
847 /**
848 * Set super administrator information
849 *
850 * @param string $login Login
851 * @param string $pass Password
852 *
853 * @return void
854 */
855 public function setAdminInfos($login, $pass)
856 {
857 $this->_admin_login = $login;
858 $this->_admin_pass = password_hash($pass, PASSWORD_BCRYPT);
859 }
860
861 /**
862 * Retrieve super admin login
863 *
864 * @return string
865 */
866 public function getAdminLogin()
867 {
868 return $this->_admin_login;
869 }
870
871 /**
872 * Retrieve super admin password
873 *
874 * @return string
875 */
876 public function getAdminPass()
877 {
878 return $this->_admin_pass;
879 }
880
881 /**
882 * Set step to telemetry
883 *
884 * @return void
885 */
886 public function atTelemetryStep()
887 {
888 $this->_step = self::STEP_TELEMETRY;
889 }
890
891 /**
892 * Are we at telemetry step?
893 *
894 * @return boolean
895 */
896 public function isTelemetryStep()
897 {
898 return $this->_step === self::STEP_TELEMETRY;
899 }
900
901 /**
902 * Set step to Galette initialization
903 *
904 * @return void
905 */
906 public function atGaletteInitStep()
907 {
908 $this->_step = self::STEP_GALETTE_INIT;
909 }
910
911 /**
912 * Are we at Galette initialization step?
913 *
914 * @return boolean
915 */
916 public function isGaletteInitStep()
917 {
918 return $this->_step === self::STEP_GALETTE_INIT;
919 }
920
921 /**
922 * Load existing config
923 *
924 * @param array $post_data Data posted
925 * @param array $error_detected Errors array
926 *
927 * @return void
928 */
929 public function loadExistingConfig($post_data, &$error_detected)
930 {
931 if (file_exists(GALETTE_CONFIG_PATH . 'config.inc.php')) {
932 $existing = $this->loadExistingConfigFile($post_data);
933
934 if ($existing['db_type'] !== null) {
935 $this->setDbType($existing['db_type'], $error_detected);
936 }
937
938 if (
939 $existing['db_host'] !== null
940 || $existing['db_user'] !== null
941 || $existing['db_name'] !== null
942 ) {
943 $this->setDsn(
944 $existing['db_host'],
945 $existing['db_port'],
946 $existing['db_name'],
947 $existing['db_user'],
948 null
949 );
950 }
951
952 if ($existing['prefix'] !== null) {
953 $this->setTablesPrefix(
954 $existing['prefix']
955 );
956 }
957 }
958 }
959
960 /**
961 * Load contents from existing config file
962 *
963 * @param array $post_data Data posted
964 * @param boolean $pass Retrieve password
965 *
966 * @return array
967 */
968 private function loadExistingConfigFile($post_data = array(), $pass = false)
969 {
970 $existing = array(
971 'db_type' => null,
972 'db_host' => null,
973 'db_port' => null,
974 'db_user' => null,
975 'db_name' => null,
976 'prefix' => null
977 );
978
979 if (file_exists(GALETTE_CONFIG_PATH . 'config.inc.php')) {
980 $conf = file_get_contents(GALETTE_CONFIG_PATH . 'config.inc.php');
981 if ($conf !== false) {
982 if (!isset($post_data['install_dbtype'])) {
983 preg_match(
984 '/TYPE_DB["\'], ["\'](.*)["\']\);/',
985 $conf,
986 $matches
987 );
988 if (isset($matches[1])) {
989 $existing['db_type'] = $matches[1];
990 }
991 }
992 if (!isset($post_data['install_dbhost'])) {
993 preg_match(
994 '/HOST_DB["\'], ["\'](.*)["\']\);/',
995 $conf,
996 $matches
997 );
998 if (isset($matches[1])) {
999 $existing['db_host'] = $matches[1];
1000 }
1001 }
1002 if (!isset($post_data['install_dbport'])) {
1003 preg_match(
1004 '/PORT_DB["\'], ["\'](.*)["\']\);/',
1005 $conf,
1006 $matches
1007 );
1008 if (isset($matches[1])) {
1009 $existing['db_port'] = $matches[1];
1010 }
1011 }
1012 if (!isset($post_data['install_dbuser'])) {
1013 preg_match(
1014 '/USER_DB["\'], ["\'](.*)["\']\);/',
1015 $conf,
1016 $matches
1017 );
1018 if (isset($matches[1])) {
1019 $existing['db_user'] = $matches[1];
1020 }
1021 }
1022 if (!isset($post_data['install_dbname'])) {
1023 preg_match(
1024 '/NAME_DB["\'], ["\'](.*)["\']\);/',
1025 $conf,
1026 $matches
1027 );
1028 if (isset($matches[1])) {
1029 $existing['db_name'] = $matches[1];
1030 }
1031 }
1032
1033
1034 if (!isset($post_data['install_dbprefix'])) {
1035 preg_match(
1036 '/PREFIX_DB["\'], ["\'](.*)["\']\);/',
1037 $conf,
1038 $matches
1039 );
1040 if (isset($matches[1])) {
1041 $existing['prefix'] = $matches[1];
1042 }
1043 }
1044
1045 if ($pass === true) {
1046 preg_match(
1047 '/PWD_DB["\'], ["\'](.*)["\']\);/',
1048 $conf,
1049 $matches
1050 );
1051 if (isset($matches[1])) {
1052 $existing['pwd_db'] = $matches[1];
1053 }
1054 }
1055 }
1056 }
1057
1058 return $existing;
1059 }
1060
1061 /**
1062 * Write configuration file to disk
1063 *
1064 * @return boolean
1065 */
1066 public function writeConfFile()
1067 {
1068 $error = false;
1069 $ret = array(
1070 'message' => _T("Write configuration file"),
1071 'res' => false
1072 );
1073
1074 //if config file is already up-to-date, nothing to write
1075 $existing = $this->loadExistingConfigFile(array(), true);
1076
1077 if (
1078 isset($existing['db_type'])
1079 && $existing['db_type'] == $this->_db_type
1080 && isset($existing['db_host'])
1081 && $existing['db_host'] == $this->_db_host
1082 && isset($existing['db_port'])
1083 && $existing['db_port'] == $this->_db_port
1084 && isset($existing['db_user'])
1085 && $existing['db_user'] == $this->_db_user
1086 && isset($existing['pwd_db'])
1087 && $existing['pwd_db'] == $this->_db_pass
1088 && isset($existing['db_name'])
1089 && $existing['db_name'] == $this->_db_name
1090 && isset($existing['prefix'])
1091 && $existing['prefix'] == $this->_db_prefix
1092 ) {
1093 Analog::log(
1094 'Config file is already up-to-date, nothing to do.',
1095 Analog::INFO
1096 );
1097
1098 $this->_report[] = array(
1099 'message' => _T("Config file already exists and is up to date"),
1100 'res' => true
1101 );
1102 return true;
1103 }
1104
1105 $conffile = GALETTE_CONFIG_PATH . 'config.inc.php';
1106 if (
1107 is_writable(GALETTE_CONFIG_PATH)
1108 && (!file_exists($conffile) || file_exists($conffile) && is_writable($conffile))
1109 && $fd = @fopen($conffile, 'w')
1110 ) {
1111 $data = "<?php
1112 define('TYPE_DB', '" . $this->_db_type . "');
1113 define('HOST_DB', '" . $this->_db_host . "');
1114 define('PORT_DB', '" . $this->_db_port . "');
1115 define('USER_DB', '" . $this->_db_user . "');
1116 define('PWD_DB', '" . $this->_db_pass . "');
1117 define('NAME_DB', '" . $this->_db_name . "');
1118 define('PREFIX_DB', '" . $this->_db_prefix . "');
1119 ";
1120 fwrite($fd, $data);
1121 fclose($fd);
1122 $ret['res'] = true;
1123 Analog::log('Configuration file written on disk', Analog::INFO);
1124 } else {
1125 $str = str_replace(
1126 '%path',
1127 $conffile,
1128 _T("Unable to create configuration file (%path)")
1129 );
1130 Analog::log($str, Analog::WARNING);
1131 $ret['error'] = $str;
1132 $error = true;
1133 }
1134 $this->_report[] = $ret;
1135 return !$error;
1136 }
1137
1138 /**
1139 * Initialize Galette relevant objects
1140 *
1141 * @param I18n $i18n I18n
1142 * @param Db $zdb Database instance
1143 * @param Login $login Loged in instance
1144 *
1145 * @return boolean
1146 */
1147 public function initObjects(I18n $i18n, Db $zdb, Login $login)
1148 {
1149 if ($this->isInstall()) {
1150 $preferences = new Preferences($zdb, false);
1151 $ct = new \Galette\Entity\ContributionsTypes($zdb);
1152 $status = new \Galette\Entity\Status($zdb);
1153 include_once '../includes/fields_defs/members_fields.php';
1154 include_once '../includes/fields_defs/members_fields_cats.php';
1155 $fc = new \Galette\Entity\FieldsConfig(
1156 $zdb,
1157 \Galette\Entity\Adherent::TABLE,
1158 //@phpstan-ignore-next-line
1159 $members_fields,
1160 //@phpstan-ignore-next-line
1161 $members_fields_cats,
1162 true
1163 );
1164
1165 global $login;
1166 $login = new \Galette\Core\Login($zdb, $i18n);
1167 //$fc = new \Galette\Entity\FieldsCategories();
1168 $texts = new \Galette\Entity\Texts($preferences);
1169 $titles = new \Galette\Repository\Titles($zdb);
1170
1171 $models = new \Galette\Repository\PdfModels($zdb, $preferences, $login);
1172
1173 $this->_error = false;
1174
1175 //Install preferences
1176 $res = $preferences->installInit(
1177 $i18n->getID(),
1178 $this->getAdminLogin(),
1179 $this->getAdminPass()
1180 );
1181 $this->proceedReport(_T("Preferences"), $res);
1182
1183 //Install contributions types
1184 $res = $ct->installInit();
1185 $this->proceedReport(_T("Contributions types"), $res);
1186
1187 //Install statuses
1188 $res = $status->installInit();
1189 $this->proceedReport(_T("Status"), $res);
1190
1191 //Install fields configuration and categories
1192 $res = $fc->installInit();
1193 $this->proceedReport(_T("Fields config and categories"), $res);
1194
1195 //Install texts
1196 $res = $texts->installInit(false);
1197 $this->proceedReport(_T("Mails texts"), $res);
1198
1199 //Install titles
1200 $res = $titles->installInit();
1201 $this->proceedReport(_T("Titles"), $res);
1202
1203 //Install PDF models
1204 $res = $models->installInit(false);
1205 $this->proceedReport(_T("PDF models"), $res);
1206
1207 return !$this->_error;
1208 } elseif ($this->isUpgrade()) {
1209 $preferences = new Preferences($zdb);
1210 $preferences->store();
1211 $this->proceedReport(_T("Update preferences"), true);
1212
1213 $models = new \Galette\Repository\PdfModels($zdb, $preferences, new Login($zdb, $i18n));
1214 $models->installInit(true);
1215 $this->proceedReport(_T("Update models"), true);
1216
1217 $texts = new \Galette\Entity\Texts($preferences);
1218 $texts->installInit(true);
1219 $this->proceedReport(_T("Mails texts"), true);
1220
1221 return true;
1222 }
1223 return false;
1224 }
1225
1226 /**
1227 * Proceed installation report for each Entity/Repository
1228 *
1229 * @param string $msg Report message title
1230 * @param mixed $res Initialialization result
1231 *
1232 * @return void
1233 */
1234 private function proceedReport($msg, $res)
1235 {
1236 $ret = array(
1237 'message' => $msg,
1238 'res' => false
1239 );
1240
1241 if ($res instanceof \Exception) {
1242 $ret['debug'] = $res->getMessage();
1243 $this->_error = true;
1244 } else {
1245 $ret['res'] = true;
1246 }
1247 $this->_report[] = $ret;
1248 }
1249 /**
1250 * Retrieve galette initialization report
1251 *
1252 * @return array
1253 */
1254 public function getInitializationReport()
1255 {
1256 return $this->_report;
1257 }
1258
1259 /**
1260 * Set step to database installation
1261 *
1262 * @return void
1263 */
1264 public function atEndStep()
1265 {
1266 $this->_step = self::STEP_END;
1267 }
1268
1269 /**
1270 * Are we at end step?
1271 *
1272 * @return boolean
1273 */
1274 public function isEndStep()
1275 {
1276 return $this->_step === self::STEP_END;
1277 }
1278
1279 /**
1280 * Set installed version if we're upgrading
1281 *
1282 * @param string $version Installed version
1283 *
1284 * @return void
1285 */
1286 public function setInstalledVersion($version)
1287 {
1288 $this->_installed_version = $version;
1289 }
1290
1291 /**
1292 * Current Galette installed version, according to database
1293 *
1294 * @param Db $zdb Database instance
1295 *
1296 * @return string|false
1297 */
1298 public function getCurrentVersion($zdb)
1299 {
1300 try {
1301 $db_ver = $zdb->getDbVersion(true);
1302 if (isset($this->versions_mapper[$db_ver])) {
1303 return $this->versions_mapper[$db_ver];
1304 } else {
1305 return (string)$db_ver;
1306 }
1307 } catch (\LogicException $e) {
1308 return false;
1309 }
1310 }
1311
1312 /**
1313 * Check if step is passed
1314 *
1315 * @param int $step Step
1316 *
1317 * @return boolean
1318 */
1319 public function isStepPassed($step)
1320 {
1321 return $this->_step > $step;
1322 }
1323 }