Skip to content
Code-Schnipsel Gruppen Projekte
Manager.php 15,8 KiB
Newer Older
Henning Leutz's avatar
Henning Leutz committed
/**
 * This file contains QUI\Ckeditor\Plugins\Manager
 */

namespace QUI\Ckeditor\Plugins;

Henning Leutz's avatar
Henning Leutz committed
use QUI;
Florian Bogner's avatar
Florian Bogner committed
use QUI\Exception;
use QUI\System\Log;
Florian Bogner's avatar
Florian Bogner committed
use QUI\Utils\Security\Orthos;
use QUI\Utils\System\File;

/**
 * Class Manager
 *
 * @package QUI\Ckeditor\Plugins
 */
Henning Leutz's avatar
Henning Leutz committed
    /**
     * @var string
     */
Florian Bogner's avatar
Florian Bogner committed
    protected $activePluginDir;
Henning Leutz's avatar
Henning Leutz committed

    /**
     * @var string
     */
Florian Bogner's avatar
Florian Bogner committed
    protected $installedPluginDir;

Henning Leutz's avatar
Henning Leutz committed
    /**
     * @var
     */
    protected $dependencies;

    /**
     * List of plugins which should not be installed
     * @var array
     */
Henning Leutz's avatar
Henning Leutz committed
    protected $blacklist = [
        "copyformatting",
        "crossreference",
        "footnotes",
        "textselection"
Henning Leutz's avatar
Henning Leutz committed
    ];
Florian Bogner's avatar
Florian Bogner committed
    /**
     * Manager constructor.
     */
    public function __construct()
    {
Henning Leutz's avatar
Henning Leutz committed
        $Package = QUI::getPackage("quiqqer/ckeditor4");
Henning Leutz's avatar
Henning Leutz committed
        $this->activePluginDir = $Package->getVarDir() . "/plugins/bin";
        $this->installedPluginDir = $Package->getVarDir() . "/plugins/installed";
Florian Bogner's avatar
Florian Bogner committed

        if (!is_dir($this->activePluginDir)) {
            mkdir($this->activePluginDir, 0755, true);
        }

        if (!is_dir($this->installedPluginDir)) {
            mkdir($this->installedPluginDir, 0755, true);
        }
    }

    #########################################
    #             Installation              #
    #########################################

Florian Bogner's avatar
Florian Bogner committed
    /**
     * Updates the plugins
    public function updatePlugins()
Henning Leutz's avatar
Henning Leutz committed
        $srcDirs = [
            OPT_DIR . "ckeditor/ckeditor/plugins",
            OPT_DIR . "quiqqer/ckeditor4/plugins/quiqqer",
            OPT_DIR . "quiqqer/ckeditor4/plugins/ckeditor4"
        ];
        foreach ($srcDirs as $srcDir) {
            if (!is_dir($srcDir)) {
                return;
            }
            foreach (scandir($srcDir) as $entry) {
                if ($entry == "." || $entry == "..") {
                    continue;
                }
Henning Leutz's avatar
Henning Leutz committed
                if (!is_dir($srcDir . "/" . $entry)) {
                $pluginName = $entry;
                // Special case, because gitlab gets confused with the dirctory named "codeTag"
                if ($entry == "code") {
Henning Leutz's avatar
Henning Leutz committed
                    $pluginName = "codeTag";
Henning Leutz's avatar
Henning Leutz committed

                # Check if/where the plugin is installed
Henning Leutz's avatar
Henning Leutz committed
                $targetDir = $this->installedPluginDir . "/" . $pluginName;
Henning Leutz's avatar
Henning Leutz committed

Henning Leutz's avatar
Henning Leutz committed
                if (is_dir($this->activePluginDir . "/" . $pluginName)) {
                    $targetDir = $this->activePluginDir . "/" . $pluginName;
                if (is_dir($targetDir)) {
                    File::deleteDir($targetDir);
                }
                File::dircopy(
Henning Leutz's avatar
Henning Leutz committed
                    $srcDir . "/" . $entry,
     * Installs the plugins from the source packages quiqqer/ckeditor4 and ckeditor4/ckeditor4
    public function installPluginsFromSource()
Henning Leutz's avatar
Henning Leutz committed
        $srcDirs = [
            OPT_DIR . "ckeditor/ckeditor/plugins",
            OPT_DIR . "quiqqer/ckeditor4/plugins/quiqqer",
            OPT_DIR . "quiqqer/ckeditor4/plugins/ckeditor4"
        ];
Henning Leutz's avatar
Henning Leutz committed
        $activePlugins = [];
        $defaultStateFile = dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/plugins/activePlugins.json";
Henning Leutz's avatar
Henning Leutz committed

        if (file_exists($defaultStateFile)) {
Henning Leutz's avatar
Henning Leutz committed
            $json = file_get_contents($defaultStateFile);
            $activePlugins = json_decode($json, true);
        }

        foreach ($srcDirs as $srcDir) {
            if (!is_dir($srcDir)) {
                return;
            }

            foreach (scandir($srcDir) as $entry) {
                if ($entry == "." || $entry == "..") {
                    continue;
                }
Henning Leutz's avatar
Henning Leutz committed

                // Special case, because gitlab gets confused with the directory named "codeTag"
Henning Leutz's avatar
Henning Leutz committed
                    $pluginName = "codeTag";
Henning Leutz's avatar
Henning Leutz committed

                $targetDir = $this->installedPluginDir;
Henning Leutz's avatar
Henning Leutz committed

                if (in_array($entry, $activePlugins)) {
                    $targetDir = $this->activePluginDir;
                }

Henning Leutz's avatar
Henning Leutz committed
                if (!is_dir($srcDir . "/" . $entry)) {
Henning Leutz's avatar
Henning Leutz committed
                if (is_dir($this->installedPluginDir . "/" . $pluginName)) {
Henning Leutz's avatar
Henning Leutz committed

                if (is_dir($this->activePluginDir . "/" . $pluginName)) {

                if (in_array($entry, $this->blacklist)) {
                    continue;
                }

                $this->copyDir(
Henning Leutz's avatar
Henning Leutz committed
                    $srcDir . "/" . $entry,
                    $targetDir . "/" . $pluginName
Henning Leutz's avatar
Henning Leutz committed
        if (file_exists(OPT_DIR . "quiqqer/ckeditor4/plugins/dependencies.json")) {
Henning Leutz's avatar
Henning Leutz committed
                OPT_DIR . "quiqqer/ckeditor4/plugins/dependencies.json",
                $this->getPluginDir() . "/dependencies.json"
        #File::deleteDir(OPT_DIR . "ckeditor/ckeditor/plugins");
Florian Bogner's avatar
Florian Bogner committed
     *
     * @return string[] - array of plugin names
     */
    public function getInstalledPlugins()
    {
Henning Leutz's avatar
Henning Leutz committed
        $result = [];
Florian Bogner's avatar
Florian Bogner committed

        $content = scandir($this->installedPluginDir);
Henning Leutz's avatar
Henning Leutz committed

Florian Bogner's avatar
Florian Bogner committed
        if ($content === false) {
Henning Leutz's avatar
Henning Leutz committed
            return [];
Florian Bogner's avatar
Florian Bogner committed
        }

        foreach ($content as $entry) {
            if ($entry == "." || $entry == "..") {
                continue;
            }

Henning Leutz's avatar
Henning Leutz committed
            $fullpath = $this->installedPluginDir . "/" . $entry;
Florian Bogner's avatar
Florian Bogner committed

            if (!is_dir($fullpath)) {
                continue;
            }

            $result[] = $entry;
        }

        return $result;
    /**
     * Installs a plugin zip file from the given path
     *
     * @param $pluginpath - path to the plugins zip file
     *
     * @throws Exception
     */
    public function installPlugin($pluginpath)
    {
        # Check if file exists
        if (!file_exists($pluginpath)) {
Henning Leutz's avatar
Henning Leutz committed
            throw new Exception(["quiqqer/ckeditor4", "exception.install.file.not.found"]);
Henning Leutz's avatar
Henning Leutz committed
        $tmpDir = QUI::getTemp()->createFolder();

        copy(
            $pluginpath,
Henning Leutz's avatar
Henning Leutz committed
            $tmpDir . "/archive.zip"
        );

        $Zip = new \ZipArchive();

Henning Leutz's avatar
Henning Leutz committed
        if ($Zip->open($tmpDir . "/archive.zip") === false) {
            throw new Exception(["quiqqer/ckeditor4", "exception.install.file.invalid.format"]);
Henning Leutz's avatar
Henning Leutz committed
        if ($Zip->extractTo($tmpDir . "/content") === false) {
            throw new Exception(["quiqqer/ckeditor4", "exception.install.file.extract.failed"]);
Henning Leutz's avatar
Henning Leutz committed
        $contents = scandir($tmpDir . "/content");
        foreach (array_keys($contents, ".", true) as $key) {
            unset($contents[$key]);
        }

        foreach (array_keys($contents, "..", true) as $key) {
            unset($contents[$key]);
        }


        // Check if the zip contains only one folder
        if (count($contents) !== 1) {
Henning Leutz's avatar
Henning Leutz committed
            throw new Exception([
                "quiqqer/ckeditor4",
                "exception.plugin.install.wrong.format"
Henning Leutz's avatar
Henning Leutz committed
            ]);
            if ($entry == "." || $entry == "..") {
                continue;
            }

Henning Leutz's avatar
Henning Leutz committed
            if (is_dir($this->installedPluginDir . "/" . $entry)) {
                throw new Exception(["quiqqer/ckeditor4", "exception.install.file.exists"]);
Henning Leutz's avatar
Henning Leutz committed
            if (is_dir($this->activePluginDir . "/" . $entry)) {
                throw new Exception(["quiqqer/ckeditor4", "exception.install.file.exists"]);
Henning Leutz's avatar
Henning Leutz committed
                $tmpDir . "/content/" . $entry,
                $this->installedPluginDir . "/" . $entry
            );
        }

        File::deleteDir($tmpDir);

Henning Leutz's avatar
Henning Leutz committed
        QUI::getMessagesHandler()->addSuccess(
            QUI::getLocale()->get(
                "quiqqer/ckeditor4",
                "message.plugin.install.success"
            )
        );
    }

    #########################################
    #             Enable/Disable            #
    #########################################

    /**
     * Activates the given plugin name
     *
     * @param $pluginName
     *
     * @throws Exception
     */
    public function activate($pluginName)
    {
        $pluginName = Orthos::clearPath($pluginName);
        $pluginName = str_replace("/", "", $pluginName);

Henning Leutz's avatar
Henning Leutz committed
            throw new Exception([
                "quiqqer/ckeditor4",
                "exception.plugin.activate.blacklisted"
Henning Leutz's avatar
Henning Leutz committed
            ]);
Henning Leutz's avatar
Henning Leutz committed
        if (!is_dir($this->installedPluginDir . "/" . $pluginName)) {
            throw new Exception([
Henning Leutz's avatar
Henning Leutz committed
                "quiqqer/ckeditor4",
                "exception.plugin.activate.plugin.not.found"
Henning Leutz's avatar
Henning Leutz committed
            ]);
Henning Leutz's avatar
Henning Leutz committed
        if (is_dir($this->activePluginDir . "/" . $pluginName)) {
            throw new Exception([
Henning Leutz's avatar
Henning Leutz committed
                "quiqqer/ckeditor4",
                "exception.plugin.already.active"
Henning Leutz's avatar
Henning Leutz committed
            ]);
        }

        $deps = $this->getDependencies($pluginName);
Henning Leutz's avatar
Henning Leutz committed

        foreach ($deps as $dep) {
            try {
                $this->activate($dep);
            } catch (\Exception $Exception) {
            }
        }

Henning Leutz's avatar
Henning Leutz committed
        rename(
Henning Leutz's avatar
Henning Leutz committed
            $this->installedPluginDir . "/" . $pluginName,
            $this->activePluginDir . "/" . $pluginName
Henning Leutz's avatar
Henning Leutz committed
        );
Henning Leutz's avatar
Henning Leutz committed
        QUI\Cache\Manager::clear("quiqqer/ckeditor/plugins/data");
    }

    /**
     * Deactivates the given plugin name
     *
     * @param $pluginName
     *
     * @throws Exception
     */
    public function deactivate($pluginName)
    {
        $pluginName = Orthos::clearPath($pluginName);
        $pluginName = str_replace("/", "", $pluginName);

Henning Leutz's avatar
Henning Leutz committed
        if (!is_dir($this->activePluginDir . "/" . $pluginName)) {
            throw new Exception([
Henning Leutz's avatar
Henning Leutz committed
                "quiqqer/ckeditor4",
                "exception.plugin.activate.plugin.not.active"
Henning Leutz's avatar
Henning Leutz committed
            ]);
Henning Leutz's avatar
Henning Leutz committed
        if (is_dir($this->installedPluginDir . "/" . $pluginName)) {
            File::deleteDir($this->activePluginDir . "/" . $pluginName);

            return;
        }

        foreach ($this->getDependentPlugins($pluginName) as $depName) {
            try {
                $this->deactivate($depName);
            } catch (\Exception $Exception) {
            }
        }

Henning Leutz's avatar
Henning Leutz committed
        rename(
Henning Leutz's avatar
Henning Leutz committed
            $this->activePluginDir . "/" . $pluginName,
            $this->installedPluginDir . "/" . $pluginName
Henning Leutz's avatar
Henning Leutz committed
        );
Henning Leutz's avatar
Henning Leutz committed
        QUI\Cache\Manager::clear("quiqqer/ckeditor/plugins/data");
Florian Bogner's avatar
Florian Bogner committed
    /**
     * Returns all active plugins
     *
     * @return string[] - Array of active plugin names
     */
    public function getActivePlugins()
    {
Henning Leutz's avatar
Henning Leutz committed
        $result = [];
Florian Bogner's avatar
Florian Bogner committed
        $content = scandir($this->activePluginDir);
Henning Leutz's avatar
Henning Leutz committed

Florian Bogner's avatar
Florian Bogner committed
        if ($content === false) {
Henning Leutz's avatar
Henning Leutz committed
            return [];
Florian Bogner's avatar
Florian Bogner committed
        }

        foreach ($content as $entry) {
            if ($entry == "." || $entry == "..") {
                continue;
            }
Henning Leutz's avatar
Henning Leutz committed

Henning Leutz's avatar
Henning Leutz committed
            $fullpath = $this->activePluginDir . "/" . $entry;
Florian Bogner's avatar
Florian Bogner committed
            if (!is_dir($fullpath)) {
                continue;
            }

            $result[] = $entry;
        }

        return $result;
    #########################################
    #             Dependencies            #
    #########################################

Florian Bogner's avatar
Florian Bogner committed
    /**
     * Gets all dependencies for the given plugin.
     * Including dependencies fo dependencies
     * Returns false on error
     * @param $pluginName
     *
     * @return array|false
    public function getDependencies($pluginName)
        try {
            $this->loadDependencies();
        } catch (\Exception $Exception) {
            return false;
        }
Henning Leutz's avatar
Henning Leutz committed
        $result = [];
        if (!isset($this->dependencies[$pluginName])) {
Henning Leutz's avatar
Henning Leutz committed
            return [];
        $deps = $this->dependencies[$pluginName];
Henning Leutz's avatar
Henning Leutz committed

        foreach ($deps as $dep) {
            $result[] = $dep;
            $subDeps = $this->getDependencies($dep);
Henning Leutz's avatar
Henning Leutz committed
            $result = array_merge($result, $subDeps);
        $result = array_unique($result);
        return $result;
    }
    /**
     * Returns an array of packages that depend on the given plugin
     * Returns false on error
     *
     * @param $pluginName
     *
     * @return array|bool
     */
    public function getDependentPlugins($pluginName)
    {
Henning Leutz's avatar
Henning Leutz committed
        $result = [];

        try {
            $this->loadDependencies();
        } catch (\Exception $Exception) {
            return false;
        }

        foreach ($this->dependencies as $pkg => $deps) {
            if (in_array($pluginName, $deps)) {
                $result[] = $pkg;

        $result = array_unique($result);

        return $result;
Florian Bogner's avatar
Florian Bogner committed
    /**
     * Loads the dependencies for the installed modules
     *
     * @throws Exception
    protected function loadDependencies()
        if (isset($this->dependencies) && !empty($this->dependencies)) {
            return;
        }
Henning Leutz's avatar
Henning Leutz committed
        if (!file_exists($this->getPluginDir() . "/dependencies.json")) {
            Log::addWarning("Missing dependency file: " . $this->getPluginDir() . "/dependencies.json");
            throw new Exception("missing.dependency.file");
        }
Henning Leutz's avatar
Henning Leutz committed
        $json = file_get_contents($this->getPluginDir() . "/dependencies.json");
        $deps = json_decode($json, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception(json_last_error_msg());
        }
        $this->dependencies = $deps;
    }
    #########################################
    #               Helper                  #
    #########################################

    /**
     * Returns a list of all plugins and their details
     * Format:
     * array(
     *  [0] => array(
     *      ["name"] => "pluginname",
     *      ["state"] => 0|1  (1 for active; 0 for inactive)
     *  )
     * )
     *
     * @return array
     */
    public function getAllPlugins()
    {
Henning Leutz's avatar
Henning Leutz committed
        $result = [];
        foreach ($this->getActivePlugins() as $plugin) {
Henning Leutz's avatar
Henning Leutz committed
            $result[] = [
                'name' => $plugin,
                'state' => 1
Henning Leutz's avatar
Henning Leutz committed
            ];
        foreach ($this->getInstalledPlugins() as $plugin) {
Henning Leutz's avatar
Henning Leutz committed
            $result[] = [
                'name' => $plugin,
                'state' => 0
Henning Leutz's avatar
Henning Leutz committed
            ];

        return $result;
Florian Bogner's avatar
Florian Bogner committed
    /**
     * Returns the plugin dir
     *
     * @return string
     */
    public function getPluginDir()
    {
Henning Leutz's avatar
Henning Leutz committed
        return QUI::getPackage("quiqqer/ckeditor4")->getVarDir() . "/plugins";

    /**
     * Recursively copies the target directory to the target location
     *
     * @param $src
     * @param $target
     */
    public function copyDir($src, $target)
    {
        if (!is_dir($target)) {
            mkdir($target, 0755, true);
        }

        $entries = scandir($src);

        foreach ($entries as $entry) {
            if ($entry == "." || $entry == "..") {
                continue;
            }

Henning Leutz's avatar
Henning Leutz committed
            $fullpath = $src . "/" . $entry;
Henning Leutz's avatar
Henning Leutz committed
                $this->copyDir($fullpath, $target . "/" . $entry);
Henning Leutz's avatar
Henning Leutz committed
            copy($fullpath, $target . "/" . $entry);
    public function getPluginUrlPath()
    {
        // Build the web reachable path for the plugin directory
Henning Leutz's avatar
Henning Leutz committed
        $pluginPath = QUI::getPackage("quiqqer/ckeditor4")->getVarDir() . "plugins";
        $varParent = dirname(VAR_DIR);

        # Parse the URL directory
        $pluginUrlPath = str_replace($varParent, "", $pluginPath);

        return $pluginUrlPath;
    }
Florian Bogner's avatar
Florian Bogner committed
}