3
* Xibo - Digital Signage - http://www.xibo.org.uk
4
* Copyright (C) 2006-2014 Daniel Garner
6
* This file is part of Xibo.
8
* Xibo is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU Affero General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
13
* Xibo is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU Affero General Public License for more details.
18
* You should have received a copy of the GNU Affero General Public License
19
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
21
defined('XIBO') or die("Sorry, you are not allowed to directly access this page.<br /> Please press the back button in your browser.");
29
private $db_admin_user;
30
private $db_admin_pass;
35
private $existing_db_host;
36
private $existing_db_user;
37
private $existing_db_pass;
38
private $existing_db_name;
40
public function Step1() {
41
Theme::Set('form_action', 'install.php');
43
$config = new Config();
45
$environment = $config->CheckEnvironment();
47
$formFields = array();
48
$formButtons = array();
49
$formFields[] = FormManager::AddMessage(sprintf(__("First we need to check if your server meets %s's requirements."), Theme::GetConfig('app_name')));
51
$formFields[] = FormManager::AddRaw($environment);
53
if ($config->EnvironmentFault()) {
54
$formFields[] = FormManager::AddHidden('step', 1);
55
$formButtons[] = FormManager::AddButton(__('Retest'));
57
else if ($config->EnvironmentWarning()) {
58
$formFields[] = FormManager::AddHidden('step', 2);
59
$formButtons[] = FormManager::AddButton(__('Retest'), 'link', 'install.php?step=1');
60
$formButtons[] = FormManager::AddButton(__('Next'));
63
$formFields[] = FormManager::AddHidden('step', 2);
64
$formButtons[] = FormManager::AddButton(__('Next'));
67
// Return a rendered form
68
Theme::Set('form_fields', $formFields);
69
Theme::Set('form_buttons', $formButtons);
70
return Theme::RenderReturn('form_render');
73
public function Step2() {
74
Theme::Set('form_action', 'install.php');
75
// Choice of new or existing database
78
$tabs[] = FormManager::AddTab('new', __('Create a new database'));
79
$tabs[] = FormManager::AddTab('existing', __('Use an existing database'));
80
Theme::Set('form_tabs', $tabs);
82
$formFields = array();
85
$this->db_create = ($this->db_create == '') ? 1 : $this->db_create;
86
$this->new_db_host = ($this->new_db_host == '') ? 'localhost' : $this->new_db_host;
87
$this->db_admin_user = ($this->db_admin_user == '') ? 'root' : $this->db_admin_user;
90
$formFields['new'][] = FormManager::AddHidden('step', 3);
92
$formFields['new'][] = FormManager::AddMessage(sprintf(__("%s needs to set-up a connection to your MySQL database."), Theme::GetConfig('app_name')));
94
$formFields['new'][] = FormManager::AddMessage(__('If you have not yet created an empty database and database user for Xibo to use, and know the user name / password of a MySQL administrator stay on this tab, otherwise click "Use Existing".'));
96
$formFields['new'][] = FormManager::AddRadio('db_create', 'db_create1', __('Create a new database'), $this->db_create, 1,
97
__('Select to create a new database'), 'c');
99
$formFields['new'][] = FormManager::AddText('host', __('Host'), $this->new_db_host,
100
__('Please enter the hostname for the MySQL server. This is usually localhost.'), 'h');
102
$formFields['new'][] = FormManager::AddText('admin_username', __('Admin Username'), $this->db_admin_user,
103
__('Please enter the user name of an account that has administrator privileges on the MySQL server.'), 'h');
105
$formFields['new'][] = FormManager::AddPassword('admin_password', __('Admin Password'), $this->db_admin_pass,
106
__('Please enter password for the Admin account.'), 'h');
108
$formFields['new'][] = FormManager::AddText('db_name', __('Database Name'), $this->new_db_name,
109
__('Please enter the name of the database that should be created.'), 'h');
111
$formFields['new'][] = FormManager::AddText('db_username', __('Database Username'), $this->new_db_user,
112
__('Please enter the name of the database user that should be created.'), 'h');
114
$formFields['new'][] = FormManager::AddPassword('db_password', __('Database Password'), $this->new_db_pass,
115
__('Please enter a password for this user.'), 'h');
118
$formFields['existing'][] = FormManager::AddRadio('db_create', 'db_create2', __('Use an existing database'), $this->db_create, 2,
119
__('Select to use an existing database. Please note that when you use an existing database it must be empty of all other contents.'), 'e');
121
$formFields['existing'][] = FormManager::AddText('existing_host', __('Host'), $this->existing_db_host,
122
__('Please enter the hostname for the MySQL server. This is usually localhost.'), 'h');
124
$formFields['existing'][] = FormManager::AddText('existing_db_name', __('Database Name'), $this->existing_db_name,
125
__('Please enter the name of the database that should be created.'), 'h');
127
$formFields['existing'][] = FormManager::AddText('existing_db_username', __('Database Username'), $this->existing_db_user,
128
__('Please enter the name of the database user that should be created.'), 'h');
130
$formFields['existing'][] = FormManager::AddPassword('existing_db_password', __('Database Password'), $this->existing_db_pass,
131
__('Please enter a password for this user.'), 'h');
133
// Put up an error message if one has been set (and then unset it)
134
if ($this->errorMessage != '') {
135
Theme::Set('message', $this->errorMessage);
136
Theme::Set('prepend', Theme::RenderReturn('message_box'));
137
$this->errorMessage == '';
140
// Return a rendered form
141
Theme::Set('form_fields_new', $formFields['new']);
142
Theme::Set('form_fields_existing', $formFields['existing']);
143
Theme::Set('form_buttons', array(FormManager::AddButton(__('Next'))));
144
return Theme::RenderReturn('form_render');
147
public function Step3() {
149
// Have we been told to create a new database
150
$this->db_create = Kit::GetParam('db_create', _POST, _INT);
152
// Check all parameters have been specified
153
$this->db_admin_user = Kit::GetParam('admin_username', _POST, _PASSWORD);
154
$this->db_admin_pass = Kit::GetParam('admin_password', _POST, _PASSWORD);
156
$this->new_db_host = Kit::GetParam('host', _POST, _STRING);
157
$this->new_db_user = Kit::GetParam('db_username', _POST, _PASSWORD);
158
$this->new_db_pass = Kit::GetParam('db_password', _POST, _PASSWORD);
159
$this->new_db_name = Kit::GetParam('db_name', _POST, _PASSWORD);
161
$this->existing_db_host = Kit::GetParam('existing_host', _POST, _STRING);
162
$this->existing_db_user = Kit::GetParam('existing_db_username', _POST, _PASSWORD);
163
$this->existing_db_pass = Kit::GetParam('existing_db_password', _POST, _PASSWORD);
164
$this->existing_db_name = Kit::GetParam('existing_db_name', _POST, _PASSWORD);
166
// If an administrator user name / password has been specified then we should create a new DB
167
if ($this->db_create == 1) {
168
// Check details for a new database
169
if ($this->new_db_host == '')
170
throw new Exception(__('Please provide a database host. This is usually localhost.'));
172
if ($this->new_db_user == '')
173
throw new Exception(__('Please provide a user for the new database.'));
175
if ($this->new_db_pass == '')
176
throw new Exception(__('Please provide a password for the new database.'));
178
if ($this->new_db_name == '')
179
throw new Exception(__('Please provide a name for the new database.'));
181
if ($this->db_admin_user == '')
182
throw new Exception(__('Please provide an admin user name.'));
184
// Try to create the new database
185
// Try and connect using these details and create the new database
187
$dbh = PDOConnect::connect($this->new_db_host, $this->db_admin_user, $this->db_admin_pass);
189
catch (Exception $e) {
190
throw new Exception(sprintf(__('Could not connect to MySQL with the administrator details. Please check and try again. Error Message = [%s]'), $e->getMessage()));
193
// Try to create the new database
195
$dbh = PDOConnect::init();
196
$dbh->exec(sprintf('CREATE DATABASE `%s`', $this->new_db_name));
198
catch (Exception $e) {
199
throw new Exception(sprintf(__('Could not create a new database with the administrator details [%s]. Please check and try again. Error Message = [%s]'), $this->db_admin_user, $e->getMessage()));
202
// Try to create the new user
204
$dbh = PDOConnect::init();
206
// Create the user and grant privileges
207
if ($this->new_db_host == 'localhost') {
208
$dbh->exec(sprintf('GRANT ALL PRIVILEGES ON `%s`.* to %s@%s IDENTIFIED BY %s',
210
$dbh->quote($this->new_db_user),
211
$dbh->quote($this->new_db_host),
212
$dbh->quote($this->new_db_pass))
216
$dbh->exec(sprintf("GRANT ALL PRIVILEGES ON `%s`.* to %s@%% IDENTIFIED BY %s",
218
$dbh->quote($this->new_db_user),
219
$dbh->quote($this->new_db_pass))
224
$dbh->exec('FLUSH PRIVILEGES');
226
catch (Exception $e) {
227
throw new Exception(sprintf(__('Could not create a new user with the administrator details. Please check and try again. Error Message = [%s]'), $e->getMessage()));
230
// Set our DB details
231
$this->existing_db_host = $this->new_db_host;
232
$this->existing_db_user = $this->new_db_user;
233
$this->existing_db_pass = $this->new_db_pass;
234
$this->existing_db_name = $this->new_db_name;
236
// Close the connection
240
// Check details for a new database
241
if ($this->existing_db_host == '')
242
throw new Exception(__('Please provide a database host. This is usually localhost.'));
244
if ($this->existing_db_user == '')
245
throw new Exception(__('Please provide a user for the existing database.'));
247
if ($this->existing_db_pass == '')
248
throw new Exception(__('Please provide a password for the existing database.'));
250
if ($this->existing_db_name == '')
251
throw new Exception(__('Please provide a name for the existing database.'));
254
// Try and make a connection with this database
256
$dbh = PDOConnect::connect($this->existing_db_host, $this->existing_db_user, $this->existing_db_pass, $this->existing_db_name);
258
catch (Exception $e) {
259
throw new Exception(sprintf(__('Could not connect to MySQL with the administrator details. Please check and try again. Error Message = [%s]'), $e->getMessage()));
262
// We should have a database that we can access and populate with our tables.
263
$sql_files = array('structure.sql', 'data.sql');
264
$sqlStatementCount = 0;
267
$dbh = PDOConnect::init();
269
foreach ($sql_files as $filename) {
271
$sql_file = @file_get_contents('install/master/' . $filename);
272
$sql_file = Install::remove_remarks($sql_file);
273
$sql_file = Install::split_sql_file($sql_file, $delimiter);
275
foreach ($sql_file as $sql) {
276
$sqlStatementCount++;
282
catch (Exception $e) {
283
throw new Exception(sprintf(__('An error occurred populating the database. Statement number: %d. Error Message = [%s]'), $sqlStatementCount, $e->getMessage()));
287
// Write out a new settings.php
288
$fh = fopen('settings.php', 'wt');
291
throw new Exception(__('Unable to write to settings.php. We already checked this was possible earlier, so something changed.'));
293
// Generate a secret key for various reasons
294
$secretKey = Install::gen_secret();
296
// Escape the password before we write it to disk
297
$dbh = PDOConnect::init();
298
$existing_db_pass = addslashes($this->existing_db_pass);
304
* Xibo - Digital Signage - http://www.xibo.org.uk
306
* This file is part of Xibo - and is automatically generated by the installer
308
* You should not need to edit this file, unless your SQL connection details have changed.
311
defined('XIBO') or die(__("Sorry, you are not allowed to directly access this page.") . "<br />" . __("Please press the back button in your browser."));
318
\$dbhost = '$this->existing_db_host';
319
\$dbuser = '$this->existing_db_user';
320
\$dbpass = '$existing_db_pass';
321
\$dbname = '$this->existing_db_name';
323
define('SECRET_KEY', '$secretKey');
327
if (!fwrite($fh, $settings))
328
throw new Exception(__('Unable to write to settings.php. We already checked this was possible earlier, so something changed.'));
332
// If we get here, we want to move on to the next step.
333
// This is handled by the calling function (i.e. there is no output from this call, we just reload and move on)
336
public function Step4() {
337
// Form to collect an admin user account and password.
338
$formFields = array();
340
$formFields[] = FormManager::AddHidden('step', 5);
342
$formFields[] = FormManager::AddMessage(sprintf(__("%s needs an administrator user account to be the first user account that has access to the CMS. Please enter your chosen details below."), Theme::GetConfig('app_name')));
344
// User name and password
345
$formFields[] = FormManager::AddText('admin_username', __('Admin Username'), NULL,
346
__('Please enter a user name for the first administrator account.'), 'n');
348
$formFields[] = FormManager::AddPassword('admin_password', __('Admin Password'), NULL,
349
__('Please enter a password for this user. This user will have full access to the system'), 'p');
351
// Put up an error message if one has been set (and then unset it)
352
if ($this->errorMessage != '') {
353
Theme::Set('message', $this->errorMessage);
354
Theme::Set('prepend', Theme::RenderReturn('message_box'));
355
$this->errorMessage == '';
358
// Return a rendered form
359
Theme::Set('form_action', 'install.php');
360
Theme::Set('form_fields', $formFields);
361
Theme::Set('form_buttons', array(FormManager::AddButton(__('Next'))));
362
return Theme::RenderReturn('form_render');
365
public function Step5() {
366
// Configure the user account
367
$username = Kit::GetParam('admin_username', _POST, _STRING);
368
$password = Kit::GetParam('admin_password', _POST, _PASSWORD);
371
throw new Exception(__('Missing the admin username.'));
374
throw new Exception(__('Missing the admin password.'));
376
// Update user id 1 with these details.
378
$dbh = PDOConnect::init();
380
$sth = $dbh->prepare('UPDATE `user` SET UserName = :username, UserPassword = :password WHERE UserID = 1 LIMIT 1');
382
'username' => $username,
383
'password' => md5($password)
386
// Update group ID 3 with the user name
387
$sth = $dbh->prepare('UPDATE `group` SET `group` = :username WHERE groupId = 3 LIMIT 1');
389
'username' => $username
392
catch (Exception $e) {
393
throw new Exception(sprintf(__('Unable to set the user details. This is an unexpected error, please contact support. Error Message = [%s]'), $e->getMessage()));
397
public function Step6() {
398
// Form to collect the library location and server key
399
$formFields = array();
400
$formFields[] = FormManager::AddHidden('step', 7);
402
$formFields[] = FormManager::AddText('library_location', __('Library Location'), NULL,
403
sprintf(__('%s needs somewhere to store the things you upload to be shown. Ideally, this should be somewhere outside the root of your web server - that is such that is not accessible by a web browser. Please input the full path to this folder. If the folder does not already exist, we will attempt to create it for you.'), Theme::GetConfig('app_name')), 'n');
405
$formFields[] = FormManager::AddText('server_key', __('Server Key'), Install::gen_secret(6),
406
sprintf(__('%s needs you to choose a "key". This will be required each time you set-up a new client. It should be complicated, and hard to remember. It is visible in the CMS interface, so it need not be written down separately.'), Theme::GetConfig('app_name')), 'n');
408
$formFields[] = FormManager::AddCheckbox('stats', __('Statistics'), 1,
409
sprintf(__('We\'d love to know you\'re running %s. If you\'re happy for us to collect anonymous statistics (version number, number of displays) then please leave the box ticked. Please un tick the box if your server does not have direct access to the internet.'), Theme::GetConfig('app_name')), 'n');
411
// Put up an error message if one has been set (and then unset it)
412
if ($this->errorMessage != '') {
413
Theme::Set('message', $this->errorMessage);
414
Theme::Set('prepend', Theme::RenderReturn('message_box'));
415
$this->errorMessage == '';
418
// Return a rendered form
419
Theme::Set('form_action', 'install.php');
420
Theme::Set('form_fields', $formFields);
421
Theme::Set('form_buttons', array(FormManager::AddButton(__('Next'))));
422
return Theme::RenderReturn('form_render');
425
public function Step7() {
426
$server_key = Kit::GetParam('server_key', _POST, _STRING);
427
$library_location = Kit::GetParam('library_location', _POST, _STRING);
428
$stats = Kit::GetParam('stats', _POST, _CHECKBOX);
430
if ($server_key == '')
431
throw new Exception(__('Missing the server key.'));
433
if ($library_location == '')
434
throw new Exception(__('Missing the library location.'));
436
// Remove trailing white space from the path given.
437
$library_location = trim($library_location);
439
if (! is_dir($library_location)) {
440
// Make sure they haven't given a file as the library location
441
if (is_file($library_location))
442
throw new Exception(__('A file exists with the name you gave for the Library Location. Please choose another location'));
444
// Directory does not exist. Attempt to make it
445
// Using mkdir recursively, so it will attempt to make any
446
// intermediate folders required.
447
if (!mkdir($library_location, 0755, true)) {
448
throw new Exception(__('Could not create the Library Location directory for you. Please ensure the webserver has permission to create a folder in this location, or create the folder manually and grant permission for the webserver to write to the folder.'));
452
// Is library_location writable?
453
if (!is_writable($library_location))
454
throw new Exception(__('The Library Location you gave is not writable by the webserver. Please fix the permissions and try again.'));
456
// Is library_location empty?
457
if (count(Install::ls("*",$library_location,true)) > 0)
458
throw new Exception(__('The Library Location you gave is not empty. Please give the location of an empty folder'));
460
// Check if the user has added a trailing slash. If not, add one.
461
if (!((substr($library_location, -1) == '/') || (substr($library_location, -1) == '\\'))) {
462
$library_location = $library_location . '/';
466
$dbh = PDOConnect::init();
469
$sth = $dbh->prepare('UPDATE `setting` SET `value` = :value WHERE `setting`.`setting` = \'LIBRARY_LOCATION\' LIMIT 1');
470
$sth->execute(array('value' => $library_location));
473
$sth = $dbh->prepare('UPDATE `setting` SET `value` = :value WHERE `setting`.`setting` = \'SERVER_KEY\' LIMIT 1');
474
$sth->execute(array('value' => $server_key));
477
$sth = $dbh->prepare('UPDATE `setting` SET `value` = :value WHERE `setting`.`setting` = \'defaultTimezone\' LIMIT 1');
478
$sth->execute(array('value' => date_default_timezone_get()));
481
$sth = $dbh->prepare('UPDATE `setting` SET `value` = :value WHERE `setting`.`setting` = \'PHONE_HOME\' LIMIT 1');
482
$sth->execute(array('value' => (($stats == 1) ? 'On' : 'Off')));
485
$sth = $dbh->prepare('UPDATE `setting` SET `value` = :value WHERE `setting`.`setting` = \'PHONE_HOME_KEY\' LIMIT 1');
486
$sth->execute(array('value' => md5(uniqid(rand(), true))));
488
catch (Exception $e) {
489
throw new Exception(sprintf(__('An error occurred updating these settings. This is an unexpected error, please contact support. Error Message = [%s]'), $e->getMessage()));
493
public function Step8() {
497
// Define the VERSION
500
Theme::Set('form_action', 'index.php?q=login');
501
Theme::Set('about_url', 'index.php?p=index&q=About');
502
Theme::Set('source_url', Theme::SourceLink());
504
// Message (either from the URL or the session)
505
Theme::Set('login_message', sprintf(__("%s was successfully installed. Please log-in with the user details you chose earlier."), Theme::GetConfig('app_name')));
507
Theme::Render('login_page');
510
Media::installAllModuleFiles();
513
if (!unlink('install.php'))
514
throw new Exception(__("Unable to delete install.php. Please ensure the webserver has permission to unlink this file and retry"));
520
* Third party classes
523
// Taken from http://forums.devshed.com/php-development-5/php-wont-load-sql-from-file-515902.html
526
* remove_remarks will strip the sql comment lines out of an uploaded sql file
528
public static function remove_remarks($sql){
529
$sql = preg_replace('/\n{2,}/', "\n", preg_replace('/^[-].*$/m', "\n", $sql));
530
$sql = preg_replace('/\n{2,}/', "\n", preg_replace('/^#.*$/m', "\n", $sql));
534
// Taken from http://forums.devshed.com/php-development-5/php-wont-load-sql-from-file-515902.html
537
* split_sql_file will split an uploaded sql file into single sql statements.
538
* Note: expects trim() to have already been run on $sql.
540
public static function split_sql_file($sql, $delimiter){
541
$sql = str_replace("\r" , '', $sql);
542
$data = preg_split('/' . preg_quote($delimiter, '/') . '$/m', $sql);
543
$data = array_map('trim', $data);
545
$end_data = end($data);
546
if (empty($end_data)) {
547
unset($data[key($data)]);
553
* This function will take a pattern and a folder as the argument and go thru it(recursively if needed)and return the list of
554
* all files in that folder.
555
* Link : http://www.bin-co.com/php/scripts/filesystem/ls/
557
* Arguments : $pattern - The pattern to look out for [OPTIONAL]
558
* $folder - The path of the directory of which's directory list you want [OPTIONAL]
559
* $recursivly - The funtion will traverse the folder tree recursivly if this is true. Defaults to false. [OPTIONAL]
560
* $options - An array of values 'return_files' or 'return_folders' or both
561
* Returns : A flat list with the path of all the files(no folders) that matches the condition given.
563
public static function ls($pattern="*", $folder="", $recursivly=false, $options=array('return_files','return_folders')) {
565
$current_folder = realpath('.');
566
if(in_array('quiet', $options)) { // If quiet is on, we will suppress the 'no such folder' error
567
if(!file_exists($folder)) return array();
570
if(!chdir($folder)) return array();
574
$get_files = in_array('return_files', $options);
575
$get_folders= in_array('return_folders', $options);
579
// Get the all files and folders in the given directory.
580
if($get_files) $both = glob($pattern, GLOB_BRACE + GLOB_MARK);
581
if($recursivly or $get_folders) $folders = glob("*", GLOB_ONLYDIR + GLOB_MARK);
583
//If a pattern is specified, make sure even the folders match that pattern.
584
$matching_folders = array();
585
if($pattern !== '*') $matching_folders = glob($pattern, GLOB_ONLYDIR + GLOB_MARK);
587
//Get just the files by removing the folders from the list of all files.
588
$all = array_values(array_diff($both,$folders));
590
if($recursivly or $get_folders) {
591
foreach ($folders as $this_folder) {
593
//If a pattern is specified, make sure even the folders match that pattern.
594
if($pattern !== '*') {
595
if(in_array($this_folder, $matching_folders)) array_push($all, $this_folder);
597
else array_push($all, $this_folder);
601
// Continue calling this function for all the folders
602
$deep_items = Install::ls($pattern, $this_folder, $recursivly, $options); # :RECURSION:
603
foreach ($deep_items as $item) {
604
array_push($all, $this_folder . $item);
610
if($folder) chdir($current_folder);
614
public static function gen_secret($length = 12) {
615
# Generates a random 12 character alphanumeric string to use as a salt
616
mt_srand((double)microtime()*1000000);
618
for ($i=0; $i < $length; $i++) {
621
$key .= chr(mt_rand(65,90));
624
$key .= chr(mt_rand(97,122));
627
$key .= chr(mt_rand(48,57));
b'\\ No newline at end of file'