Source of file Update.php

Size: 17,039 Bytes - Last Modified: 2020-10-24T02:46:31+00:00

/home/travis/build/NextDom/nextdom-core/src/Model/Entity/Update.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
<?php
/* This file is part of NextDom Software.
 *
 * NextDom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * NextDom Software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with NextDom Software. If not, see <http://www.gnu.org/licenses/>.
 */

namespace NextDom\Model\Entity;

use NextDom\Com\ComHttp;
use NextDom\Enums\Common;
use NextDom\Enums\LogTarget;
use NextDom\Enums\NextDomObj;
use NextDom\Exceptions\CoreException;
use NextDom\Helpers\FileSystemHelper;
use NextDom\Helpers\LogHelper;
use NextDom\Helpers\NextDomHelper;
use NextDom\Helpers\SystemHelper;
use NextDom\Helpers\Utils;
use NextDom\Managers\ConfigManager;
use NextDom\Managers\EqLogicManager;
use NextDom\Managers\EventManager;
use NextDom\Managers\PluginManager;
use NextDom\Managers\UpdateManager;
use NextDom\Model\Entity\Parents\BaseEntity;
use NextDom\Model\Entity\Parents\ConfigurationEntity;
use NextDom\Model\Entity\Parents\LogicalIdEntity;
use NextDom\Model\Entity\Parents\NameEntity;
use NextDom\Model\Entity\Parents\RefreshEntity;
use NextDom\Model\Entity\Parents\TypeEntity;
use ZipArchive;

/**
 * Update
 *
 * @ORM\Table(name="update", indexes={@ORM\Index(name="status", columns={"status"})})
 * @ORM\Entity
 */
class Update extends BaseEntity
{
    const TABLE_NAME = NextDomObj::UPDATE;

    use ConfigurationEntity, LogicalIdEntity, NameEntity, RefreshEntity, TypeEntity;

    /**
     * @var string
     *
     * @ORM\Column(name="localVersion", type="string", length=127, nullable=true)
     */
    protected $localVersion;

    /**
     * @var string
     *
     * @ORM\Column(name="remoteVersion", type="string", length=127, nullable=true)
     */
    protected $remoteVersion;

    /**
     * @var string
     *
     * @ORM\Column(name="source", type="string", length=127, nullable=true)
     */
    protected $source = Common::MARKET;

    /**
     * @var string
     *
     * @ORM\Column(name="status", type="string", length=127, nullable=true)
     */
    protected $status;

    protected $_changeUpdate = false;

    public function __construct()
    {
        if ($this->type === null) {
            $this->type = NextDomObj::PLUGIN;
        }
    }

    /**
     * Obtenir les informations de la mise à jour
     *
     * @return array
     * @throws \Exception
     */
    public function getInfo()
    {
        $result = [];
        if (!$this->isType(Common::CORE)) {
            $repoClass = 'Repo' . $this->getSource();
            if (class_exists($repoClass) && method_exists($repoClass, 'objectInfo') && ConfigManager::byKey($this->getSource() . '::enable') == 1) {
                $result = $repoClass::objectInfo($this);
            }
        }
        return $result;
    }

    /**
     * @return string
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     * @param $_source
     * @return $this
     */
    public function setSource($_source)
    {
        $this->updateChangeState($this->source, $_source);
        $this->source = $_source;
        return $this;
    }

    /**
     * Start update
     *
     * @throws CoreException
     * @throws \Throwable
     */
    public function doUpdate()
    {
        if ($this->getConfiguration('doNotUpdate') == 1 && !$this->isType(Common::CORE)) {
            LogHelper::addAlert(LogTarget::UPDATE, __('Vérification des mises à jour, mise à jour et réinstallation désactivées sur ') . $this->getLogicalId());
            return;
        }
        if ($this->isType(Common::CORE)) {
            NextDomHelper::update(['core' => 1]);
        } else {
            $repoClass = UpdateManager::getRepoDataFromName($this->getSource())['phpClass'];
            if (class_exists($repoClass) && method_exists($repoClass, 'downloadObject') && ConfigManager::byKey($this->getSource() . '::enable') == 1) {
                $this->preInstallUpdate();
                $cibDir = NextDomHelper::getTmpFolder(Common::MARKET) . '/' . $this->getLogicalId();
                if (file_exists($cibDir)) {
                    rrmdir($cibDir);
                }
                mkdir($cibDir);
                if (!file_exists($cibDir) && !mkdir($cibDir, 0775, true)) {
                    throw new CoreException(__('Impossible de créer le dossier  : ' . $cibDir . '. Problème de droits ?'));
                }
                LogHelper::addAlert(LogTarget::UPDATE, __('Téléchargement du plugin...'));
                $info = $repoClass::downloadObject($this);
                if ($info['path'] !== false) {
                    $tmp = $info['path'];
                    LogHelper::addAlert(LogTarget::UPDATE, __("OK\n"));

                    if (!file_exists($tmp)) {
                        throw new CoreException(__('Impossible de trouver le fichier zip : ') . $this->getConfiguration('path'));
                    }
                    if (filesize($tmp) < 100) {
                        throw new CoreException(__('Échec lors du téléchargement du fichier. Veuillez réessayer plus tard (taille inférieure à 100 octets). Cela peut être lié à un manque de place, une version minimale requise non consistente avec votre version de NextDom, un soucis du plugin sur le market, etc.'));
                    }
                    $extension = strtolower(strrchr($tmp, '.'));
                    if (!in_array($extension, ['.zip'])) {
                        throw new CoreException('Extension du fichier non valide (autorisé .zip) : ' . $extension);
                    }
                    LogHelper::addAlert(LogTarget::UPDATE, __('Décompression du zip...'));
                    $zip = new ZipArchive;
                    $res = $zip->open($tmp);
                    if ($res) {
                        if (!$zip->extractTo($cibDir . '/')) {
                            $content = file_get_contents($tmp);
                            throw new CoreException(__('Impossible d\'installer le plugin. Les fichiers n\'ont pas pu être décompressés : ') . substr($content, 255));
                        }
                        $zip->close();
                        unlink($tmp);
                        try {
                            if (file_exists(NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId() . '/doc')) {
                                shell_exec('sudo rm -rf ' . NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId() . '/doc');
                            }
                            if (file_exists(NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId() . '/docs')) {
                                shell_exec('sudo rm -rf ' . NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId() . '/docs');
                            }
                        } catch (\Exception $e) {

                        }
                        if (!file_exists($cibDir . '/plugin_info')) {
                            $files = FileSystemHelper::ls($cibDir, '*');
                            if (count($files) == 1 && file_exists($cibDir . '/' . $files[0] . 'plugin_info')) {
                                $cibDir = $cibDir . '/' . $files[0];
                            }
                        }
                        rmove($cibDir, NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId(), false, [], true);
                        rrmdir($cibDir);
                        $cibDir = NextDomHelper::getTmpFolder(Common::MARKET) . '/' . $this->getLogicalId();
                        if (file_exists($cibDir)) {
                            rrmdir($cibDir);
                        }
                        LogHelper::addAlert(LogTarget::UPDATE, __("OK\n"));
                    } else {
                        throw new CoreException(__('Impossible de décompresser l\'archive zip : ') . $tmp . ' => ' . Utils::getZipErrorMessage($res));
                    }
                }
                $this->postInstallUpdate($info);
            }
        }
        $this->refresh();
        $this->checkUpdate();
    }

    /**
     * Lance la procédure de préinstallation d'un objet
     *
     * @throws \Exception
     */
    public function preInstallUpdate()
    {
        if (!file_exists(NEXTDOM_ROOT . '/plugins')) {
            mkdir(NEXTDOM_ROOT . '/plugins');
            @chown(NEXTDOM_ROOT . '/plugins', SystemHelper::getWWWUid());
            @chgrp(NEXTDOM_ROOT . '/plugins', SystemHelper::getWWWGid());
            @chmod(NEXTDOM_ROOT . '/plugins', 0775);
        }
        LogHelper::addAlert(LogTarget::UPDATE, __('Début de la mise à jour de : ') . $this->getLogicalId() . "\n");
        if ($this->isType(NextDomObj::PLUGIN)) {
            $targetDir = NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId();
            if (!file_exists($targetDir) && !mkdir($targetDir, 0775, true)) {
                throw new CoreException(__('Impossible de créer le dossier  : ' . $targetDir . '. Problème de droits ?'));
            }
            try {
                $plugin = PluginManager::byId($this->getLogicalId());
                if (is_object($plugin)) {
                    LogHelper::addAlert(LogTarget::UPDATE, __('Action de pré-update...'));
                    $plugin->callInstallFunction('pre_update');
                    LogHelper::addAlert(LogTarget::UPDATE, __("OK\n"));
                }
            } catch (\Exception $e) {

            }
        }
    }

    /**
     * Lancer la procédure post installation
     *
     * @param $informations
     * @throws CoreException
     * @throws \Throwable
     */
    public function postInstallUpdate($informations)
    {
        LogHelper::addAlert(LogTarget::UPDATE, __('Post-installation de ') . $this->getLogicalId() . '...');
        if ($this->isType(NextDomObj::PLUGIN)) {
            try {
                $plugin = PluginManager::byId($this->getLogicalId());
            } catch (\Exception $e) {
                $this->remove();
                throw new CoreException(__('Impossible d\'installer le plugin. Le nom du plugin est différent de l\'ID ou le plugin n\'est pas correctement formé. Veuillez contacter l\'auteur.'));
            }
            if (is_object($plugin) && $plugin->isActive()) {
                $plugin->setIsEnable(1);
            }
        }
        if (isset($informations['localVersion'])) {
            $this->setLocalVersion($informations['localVersion']);
        }
        $this->save();
        LogHelper::addAlert(LogTarget::UPDATE, __("OK\n"));
    }

    /**
     * Vérifier si une mise à jour est disponible
     *
     * @return bool
     * @throws \Exception
     */
    public function checkUpdate()
    {
        if ($this->getConfiguration('doNotUpdate') == 1 && !$this->isType(Common::CORE)) {
            LogHelper::addAlert(LogTarget::UPDATE, __('Vérification des mises à jour, mise à jour et réinstallation désactivées sur ') . $this->getLogicalId());
            return false;
        }
        if ($this->isType(Common::CORE)) {
            if (ConfigManager::byKey('update::allowCore', Common::CORE, 1) != 1) {
                return false;
            }
            if (ConfigManager::byKey('core::repo::provider') == 'default') {
                $this->setRemoteVersion(self::getLastAvailableVersion());
            } else {
                $class = 'Repo' . ConfigManager::byKey('core::repo::provider');
                if (!method_exists($class, 'versionCore') || ConfigManager::byKey(ConfigManager::byKey('core::repo::provider') . '::enable') != 1) {
                    $version = $this->getLocalVersion();
                } else {
                    $version = $class::versionCore();
                    if ($version === null) {
                        $version = $this->getLocalVersion();
                    }
                }
                $this->setRemoteVersion($version);
            }
            if (version_compare($this->getRemoteVersion(), $this->getLocalVersion(), '>')) {
                $this->setStatus('update');
            } else {
                $this->setStatus('ok');
            }
            $this->save();
        } else {
            try {
                $class = UpdateManager::getRepoDataFromName($this->getSource())['phpClass'];
                if (class_exists($class) && method_exists($class, 'checkUpdate') && ConfigManager::byKey($this->getSource() . '::enable') == 1) {
                    $class::checkUpdate($this);
                }
            } catch (\Exception $ex) {

            }
        }
        return true;
    }

    /**
     * Obtenir la dernière version disponible
     *
     * @return null|string
     */
    public static function getLastAvailableVersion()
    {
        try {
            $url = 'https://raw.githubusercontent.com/NextDom/nextdom-core/' . ConfigManager::byKey('core::branch', Common::CORE, 'master') . '/assets/config/Nextdom_version';
            $request_http = new ComHttp($url);
            return trim($request_http->exec());
        } catch (\Exception $e) {

        }
        return null;
    }

    /**
     * @return string
     */
    public function getLocalVersion()
    {
        return $this->localVersion;
    }

    /**
     * @param $_localVersion
     * @return $this
     */
    public function setLocalVersion($_localVersion)
    {
        $this->updateChangeState($this->localVersion, $_localVersion);
        $this->localVersion = $_localVersion;
        return $this;
    }

    /**
     * @return string
     */
    public function getRemoteVersion()
    {
        return $this->remoteVersion;
    }

    /**
     * @param $_remoteVersion
     * @return $this
     */
    public function setRemoteVersion($_remoteVersion)
    {
        $this->updateChangeState($this->remoteVersion, $_remoteVersion);
        $this->remoteVersion = $_remoteVersion;
        return $this;
    }

    /**
     * Supprime une information de mise à jour
     *
     * @throws \Throwable
     */
    public function deleteObjet()
    {
        if ($this->isType(Common::CORE)) {
            throw new CoreException(__('Vous ne pouvez pas supprimer le core de NextDom'));
        } else {
            if ($this->isType(NextDomObj::PLUGIN)) {
                try {
                    $plugin = PluginManager::byId($this->getLogicalId());
                    if (is_object($plugin)) {
                        try {
                            $plugin->setIsEnable(0);
                        } catch (\Exception $e) {

                        }
                        foreach (EqLogicManager::byType($this->getLogicalId()) as $eqLogic) {
                            try {
                                $eqLogic->remove();
                            } catch (\Exception $e) {

                            }
                        }
                    }
                    ConfigManager::remove('*', $this->getLogicalId());
                } catch (\Exception $e) {

                }
            }
            try {
                $class = 'Repo' . $this->getSource();
                if (class_exists($class) && method_exists($class, 'deleteObjet') && ConfigManager::byKey($this->getSource() . '::enable') == 1) {
                    $class::deleteObjet($this);
                }
            } catch (\Exception $e) {

            }
            if ($this->isType(NextDomObj::PLUGIN)) {
                $cibDir = NEXTDOM_ROOT . '/plugins/' . $this->getLogicalId();
                if (file_exists($cibDir)) {
                    rrmdir($cibDir);
                }
            }
            $this->remove();
        }
    }

    /**
     * Prépare l'objet avant la sauvegarde
     * @TODO: Bizarre, en gros le nom = logicialId
     * @throws CoreException
     */
    public function preSave()
    {
        if ($this->getLogicalId() == '') {
            throw new CoreException(__('Le logical ID ne peut pas être vide'));
        }
        if ($this->getName() == '') {
            $this->setName($this->getLogicalId());
        }
    }

    /**
     * Envoi un évènement
     */
    public function postSave()
    {
        if ($this->_changeUpdate) {
            EventManager::add('update::refreshUpdateNumber');
        }
    }

    public function postRemove()
    {
        EventManager::add('update::refreshUpdateNumber');
    }

    /**
     * @return string
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * @param $_status
     * @return $this
     */
    public function setStatus($_status)
    {
        if ($_status != $this->status) {
            $this->_changeUpdate = true;
            $this->_changed = true;
        }
        $this->status = $_status;
        return $this;
    }
}