~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Controller/Upgrade.php

  • Committer: Dan Garner
  • Date: 2015-03-26 14:08:33 UTC
  • Revision ID: git-v1:70d14044444f8dc5d602b99890d59dea46d9470c
Moved web servable files to web folder

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 * along with Xibo.  If not, see <http://www.gnu.org/licenses/>.
20
20
 */
21
21
namespace Xibo\Controller;
22
 
use Xibo\Exception\AccessDeniedException;
23
 
use Xibo\Exception\ConfigurationException;
24
 
use Xibo\Factory\UpgradeFactory;
25
 
use Xibo\Helper\Environment;
26
 
use Xibo\Service\ConfigServiceInterface;
27
 
use Xibo\Service\DateServiceInterface;
28
 
use Xibo\Service\LogServiceInterface;
29
 
use Xibo\Service\SanitizerServiceInterface;
30
 
use Xibo\Storage\StorageServiceInterface;
 
22
use Config;
 
23
use Exception;
 
24
use FormManager;
 
25
use Media;
 
26
use Xibo\Helper\Install;
 
27
use Xibo\Helper\Theme;
31
28
 
32
 
/**
33
 
 * Class Upgrade
34
 
 * @package Xibo\Controller
35
 
 */
36
29
class Upgrade extends Base
37
30
{
38
 
    /** @var  StorageServiceInterface */
39
 
    private $store;
40
 
 
41
 
    /** @var  UpgradeFactory */
42
 
    private $upgradeFactory;
43
 
 
44
 
    /** @var  string */
45
31
    public $errorMessage;
46
32
 
47
 
    /**
48
 
     * Set common dependencies.
49
 
     * @param LogServiceInterface $log
50
 
     * @param SanitizerServiceInterface $sanitizerService
51
 
     * @param \Xibo\Helper\ApplicationState $state
52
 
     * @param \Xibo\Entity\User $user
53
 
     * @param \Xibo\Service\HelpServiceInterface $help
54
 
     * @param DateServiceInterface $date
55
 
     * @param ConfigServiceInterface $config
56
 
     * @param Store $store
57
 
     * @param UpgradeFactory $upgradeFactory
58
 
     */
59
 
    public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $store, $upgradeFactory)
60
 
    {
61
 
        $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config);
62
 
 
63
 
        $this->store = $store;
64
 
        $this->upgradeFactory = $upgradeFactory;
65
 
    }
66
 
 
67
 
    /**
68
 
     *
69
 
     */
70
33
    public function displayPage()
71
34
    {
72
 
        // Assume we will show the upgrade page
73
 
        $this->getState()->template = 'upgrade-page';
74
 
 
75
 
        // Is there a pending upgrade (i.e. are there any pending upgrade steps).
76
 
        $steps = $this->upgradeFactory->getIncomplete();
77
 
 
78
 
        if (count($steps) <= 0) {
79
 
            // No pending steps, check to see if we need to insert them
80
 
            if (!$this->getConfig()->isUpgradePending()) {
81
 
                $this->getState()->template = 'upgrade-not-required-page';
82
 
                return;
83
 
            }
84
 
 
85
 
            if ($this->getUser()->userTypeId != 1) {
86
 
                $this->getState()->template = 'upgrade-in-progress-page';
87
 
                return;
88
 
            }
89
 
 
90
 
            // Insert pending upgrade steps.
91
 
            $steps = $this->upgradeFactory->createSteps(DBVERSION, Environment::$WEBSITE_VERSION);
92
 
 
93
 
            foreach ($steps as $step) {
94
 
                /* @var \Xibo\Entity\Upgrade $step */
95
 
                $step->save();
96
 
            }
97
 
 
98
 
        }
99
 
 
100
 
        // We have pending steps to process, show them in a list
101
 
        $this->getState()->setData([
102
 
           'steps' => $steps
103
 
        ]);
104
 
    }
105
 
 
106
 
    /**
107
 
     * Do Step
108
 
     * @param int $stepId
109
 
     * @throws \Exception
110
 
     */
111
 
    public function doStep($stepId)
112
 
    {
113
 
        // Check we are a super admin
114
 
        if (!$this->getUser()->userTypeId == 1)
115
 
            throw new AccessDeniedException();
116
 
 
117
 
        // Get upgrade step
118
 
        $upgradeStep = $this->upgradeFactory->getByStepId($stepId);
119
 
 
120
 
        if ($upgradeStep->complete == 1)
121
 
            throw new \InvalidArgumentException(__('Upgrade step already complete'));
122
 
 
123
 
        // Assume success
124
 
        $stepFailed = false;
125
 
        $stepFailedMessage = null;
126
 
 
 
35
 
 
36
        if (DBVERSION == WEBSITE_VERSION) {
 
37
            Theme::Set('message', sprintf(__('Sorry you have arrived at this page in error, please try to navigate away.'), Theme::GetConfig('app_name')));
 
38
 
 
39
            $this->getState()->html .= Theme::RenderReturn('message_box');
 
40
            return;
 
41
        }
 
42
 
 
43
        if ($this->getUser()->userTypeId != 1) {
 
44
            // Make sure we actually need to do an upgrade
 
45
            Theme::Set('message', sprintf(__('The CMS is temporarily off-line as an upgrade is in progress. Please check with your system administrator for updates or refresh your page in a few minutes.'), Theme::GetConfig('app_name')));
 
46
 
 
47
            $this->getState()->html .= Theme::RenderReturn('message_box');
 
48
            return;
 
49
        } else {
 
50
            // We want a static form (traditional rather than ajax)
 
51
            Theme::Set('form_class', 'StaticForm');
 
52
 
 
53
            // What step are we on
 
54
            $xibo_step = \Kit::GetParam('step', _REQUEST, _INT, 1);
 
55
 
 
56
            $content = '';
 
57
 
 
58
            switch ($xibo_step) {
 
59
 
 
60
                case 1:
 
61
                    // Checks environment
 
62
                    $content = $this->Step1();
 
63
                    break;
 
64
 
 
65
                case 2:
 
66
                    // Collect upgrade details
 
67
                    $content = $this->Step2();
 
68
                    break;
 
69
 
 
70
                case 3:
 
71
                    // Execute upgrade
 
72
                    try {
 
73
                        $content = $this->Step3();
 
74
                    } catch (Exception $e) {
 
75
                        $this->errorMessage = $e->getMessage();
 
76
 
 
77
                        // Reload step 2
 
78
                        $content = $this->Step2();
 
79
                    }
 
80
                    break;
 
81
            }
 
82
 
 
83
            Theme::Set('step', $xibo_step);
 
84
            Theme::Set('page_content', $content);
 
85
            $this->getState()->html .= Theme::RenderReturn('upgrade_page');
 
86
        }
 
87
    }
 
88
 
 
89
    public function Step1()
 
90
    {
 
91
        Theme::Set('form_action', 'index.php?p=upgrade');
 
92
        // Check environment
 
93
        $config = new Config();
 
94
 
 
95
        $environment = $config->CheckEnvironment();
 
96
 
 
97
        $formFields = array();
 
98
        $formButtons = array();
 
99
        $formFields[] = FormManager::AddMessage(sprintf(__('First we need to re-check if your server meets %s\'s requirements. The CMS requirements may change from release to release. If this is the case there will be further information in the release notes.'), Theme::GetConfig('app_name')));
 
100
 
 
101
        $formFields[] = FormManager::AddRaw($environment);
 
102
 
 
103
        if ($config->EnvironmentFault()) {
 
104
            $formFields[] = FormManager::AddHidden('step', 1);
 
105
            $formButtons[] = FormManager::AddButton(__('Retest'));
 
106
        } else if ($config->EnvironmentWarning()) {
 
107
            $formFields[] = FormManager::AddHidden('step', 2);
 
108
            $formButtons[] = FormManager::AddButton(__('Retest'), 'link', 'index.php?p=upgrade&step=1');
 
109
            $formButtons[] = FormManager::AddButton(__('Next'));
 
110
        } else {
 
111
            $formFields[] = FormManager::AddHidden('step', 2);
 
112
            $formButtons[] = FormManager::AddButton(__('Next'));
 
113
        }
 
114
 
 
115
        // Return a rendered form
 
116
        Theme::Set('form_fields', $formFields);
 
117
        Theme::Set('form_buttons', $formButtons);
 
118
        return Theme::RenderReturn('form_render');
 
119
    }
 
120
 
 
121
    public function Step2()
 
122
    {
 
123
 
 
124
 
 
125
        // Work out what is involved in this upgrade
 
126
        $_SESSION['upgradeFrom'] = Config::Version('DBVersion');
 
127
 
 
128
        if ($_SESSION['upgradeFrom'] < 1) {
 
129
            $_SESSION['upgradeFrom'] = 1;
 
130
        }
 
131
 
 
132
        // Get a list of .sql and .php files for the upgrade
 
133
        $sql_files = Install::ls('*.sql', 'install/database', false, array('return_files'));
 
134
        $php_files = Install::ls('*.php', 'install/database', false, array('return_files'));
 
135
 
 
136
        // Sort by natural filename (eg 10 is bigger than 2)
 
137
        natcasesort($sql_files);
 
138
        natcasesort($php_files);
 
139
 
 
140
        $_SESSION['phpFiles'] = $php_files;
 
141
        $_SESSION['sqlFiles'] = $sql_files;
 
142
 
 
143
        $max_sql = \Kit::ValidateParam(substr(end($sql_files), 0, -4), _INT);
 
144
        $max_php = \Kit::ValidateParam(substr(end($php_files), 0, -4), _INT);
 
145
        $_SESSION['upgradeTo'] = max($max_sql, $max_php);
 
146
 
 
147
        if (!$_SESSION['upgradeTo'])
 
148
            throw new Exception(__('Unable to calculate the upgradeTo value. Check for non-numeric SQL and PHP files in the "install / database" directory.'));
 
149
 
 
150
        if ($_SESSION['upgradeTo'] < $_SESSION['upgradeFrom'])
 
151
            $_SESSION['upgradeTo'] = $_SESSION['upgradeFrom'];
 
152
 
 
153
        // Form to collect some information.
 
154
        $formFields = array();
 
155
        $formButtons = array();
 
156
 
 
157
        // Put up an error message if one has been set (and then unset it)
 
158
        if ($this->errorMessage != '') {
 
159
            Theme::Set('message', $this->errorMessage);
 
160
            Theme::Set('prepend', Theme::RenderReturn('message_box'));
 
161
            $this->errorMessage == '';
 
162
        }
 
163
 
 
164
        $formFields[] = FormManager::AddHidden('step', 3);
 
165
        $formFields[] = FormManager::AddHidden('upgradeFrom', $_SESSION['upgradeFrom']);
 
166
        $formFields[] = FormManager::AddHidden('upgradeTo', $_SESSION['upgradeTo']);
 
167
        $formFields[] = FormManager::AddHidden('includes', true);
 
168
 
 
169
        $formFields[] = FormManager::AddMessage(sprintf(__('Upgrading from database version %d to %d'), $_SESSION['upgradeFrom'], $_SESSION['upgradeTo']));
 
170
 
 
171
        // Loop for $i between upgradeFrom + 1 and upgradeTo.
 
172
        // If a php file exists for that upgrade, make an instance of it and call Questions so we can
 
173
        // Ask the user for input.
 
174
        for ($i = $_SESSION['upgradeFrom'] + 1; $i <= $_SESSION['upgradeTo']; $i++) {
 
175
            if (file_exists('install/database/' . $i . '.php')) {
 
176
                include_once('install/database/' . $i . '.php');
 
177
                $stepName = 'Step' . $i;
 
178
 
 
179
                // Check that a class called Step$i exists
 
180
                if (class_exists($stepName)) {
 
181
                    $_SESSION['Step' . $i] = new $stepName($this->db);
 
182
                    // Call Questions on the object and send the resulting hash to createQuestions routine
 
183
                    $questionFields = $this->createQuestions($i, $_SESSION['Step' . $i]->Questions());
 
184
                    $formFields = array_merge($formFields, $questionFields);
 
185
                } else {
 
186
                    $formFields[] = FormManager::AddMessage(sprintf(__('Warning: We included %s.php, but it did not include a class of appropriate name.'), $i));
 
187
                }
 
188
            }
 
189
        }
 
190
 
 
191
        $formFields[] = FormManager::AddCheckbox('doBackup', 'I agree I have a valid database backup and can restore it should the upgrade process fail', 0, __('It is important to take a database backup before running the upgrade wizard. A backup is essential for recovering your CMS should there be a problem with the upgrade.'), 'b');
 
192
 
 
193
        // Return a rendered form
 
194
        Theme::Set('form_action', 'index.php?p=upgrade');
 
195
        Theme::Set('form_fields', $formFields);
 
196
        Theme::Set('form_buttons', array(FormManager::AddButton(__('Next'))));
 
197
        return Theme::RenderReturn('form_render');
 
198
    }
 
199
 
 
200
    public function Step3()
 
201
    {
 
202
 
 
203
        set_time_limit(0);
 
204
        $fault = false;
 
205
        $fault_string = '';
 
206
 
 
207
        foreach ($_POST as $key => $post) {
 
208
            // $key should be like 1-2, 1-3 etc
 
209
            // Split $key on - character.
 
210
 
 
211
            $parts = explode('-', $key);
 
212
            if (count($parts) == 2) {
 
213
                $step_num = 'Step' . $parts[0];
 
214
                include_once('install/database/' . $parts[0] . '.php');
 
215
 
 
216
                $response = $_SESSION[$step_num]->ValidateQuestion($parts[1], $post);
 
217
                if (!$response == true) {
 
218
                    // The upgrade routine for this step wasn't happy.
 
219
                    $fault = true;
 
220
                    $fault_string .= $response . "<br />\n";
 
221
                }
 
222
            }
 
223
        }
 
224
 
 
225
        if ($fault)
 
226
            throw new Exception($fault_string);
 
227
 
 
228
        $doBackup = \Xibo\Helper\Sanitize::getCheckbox('doBackup');
 
229
 
 
230
        if ($doBackup == 0)
 
231
            throw new Exception(__('You MUST have a valid database backup to continue. Please take and verify a backup and upgrade again.'));
 
232
 
 
233
        $sql_file = '';
 
234
        $sql = '';
 
235
        $i = 0;
 
236
 
 
237
        // Now loop over the entire upgrade. Run the SQLs and PHP interleaved.
127
238
        try {
128
 
            $upgradeStep->doStep();
129
 
            $upgradeStep->complete = 1;
130
 
 
131
 
            // Commit
132
 
            $this->store->commitIfNecessary('upgrade');
133
 
 
134
 
        } catch (\Exception $e) {
135
 
            // Failed to run upgrade step
136
 
            $this->getLog()->error('Unable to run upgrade stepId ' . $upgradeStep->stepId . '. Message = ' . $e->getMessage());
137
 
            $this->getLog()->error($e->getTraceAsString());
138
 
            $stepFailedMessage = $e->getMessage();
139
 
 
140
 
            try {
141
 
                $this->store->getConnection('upgrade')->rollBack();
142
 
            } catch (\Exception $exception) {
143
 
                $this->getLog()->error('Unable to rollback. E = ' . $e->getMessage());
 
239
            $dbh = \Xibo\Storage\PDOConnect::init();
 
240
            //$dbh->beginTransaction();
 
241
 
 
242
            for ($i = $_SESSION['upgradeFrom'] + 1; $i <= $_SESSION['upgradeTo']; $i++) {
 
243
                if (file_exists('install/database/' . $i . '.sql')) {
 
244
 
 
245
                    $delimiter = ';';
 
246
                    $sql_file = @file_get_contents('install/database/' . $i . '.sql');
 
247
                    $sql_file = Install::remove_remarks($sql_file);
 
248
                    $sql_file = Install::split_sql_file($sql_file, $delimiter);
 
249
 
 
250
                    foreach ($sql_file as $sql) {
 
251
                        $dbh->exec($sql);
 
252
                    }
 
253
                }
 
254
 
 
255
                if (file_exists('install/database/' . $i . '.php')) {
 
256
                    $stepName = 'Step' . $i;
 
257
 
 
258
                    if (!$_SESSION[$stepName]->Boot())
 
259
                        throw new Exception(__('Failed with %s', $stepName));
 
260
                }
144
261
            }
145
262
 
146
 
            $stepFailed = true;
147
 
        }
148
 
 
149
 
        $upgradeStep->lastTryDate = $this->getDate()->parse()->format('U');
150
 
        $upgradeStep->save();
151
 
 
152
 
        if ($stepFailed) {
153
 
            // Commit the default connection before we raise an error.
154
 
            // the framework won't commit if we don't.
155
 
            $this->store->commitIfNecessary();
156
 
 
157
 
            // Throw the exception
158
 
            throw new ConfigurationException($stepFailedMessage);
159
 
        }
160
 
 
161
 
        // Are we on the last step?
162
 
        if (count($this->upgradeFactory->getIncomplete()) <= 0) {
163
 
            // Clear all Task statuses
164
 
            $this->store->update('UPDATE `task` SET `status` = 0 WHERE `status` = 1;', []);
165
 
 
166
 
            // Install all module files if we are on the last step
167
 
            $this->getApp()->container->get('\Xibo\Controller\Library')->installAllModuleFiles();
168
 
 
169
 
            // Attempt to delete the install/index.php file
170
 
            if (file_exists(PROJECT_ROOT . '/web/install/index.php') && !unlink(PROJECT_ROOT . '/web/install/index.php'))
171
 
                $this->getLog()->critical('Unable to delete install.php file after upgrade');
172
 
        }
173
 
 
174
 
 
175
 
        $this->getState()->hydrate([
176
 
            'httpStatus' => 204,
177
 
            'message' => __('Complete')
178
 
        ]);
 
263
            //$dbh->commit();
 
264
        } catch (Exception $e) {
 
265
            //$dbh->rollBack();
 
266
            throw new Exception(sprintf(__('An error occurred running the upgrade. Please take a screen shot of this page and seek help. Statement number: %d. Error Message = [%s]. File = [%s]. SQL = [%s].'), $i, $e->getMessage(), $sql_file, $sql));
 
267
        }
 
268
 
 
269
        // Install files
 
270
        Media::installAllModuleFiles();
 
271
 
 
272
        // Delete install
 
273
        if (!unlink('install.php'))
 
274
            $formFields[] = FormManager::AddMessage(__("Unable to delete install.php. Please ensure the webserver has permission to unlink this file and retry"));
 
275
 
 
276
        $formFields[] = FormManager::AddMessage(__('The upgrade was a success!'));
 
277
 
 
278
        // Return a rendered form
 
279
        Theme::Set('form_fields', $formFields);
 
280
        return Theme::RenderReturn('form_render');
179
281
    }
180
282
 
181
 
    /**
182
 
     * Skips a step
183
 
     * @param $stepId
184
 
     */
185
 
    public function skipStep($stepId)
 
283
    private function createQuestions($step, $questions)
186
284
    {
187
 
        // Check we are a super admin
188
 
        if (!$this->getUser()->userTypeId == 1)
189
 
            throw new AccessDeniedException();
190
 
 
191
 
        // Get upgrade step
192
 
        $upgradeStep = $this->upgradeFactory->getByStepId($stepId);
193
 
 
194
 
        if ($upgradeStep->complete == 1)
195
 
            throw new \InvalidArgumentException(__('Upgrade step already complete'));
196
 
 
197
 
        $this->getLog()->critical('Upgrade step skipped. id = ' . $stepId);
198
 
 
199
 
        $upgradeStep->complete = 2;
200
 
        $upgradeStep->save();
 
285
        // Takes a multi-dimensional array eg:
 
286
        // $q[0]['question'] = "May we collect anonymous usage statistics?";
 
287
        // $q[0]['type'] = _CHECKBOX;
 
288
        // $q[0]['default'] = true;
 
289
        $formFields = array();
 
290
 
 
291
        foreach ($questions as $qnum => $question) {
 
292
 
 
293
            $title = ($step < 80) ? __('Question %d of Step %s', $qnum + 1, $step) : $question['title'];
 
294
 
 
295
            if ($question['type'] == _INPUTBOX) {
 
296
                $formFields[] = FormManager::AddText($step . '-' . $qnum, $title, $question['default'],
 
297
                    $question['question'], 'q');
 
298
            } elseif ($question['type'] == _PASSWORD) {
 
299
                $formFields[] = FormManager::AddPassword($step . '-' . $qnum, $title, $question['default'],
 
300
                    $question['question'], 'q');
 
301
            } elseif ($question['type'] == _CHECKBOX) {
 
302
                $formFields[] = FormManager::AddCheckbox($step . '-' . $qnum, $title, (($question['default']) ? 1 : 0),
 
303
                    $question['question'], 'q');
 
304
            }
 
305
        }
 
306
 
 
307
        return $formFields;
201
308
    }
202
 
}
 
 
b'\\ No newline at end of file'
 
309
}
 
310
 
 
311
?>