DerDu/SPHERE-Framework

View on GitHub
Application/Platform/System/Database/Database.php

Summary

Maintainability
F
5 days
Test Coverage
<?php
namespace SPHERE\Application\Platform\System\Database;

use SPHERE\Application\IModuleInterface;
use SPHERE\Application\IServiceInterface;
use SPHERE\Application\Platform\Gatekeeper\Authorization\Account\Account;
use SPHERE\Application\Platform\Gatekeeper\Authorization\Consumer\Consumer;
use SPHERE\Application\Platform\Gatekeeper\Authorization\Consumer\Service\Entity\TblConsumer;
use SPHERE\Common\Frontend\Icon\Repository\Ok;
use SPHERE\Common\Frontend\Icon\Repository\Repeat;
use SPHERE\Common\Frontend\Icon\Repository\Warning;
use SPHERE\Common\Frontend\IFrontendInterface;
use SPHERE\Common\Frontend\Layout\Repository\Listing;
use SPHERE\Common\Frontend\Layout\Repository\PullClear;
use SPHERE\Common\Frontend\Layout\Repository\PullLeft;
use SPHERE\Common\Frontend\Layout\Repository\Title;
use SPHERE\Common\Frontend\Link\Repository\External;
use SPHERE\Common\Frontend\Link\Repository\Standard;
use SPHERE\Common\Frontend\Table\Structure\Table;
use SPHERE\Common\Frontend\Table\Structure\TableBody;
use SPHERE\Common\Frontend\Table\Structure\TableColumn;
use SPHERE\Common\Frontend\Table\Structure\TableHead;
use SPHERE\Common\Frontend\Table\Structure\TableRow;
use SPHERE\Common\Frontend\Text\ITextInterface;
use SPHERE\Common\Frontend\Text\Repository\Danger;
use SPHERE\Common\Frontend\Text\Repository\Success;
use SPHERE\Common\Main;
use SPHERE\Common\Window\Navigation\Link;
use SPHERE\Common\Window\Stage;
use SPHERE\System\Authenticator\Authenticator;
use SPHERE\System\Authenticator\Type\Get;
use SPHERE\System\Database\Binding\AbstractService;
use SPHERE\System\Database\Link\Identifier;
use SPHERE\System\Extension\Extension;

/**
 * Class Database
 *
 * @package SPHERE\Application\System\Platform\Database
 */
class Database extends Extension implements IModuleInterface
{

    /** @var array $ServiceRegister */
    private static $ServiceRegister = array();
    /** @var array $SetupRegister */
    private static $SetupRegister = array();
    /** @var array $SetupRegister */
    private static $SetupUTF8 = array();

    public static function registerModule()
    {

        /**
         * Register Navigation
         */
        Main::getDisplay()->addModuleNavigation(
            new Link(new Link\Route(__NAMESPACE__), new Link\Name('Datenbank'))
        );
        /**
         * Register Route
         */
        Main::getDispatcher()->registerRoute(
            Main::getDispatcher()->createRoute(__NAMESPACE__,
                'Database::frontendStatus'
            )
        );
        Main::getDispatcher()->registerRoute(
            Main::getDispatcher()->createRoute(__NAMESPACE__.'/Setup/Simulation',
                __CLASS__.'::frontendSetup'
            )->setParameterDefault('Simulation', true)
        );
        Main::getDispatcher()->registerRoute(
            Main::getDispatcher()->createRoute(__NAMESPACE__.'/Setup/Execution',
                __CLASS__.'::frontendSetup'
            )->setParameterDefault('Simulation', false)
        );
        Main::getDispatcher()->registerRoute(
            Main::getDispatcher()->createRoute(__NAMESPACE__.'/Setup/Upgrade',
                __CLASS__.'::frontendSetupUpgrade'
            )
        );
        Main::getDispatcher()->registerRoute(
            Main::getDispatcher()->createRoute(__NAMESPACE__.'/Setup/Reporting',
                __CLASS__.'::frontendSetupReporting'
            )
        );
    }

    /**
     * @return IServiceInterface
     */
    public static function useService()
    {

    }

    /**
     * @return IFrontendInterface
     */
    public static function useFrontend()
    {

    }

    /**
     * Determines the order of the services that are created in the database if a dependency exists
     *
     * @param $__CLASS__
     */
    public static function registerService($__CLASS__)
    {

        if (!in_array($__CLASS__, self::$ServiceRegister)) {
            array_push(self::$ServiceRegister, trim($__CLASS__, '\\'));
        }
    }

    /**
     * @return Stage
     */
    public function frontendStatus()
    {

        $Stage = new Stage('Database', 'Status');
        $this->menuButton($Stage);
        $Configuration = parse_ini_file(__DIR__.'/../../../../System/Database/Configuration.ini', true);
        $Result = array();
        foreach ((array)$Configuration as $Service => $Parameter) {
            $Service = explode(':', $Service);

            // Force Consumer ?
            if (!isset( $Service[4] ) && ( !isset( $Parameter['Consumer'] ) || $Parameter['Consumer'] )) {
                $tblConsumerAll = Consumer::useService()->getConsumerAll();
                /** @var TblConsumer $tblConsumer */
                foreach ((array)$tblConsumerAll as $tblConsumer) {
                    $Connection = null;
                    $Service[4] = $tblConsumer->getAcronym();
                    try {
                        $Connection = new \SPHERE\System\Database\Database(
                            new Identifier(
                                $Service[0],
                                $Service[1],
                                ( isset( $Service[2] ) ? $Service[2] : null ),
                                ( isset( $Service[3] ) ? $Service[3] : null ),
                                ( isset( $Service[4] ) ? $Service[4] : null )
                            )
                        );
                        $Status = new Success(new Ok().' Verbunden');
                    } catch (\Exception $E) {
                        $Status = new Danger(new Warning().' Fehler');
                    }
                    $Result[] = $this->statusRow($Status, $Service, $Parameter, $Connection);
                }
            } else {
                $Connection = null;
                try {
                    $Connection = new \SPHERE\System\Database\Database(
                        new Identifier(
                            $Service[0],
                            $Service[1],
                            ( isset( $Service[2] ) ? $Service[2] : null ),
                            ( isset( $Service[3] ) ? $Service[3] : null ),
                            ( isset( $Service[4] ) ? $Service[4] : null )
                        )
                    );
                    $Status = new Success(new Ok().' Verbunden');
                } catch (\Exception $E) {
                    $Status = new Danger(new Warning().' Fehler');
                }
                $Result[] = $this->statusRow($Status, $Service, $Parameter, $Connection);
            }
        }

        $Stage->setContent(
            new Table(
                new TableHead(
                    new TableRow(array(
                        new TableColumn('Status'),
                        new TableColumn('Cluster'),
                        new TableColumn('Application'),
                        new TableColumn('Module'),
                        new TableColumn('Service'),
                        new TableColumn('Consumer'),
                        new TableColumn('Driver'),
                        new TableColumn('Server'),
                        new TableColumn('Port'),
                        new TableColumn('Database')
                    ))
                ),
                new TableBody(
                    $Result
                ), null, true
            )
        );

        return $Stage;
    }

    /**
     * @param Stage $Stage
     */
    private function menuButton(Stage $Stage)
    {

        $Stage->addButton(new Standard('Status', new Link\Route(__NAMESPACE__), null,
            array(), 'Datenbankverbindungen'
        ));
        $Stage->addButton(new Standard('Simulation', new Link\Route(__NAMESPACE__.'/Setup/Simulation'), null,
            array(), 'Anzeige von Strukturänderungen'
        ));
        $Stage->addButton(new Standard('Durchführung', new Link\Route(__NAMESPACE__.'/Setup/Execution'), null,
            array(), 'Durchführen von Strukturänderungen und einspielen zugehöriger Daten'
        ));
        $Stage->addButton(new Standard('1. Alle Mandanten aktualisieren', new Link\Route(__NAMESPACE__.'/Setup/Upgrade'),
            new Warning(),
            array(), 'Durchführen von Strukturänderungen und einspielen zugehöriger Daten'
        ));
        $Stage->addButton(new Standard('2. Alle Mandanten Flexible Auswertung aktualisieren', new Link\Route(__NAMESPACE__.'/Setup/Reporting'),
            new Warning(),
            array(), 'Durchführen von Viewänderungen'
        ));
        $Stage->addButton(new External('phpMyAdmin',
            $this->getRequest()->getPathBase().'/UnitTest/Console/phpMyAdmin-4.6.1'));
    }

    /**
     * @param ITextInterface                        $Status
     * @param array                                 $Service
     * @param array                                 $Parameter
     * @param \SPHERE\System\Database\Database|null $Connection
     *
     * @return TableRow
     */
    private function statusRow(
        ITextInterface $Status,
        $Service,
        $Parameter,
        \SPHERE\System\Database\Database $Connection = null
    ) {

        return new TableRow(array(
            new TableColumn($Status),
            new TableColumn($Service[0]),
            new TableColumn($Service[1]),
            new TableColumn(( isset( $Service[2] ) ? $Service[2] : null )),
            new TableColumn(( isset( $Service[3] ) ? $Service[3] : null )),
            new TableColumn(( isset( $Service[4] ) ? $Service[4] : null )),
            new TableColumn($Parameter['Driver']),
            new TableColumn($Parameter['Host']),
            new TableColumn(( isset( $Parameter['Port'] ) ? $Parameter['Port'] : 'Default' )),
            new TableColumn(isset( $Connection ) ? $Connection->getDatabase() : '-NA-')
        ));
    }

    /**
     * @return Stage
     */
    public function frontendSetupReporting()
    {
        $Stage = new Stage('Database', 'Setup aller Mandanten (Reporting)');
        $this->menuButton($Stage);

        $ReportingUpgrade = new ReportingUpgrade('127.0.0.1', 'root', 'sphere');

        $Stage->setContent( $ReportingUpgrade->migrateReporting() );

        return $Stage;
    }

    /**
     * @return Stage
     */
    public function frontendSetupUpgrade()
    {

        $Stage = new Stage('Database', 'Setup aller Mandanten (Struktur)');
        $this->menuButton($Stage);

        $tblConsumerAll = Consumer::useService()->getConsumerAll();
        $ConsumerRequestList = array();
        if ($tblConsumerAll) {
            $Authenticator = (new Authenticator(new Get()))->getAuthenticator();

            array_walk($tblConsumerAll,
                function (TblConsumer $tblConsumer) use (&$ConsumerRequestList, $Authenticator) {

                    $ConsumerRequestList[$tblConsumer->getAcronym()] =
                        'https://'.$this->getRequest()->getHost()
                        .'/Api/Platform/Database/Upgrade'
                        .'?'.http_build_query($Authenticator->createSignature(array(
                            'Consumer' => $tblConsumer->getAcronym()
                        ), '/Api/Platform/Database/Upgrade'));
                });

            ksort($ConsumerRequestList);

            if(isset($ConsumerRequestList['REF'])){
                $Api = $ConsumerRequestList['REF'];
                unset($ConsumerRequestList['REF']);
                $ConsumerRequestList['REF'] = $Api;
            }
        }

        // prepare: change to first Consumer
        if (!empty($ConsumerRequestList)) {
            $tblConsumerOne = false;
            foreach ($ConsumerRequestList as $key => $val) {
                $tblConsumerOne = Consumer::useService()->getConsumerByAcronym($key);
                break;
            }
            if ($tblConsumerOne) {
                Account::useService()->changeConsumer($tblConsumerOne);
            }
        }

        $Stage->setContent(
            new Title('Mandanten werden aktualisiert...')
            .'<div id="ConsumerProgress" class="progress" style="height: 15px; margin: 0;">
                <div class="progress-bar progress-bar-success" style="width: 0%;"></div>
                <div class="progress-bar progress-bar-info progress-bar-striped active" style="width: 0%;"></div>
                <div class="progress-bar progress-bar-warning" style="width: 100%;"></div>
            </div>'
            .'<div id="ConsumerProtocol" class="small"></div>'
            .'<script language=javascript>
            //noinspection JSUnresolvedFunction
            executeScript(function()
            {
                Client.Use(\'ModAlways\', function()
                {
                    (function($)
                    {
                        \'use strict\';
                        $.fn.ModDatabaseUpgrade = function(
                            options
                        )
                        {

                            // This is the easiest way to have default options.
                            var settings = $.extend({
                                consumer: []
                            }, options);

                            var Size = Object.keys(settings.consumer).length;

                            var getConsumer = function pop(obj) {
                              for (var key in obj) {
                                // Uncomment below to fix prototype problem.
                                //if (!Object.hasOwnProperty.call(obj, key)) continue;
                                var result = obj[key];
                                // If the property can\'t be deleted fail with an error.
                                if (!delete obj[key]) { throw new Error(); }
                                return result;
                              }
                            };

                            var ResultConsumer = [];
                            var ErrorConsumer = false;

                            var Progress = 100 / Size * Object.keys(settings.consumer).length;
                            var Info = 100 / Size;
                            var Bar = jQuery("#ConsumerProgress");
                            Bar.find(".progress-bar-info").css("width",(Info)+"%");
                            Bar.find(".progress-bar-warning").css("width",(Progress-Info)+"%");

                            var runConsumer = function( Api ){
                                Progress = 100 / Size * Object.keys(settings.consumer).length;
                                Info = 100 / Size;

                                Bar.find(".progress-bar-info").css("width",(Info)+"%");

                                if( Api ) {
                                    jQuery.get( Api, function( Result ){

                                        Bar.find(".progress-bar-success").css("width",(100-Progress)+"%").html( (Size-Object.keys(settings.consumer).length)+" / "+Size );
                                        Bar.find(".progress-bar-warning").css("width",(Progress-Info)+"%");

                                        var Consumer = Result.substr(0,Result.indexOf(\' \'));
                                        Result = Result.substr(Result.indexOf(\' \'),Result.length);
                                        jQuery("#ConsumerProtocol").append( Result );

                                        if( -1 == jQuery.inArray( Consumer, ResultConsumer ) ) {
                                            ResultConsumer.push(Consumer);
                                        } else {
                                            ErrorConsumer = true;
                                        }

                                        Api = getConsumer( settings.consumer );
                                        runConsumer( Api );
                                    }, "json" );
                                } else {
                                    if(ErrorConsumer) {
                                        Bar.find(".progress-bar-success").removeClass("progress-bar-success").addClass("progress-bar-danger").html("ERROR")
                                        } else {
                                        Bar.find(".progress-bar-success").html("DONE")
                                        }
                                        Bar.find(".progress-bar-success").removeClass("active");
                                }
                            };

                            var Api = getConsumer( settings.consumer );
                            runConsumer( Api );

                            return this;
                        };

                    }(jQuery));

                    jQuery().ModDatabaseUpgrade({consumer:'.json_encode($ConsumerRequestList).' });
                });
            });
        </script>'
        );

        return $Stage;
    }

    /**
     * @param bool $Simulation
     * @param bool $Heal
     *
     * @return Stage
     */
    public function frontendSetup($Simulation = true, $Heal = false)
    {

        $ClassList = get_declared_classes();
        self::$ServiceRegister = array_merge(
            self::$ServiceRegister, array_diff($ClassList, self::$ServiceRegister)
        );

        array_unshift(self::$ServiceRegister, 'SPHERE\Application\Platform\Gatekeeper\Authorization\Account\Account');
        array_unshift(self::$ServiceRegister, 'SPHERE\Application\Platform\Gatekeeper\Authorization\Access\Access');
        array_unshift(self::$ServiceRegister, 'SPHERE\Application\Platform\Gatekeeper\Authorization\Token\Token');
        array_unshift(self::$ServiceRegister, 'SPHERE\Application\Platform\Gatekeeper\Authorization\Consumer\Consumer');

        self::$ServiceRegister = array_unique(self::$ServiceRegister);

        $Stage = new Stage('Database', 'Setup');
        if (!$Heal) {
            $this->menuButton($Stage);
        }
        if ($Simulation) {
            self::$SetupRegister = array();
            $ClassList = self::$ServiceRegister;
            array_walk($ClassList, function (&$Class) {

                $Inspection = new \ReflectionClass($Class);
                if ($Inspection->isInternal()) {
                    $Class = false;
                } else {
                    if ($Inspection->implementsInterface('\SPHERE\Application\IModuleInterface')) {
                        /** @var IModuleInterface $Class */
                        if (!$Inspection->isAbstract()) {
                            $Class = $Inspection->newInstance();
                            $Class = $Class->useService();
                        }
                        /** @var IServiceInterface $Class */
                        if ($Class instanceof IServiceInterface) {
                            if (!array_key_exists(get_class($Class), self::$SetupRegister)) {
                                $Result = $Class->setupService(true, false, false);
                                self::$SetupRegister[get_class($Class)] = $Result;
                                $Class = $Result;
                            } else {
                                $Class = new PullClear(new PullLeft(new Repeat()).self::$SetupRegister[get_class($Class)]);
                            }
                        } else {
                            $Class = false;
                        }
                    } else {
                        $Class = false;
                    }
                }
            });
            $ClassList = array_filter($ClassList);

        } else {
            self::$SetupRegister = array();
            $ClassList = self::$ServiceRegister;
            array_walk($ClassList, function (&$Class) {

                $Inspection = new \ReflectionClass($Class);
                if ($Inspection->isInternal()) {
                    $Class = false;
                } else {
                    if ($Inspection->implementsInterface('\SPHERE\Application\IModuleInterface')) {
                        /** @var IModuleInterface $Class */
                        if (!$Inspection->isAbstract()) {
                            $Class = $Inspection->newInstance();
                            $Class = $Class->useService();
                        }
                        /** @var IServiceInterface $Class */
                        if ($Class instanceof IServiceInterface) {
                            if (!array_key_exists(get_class($Class), self::$SetupRegister)) {
                                $Result = $Class->setupService(false, false, false);
                                self::$SetupRegister[get_class($Class)] = $Result;
                                $Class = $Result;
                            } else {
                                $Class = new PullClear(new PullLeft(new Repeat()).self::$SetupRegister[get_class($Class)]);
                            }
                        } else {
                            $Class = false;
                        }
                    } else {
                        $Class = false;
                    }
                }
            });
            $ClassList = array_filter($ClassList);

            self::$SetupRegister = array();
            $DataList = self::$ServiceRegister;
            array_walk($DataList, function (&$Class) {
                $Inspection = new \ReflectionClass($Class);
                if ($Inspection->isInternal()) {
                    $Class = false;
                } else {
                    if ($Inspection->implementsInterface('\SPHERE\Application\IModuleInterface')) {
                        /** @var IModuleInterface $Class */
                        if (!$Inspection->isAbstract()) {
                            $Class = $Inspection->newInstance();
                            $Class = $Class->useService();
                        }
                        /** @var IServiceInterface $Class */
                        if ($Class instanceof IServiceInterface){
                            // prepare DB list for UTF8 Correction
                            /**@var AbstractService $Class */
                            $Binding = $Class->getBinding();
                            if(($Database = $Binding->getDatabase())){
                                // one Class in DB is enough
                                if(!isset(self::$SetupUTF8[$Database])){
                                    self::$SetupUTF8[$Database] = $Class;
                                }
                            }
                            // insert Data
                            if (!array_key_exists(get_class($Class), self::$SetupRegister)) {
                                $Result = $Class->setupService(false, true, false);
                                self::$SetupRegister[get_class($Class)] = $Result;
                                $Class = $Result;
                            } else {
                                $Class = new PullClear(new PullLeft(new Repeat()).self::$SetupRegister[get_class($Class)]);
                            }
                        } else {
                            $Class = false;
                        }
                    } else {
                        $Class = false;
                    }
                }
            });

            // set UTF8 correction
            if(!empty(self::$SetupUTF8)){
                foreach(self::$SetupUTF8 as $DatabaseName => $Class) {
                    $Class->setupService(false, false, true);
                }
            }
        }

        $Stage->setContent(new Listing($ClassList));
        return $Stage;
    }
}