Source of file PackagemanagerPackagemanagerModule.php

Size: 13,417 Bytes - Last Modified: 2016-05-18T03:08:26+02:00

buildproject/core/module_packagemanager/system/PackagemanagerPackagemanagerModule.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
<?php
/*"******************************************************************************************************
*   (c) 2007-2016 by Kajona, www.kajona.de                                                              *
*       Published under the GNU LGPL v2.1, see /system/licence_lgpl.txt                                 *
*-------------------------------------------------------------------------------------------------------*
*	$Id$                                  *
********************************************************************************************************/

namespace Kajona\Packagemanager\System;

use Kajona\System\System\CacheManager;
use Kajona\System\System\Carrier;
use Kajona\System\System\Classloader;
use Kajona\System\System\Exception;
use Kajona\System\System\Filesystem;
use Kajona\System\System\InstallerInterface;
use Kajona\System\System\InstallerRemovableInterface;
use Kajona\System\System\Logger;
use Kajona\System\System\SystemModule;
use Kajona\System\System\SystemSetting;


/**
 * Implementation to handle module-packages. List all installed module-packages and starts the installation / update.
 *
 * @package module_packagemanager
 * @author sidler@mulchprod.de
 * @since 4.0
 */
class PackagemanagerPackagemanagerModule implements PackagemanagerPackagemanagerInterface
{

    /**
     * @var PackagemanagerMetadata
     */
    protected $objMetadata;


    /**
     * Returns a list of installed packages, so a single metadata-entry
     * for each package.
     *
     * @return PackagemanagerMetadata[]
     */
    public function getInstalledPackages()
    {
        $arrReturn = array();

        //loop all modules
        $arrModules = Classloader::getInstance()->getArrModules();

        foreach ($arrModules as $strPath => $strOneModule) {
            try {
                $objMetadata = new PackagemanagerMetadata();
                $objMetadata->autoInit("/".$strPath);
                $arrReturn[] = $objMetadata;
            }
            catch (Exception $objEx) {

            }
        }

        return $arrReturn;
    }


    /**
     * Copies the extracted(!) package from the temp-folder
     * to the target-folder.
     * In most cases, this is either located at /core or at /templates.
     * The original should be deleted afterwards.
     *
     * @throws Exception
     * @return void
     */
    public function move2Filesystem()
    {
        $strSource = $this->objMetadata->getStrPath();

        if (!is_dir(_realpath_.$strSource)) {
            throw new Exception("current package ".$strSource." is not a folder.", Exception::$level_ERROR);
        }

        Logger::getInstance(Logger::PACKAGEMANAGEMENT)->addLogRow("moving ".$strSource." to ".$this->getStrTargetPath(), Logger::$levelInfo);

        $objFilesystem = new Filesystem();
        //set a chmod before copying the files - at least try to
        $objFilesystem->chmod($this->getStrTargetPath(), 0777);

        $objFilesystem->folderCopyRecursive($strSource, $this->getStrTargetPath(), true);
        $this->objMetadata->setStrPath($this->getStrTargetPath());

        //reset chmod after copying the files
        $objFilesystem->chmod($this->getStrTargetPath());

        $objFilesystem->folderDeleteRecursive($strSource);

        //shift the cache buster
        $objSetting = SystemSetting::getConfigByName("_system_browser_cachebuster_");
        if ($objSetting != null) {
            $objSetting->setStrValue((int)$objSetting->getStrValue() + 1);
            $objSetting->updateObjectToDb();
        }
    }


    /**
     * Invokes the installer, if given.
     * The installer itself is capable of detecting whether an update or a plain installation is required.
     *
     * @throws Exception
     * @return string
     */
    public function installOrUpdate()
    {
        $strReturn = "";

        if (uniStrpos($this->getObjMetadata()->getStrPath(), "core") === false) {
            throw new Exception("Current module not located in a core directory.", Exception::$level_ERROR);
        }

        if (!$this->isInstallable()) {
            throw new Exception("Current module isn't installable, not all requirements are given", Exception::$level_ERROR);
        }

        //search for an existing installer
        $arrInstaller = $this->getInstaller($this->getObjMetadata());

        //start with modules
        foreach ($arrInstaller as $objInstance) {

            if (!$objInstance instanceof \Kajona\System\System\InstallerBase) {
                continue;
            }

            //skip element installers at first run
            Logger::getInstance(Logger::PACKAGEMANAGEMENT)->addLogRow("triggering updateOrInstall() on installer ".get_class($objInstance).", all requirements given", Logger::$levelInfo);
            //trigger update or install
            $strReturn .= $objInstance->installOrUpdate();
            $this->updateDefaultTemplate();
        }

        /** @var CacheManager $objCache */
        $objCache = Carrier::getInstance()->getContainer()->offsetGet(\Kajona\System\System\ServiceProvider::STR_CACHE_MANAGER);
        $objCache->flushCache();

        return $strReturn;
    }


    /**
     * @return bool
     */
    public function updateDefaultTemplate()
    {
        $objFilesystem = new Filesystem();
        Logger::getInstance(Logger::PACKAGEMANAGEMENT)->addLogRow("updating default template from /".$this->objMetadata->getStrPath(), Logger::$levelInfo);
        if (is_dir(_realpath_."/".$this->objMetadata->getStrPath()."/templates/default/js")) {
            $objFilesystem->folderCopyRecursive($this->objMetadata->getStrPath()."/templates/default/js", "/templates/default/js", true);
        }

        if (is_dir(_realpath_."/".$this->objMetadata->getStrPath()."/templates/default/css")) {
            $objFilesystem->folderCopyRecursive($this->objMetadata->getStrPath()."/templates/default/css", "/templates/default/css", true);
        }

        if (is_dir(_realpath_."/".$this->objMetadata->getStrPath()."/templates/default/pics")) {
            $objFilesystem->folderCopyRecursive($this->objMetadata->getStrPath()."/templates/default/pics", "/templates/default/pics", true);
        }

        return true;
    }

    /**
     * @param PackagemanagerMetadata $objMetadata
     *
     * @return void
     */
    public function setObjMetadata($objMetadata)
    {
        $this->objMetadata = $objMetadata;
    }

    /**
     * @return PackagemanagerMetadata
     */
    public function getObjMetadata()
    {
        return $this->objMetadata;
    }

    /**
     * Validates, whether the current package is installable or not.
     * In nearly all cases
     *
     * @return bool
     */
    public function isInstallable()
    {

        if (!$this->getObjMetadata()->getBitProvidesInstaller()) {
            return false;
        }

        //check if required modules are given in matching versions
        $arrRequiredModules = $this->objMetadata->getArrRequiredModules();
        foreach ($arrRequiredModules as $strOneModule => $strMinVersion) {

            if (trim($strOneModule) != "") {
                $objModule = SystemModule::getModuleByName(trim($strOneModule));
                if ($objModule === null) {

                    $arrModules = Classloader::getInstance()->getArrModules();
                    $objMetadata = null;
                    foreach ($arrModules as $strPath => $strOneFolder) {
                        if (uniStrpos($strOneFolder, $strOneModule) !== false) {
                            $objMetadata = new PackagemanagerMetadata();
                            $objMetadata->autoInit("/".$strPath);

                            //but: if the package provides an installer and was not resolved by the previous calls,
                            //we shouldn't include it here
                            if ($objMetadata->getBitProvidesInstaller()) {
                                $objMetadata = null;
                            }
                        }

                    }

                    //no package found
                    if ($objMetadata === null) {
                        return false;
                    }

                    //package found, but wrong version
                    if (version_compare($strMinVersion, $objMetadata->getStrVersion(), ">")) {
                        return false;
                    }

                }
                //module found, but wrong version
                elseif (version_compare($strMinVersion, $objModule->getStrVersion(), ">")) {
                    return false;
                }
            }
        }


        //compare versions of installed elements
        $objModule = SystemModule::getModuleByName($this->getObjMetadata()->getStrTitle());
        if ($objModule !== null) {
            if (version_compare($this->objMetadata->getStrVersion(), $objModule->getStrVersion(), ">")) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return true;
        }

    }

    /**
     * Gets the version of the package currently installed.
     * If not installed, null should be returned instead.
     *
     * @return string|null
     */
    public function getVersionInstalled()
    {
        //version compare - depending on module or element
        $objModule = SystemModule::getModuleByName($this->getObjMetadata()->getStrTitle());
        if ($objModule !== null) {
            return $objModule->getStrVersion();
        }
        else {
            return null;
        }

    }

    /**
     * Queries the packagemanager for the resolved target path, so the folder to package will be located at
     * after installation (or is already located at since it's already installed.
     *
     * @return mixed
     */
    public function getStrTargetPath()
    {

        $strTarget = $this->objMetadata->getStrTarget();
        if ($strTarget == "") {
            $strTarget = uniStrtolower($this->objMetadata->getStrType()."_".createFilename($this->objMetadata->getStrTitle(), true))."";
        }

        $arrModules = array_flip(Classloader::getInstance()->getArrModules());

        if (isset($arrModules[$strTarget])) {
            return "/".$arrModules[$strTarget];
        }

        return "/core/".$strTarget;
    }

    /**
     * Validates if the current package is removable or not.
     *
     * @return bool
     */
    public function isRemovable()
    {
        $objManager = new PackagemanagerManager();

        if (count($objManager->getArrRequiredBy($this->getObjMetadata())) > 0) {
            return false;
        }

        if (!$this->getObjMetadata()->getBitProvidesInstaller()) {
            return true;
        }

        //scan installers in order to query them on their removable status
        $bitIsRemovable = true;
        foreach ($this->getInstaller($this->getObjMetadata()) as $objOneInstaller) {
            if (!$objOneInstaller instanceof InstallerRemovableInterface) {
                $bitIsRemovable = false;
                break;
            }

            if (!$objOneInstaller->isRemovable()) {
                $bitIsRemovable = false;
                break;
            }
        }

        return $bitIsRemovable;
    }

    /**
     * Removes the current package, if possible, from the system
     *
     * @param string &$strLog
     *
     * @return bool
     */
    public function remove(&$strLog)
    {

        if (!$this->isRemovable()) {
            return false;
        }

        $bitReturn = true;

        //if we reach up until here, each installer should be an instance of InstallerRemovableInterface
        foreach ($this->getInstaller($this->getObjMetadata()) as $objOneInstaller) {
            if ($objOneInstaller instanceof InstallerRemovableInterface) {
                $bitReturn = $bitReturn && $objOneInstaller->remove($strLog);
            }
        }

        //finally: delete the the module on file-system level
        if ($bitReturn) {
            $strLog .= "Deleting file-system parts...\n";
            $objFilesystem = new Filesystem();
            $bitReturn = $objFilesystem->folderDeleteRecursive($this->getObjMetadata()->getStrPath());

            if (!$bitReturn) {
                $strLog .= "Error deleting file-system parts!. Please remove manually: ".$this->getObjMetadata()->getStrPath()."";
            }
        }

        $strLog .= "\n\nRemoval finished ".($bitReturn ? "successfully" : " with errors")."\n";

        return $bitReturn;
    }


    /**
     * Internal helper, fetches all installers located within the passed package
     *
     * @param PackagemanagerMetadata $objMetadata
     *
     * @return InstallerInterface[]
     */
    protected function getInstaller(PackagemanagerMetadata $objMetadata)
    {

        $objFilesystem = new Filesystem();
        $arrInstaller = $objFilesystem->getFilelist($objMetadata->getStrPath()."/installer/", array(".php"));

        $arrReturn = array();
        //start with modules
        foreach ($arrInstaller as $strOneInstaller) {

            /** @var $objInstaller InstallerInterface */
            $objInstaller = Classloader::getInstance()->getInstanceFromFilename(_realpath_.$objMetadata->getStrPath()."/installer/".$strOneInstaller);
            $arrReturn[] = $objInstaller;
        }

        return $arrReturn;
    }

}