setName('core:update');
        $this->setDescription(Piwik::translate('CoreUpdater_ConsoleCommandDescription'));
        $this->addOption('yes', null, InputOption::VALUE_NONE, Piwik::translate('CoreUpdater_ConsoleParameterDescription'));
        $this->addOption('skip-cache-clear', null, InputOption::VALUE_NONE, Piwik::translate('CoreUpdater_SkipCacheClearDesc'));
    }
    /**
     * Execute command like: ./console core:update --yes
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $skipCacheClear = $input->getOption('skip-cache-clear');
        try {
            if ($skipCacheClear) {
                $output->writeln(Piwik::translate('CoreUpdater_SkipCacheClear'));
                Filesystem::$skipCacheClearOnUpdate = true;
            }
            $this->executeClearCaches();
            $yes = $input->getOption('yes');
            try {
                $this->makeUpdate($input, $output, true);
                if (!$yes) {
                    $yes = $this->askForUpdateConfirmation($input, $output);
                }
                if ($yes) {
                    $output->writeln("\n" . Piwik::translate('CoreUpdater_ConsoleStartingDbUpgrade'));
                    $this->makeUpdate($input, $output, false);
                    $this->writeSuccessMessage($output, array(Piwik::translate('CoreUpdater_PiwikHasBeenSuccessfullyUpgraded')));
                } else {
                    $this->writeSuccessMessage($output, array(Piwik::translate('CoreUpdater_DbUpgradeNotExecuted')));
                }
                $this->writeAlertMessageWhenCommandExecutedWithUnexpectedUser($output);
            } catch (NoUpdatesFoundException $e) {
                // Do not fail if no updates were found
                $this->writeSuccessMessage($output, array($e->getMessage()));
            }
        } finally {
            Filesystem::$skipCacheClearOnUpdate = false;
        }
    }
    private function askForUpdateConfirmation(InputInterface $input, OutputInterface $output)
    {
        $helper   = $this->getHelper('question');
        $question = new ConfirmationQuestion(''.Piwik::translate('CoreUpdater_ExecuteDbUpgrade').' (y/N) ', false);
        return $helper->ask($input, $output, $question);
    }
    protected function executeClearCaches()
    {
        Filesystem::deleteAllCacheOnUpdate();
    }
    protected function makeUpdate(InputInterface $input, OutputInterface $output, $doDryRun)
    {
        $this->checkAllRequiredOptionsAreNotEmpty($input);
        $updater = $this->makeUpdaterInstance($output);
        $componentsWithUpdateFile = $updater->getComponentUpdates();
        if (empty($componentsWithUpdateFile)) {
            throw new NoUpdatesFoundException(Piwik::translate('CoreUpdater_AlreadyUpToDate'));
        }
        $output->writeln(array(
            "",
            "    *** " . Piwik::translate('CoreUpdater_UpdateTitle') . " ***"
        ));
        // handle case of existing database with no tables
        if (!DbHelper::isInstalled()) {
            $this->handleCoreError($output, Piwik::translate('CoreUpdater_EmptyDatabaseError', Config::getInstance()->database['dbname']));
            return;
        }
        $output->writeln(array(
            "",
            "    " . Piwik::translate('CoreUpdater_DatabaseUpgradeRequired'),
            "",
            "    " . Piwik::translate('CoreUpdater_YourDatabaseIsOutOfDate')
        ));
        if ($this->isUpdatingCore($componentsWithUpdateFile)) {
            $currentVersion = $this->getCurrentVersionForCore($updater);
            $output->writeln(array(
                "",
                "    " . Piwik::translate('CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY', array($currentVersion, Version::VERSION))
            ));
        }
        $pluginsToUpdate = $this->getPluginsToUpdate($componentsWithUpdateFile);
        if (!empty($pluginsToUpdate)) {
            $output->writeln(array(
                "",
                "    " . Piwik::translate('CoreUpdater_TheFollowingPluginsWillBeUpgradedX', implode(', ', $pluginsToUpdate))
            ));
        }
        $dimensionsToUpdate = $this->getDimensionsToUpdate($componentsWithUpdateFile);
        if (!empty($dimensionsToUpdate)) {
            $output->writeln(array(
                "",
                "    " . Piwik::translate('CoreUpdater_TheFollowingDimensionsWillBeUpgradedX', implode(', ', $dimensionsToUpdate))
            ));
        }
        $output->writeln("");
        if ($doDryRun) {
            $this->doDryRun($updater, $output);
        } else {
            $this->doRealUpdate($updater, $componentsWithUpdateFile, $output);
        }
    }
    private function doDryRun(Updater $updater, OutputInterface $output)
    {
        $migrationQueries = $this->getMigrationQueriesToExecute($updater);
        if(empty($migrationQueries)) {
            $output->writeln(array("    *** ".Piwik::translate('CoreUpdater_ConsoleUpdateNoSqlQueries')." ***", ""));
            return;
        }
        if ($updater->hasMajorDbUpdate()) {
            $output->writeln(array(
                "",
                sprintf("%s \n", Piwik::translate('CoreUpdater_MajorUpdateWarning1'))
            ));
        }
        $output->writeln(array("    *** ".Piwik::translate('CoreUpdater_DryRun')." ***", ""));
        foreach ($migrationQueries as $query) {
            $result = $query->__toString();
            if (!empty($result)) {
                $output->writeln("    " . $result);
            }
        }
        $output->writeln(array("", "    *** " . Piwik::translate('CoreUpdater_DryRunEnd') . " ***", ""));
    }
    private function doRealUpdate(Updater $updater, $componentsWithUpdateFile, OutputInterface $output)
    {
        $output->writeln(array("    " . Piwik::translate('CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'), ""));
        $updaterResult = $updater->updateComponents($componentsWithUpdateFile);
        if (@$updaterResult['coreError']) {
            $this->handleCoreError($output, $updaterResult['errors'], $includeDiyHelp = true);
            return;
        }
        if (!empty($updaterResult['warnings'])) {
            $this->outputUpdaterWarnings($output, $updaterResult['warnings']);
        }
        if (!empty($updaterResult['errors'])) {
            $this->outputUpdaterErrors($output, $updaterResult['errors'], $updaterResult['deactivatedPlugins']);
        }
        if (!empty($updaterResult['warnings'])
            || !empty($updaterResult['errors'])
        ) {
            $output->writeln(array(
                "    " . Piwik::translate('CoreUpdater_HelpMessageIntroductionWhenWarning'),
                "",
                "    * " . $this->getUpdateHelpMessage()
            ));
        }
    }
    private function handleCoreError(OutputInterface $output, $errors, $includeDiyHelp = false)
    {
        if (!is_array($errors)) {
            $errors = array($errors);
        }
        $output->writeln(array(
            "",
            "    [X] " . Piwik::translate('CoreUpdater_CriticalErrorDuringTheUpgradeProcess'),
            "",
        ));
        foreach ($errors as $errorMessage) {
            $errorMessage = trim($errorMessage);
            $errorMessage = str_replace("\n", "\n    ", $errorMessage);
            $output->writeln("    * $errorMessage");
        }
        $output->writeln(array(
            "",
            "    " . Piwik::translate('CoreUpdater_HelpMessageIntroductionWhenError'),
            "",
            "    * " . $this->getUpdateHelpMessage()
        ));
        if ($includeDiyHelp) {
            $output->writeln(array(
                "",
                "    " . Piwik::translate('CoreUpdater_ErrorDIYHelp'),
                "",
                "    * " . Piwik::translate('CoreUpdater_ErrorDIYHelp_1'),
                "    * " . Piwik::translate('CoreUpdater_ErrorDIYHelp_2'),
                "    * " . Piwik::translate('CoreUpdater_ErrorDIYHelp_3'),
                "    * " . Piwik::translate('CoreUpdater_ErrorDIYHelp_4'),
                "    * " . Piwik::translate('CoreUpdater_ErrorDIYHelp_5')
            ));
        }
        throw new \RuntimeException(Piwik::translate('CoreUpdater_ConsoleUpdateFailure'));
    }
    private function outputUpdaterWarnings(OutputInterface $output, $warnings)
    {
        $output->writeln(array(
            "",
            "    [!] " . Piwik::translate('CoreUpdater_WarningMessages'),
            ""
        ));
        foreach ($warnings as $message) {
            $output->writeln("    * $message");
        }
    }
    private function outputUpdaterErrors(OutputInterface $output, $errors, $deactivatedPlugins)
    {
        $output->writeln(array(
            "",
            "    [X] " . Piwik::translate('CoreUpdater_ErrorDuringPluginsUpdates'),
            ""
        ));
        foreach ($errors as $message) {
            $output->writeln("    * $message");
        }
        if (!empty($deactivatedPlugins)) {
            $output->writeln(array(
                "",
                "    [!] " . Piwik::translate('CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins', implode(', ', $deactivatedPlugins))
            ));
        }
    }
    private function getUpdateHelpMessage()
    {
        return Piwik::translate('CoreUpdater_HelpMessageContent', array('[',']',"\n    *"));
    }
    private function isUpdatingCore($componentsWithUpdateFile)
    {
        foreach ($componentsWithUpdateFile as $componentName => $updates) {
            if ($componentName == 'core') {
                return true;
            }
        }
        return false;
    }
    private function getCurrentVersionForCore(Updater $updater)
    {
        $currentVersion = $updater->getCurrentComponentVersion('core');
        if ($currentVersion === false) {
            $currentVersion = "<= 0.2.9";
        }
        return $currentVersion;
    }
    private function getPluginsToUpdate($componentsWithUpdateFile)
    {
        $plugins = array();
        foreach ($componentsWithUpdateFile as $componentName => $updates) {
            if ($componentName !== 'core'
                && 0 !== strpos($componentName, 'log_')
            ) {
                $plugins[] = $componentName;
            }
        }
        return $plugins;
    }
    private function getDimensionsToUpdate($componentsWithUpdateFile)
    {
        $dimensions = array();
        foreach ($componentsWithUpdateFile as $componentName => $updates) {
            if (0 === strpos($componentName, 'log_')) {
                $dimensions[] = $componentName;
            }
        }
        sort($dimensions);
        return $dimensions;
    }
    private function getMigrationQueriesToExecute(Updater $updater)
    {
        if (empty($this->migrationQueries)) {
            $this->migrationQueries = $updater->getSqlQueriesToExecute();
        }
        return $this->migrationQueries;
    }
    private function makeUpdaterInstance(OutputInterface $output)
    {
        $updater = new Updater();
        $migrationQueryCount = count($this->getMigrationQueriesToExecute($updater));
        $updater->addUpdateObserver(new CliUpdateObserver($output, $migrationQueryCount));
        return $updater;
    }
    /**
     * @param OutputInterface $output
     */
    protected function writeAlertMessageWhenCommandExecutedWithUnexpectedUser(OutputInterface $output)
    {
        if (SettingsServer::isWindows()) {
            // does not work on windows
            return;
        }
        $processUserAndGroup = Filechecks::getUserAndGroup();
        $fileOwnerUserAndGroup = Filechecks::getOwnerOfPiwikFiles();
        if (!$fileOwnerUserAndGroup || $processUserAndGroup == $fileOwnerUserAndGroup) {
            // current process user/group appear to be same as the Matomo filesystem user/group -> OK
            return;
        }
        $output->writeln(
            sprintf("%s", Piwik::translate('CoreUpdater_ConsoleUpdateUnexpectedUserWarning', [
                $processUserAndGroup,
                $fileOwnerUserAndGroup,
                Filechecks::getCommandToChangeOwnerOfPiwikFiles()
            ]))
        );
    }
}