<?php

/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */

namespace con4gis\CoreBundle\Resources\contao\classes;

/**
 * Class C4GMigration
 * @package c4g
 */
class C4GMigration extends \BackendModule
{
    protected $strTemplate = 'be_c4g_migration';
    protected $module = '';
    protected $action = '';
    protected $output = array();
    protected $buttons = array();

    /**
     * [__construct description]
     * @param [type] $mod [description]
     */
    public function __construct( $mod )
    {
        parent::__construct();
        // import database
        // $this->import('Database');

        // set targeted module
        $this->module = $mod;

    }

    /**
     * Generate the module
     * @return string
     */
    public function generate()
    {
        if (\Input::get('act') != '') {
            $this->action = \Input::get('act');
        } else {
            $this->action = 'init';
        }

        switch( $this->action )
        {
            case 'exec':
                $this->migrate();
                break;
            case 'back':
                \Controller::redirect( \Environment::get('script') . '?do=c4g_core' );
            case 'dbupdate':
                \Controller::redirect( \Environment::get('script') . '?do=repository_manager&update=database' );
            case 'uninstall':
                \Controller::redirect( \Environment::get('script') . '?do=repository_manager&uninstall=cfs_' . $this->module );
            case 'init':
            default:
                $this->output[] = sprintf($GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['INTRO'], $this->module);
                $this->output[] = '' . sprintf($GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['INTROWARN'], $this->module) . '';
                if ($this->checkMod()) {
                    $this->buttons[] = array(
                        action  => 'exec',
                        label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BTN']['MIGRATE']
                    );
                    $this->buttons[] = array(
                        action  => 'back',
                        label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['CANCEL']
                    );
                } else {
                    $this->buttons[] = array(
                        action  => 'back',
                        label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BACK']
                    );
                }
                break;
        }

        return parent::generate();
    }

    /**
     * Generate the module
     */
    protected function compile()
    {
        $this->Template->mod = $this->module;

        $this->Template->action = $this->action ?: false;
        $this->Template->output = $this->output ?: '';

        $this->Template->buttons = $this->buttons ?: '';

        // $GLOBALS['TL_CSS'][] = 'bundels/con4gisMapsBundle/css/be_c4g_info.css';
        // $this->Template->c4gModules->con4gis_maps->installed = $GLOBALS[];
    }

    protected function checkMod()
    {
        if (!$GLOBALS['con4gis'][$this->module]['installed']) {
            $this->output[] = '' .
                                sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['NOMODULEERROR'], 'con4gis_'.$this->module ) .
                                '';
            return false;
        }

        // everything is okay
        return true;
    }

    protected function updateType( $table )
    {
        $type = 'cfs_' . $this->module;
        if ($this->module == 'forum') {
            $type .= '_comfort';
        }
        $newType = 'c4g_' . $this->module;
        $success = true;

        $output = '' . $table . ' (type):
 ' . $type . ' -> ' . $newType . ': ';

        if ($this->Database->prepare( "UPDATE $table SET type = ? WHERE type = ?" )->execute( $newType, $type )) {
            $output .= '' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['SUCCESS'] . '';
        } else {
            $output .= '' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
            $success = false;
        }

        $this->output[] = $output;

        return $success;
    }

    protected function migrate()
    {
        $successCount = 0;
        $errorCount = 0;
        // [tricky]: little specialcasing for cfs_maps, since most of the tables are named "cfs_map_*"
        $mod = ($this->module == 'maps') ? 'map' : $this->module;

        // fetch all related tables
        foreach ($this->Database->listTables() as $table) {
            if (C4GUtils::startsWith($table, 'tl_cfs_'.$mod)) {
                // calculate the new name
                $newTable = str_replace('_cfs_', '_c4g_', $table);
                $output = '' . $table . '  ->  ' . $newTable . ':
   ';
                // copy the data and generate an appropriate output
                try {
                    // recreate new table (since "DISABLE KEYS" didn't work)
                    $this->Database->prepare( "DROP TABLE $newTable" )->execute();
                    $this->Database->prepare( "CREATE TABLE $newTable LIKE $table" )->execute();
                    if ($this->Database->prepare( "INSERT INTO $newTable SELECT * FROM $table" )->execute()) {
                        $output .= ' ' .
                                    sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['TRANSFEREDROW'],
                                        $this->Database->prepare( "SELECT COUNT(*) AS count FROM $newTable" )->execute()->first()->count,
                                        $this->Database->prepare( "SELECT COUNT(*) AS count FROM $table" )->execute()->first()->count
                                    ) .
                                    ' ';

                        $successCount++;
                    } else {
                        $output .= ' ' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
                        $errorCount++;
                    }
                } catch (Exception $e) {
                    $errorCount++;
                    $output .= ' ' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
                }
                $this->output[] = $output;

            } elseif ($table == 'tl_module' || $table == 'tl_content' || ($mod == 'map' && $table == 'tl_calendar_events')) {
                $successCountF = 0;
                $errorCountF = 0;
                $output = '' . $table . ':';

                foreach ($this->Database->getFieldNames( $table ) as $field) {
                    if ($table == 'tl_calendar_events') {
                        $identifier = 'cfs_';
                    } else {
                        $identifier = 'cfs_' . $mod;
                    }
                    if (C4GUtils::startsWith($field, $identifier)) {
                        // calculate the new name
                        $newField = str_replace('cfs_', 'c4g_', $field);
                        $output .= '
   ' . $field . '  ->  ' . $newField . ':';
                        try {
                            $output .= $this->Database->prepare( "UPDATE $table SET $newField = $field" )->execute()?
                                    ' ' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['SUCCESS'] . '' :
                                    ' ' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
                            $successCountF++;
                        } catch (Exception $e) {
                            $errorCountF++;
                            $output .= ' ' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
                        }
                    }
                }
                if ($errorCountF <= 0) {
                    $msg = '
' . sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['TRANSFEREDCOL'], $successCountF ) . '';
                    $successCount++;
                } else {
                    $msg = '
' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAIL'] . '';
                    $errorCount++;
                }
                $this->output[] = $output . $msg;
            }
        }

        if ($errorCount <= 0) {
            $this->output[] = '
' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['SUCCESSMSG_1'] . '
';

            //re-add "editorfield"
            if ($mod == 'map') {
                $this->Database->prepare( "ALTER TABLE `tl_c4g_map_profiles` ADD `editor` char(1) NOT NULL default ''" )->execute();
            }

            if ($this->updateType('tl_module') && $this->updateType('tl_content')) {
                $this->output[] = '
' .
                                sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['SUCCESSMSG_2'], $this->module ) .
                                '';
                $this->output[] = '
' .
                                sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['SUCCESSMSG_3'], $this->module ) .
                                '';
                $this->buttons[] = array(
                    action  => 'dbupdate',
                    label   => sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BTN']['DBUPDATE'], $this->module)
                );
                $this->buttons[] = array(
                    action  => 'uninstall',
                    label   => sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BTN']['UNINSTALL'], $this->module)
                );
            } else {
                $this->output[] = '
' .
                                $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAILMSG_2'] .
                                '';
            }

        } else {
            $this->output[] = '
' .
                                sprintf( $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MIGRATION']['FAILMSG_1'], $errorCount, $errorCount+$successCount ) .
                                '';
        }

        $this->buttons[] = array(
                    action  => 'back',
                    label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BACK']
                );
    }
}


<?php

/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */

namespace con4gis\CoreBundle\Resources\contao\modules;

use con4gis\CoreBundle\Resources\contao\models\C4gActivationkeyModel;
use Contao\System;

/**
 * Class Content_c4g_activationpage
 * @package c4g
 */
class ContentC4gActivationpage extends \Module
{
    /**
     * Template
     * @var string
     */
    protected $strTemplate = 'ce_c4g_activationpage';

    /**
     * Generate content element
     */
    public function generate()
    {
        if (TL_MODE == 'BE')
        {
            $objTemplate = new \BackendTemplate('be_wildcard');

            $objTemplate->wildcard = '### ' . ($this->c4g_activationpage_action_handler ?: utf8_strtoupper($GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['msc']['auto_action_handler']) ). ' ###';
            $objTemplate->title = $this->headline;
            $objTemplate->id = $this->id;
            $objTemplate->link = $this->name;
            $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id;

            return $objTemplate->parse();
        }

        return parent::generate();
    }

    /**
     * Generate module
     */
    protected function compile()
    {
        // prepare stuff
        $this->import('FrontendUser', 'User');
        System::loadLanguageFile('tl_content');
        $stateClass = array
        (
            1 => 'c4g_ap_success',
            0 => 'c4g_ap_confirm',
            -1 => 'c4g_ap_failure'
        );

        // $session = \Session::getInstance();

        // write key to session, so it will not get lost
        if (\Input::get('key')) {
            // $session->set('c4g_activationkey_' . $this->id, \Input::get('key'));
            $_SESSION['c4g_activationkey_' . $this->id] = \Input::get('key');
        }

        if (!FE_USER_LOGGED_IN && $this->c4g_activationpage_visitor_redirect) {
            // redirect to defined page
            $objPage = $this->Database->prepare("SELECT id, alias FROM tl_page WHERE id=?")
                ->limit(1)
                ->execute($this->c4g_activationpage_visitor_redirect);

            if ($objPage->numRows) {
                \Controller::redirect($this->generateFrontendUrl($objPage->fetchAssoc()));
            } else {
                $this->Template->state = $stateClass[-1];
                $this->Template->output = 'Redirect error';
                return false;
            }
        }

        // load default CSS if enabled
        if ($this->c4g_activationpage_use_default_css) {
            $GLOBALS['TL_CSS']['c4g_activationpage'] = 'bundles/con4giscore/css/ce_c4g_activationpage.css';
        }

        // check if a confirmation is needed
        if ($this->c4g_activationpage_confirmation && !\Input::get('confirm')) {
            $this->Template->state = $stateClass[0];
            $this->Template->output = $this->c4g_activationpage_confirmation_text;
            $delim = (preg_match('/\?/', $this->replaceInsertTags('{{env::request}}')) > 0) ? '&' : '?';
            $this->Template->output .= '' . ($this->c4g_activationpage_confirmation_button ?: $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['msc']['default_confirmation_button']) . '';
        } else {
            // 1) check key
            $action = '';
            $this->Template->state = $stateClass[-1];
            // $key = \Input::get('key') ?: $session->get('c4g_activationkey_' . $this->id);
            $key = \Input::get('key') ?: $_SESSION['c4g_activationkey_' . $this->id];
            $this->Template->output = '';
            if (!empty( $key ) && C4gActivationkeyModel::keyIsValid( $key )) {
                $action = C4gActivationkeyModel::getActionForKey( $key );
            }
            if (!empty( $action )) {
                // the key is valid
                //
                $action = explode(':', $action);
                if ($action[1]) {
                    $action[1] = explode('&', $action[1]);
                }
                // 2) find handler
                if (!$this->c4g_activationpage_action_handler || $this->c4g_activationpage_action_handler === $action[0]) {
                    $actionHandler = $GLOBALS['C4G_ACTIVATIONACTION'][$action[0]];
                }
                if (!empty( $actionHandler )) {
                    // handler found
                    // 3) execute handler
                    try {
                        $clActionHandler = new $actionHandler();
                        $arrResponse = $clActionHandler->performActivationAction( $action[0], $action[1] ?: array() );
                        if ($arrResponse && $arrResponse['success']) {
                            // everything went right
                            // claim key and return the success
                            $this->Template->state = $stateClass[1];
                            $this->Template->output .= $this->c4g_activationpage_success_msg ?: ($arrResponse['output'] ?: $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['msc']['success_msg']);
                            if( !C4gActivationkeyModel::assignUserToKey( (FE_USER_LOGGED_IN ? $this->User->id : -1), $key) ) {
                                $this->Template->output .= '
' . $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['errors']['key_not_claimed'] . '
';
                            }
                        } else {
                            // ERROR: the handlers action failed
                            $this->Template->output .= $this->c4g_activationpage_handler_error_msg ?: ($arrResponse['output'] ?: $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['errors']['handler_failed']);
                        }
                    } catch (Exeption $e) {
                        $this->Template->output .= $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['errors']['no_handler'];
                    }
                } else {
                    // ERROR: no appropriate handler found
                    $this->Template->output .= $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['errors']['no_handler'];
                }
            } else {
                // ERROR: the key is invalid
                $this->Template->output .= $this->c4g_activationpage_invalid_key_msg ?: $GLOBALS['TL_LANG']['tl_content']['c4g_activationpage']['errors']['invalid_key'];
            }
        }
    }

}


<?php

/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */

namespace con4gis\CoreBundle\Resources\contao\models;

/**
 * Class C4gActivationkeyModel
 * @package c4g
 */
class C4gActivationkeyModel extends \Model
{
	/**
	 * Table name
	 * @var string
	 */
	protected static $strTable = 'tl_c4g_activationkey';

	/**
	 * generates an activationlink from geiven key
	 * @param  string $key
	 * @return string
	 */
	public static function generateActivationLinkFromKey($key,$keyAction='')
	{
		// check if key exists
		if (empty( $key )) {
			return false;
		}

		// get action for this key
        if (!$keyAction) {
            $keyAction = C4gActivationkeyModel::getActionForKey($key);
        }

		$keyAction = explode(':', $keyAction);

		// find an appropriate activationpage
		//
		// try to find a page with a specific handler for the key-action
        $db = \Database::getInstance();
        $objActivationPages = $db->prepare("SELECT * FROM tl_content WHERE type=? AND c4g_activationpage_action_handler=?")
            ->execute('c4g_activationpage', $keyAction[0]);

        if (!$objActivationPages) {
            // if no page was found, try to find pages with automatic-handlers
            $db = \Database::getInstance();
            $objActivationPages = $db->prepare("SELECT * FROM tl_content WHERE type=? AND c4g_activationpage_action_handler=?")
                ->execute('c4g_activationpage', '');

            // if still no page is found, the function failed
            if (!$objActivationPages) {
                return false;
            }
        }

		// use the first page (even if more pages are found)
		$objActivationPages->next();

		// get the article for this content-element
		$objArticle = \ArticleModel::findByPk( $objActivationPages->pid );
		if ($objArticle) {
			// if found, find the Page, where this article is nested
			$objPage = \PageModel::findByPk( $objArticle->pid );
			if ($objPage) {
				// if found build the desired URL (base + page-url + key)
				return \Environment::get('base') . \Controller::generateFrontendUrl( $objPage->row() ) . '?key=' . $key;
			}
		}

		// article or page not found
		return false;
	}

	/**
	 * Generates an Activationkey and saves it in the DB, if $saveInDB is true
	 * @param  string  $action
	 * @param  boolean $saveInDB
	 * @return string
	 */
	public static function generateActivationkey( $action, $saveInDB=true, $durabilityInDays=30 )
	{
		// generate a unique key
		$attempts = 42;
		do {
			$key = md5(uniqid(rand(), true));
			$hasKey = C4gActivationkeyModel::findOneBy( 'activationkey', $key );
			$attempts--;
		} while (!empty( $hasKey ) && $attempts > 0);
		if (!empty( $hasKey )) { return false; }

		// save key in database
		if ($saveInDB) {
			$objKey = new C4gActivationkeyModel();
			$objKey->activationkey = hash('sha256', $key);
			$objKey->expiration_date = strtotime('+' . $durabilityInDays . ' days', time());
			$objKey->key_action = $action;
			$objKey->save();
		}

		return $key;
	}

	/**
	 * assigns an user to a key, if not already claimed
	 * @param  int $userId
	 * @param  string $key
	 * @return boolean
	 */
	public static function assignUserToKey( $userId, $key )
	{
		$objKey = C4gActivationkeyModel::findBy( 'activationkey', hash('sha256', $key) );
		if (empty( $objKey ) || $objKey->used_by != 0) { return false; }
		$objKey->used_by = $userId;
		$objKey->save();
		return true;
	}

	/**
	 * returns the action connected to the given key
	 * @param  string $key
	 * @return string
	 */
	public static function getActionForKey( $key )
	{
        return C4gActivationkeyModel::findOneBy( 'activationkey', hash('sha256', $key) )->key_action;
	}

	/**
	 * returns the action connected to the given key
	 * @param  string $key
	 * @return string
	 */
	public static function keyIsValid( $key )
	{
		$key = C4gActivationkeyModel::findOneBy( 'activationkey', hash('sha256', $key) );
		// the key exists, is not already claimed and is not expired
		return (!empty( $key ) && empty( $key->used_by ) && ($key->expiration_date == 0 || $key->expiration_date > time()));
	}
}


<?php

namespace Saritasa\DingoApi\Traits;

use Saritasa\DingoApi\Paging\CursorRequest;
use Saritasa\DingoApi\Paging\PagingInfo;
use Illuminate\Http\Request;

const PAGE_SIZE = 'per_page';
const API_PAGE_SIZE_MAX = 'api.maxPageSize';
const API_PAGE_SIZE_DEFAULT = 'api.defaultPageSize';

trait PaginatedOutput
{
    /**
     * Read request paging data
     *
     * If checking value specified, allows only integer positive value equal or less 100 (setting).
     * If value not specified, take the default value from settings.
     *
     * @param Request $request raw request data
     * @return PagingInfo
     */
    protected function readPaging(Request $request): PagingInfo
    {
        $input = $request->only(PagingInfo::KEYS);
        if (isset($input[PAGE_SIZE])) {
            $pageSize = (int)$input[PAGE_SIZE];
            if ($pageSize <= 0) {
                $input[PAGE_SIZE] = config(API_PAGE_SIZE_MAX);
            }
            if ($pageSize > config(API_PAGE_SIZE_MAX)) {
                $input[PAGE_SIZE] = config(API_PAGE_SIZE_MAX);
            }
        }
        return new PagingInfo($input);
    }

    protected function readCursor(Request $request): CursorRequest
    {
        $input = $request->only(CursorRequest::KEYS);
        if (isset($input[PAGE_SIZE])) {
            $pageSize = (int)$input[PAGE_SIZE];
            if ($pageSize <= 0) {
                $input[PAGE_SIZE] = config(API_PAGE_SIZE_DEFAULT);
            }
            if ($pageSize > config(API_PAGE_SIZE_MAX)) {
                $input[PAGE_SIZE] = config(API_PAGE_SIZE_MAX);
            }
        }
        return new CursorRequest($input);
    }
}


<?php
/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */
namespace con4gis\CoreBundle\Classes\Helper;

use Contao\Input;

/**
 * Class InputHelper
 * @package con4gis\CoreBundle\Classes\Helper
 */
class InputHelper
{

    /**
     * Gibt alle Daten einer Eingabemethode [POST|GET] zurück.
     * @param string $methode
     * @param bool   $decode
     * @return array
     */
    public static function getAllData($methode = 'post', $decode = false)
    {
        $data = array();

        if ($methode == 'post') {
            $keys = array_keys($_POST);

            foreach ($keys as $key) {
                $data[$key] = ($decode) ? urldecode(Input::post($key)) : Input::post($key);
            }
        } elseif ($methode = 'get') {
            $keys = array_keys($_GET);

            foreach ($keys as $key) {
                $data[$key] = ($decode) ? urldecode(Input::get($key)) : Input::get($key);
            }
        }

        return $data;
    }

    /**
     * Gibt den RequestToken zurück.
     * @param string $serviceName
     * @param string $tokenName
     * @return mixed
     */
    public static function getRequestToken(
        $serviceName = 'security.csrf.token_manager',
        $tokenName = 'contao.csrf_token_name'
    ) {
        $c      = \System::getContainer();
        $param  = $c->getParameter($tokenName);
        return $c->get($serviceName)->getToken($param)->getValue();
    }
}


<?php

/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */

namespace con4gis\CoreBundle\Resources\contao\classes;

use con4gis\GroupsBundle\Resources\models\MemberModel;
use con4gis\GroupsBundle\Resources\models\MemberGroupModel;

/**
 * Class C4GMembergroupSync
 * @package c4g
 */
class C4GMembergroupSync extends \BackendModule
{
    protected $strTemplate = 'be_c4g_membergroupsync';
    protected $action = '';
    protected $output = array();
    protected $buttons = array();

    /**
     * [__construct description]
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Generate the module
     * @return string
     */
    public function generate()
    {
        if (\Input::get('act') != '') {
            $this->action = \Input::get('act');
        } else {
            $this->action = 'init';
        }

        switch( $this->action )
        {
            case 'exec':
                $this->syncMemberGroupBindings();
                break;
            case 'back':
                \Controller::redirect( 'contao/main.php?do=c4g_core' );
            case 'init':
            default:
                $this->output[] = $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MEMBERGROUPSYNC']['INTRO'];
                $this->output[] = '' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MEMBERGROUPSYNC']['WARNING'] . '';

                $this->buttons[] = array(
                    action  => 'exec',
                    label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BTN']['SYNCBINDINGS']
                );
                break;
        }
        $this->buttons[] = array(
            action  => 'back',
            label   => $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['BACK']
        );

        return parent::generate();
    }

    /**
     * Generate the module
     */
    protected function compile()
    {
        $this->Template->action = $this->action ?: false;
        $this->Template->output = $this->output ?: '';

        $this->Template->buttons = $this->buttons ?: '';
    }

    protected function syncMemberGroupBindings()
    {
        if (class_exists('con4gis\GroupsBundle\Resources\models\MemberModel') && class_exists('con4gis\GroupsBundle\Resources\models\MemberGroupModel')) {

            // fetch all enabled members
            $objMembers = MemberModel::findAll(array('disable' => ''));

            if ($objMembers) {
                foreach ($objMembers as $objMember) {
                    $memberGroupIds = $objMember->groups ? unserialize($objMember->groups) : array();

                    foreach ($memberGroupIds as $memberGroupId) {
                        if (!MemberGroupModel::isMemberOfGroup( $memberGroupId, $objMember->id )) {
                            $objGroup = MemberGroupModel::findByPk( $memberGroupId );
                            if ($objGroup) {
                                // check if the group has a member-limitation
                                if ($objGroup->cg_max_member > 0 && $objGroup->cg_max_member <= count(unserialize($objGroup->cg_member))) {
                                    $this->output[] = '' . sprintf($GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MEMBERGROUPSYNC']['ERROR_GROUPLIMITREACHED'], $objMember->id, $objGroup->id) . '';
                                    continue;
                                }

                                $members = $objGroup->cg_member ? unserialize( $objGroup->cg_member ) : array();
                                $members[] = $objMember->id;
                                $objGroup->cg_member = serialize( $members );
                                $objGroup->save();
                            }
                        }
                    }
                }
                $this->output[] = '' . $GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MEMBERGROUPSYNC']['SUCCESS'] . '';
                return true;
            }
        }

        $this->output[] = '' .$GLOBALS['TL_LANG']['MSC']['C4G_BE_INFO']['MEMBERGROUPSYNC']['FAILED'] . '';
        return false;
    }

}


<?php
/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */
namespace con4gis\CoreBundle\Entity;

/**
 * Class BaseEntity
 * @package con4gis\CoreBundle\Entity
 */
abstract class BaseEntity
{

    /**
     * BaseEntiy constructor.
     * @param array $data
     */
    public function __construct($data = array())
    {
        $this->setData($data);
    }

    /**
     * Setzt die Daten eines Arrays als Eigenschaften der Klasse.
     *  ¿Por qué no inglés? ¡No entendo alemán!
     * @param $data
     */
    public function setData($data)
    {
        if (is_array($data) && count($data)) {
            foreach ($data as $column => $value) {
                if (property_exists($this, $column)) {
                    $column = 'set' . ucfirst($column);
                    $this->$column($value);
                }
            }
        }
    }

    public function toArray()
    {
        return get_object_vars($this);
    }
}


<?php
/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */
namespace con4gis\CoreBundle\Classes\Helper;

use Contao\CoreBundle\HttpKernel\Bundle\ContaoModuleBundle;

/**
 * Class BundleHelper
 * @package con4gis\CoreBundle\Classes\Helper
 */
class BundleHelper
{

    /**
     * Pfad zu den Erweiterunen
     * @var string
     */
    protected static $extensionsPath = '/system/modules';

    /**
     * Pfad zu den Bundles
     * @var string
     */
    protected static $bundlePath = '/src';

    /**
     * Erstellt die Einträge der Contao-Module für den AppKernel.
     * @param $path
     * @param $bundles
     * @return array
     */
    public static function generateContaoBundles($path, &$bundles)
    {
        $bundelPatteren = dirname($path) . self::$extensionsPath . '/*';
        $dirs           = glob($bundelPatteren);

        foreach ($dirs as $dir) {
            $bundles[] = new ContaoModuleBundle(basename($dir), $path);
        }

        return $bundles;
    }

    /**
     * Erstellt die Einträge der Bundles für den AppKernel.
     * @param $path
     * @param $bundles
     * @return array
     */
    public static function generateSymfonyBundles($path, &$bundles)
    {
        $bundleRoot     = dirname($path) . self::$bundlePath;
        $bundelPatteren = $bundleRoot . '/*/*/*Bundle.php';
        $bundleFiles    = glob($bundelPatteren);

        if (is_array($bundleFiles) && count($bundleFiles)) {
            foreach ($bundleFiles as $bundleFile) {
                // coreBundle muss seperat registriert werden, damit der BundleHelper gefunden wird!
                if (!substr_count($bundleFile, 'src/con4gis/CoreBundle/con4gisCoreBundle.php')) {
                    $class     = str_replace($bundleRoot . '/', '', $bundleFile);
                    $class     = str_replace('/', '\\', $class);
                    $class     = str_replace('.php', '', $class);
                    $bundles[] = new $class();
                }
            }
        }

        return $bundles;
    }
}


<?php
/**
 * @package     esitlib
 * @version     1.0.0
 */
namespace con4gis\CoreBundle\Classes\Helper;

/**
 * Class AutoloadHelper
 * @package con4gis\CoreBundle\Classes\Helper
 */
class AutoloadHelper
{

    /**
     * Ruft das Laden der Templates (*.html5, *.xhtml und *.html) für den
     * übergebenen Pfad (inkl. Unterordnern) auf.
     * @param        $strPath
     * @param string $strRgex
     */
    public static function loadTemplates($strPath, $strRgex = '/^.+\.[x]*html[5]*$/i')
    {
        $folder     = (substr_count($strPath, 'system/modules')) ? '/templates' : '/Resources/contao/templates';
        $strPath    = self::makePath($strPath, $folder);
        $objFiles   = self::getFiles($strPath, $strRgex);

        if ($objFiles) {
            self::registerTemplates($objFiles);
        }
    }

    /**
     * Registriert die gefundenen Templates bei Contao.
     * @param $objFiles
     */
    protected static function registerTemplates($objFiles)
    {
        foreach ($objFiles as $varFile) {
            $strFile = (is_array($varFile)) ? array_shift($varFile) : $varFile;
            $objFile = pathinfo($strFile);
            \TemplateLoader::addFile($objFile['filename'], str_replace(TL_ROOT . '/', '', $objFile['dirname']));
        }
    }

    /**
     * Prüft den übergebenen Pfad und ergänzt die fehlenden Bestandteile.
     * @param $strPath
     * @param $folder
     * @return mixed|string
     */
    protected static function makePath($strPath, $folder)
    {
        $strPath = (!substr_count($strPath, TL_ROOT)) ? TL_ROOT . '/' . $strPath : $strPath;
        $strPath.="$folder/";
        $strPath = str_replace('//', '/', $strPath);

        return $strPath;
    }

    /**
     * Sucht die Templates im übergebenen Pfad.
     * @param        $strPath
     * @param string $strRgex
     * @return null|\RegexIterator
     */
    protected static function getFiles($strPath, $strRgex = '/^.+\.php$/i')
    {
        if (is_dir($strPath)) {
            $objDirectory   = new \RecursiveDirectoryIterator($strPath, \FilesystemIterator::SKIP_DOTS);
            $objIterator    = new \RecursiveIteratorIterator($objDirectory);
            $objFiles       = new \RegexIterator($objIterator, $strRgex, \RecursiveRegexIterator::GET_MATCH);

            return $objFiles;
        }

        return null;
    }
}


<?php
/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */
namespace con4gis\CoreBundle\Classes\Helper;

use con4gis\MapsBundle\Resources\contao\classes\Utils;
use con4gis\ProjectsBundle\Classes\Common\C4GBrickCommon;
use Contao\Database;
use Contao\Image;
use Contao\Input;
use Contao\System;

/**
 * Class DcaHelper
 * @package con4gis\CoreBundle\Classes\Helper
 */
class DcaHelper
{

    /**
     * save_callback: Erstelle eine Uuid.
     * @param $varValue
     * @param $dc
     * @return string
     */
    public static function generateUuid($varValue, $dc)
    {
        if (!$varValue) {
            return C4GBrickCommon::getGUID();
        }

        return $varValue;
    }

    /**
     * options_callback: Gibt eine Liste der Tabellen zurück.
     * @return array
     */
    public function cbGetTables()
    {
        $db     = Database::getInstance();
        $tables = $db->listTables();
        return $tables;
    }

    /**
     * options_callback: Gibt eine Liste der Felder einer Tabelle zurück.
     * @param $dc
     * @return array
     */
    public function cbGetFields($dc)
    {
        $data   = array();
        $table  = $dc->activeRecord->srctable;
        $db     = Database::getInstance();

        if ($table) {
            System::loadLanguageFile($table);
            $fields = $db->listFields($table);

            if (is_array($fields) && count($fields)) {
                foreach ($fields as $field) {
                    if ($field['name'] != 'PRIMARY') {
                        if (isset($GLOBALS['TL_LANG'][$table][$field['name']][0])) {
                            $label                  = $GLOBALS['TL_LANG'][$table][$field['name']][0];
                            $label                 .= ' [' . $field['name'] . ']';
                            $data[$field['name']]   = $label;
                        } else {
                            $data[$field['name']] = $field['name'];
                        }
                    }
                }
            }
        }

        return $data;
    }

    /**
     * @param $varValue
     * @param $dc
     * @return mixed
     * @throws \Exception
     */
    public function setLocLon($varValue, $dc) {
        if (!Utils::validateLon($varValue)) {
            throw new \Exception($GLOBALS['TL_LANG']['c4g_maps']['geox_invalid']);
        }
        return $varValue;
    }

    /**
     * @param $varValue
     * @param $dc
     * @return mixed
     * @throws \Exception
     */
    public function setLocLat($varValue, $dc) {
        if (!Utils::validateLat($varValue)) {
            throw new \Exception($GLOBALS['TL_LANG']['c4g_maps']['geoy_invalid']);
        }
        return $varValue;
    }

    /**
     * Gibt einen HTML-String zurück, der den Geopicker enthält.
     * @param \DataContainer $dc
     * @return string
     */
    public function getPickerLink(\DataContainer $dc)
    {
        if (!$GLOBALS['TL_JAVASCRIPT']['c4g-maps-backend'])
        {
            $GLOBALS['TL_JAVASCRIPT']['c4g-maps-backend'] = 'bundles/con4gismaps/js/c4g-maps-backend.js';
        }
        $input = Input::get('act');
        $strField = 'ctrl_' . $dc->field . (($input == 'editAll') ? '_' . $dc->id : '');
        if (substr($strField,-1,1)=='y') {
            $strFieldX = substr($strField,0,-1).'x';
            $strFieldY = $strField;
        } else {
            $strFieldX = $strField;
            $strFieldY = substr($strField,0,-1).'y';
        }

        return ' ' .
            Image::getHtml('bundles/con4gismaps/js/images/be-icons/geopicker.png',
                $GLOBALS['TL_LANG']['tl_content']['editalias'][0], 'style="vertical-align:top"') . '';

    }
}


<?php

namespace Saritasa\DingoApi\Exceptions;

use Dingo\Api\Exception\Handler as DingoApiHandler;
use Dingo\Api\Facade\API;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Debug\ExceptionHandler as IlluminateExceptionHandler;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use Saritasa\DingoApi\Exceptions\ValidationException as CustomValidationException;
use Saritasa\Exceptions\PermissionsException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;

class ApiExceptionHandler extends DingoApiHandler
{
    /**
     * ApiHandler constructor.
     *
     * @param IlluminateExceptionHandler $parentHandler Laravel exception handler
     */
    public function __construct(IlluminateExceptionHandler $parentHandler)
    {
        parent::__construct($parentHandler, config('api.errorFormat'), config('api.debug'));
        $this->registerCustomHandlers();
    }

    /**
     * Register certain handlers via Dingo\API facade
     *
     * We can't pass class method as callback directly,
     * so we wrap them into closures.
     */
    private function registerCustomHandlers()
    {
        API::error(function (UnauthorizedHttpException $e) {
            return $this->handleUnauthorized($e);
        });
        API::error(function (AuthorizationException $e) {
            return $this->handleAuthorizationError($e);
        });
        Api::error(function (PermissionsException $e) {
            return $this->handleAuthorizationError($e);
        });
        API::error(function (ValidationException $e) {
            return $this->handleValidation($e);
        });
        API::error(function (ModelNotFoundException $e) {
            return $this->handleModelNotFound($e);
        });
    }

    /**
     * Security gateway throws AuthorizationException without HTTP code set.
     * Render AccessDeniedHttpException instead to produce 403 HTTP code instead of 500
     *
     * @param \Exception $e Original exception to wrap and handle
     * @return Response
     */
    public function handleAuthorizationError(\Exception $e)
    {
        $e = new AccessDeniedHttpException($e->getMessage(), $e);
        return $this->handle($e);
    }

    /**
     * Dingo API does not handle default Laravel validation exception.
     * Wrap it into ValidationHttpException, processed properly
     *
     * @param ValidationException $e Original exception to wrap and handle
     * @return Response
     */
    public function handleValidation(ValidationException $e)
    {
        $e = new CustomValidationException($e->validator->getMessageBag(), $e->getMessage(), $e, [], $e->getCode());
        return $this->handle($e);
    }

    /**
     * Replace model not found exception to guarantee 404 error instead of 500
     *
     * @param ModelNotFoundException $e Original exception to wrap and handle
     * @return Response
     */
    private function handleModelNotFound(ModelNotFoundException $e)
    {
        $e = new NotFoundHttpException($e->getMessage(), $e, $e->getCode());
        return $this->handle($e);
    }

    /**
     * Add special handling for expired JWT Token.
     * According to HTTP specification, we must return code 401, but allow client application
     * determine, that it should refresh auth token by additional flag:
     * code = 498
     * in body response
     *
     * @param UnauthorizedHttpException $e Original exception to wrap and handle
     * @return Response
     */
    private function handleUnauthorized(UnauthorizedHttpException $e)
    {
        if ($e->getPrevious() instanceof TokenExpiredException) {
            $e = new UnauthorizedHttpException('JWTAuth', $e->getMessage(), $e, 498);
        }
        return $this->handle($e);
    }
}


<?php
/*
 * This file is part of con4gis,
 * the gis-kit for Contao CMS.
 *
 * @package    con4gis
 * @version    6
 * @author     con4gis contributors (see "authors.txt")
 * @license    LGPL-3.0-or-later
 * @copyright  Küstenschmiede GmbH Software & Design
 * @link       https://www.con4gis.org
 */
namespace con4gis\CoreBundle\Resources\contao\classes\stack;

/**
 * Class StackDatabase
 * @package c4g\core
 */
class StackDatabase implements StackInterface
{

    /**
     * Name der Tabelle, in der der Stack gespeichert wird.
     * (Wird in der Kindklasse gesetzt!)
     * @var string
     */
    protected $table = '';

    /**
     * Gibt die Datenbanklasse zurück.
     * @return \Contao\Database
     */
    protected function getDb()
    {
        return \Contao\Database::getInstance();
    }

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

    /**
     * Führt eine Datanbankabfrage aus.
     * (Kapselt den Zugriff auf \Contao\Database)
     * @param $query
     */
    public function execute($query)
    {
        $db = $this->getDb();
        return $db->execute($query);
    }

    /**
     * Prüft, ob ein Feld existiert.
     * (Kapselt den Zugirff auf \Contao\Database)
     * @param $field
     * @return bool
     */
    public function fieldExists($field)
    {
        $db = $this->getDb();
        return $db->fieldExists($field, $this->table);
    }

    /**
     * Fügt dem Stack ein Element hinzu.
     * @param $item
     */
    public function push(array $item)
    {
        if (count($item)) {
            $data   = serialize($item);
            $query  = 'INSERT INTO `' . $this->table . '` SET ';
            $query .= '`data` = \'' . $data . '\', ';
            $query .= ' `tstamp` = ' . time();
            $this->execute($query);
        }
    }

    /**
     * Entfernt ein Element vom Stack.
     * @return array|mixed
     */
    public function pop()
    {
        $data = $this->top();

        if (is_array($data) && count($data)) {
            $query = 'DELETE FROM `' . $this->table . '` WHERE `id` = ' . $data["id"];
            $this->execute($query);
            return $data;
        }

        return array();
    }

    /**
     * Gibt das oberste Element vom Stack zurück.
     * @return array
     */
    public function top()
    {
        $query  = 'SELECT * FROM `' . $this->table . '` ORDER BY id ASC LIMIT 1';
        $result = $this->execute($query);

        if ($result->numRows) {
            return $result->fetchAssoc();
        }

        return array();
    }

    /**
     * Prüft, ob der Stack leer ist.
     * @return bool
     */
    public function isEmpty()
    {
        $query  = 'SELECT * FROM `' . $this->table . '` ORDER BY id ASC LIMIT 1';
        $result = $this->execute($query);

        return ($result->numRows) ? false : true;

    }
}


<?php

declare(strict_types=1);

/*
 * (c) Jeroen van den Enden <info@endroid.nl>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Endroid\CmSms;

use MyCLabs\Enum\Enum;

final class StatusCode extends Enum
{
    const UNSENT = -2;      // Not sent by the client
    const SENT = -1;        // Sent by the client
    const ACCEPTED = 0;     // Accepted by the operator (CM state)
    const REJECTED = 1;     // Rejected by CM or the operator (CM state)
    const DELIVERED = 2;    // Delivered (CM state)
    const FAILED = 3;       // Failed and will not be delivered (CM state)

    public static function getTranslationKeys(): array
    {
        return [
            self::UNSENT => 'unsent',
            self::SENT => 'sent',
            self::ACCEPTED => 'accepted',
            self::REJECTED => 'rejected',
            self::DELIVERED => 'delivered',
            self::FAILED => 'failed',
        ];
    }
}


<?php

namespace Saritasa\DingoApi\Paging;

const PAGE = 'page';
const PAGE_SIZE = 'per_page';
const TOTAL_PAGES = 'total_pages';
const TOTAL_COUNT = 'total_count';

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Saritasa\Exceptions\PagingException;
use Saritasa\Transformers\Traits\SimpleJsonSerialize;

/**
 * Paging Data - information about page size, current page, number of total pages and items count
 *
 * @property integer $page Selected page in data set
 * @property integer $pageSize Number of records in single page
 * @property-read integer $totalPages Number of pages in data set
 * @property integer $totalCount Total number of records in data set
 */
class PagingInfo implements Arrayable, Jsonable, \JsonSerializable
{
    use SimpleJsonSerialize;

    private $page = 1;
    private $pageSize = 0;
    private $totalPages = 0;
    private $totalCount = 0;

    const KEYS = [PAGE, PAGE_SIZE, TOTAL_PAGES, TOTAL_COUNT];
    const PROPERTIES = ['page', 'pageSize', 'totalPages', 'totalCount'];

    public function __construct(array $input = null)
    {
        $this->pageSize = config('api.defaultLimit');
        if ($input) {
            if (isset($input[PAGE])) {
                $this->setPage($input[PAGE]);
            }
            if (isset($input[PAGE_SIZE])) {
                $this->setPageSize($input[PAGE_SIZE]);
            }
            if (isset($input[TOTAL_COUNT])) {
                $this->setTotalCount($input[TOTAL_COUNT]);
            }
        }
    }

    public function __get(string $key): int
    {
        if (in_array($key, static::PROPERTIES)) {
            return $this->$key;
        } else {
            throw new PagingException("Unknown property $key requested");
        }
    }

    public function __set(string $key, $value)
    {
        if (in_array($key, static::PROPERTIES)) {
            $method = 'set'.ucfirst($key);
            $this->$method($value);
        } else {
            throw new PagingException("Trying to set unknown property $key");
        }
    }

    protected function setPage(int $value)
    {
        $val = (int)$value;
        if ($val < 1) {
            throw new PagingException("Page number cannot be less, than 1");
        }
        $this->page = $val;
    }

    protected function setPageSize(int  $value)
    {
        $val = (int)$value;
        if ($val < 1) {
            throw new PagingException("Page size cannot be less, than 1");
        }
        // We do not handle config('api.maxLimit') here, because, unlike user input,
        // it's OK, if developer set it programmatically to greater value
        $this->pageSize = $val;
        $this->totalPages = (int)ceil($this->totalCount / $this->pageSize);
    }

    protected function setTotalPages(int $value)
    {
        $val = (int)$value;
        if ($val < 0) {
            throw new PagingException("Total pages count cannot be less, than 0");
        }
        $this->totalPages = $val;
    }

    protected function setTotalCount(int $value)
    {
        $val = (int)$value;
        if ($val < 0) {
            throw new PagingException("Total items count cannot be less, than 0");
        }
        $this->totalCount = $val;
        $this->totalPages = (int)ceil($this->totalCount / $this->pageSize);
    }

    public function getOffset()
    {
        $pageZeroIndexed = min($this->page - 1, 0);
        $offset = $this->pageSize * $pageZeroIndexed;
        if ($offset > $this->totalCount) {
            $actualPageCount = (int)ceil($this->totalCount / $this->pageSize);
            throw new PagingException("Non existing page requested: $this->pageSize of $actualPageCount");
        }
        return $offset;
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return [
            PAGE => $this->page,
            PAGE_SIZE => $this->pageSize,
            TOTAL_PAGES => $this->totalPages,
            TOTAL_COUNT => $this->totalCount
        ];
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\console;

use Yii;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use yii\helpers\Json;

class BowerPackageController extends \yii\console\Controller
{
    /**
     * Fetches TOP-$count components from Bower ans saves to `config/bower.list`.
     *
     * @param int $count
     * @param bool $skipCache
     */
    public function actionFetchTop($count = 1000, $skipCache = false)
    {
        $result = [];
        $components = $this->getComponents($skipCache);
        ArrayHelper::multisort($components, 'stars', SORT_DESC, SORT_NUMERIC);

        foreach (array_slice($components, 0, $count) as $component) {
            $result[] = 'bower-asset/' . $component['name'];
            echo Console::renderColoredString("%R{$component['stars']}%N - %g{$component['name']}%N");
            Console::moveCursorTo(0);
            Console::clearLine();
        }

        $componentsListPath = Yii::getAlias('@hiqdev/assetpackagist/config/bower.list');
        file_put_contents($componentsListPath, implode("\n", $result));

        echo Console::renderColoredString('Fetched %YBower%N components list. Found %G' . count($components) . "%N components.\n");
        echo Console::renderColoredString('Only %bTOP-' . $count . "%N components were added to the packages list.\n");
        echo Console::renderColoredString('See %G' . $componentsListPath . "%N\n");
    }

    /**
     * Gets components array from bower API.
     *
     * @param bool $skipCache whether to skip using local cache
     * @return array
     */
    private function getComponents($skipCache = false)
    {
        $url = 'http://bower-component-list.herokuapp.com';
        $componentsFilePath = Yii::getAlias('@runtime/bower-cache/components.list');

        if (!$skipCache && is_file($componentsFilePath) && (time() - filemtime($componentsFilePath) < 60 * 60 * 6)) { // 6 hours
            $raw = file_get_contents($componentsFilePath);
        } else {
            $result = (new \GuzzleHttp\Client())->request('GET', $url);
            $raw = $result->getBody();
            FileHelper::createDirectory(dirname($componentsFilePath));
            file_put_contents($componentsFilePath, $raw);
        }

        return Json::decode($raw);
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\console;

use hiqdev\assetpackagist\commands\AbstractPackageCommand;
use Yii;
use yii\base\Event;
use yii\base\Module;
use yii\console\Controller;
use yii\helpers\Console;
use yii\queue\cli\Signal;
use yii\queue\ErrorEvent;
use yii\queue\JobEvent;
use yii\queue\Queue;
use yii\queue\cli\Queue as CliQueue;

/**
 * Manages service Queue.
 */
class QueueController extends Controller
{
    /**
     * @var Queue|\yii\queue\db\Queue
     */
    private $queue;

    /**
     * How many jobs will be executed without process restart
     */
    const MAX_EXECUTED_JOBS = 100;

    /**
     * @var int
     */
    private $executedJobsCount = 0;

    public function __construct($id, Module $module, Queue $queue, array $config = [])
    {
        parent::__construct($id, $module, $config);
        $this->queue = $queue;
    }

    public function init()
    {
        $this->attachEventHandlers();
    }

    /**
     * Runs the queue.
     */
    public function actionRun()
    {
        $this->queue->run(true);
    }

    /**
     * Test action to ensure that ErrorHandler flushes stack immediately.
     */
    public function actionTestErrorHandler()
    {
        Yii::info(Console::renderColoredString('Lorem ipsum for time %y' . time() . '%n ' . "\n"), \hiqdev\assetpackagist\commands\CollectDependenciesCommand::class);
        sleep(1);
    }

    private function ensureLimits()
    {
        if ($this->executedJobsCount++ > static::MAX_EXECUTED_JOBS && function_exists('posix_kill')) {
            return 15; // SIGTERM
        }

        return null;
    }

    /**
     * Attaches handlers on Queue events.
     */
    private function attachEventHandlers()
    {
        $out = function ($string) {
            $this->stdout(Console::renderColoredString($string));
        };

        Event::on(Queue::class, Queue::EVENT_BEFORE_EXEC, function ($event) use ($out) {
            /** @var JobEvent $event */
            $out("%GNew job%n '" . get_class($event->job) . "'\n");
            $this->ensureLimits();
        });

        Event::on(Queue::class, Queue::EVENT_AFTER_EXEC, function ($event) use ($out) {
            /** @var JobEvent $event */
            $out("%GJob%n '" . get_class($event->job) . "' %Gis completed%n\n");
        });

        Event::on(Queue::class, Queue::EVENT_AFTER_ERROR, function ($event) use ($out) {
            /** @var ErrorEvent $event */
            $out("%RJob '" . get_class($event->job) . "' finished with error:%n '" . $event->error . "'\n");
        });

        Event::on(Queue::class, CliQueue::EVENT_WORKER_LOOP, function (\yii\queue\cli\WorkerEvent $event) use ($out) {
            $exitCode = $this->ensureLimits();
            if ($exitCode !== null) {
                $out('Reached limit of ' . static::MAX_EXECUTED_JOBS . " executed jobs. Stopping process.\n");
                $event->exitCode = $exitCode;
            }
        });

        Event::on(AbstractPackageCommand::class, AbstractPackageCommand::EVENT_BEFORE_RUN, function ($event) use ($out) {
            /** @var AbstractPackageCommand $command */
            $command = $event->sender;
            $out('%g[' . get_class($command) . ']%n Working on package %N' . $command->getPackage()->getFullName() . "%n\n");
        });
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\components;

use hiqdev\assetpackagist\exceptions\AssetFileStorageException;
use hiqdev\assetpackagist\models\AssetPackage;
use Yii;
use yii\base\Component;
use yii\helpers\Json;

class Storage extends Component implements StorageInterface
{
    protected $_path;
    protected $_locker;

    /**
     * {@inheritdoc}
     */
    public function init()
    {
        $this->_path = Yii::getAlias('@storage', false);
    }

    protected function acquireLock()
    {
        /* @var $mutex \yii\mutex\Mutex */
        $mutex = Yii::$app->mutex;

        if (!$mutex->acquire('lock', 5)) {
            throw new \Exception('failed get lock');
        }
    }

    protected function releaseLock()
    {
        /* @var $mutex \yii\mutex\Mutex */
        $mutex = Yii::$app->mutex;

        $mutex->release('lock');
    }

    /**
     * {@inheritdoc}
     */
    public function getNextId()
    {
        $this->acquireLock();
        {
            $nextID = $this->readLastId() + 1;
            $this->writeLastId($nextID);
        }
        $this->releaseLock();

        return $nextID;
    }

    protected function readLastId()
    {
        $path = $this->getLastIdPath();

        return (file_exists($path) ? (int) file_get_contents($path) : 0) ?: 1000000;
    }

    protected function writeLastId($value)
    {
        if (file_put_contents($this->getLastIdPath(), $value) === false) {
            throw new AssetFileStorageException('Filed to write lastId to the storage');
        }
    }

    protected function getLastIdPath()
    {
        return $this->buildPath('lastid');
    }

    /**
     * {@inheritdoc}
     */
    public function writePackage(AssetPackage $package)
    {
        $name = $package->getNormalName();
        $data = [
            'packages' => [
                $name => $package->getReleases(),
            ],
        ];
        $json = Json::encode($data);
        $hash = hash('sha256', $json);
        $path = $this->buildHashedPath($name, $hash);
        $latestPath = $this->buildHashedPath($name);
        if (!file_exists($path)) {
            $this->acquireLock();
            try {
                if ($this->mkdir(dirname($path)) === false) {
                    throw new AssetFileStorageException('Failed to create a directory for asset-package', $package);
                }
                if (file_put_contents($path, $json) === false) {
                    throw new AssetFileStorageException('Failed to write package', $package);
                }
                if (file_put_contents($latestPath, $json) === false) {
                    throw new AssetFileStorageException('Failed to write file "latest.json" for asset-packge', $package);
                }
                $this->writeProviderLatest($name, $hash);
            } finally {
                $this->releaseLock();
            }
        } else {
            touch($latestPath);
        }

        return $hash;
    }

    /**
     * {@inheritdoc}
     */
    public function readPackage(AssetPackage $package)
    {
        $name = $package->getNormalName();
        $path = $this->buildHashedPath($name);
        clearstatcache(false, $path);
        if (!file_exists($path)) {
            return null;
        }
        $json = file_get_contents($path);
        $updateTime = filemtime($path);
        $hash = hash('sha256', $json);
        $data = Json::decode($json);
        $releases = isset($data['packages'][$name]) ? $data['packages'][$name] : [];

        return compact('hash', 'releases', 'updateTime');
    }

    protected function buildPath()
    {
        $args = func_get_args();
        array_unshift($args, $this->_path);

        return implode(DIRECTORY_SEPARATOR, $args);
    }

    protected function buildHashedPath($name, $hash = 'latest')
    {
        return $this->buildPath('p', $name, $hash . '.json');
    }

    protected function writeProviderLatest($name, $hash)
    {
        $latestPath = $this->buildHashedPath('provider-latest');
        if (file_exists($latestPath)) {
            $data = Json::decode(file_get_contents($latestPath) ?: '[]');
        }
        if (!isset($data) || !is_array($data)) {
            $data = [];
        }
        if (!isset($data['providers'])) {
            $data['providers'] = [];
        }
        $data['providers'][$name] = ['sha256' => $hash];
        $json = Json::encode($data);
        $hash = hash('sha256', $json);
        $path = $this->buildHashedPath('provider-latest', $hash);

        if (!file_exists($path)) {
            $this->acquireLock();

            try {
                if ($this->mkdir(dirname($path)) === false) {
                    throw new AssetFileStorageException('Failed to create a directory for provider-latest storage');
                }
                if (file_put_contents($path, $json) === false) {
                    throw new AssetFileStorageException('Failed to write package to provider-latest storage for package "' . $name . '"');
                }
                if (file_put_contents($latestPath, $json) === false) {
                    throw new AssetFileStorageException('Failed to write file "latest.json" to provider-latest storage for package "' . $name . '"');
                }
                $this->writePackagesJson($hash);
            } finally {
                $this->releaseLock();
            }
        } else {
            touch($latestPath);
        }

        return $hash;
    }

    protected function writePackagesJson($hash)
    {
        $data = [
            'providers-url'     => '/p/%package%/%hash%.json',
            'provider-includes' => [
                'p/provider-latest/%hash%.json' => [
                    'sha256' => $hash,
                ],
            ],
        ];
        $this->acquireLock();
        $filename = $this->buildPath('packages.json');
        try {
            if (file_put_contents($filename, Json::encode($data)) === false) {
                throw new AssetFileStorageException('Failed to write main packages.json');
            }
            touch($filename);
        } finally {
            $this->releaseLock();
        }
    }

    /**
     * Creates directory $dir and sets chmod 777.
     * @param string $dir
     * @return bool whether the directory was created successfully
     */
    protected function mkdir($dir)
    {
        if (!file_exists($dir)) {
            return @mkdir($dir, 0777, true);
        }

        return true;
    }

    public function readJson($path)
    {
        return Json::decode(file_get_contents($this->buildPath($path)));
    }

    protected function readPackagesJson()
    {
        $data = $this->readJson('packages.json');

        return $data['provider-includes'];
    }

    protected function readProvider($path)
    {
        $data = $this->readJson($path);

        return $data['providers'];
    }

    /**
     * {@inheritdoc}
     */
    public function listPackages()
    {
        $packages = [];
        $providers = $this->readPackagesJson();
        foreach ($providers as $path => $data) {
            $path = strtr($path, ['%hash%' => $data['sha256']]);
            $packages = array_merge($packages, $this->readProvider($path));
        }

        return $packages;
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\librariesio;

use GuzzleHttp\Client;
use yii\base\Component;

class LibrariesioRepository extends Component
{
    /**
     * @var string Base URI for https://libraries.io
     */
    public $baseUri = 'https://libraries.io/api/';

    /**
     * Without api key, has 30/request/minute hate limit
     * With api key, has 60/request/minute hate limit.
     * @var string The user API Key
     */
    public $apiKey;

    /**
     * Options for Guzzle client
     * Example:
     * [
     *   'timeout'         => 0,
     *   'allow_redirects' => false,
     *   'proxy'           => '192.168.16.1:10'
     * ].
     *
     * @var array
     */
    public $clientOptions = [];

    /**
     * The Guzzle client.
     * @var Client
     */
    protected $client;

    public function init()
    {
        parent::init();

        $this->clientOptions['base_uri'] = $this->baseUri;

        $this->client = new Client($this->clientOptions);
    }

    /**
     * Send request to server with api_key and return a Response.
     * @param string $method
     * @param string $uri
     * @param array $options
     * @throws \GuzzleHttp\Exception\BadResponseException
     * @return \GuzzleHttp\Psr7\Response
     */
    public function request($method, $uri = '', array $options = [])
    {
        if (!isset($options['query'])) {
            $options['query'] = [];
        }
        if ($this->apiKey && !isset($options['query']['api_key'])) {
            $options['query']['api_key'] = $this->apiKey;
        }

        try {
            return $this->client->request($method, $uri, $options);
        } catch (\GuzzleHttp\Exception\BadResponseException $ex) {
            if ($ex->hasResponse()) {
                return $ex->getResponse();
            }
            throw $ex;
        }
    }

    /**
     * Search package in https://libraries.io.
     * @see https://libraries.io/api/#project-search
     * @param array $query
     * @return \GuzzleHttp\Psr7\Response
     */
    public function search($query = [])
    {
        return $this->request('GET', 'search', ['query' => $query]);
    }

    /**
     * Return the package info from https://libraries.io.
     * @see https://libraries.io/api/#project
     * @param string $platform
     * @param string $name
     * @return \GuzzleHttp\Psr7\Response
     */
    public function getProject($platform, $name)
    {
        return $this->request('GET', $platform . '/' . $name);
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\models;

use Composer\Package\Link;
use Exception;
use hiqdev\assetpackagist\components\Storage;
use hiqdev\assetpackagist\registry\RegistryFactory;
use hiqdev\assetpackagist\repositories\PackageRepository;
use Yii;
use yii\base\Object;

class AssetPackage extends Object
{
    protected $_type;
    protected $_name;
    protected $_hash;
    /**
     * @var array
     */
    protected $_releases = [];
    protected $_saved;

    /**
     * @var integer UNIX Epoch timestamp of the latest package update
     */
    protected $_updateTime;

    public static function normalizeName($name)
    {
        return strtolower(static::normalizeScopedName($name));
    }

    public static function normalizeScopedName($name)
    {
        return preg_replace("#@(.+?)/#", '${1}--', $name);
    }

    /**
     * AssetPackage constructor.
     * @param string $type
     * @param string $name
     * @param array $config
     * @throws Exception
     */
    public function __construct($type, $name, $config = [])
    {
        parent::__construct($config);

        if (!$this->checkType($type)) {
            throw new Exception('wrong type');
        }
        if (!$this->checkName($name)) {
            throw new Exception('wrong name');
        }
        $this->_type = $type;
        $this->_name = $name;
    }

    /**
     * @return RegistryFactory
     */
    public function getRegistry()
    {
        return Yii::$app->get('registryFactory');
    }

    public function checkType($type)
    {
        return $type === 'bower' || $type === 'npm';
    }

    public function checkName($name)
    {
        return strlen($name) > 0;
    }

    public function getFullName()
    {
        return static::buildFullName($this->_type, $this->_name);
    }

    public static function buildNormalName($type, $name)
    {
        return static::buildFullName($type, static::normalizeName($name));
    }

    public static function buildFullName($type, $name)
    {
        return $type . '-asset/' . $name;
    }

    public static function splitFullName($full)
    {
        list($temp, $name) = explode('/', $full);
        list($type) = explode('-', $temp);

        return [$type, $name];
    }

    /**
     * @param string $full package name
     * @return static
     */
    public static function fromFullName($full)
    {
        list($type, $name) = static::splitFullName($full);
        return new static($type, $name);
    }

    public function getType()
    {
        return $this->_type;
    }

    public function getNormalName()
    {
        return static::buildNormalName($this->_type, $this->_name);
    }

    public function getName()
    {
        return $this->_name;
    }

    public function getHash()
    {
        return $this->_hash;
    }

    /**
     * findOne.
     *
     * @param string $type
     * @param string $name
     * @return static|null
     */
    public static function findOne($type, $name)
    {
        $package = new static($type, $name);
        $package->load();

        return $package;
    }

    public function load()
    {
        $data = $this->getStorage()->readPackage($this);
        if ($data !== null) {
            $this->_hash = $data['hash'];
            $this->_releases = $data['releases'];
            $this->_updateTime = $data['updateTime'];
        }
    }

    public function update()
    {
        $pool = $this->getRegistry()->getPool();
        $this->_releases = $this->prepareReleases($pool);
        $this->getStorage()->writePackage($this);
        $this->load();
    }

    /**
     * @param \Composer\DependencyResolver\Pool $pool
     * @return array
     */
    public function prepareReleases($pool)
    {
        $releases = [];

        foreach ($pool->whatProvides($this->getFullName()) as $package) {
            if ($package instanceof \Composer\Package\AliasPackage) {
                continue;
            }

            $version = $this->prepareVersion($package->getPrettyVersion());
            $require = $this->prepareRequire($package->getRequires());
            $release = [
                'uid' => $this->prepareUid($version),
                'name' => $this->getNormalName(),
                'version' => $version,
                'version_normalized' => $this->prepareVersion($package->getVersion()),
                'type' => $this->getType() . '-asset',
            ];
            if ($require) {
                $release['require'] = $require;
            }
            if ($package->getDistUrl()) {
                $release['dist'] = [
                    'type' => $package->getDistType(),
                    'url' => $package->getDistUrl(),
                    'reference' => $package->getDistReference(),
                ];
            }
            if ($package->getLicense()) {
                $release['license'] = $package->getLicense();
            }
            if ($package->getSourceUrl()) {
                $release['source'] = [
                    'type' => $package->getSourceType(),
                    'url' => $package->getSourceUrl(),
                    'reference' => $package->getSourceReference(),
                ];
            }
            if ((isset($release['dist']) && $release['dist']) || (isset($release['source']) && $release['source'])) {
                $releases[$version] = $release;
            }
        }

        //Sort before save
        \hiqdev\assetpackagist\components\PackageUtil::sort($releases);

        return $releases;
    }

    protected function prepareVersion($version)
    {
        if ($this->getNormalName() === 'bower-asset/angular') {
            return $this->convertPatchToRC($version);
        }

        return $version;
    }

    protected function convertPatchToRC($version)
    {
        return preg_replace('/-patch(.+)/', '-RC${1}', $version);
    }

    /**
     * Prepares array of requires: name => constraint.
     * @param Link[] array of package requires
     * @return array
     */
    public function prepareRequire(array $links)
    {
        $requires = [];
        foreach ($links as $name => $link) {
            /** @var Link $link */
            $requires[$name] = $link->getPrettyConstraint();
        }

        return $requires;
    }

    public function prepareUid($version)
    {
        $known = $this->getSaved()->getRelease($version);

        return isset($known['uid']) ? $known['uid'] : $this->getStorage()->getNextId();
    }

    /**
     * @return array
     */
    public function getReleases()
    {
        return $this->_releases;
    }

    /**
     * @param $version
     * @return array
     */
    public function getRelease($version)
    {
        return isset($this->_releases[$version]) ? $this->_releases[$version] : [];
    }

    public function getSaved()
    {
        if ($this->_saved === null) {
            $this->_saved = static::findOne($this->getType(), $this->getName());
        }

        return $this->_saved;
    }

    /**
     * @return Storage
     */
    public function getStorage()
    {
        return Yii::$app->get('packageStorage');
    }

    /**
     * Returns the latest update time (UNIX Epoch).
     * @return int|null
     */
    public function getUpdateTime()
    {
        return $this->_updateTime;
    }

    /**
     * Package can be updated not more often than once in 10 min.
     * @return bool
     */
    public function canBeUpdated()
    {
        return time() - $this->getUpdateTime() > 60 * 10; // 10 min
    }

    /**
     * Whether tha package should be auth-updated (if it is older than 1 day).
     * @return bool
     */
    public function canAutoUpdate()
    {
        return time() - $this->getUpdateTime() > 60 * 60 * 24; // 1 day
    }

    public function isAvailable()
    {
        $repository = Yii::createObject(PackageRepository::class, []);

        return $repository->exists($this);
    }

    /**
     * @return array
     */
    public function __sleep()
    {
        return ['_type', '_name', '_hash'];
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\librariesio;

use hiqdev\assetpackagist\models\AssetPackage;
use hiqdev\assetpackagist\repositories\PackageRepository;
use Yii;
use yii\base\Model;

/**
 * @property string $fullName Full name of package
 */
class Project extends Model
{
    public $name;
    public $platform;
    public $description;
    public $homepage;
    public $repository_url;
    public $normalized_licenses;
    public $rank;
    public $latest_release_published_at;
    public $latest_release_number;
    public $language;
    public $status;
    public $package_manager_url;
    public $stars;
    public $forks;
    public $keywords;

    public function rules()
    {
        return [
            ['name', 'string'],
            ['platform', 'string'],
            ['description', 'string'],
            ['homepage', 'url'],
            ['repository_url', 'url'],
            ['normalized_licenses', 'each', 'rule' => ['string']],
            ['rank', 'integer'],
            ['latest_release_published_at', 'datetime', 'timestampAttribute' => 'latest_release_published_at'],
            ['latest_release_number', 'string'],
            ['language', 'string'],
            ['status', 'string'],
            ['package_manager_url', 'url'],
            ['stars', 'integer'],
            ['forks', 'integer'],
            ['keywords', 'each', 'rule' => ['string']],
        ];
    }

    public function getFullName()
    {
        $fullname = AssetPackage::buildFullName($this->platform, $this->name);
        return AssetPackage::normalizeName($fullname);
    }

    /**
     * Check the package exists in Asset-Packagist.
     * @return boolean
     */
    public function isAvailable()
    {
        $package = AssetPackage::fromFullName($this->getFullName());

        $repository = Yii::createObject(PackageRepository::class, []);

        return $repository->exists($package);
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\log;

use yii\helpers\Console;
use yii\helpers\VarDumper;
use yii\log\Logger;
use yii\log\Target;

class StdoutTarget extends Target
{
    /**
     * Exports log [[messages]] to a specific destination.
     * Child classes must implement this method.
     */
    public function export()
    {
        foreach ($this->messages as $message) {
            list($text, $level, $category, $timestamp) = $message;
            if (!is_string($text)) {
                // exceptions may not be serializable if in the call stack somewhere is a Closure
                if ($text instanceof \Throwable || $text instanceof \Exception) {
                    $text = (string) $text;
                } else {
                    $text = VarDumper::export($text);
                }
            }

            $string = "[$level][$category] $text";

            if ($level === Logger::LEVEL_ERROR) {
                Console::stderr($string);
            } else {
                Console::stdout($string);
            }
        }
    }
}


<?php
/**
 * Asset Packagist.
 *
 * @link      https://github.com/hiqdev/asset-packagist
 * @package   asset-packagist
 * @license   BSD-3-Clause
 * @copyright Copyright (c) 2016-2017, HiQDev (http://hiqdev.com/)
 */

namespace hiqdev\assetpackagist\console;

use hiqdev\assetpackagist\commands\PackageUpdateCommand;
use hiqdev\assetpackagist\components\StorageInterface;
use hiqdev\assetpackagist\models\AssetPackage;
use hiqdev\assetpackagist\repositories\PackageRepository;
use Yii;
use yii\console\Controller;
use yii\helpers\Console;

/**
 * Provides maintenance actions for the asset-packagist service.
 */
class MaintenanceController extends Controller
{
    /**
     * @var StorageInterface
     */
    protected $packageStorage;

    /**
     * @var PackageRepository
     */
    protected $packageRepository;

    /**
     * MaintenanceController constructor.
     * @param StorageInterface $packageStorage
     * @param PackageRepository $packageRepository
     * {@inheritdoc}
     */
    public function __construct($id, $module, StorageInterface $packageStorage, PackageRepository $packageRepository, $config = [])
    {
        parent::__construct($id, $module, $config);

        $this->packageStorage = $packageStorage;
        $this->packageRepository = $packageRepository;
    }

    /**
     * Synchronizes file system packages to the database.
     */
    public function actionSyncToDb()
    {
        $packages = $this->packageStorage->listPackages();

        foreach ($packages as $name => $data) {
            $message = "Package %N$name%n ";
            $package = AssetPackage::fromFullName($name);
            $package->load();

            $message .= $this->packageRepository->exists($package)
                ? 'already exists. %BUpdated.%n'
                : 'does not exist. %GCreated.%n';

            $this->packageRepository->save($package);

            $this->stdout(Console::renderColoredString($message . "\n"));
        }
    }

    /**
     * Updates expired packages.
     */
    public function actionUpdateExpired()
    {
        $packages = $this->packageRepository->getExpiredForUpdate();

        foreach ($packages as $package) {
            $package->load();
            Yii::$app->queue->push(Yii::createObject(PackageUpdateCommand::class, [$package]));

            $message = 'Package %N' . $package->getFullName() . '%n';
            $message .= ' was updated ' . Yii::$app->formatter->asRelativeTime($package->getUpdateTime());
            $message .= ". %GAdded to queue for update%n\n";
            $this->stdout(Console::renderColoredString($message));
        }
    }
}


<?php

declare(strict_types=1);

namespace Neo4j\Neo4jBundle\Collector;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;

/**
 * @author Xavier Coureau <xavier@pandawan-technology.com>
 */
final class Neo4jDataCollector extends DataCollector
{
    /**
     * @var QueryLogger
     */
    private $queryLogger;

    public function __construct(QueryLogger $logger)
    {
        $this->queryLogger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function collect(Request $request, Response $response, \Exception $exception = null)
    {
        $this->data['time'] = $this->queryLogger->getElapsedTime();
        $this->data['nb_queries'] = count($this->queryLogger);
        $this->data['statements'] = $this->queryLogger->getStatements();
        $this->data['failed_statements'] = array_filter($this->queryLogger->getStatements(), function ($statement) {
            return !isset($statement['success']) || !$statement['success'];
        });
    }

    public function reset()
    {
        $this->data = [];
        $this->queryLogger->reset();
    }

    /**
     * @return int
     */
    public function getQueryCount()
    {
        return $this->data['nb_queries'];
    }

    /**
     * Return all statements, successful and not successful.
     *
     * @return array
     */
    public function getStatements()
    {
        return $this->data['statements'];
    }

    /**
     * Return not successful statements.
     *
     * @return array
     */
    public function getFailedStatements()
    {
        return $this->data['failed_statements'];
    }

    /**
     * @return float
     */
    public function getTime()
    {
        return $this->data['time'];
    }

    /**
     * @return float
     */
    public function getTimeForQuery()
    {
        return $this->data['time'];
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'neo4j';
    }
}


<?php

declare(strict_types=1);

namespace Neo4j\Neo4jBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This class contains the configuration information for the bundle.
 *
 * This information is solely responsible for how the different configuration
 * sections are normalized, and merged.
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class Configuration implements ConfigurationInterface
{
    /**
     * Whether to use the debug mode.
     *
     * @see https://github.com/doctrine/DoctrineBundle/blob/v1.5.2/DependencyInjection/Configuration.php#L31-L41
     *
     * @var bool
     */
    private $debug;

    /**
     * @param bool $debug
     */
    public function __construct($debug)
    {
        $this->debug = (bool) $debug;
    }

    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder('neo4j');
        // Keep compatibility with symfony/config < 4.2
        if (!method_exists($treeBuilder, 'getRootNode')) {
            $root = $treeBuilder->root('neo4j');
        } else {
            $root = $treeBuilder->getRootNode();
        }

        $root->children()
            ->arrayNode('profiling')
                ->addDefaultsIfNotSet()
                ->treatFalseLike(['enabled' => false])
                ->treatTrueLike(['enabled' => true])
                ->treatNullLike(['enabled' => $this->debug])
                ->info('Extend the web profiler with information about queries.')
                ->children()
                    ->booleanNode('enabled')
                        ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
                        ->defaultValue($this->debug)
                    ->end()
                ->end()
            ->end()
            ->arrayNode('clients')
                ->requiresAtLeastOneElement()
                ->useAttributeAsKey('name')
                ->prototype('array')
                ->addDefaultsIfNotSet()
                ->fixXmlConfig('connection')
                ->children()
                    ->arrayNode('connections')
                        ->prototype('scalar')->end()
                    ->end()
                ->end()
            ->end()->end()
            ->arrayNode('entity_managers')
                ->requiresAtLeastOneElement()
                ->useAttributeAsKey('name')
                ->prototype('array')
                ->addDefaultsIfNotSet()
                ->children()
                    ->scalarNode('client')->defaultValue('default')->end()
                    ->scalarNode('cache_dir')->defaultNull()->end()
                ->end()
            ->end()->end()
            ->arrayNode('connections')
                ->isRequired()
                ->requiresAtLeastOneElement()
                ->useAttributeAsKey('name')
                ->prototype('array')
                ->addDefaultsIfNotSet()
                ->children()
                    ->enumNode('scheme')->values(['http', 'bolt'])->defaultValue('bolt')->end()
                    ->scalarNode('host')->defaultValue('localhost')->end()
                    ->scalarNode('port')->end()
                    ->scalarNode('username')->defaultValue('neo4j')->end()
                    ->scalarNode('password')->defaultValue('neo4j')->end()
                    ->scalarNode('dsn')->defaultNull()->end()
                ->end()
            ->end()
        ->end();

        return $treeBuilder;
    }
}


<?php

declare(strict_types=1);

namespace Neo4j\Neo4jBundle\Factory;

use GraphAware\Neo4j\Client\Client;
use GraphAware\Neo4j\Client\ClientInterface;
use GraphAware\Neo4j\Client\Connection\ConnectionManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class ClientFactory
{
    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @var ConnectionManager
     */
    private $connectionManager;

    /**
     * @param ConnectionManager             $connectionManager
     * @param EventDispatcherInterface|null $eventDispatcher
     */
    public function __construct(ConnectionManager $connectionManager, EventDispatcherInterface $eventDispatcher = null)
    {
        $this->connectionManager = $connectionManager;
        $this->eventDispatcher = $eventDispatcher;
    }

    /**
     * Build an Client form multiple connection.
     *
     * @param string $names
     *
     * @return ClientInterface
     */
    public function create(array $names): ClientInterface
    {
        // Create a new connection manager specific for this client
        $clientConnectionManager = new ConnectionManager();
        foreach ($names as $name) {
            $clientConnectionManager->registerExistingConnection($name, $this->connectionManager->getConnection($name));
        }

        $firstName = reset($names);
        $clientConnectionManager->setMaster($firstName);

        return new Client($clientConnectionManager, $this->eventDispatcher);
    }
}


<?php

declare(strict_types=1);

namespace Neo4j\Neo4jBundle\Collector\Twig;

use GraphAware\Neo4j\Client\Formatter\Type\Node;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class Neo4jResultExtension extends \Twig_Extension
{
    /**
     * {@inheritdoc}
     *
     * @return array
     */
    public function getFilters()
    {
        return [
            new \Twig_SimpleFilter('neo4jResult', [$this, 'getType']),
        ];
    }

    /**
     * @param mixed $object
     *
     * @return string
     */
    public function getType($object): string
    {
        return $this->doGetType($object, true);
    }

    public function getName(): string
    {
        return 'neo4j.result';
    }

    /**
     * @param mixed $object
     * @param bool  $recursive
     *
     * @return string
     */
    private function doGetType($object, bool $recursive): string
    {
        if ($object instanceof Node) {
            return sprintf('%s: %s', $object->identity(), implode(', ', $object->labels()));
        } elseif (is_array($object) && $recursive) {
            if (empty($object)) {
                return 'Empty array';
            }
            $ret = [];
            foreach ($object as $o) {
                $ret[] = $this->doGetType($o, false);
            }

            return sprintf('[%s]', implode(', ', $ret));
        }

        return is_object($object) ? get_class($object) : gettype($object);
    }
}


<?php

declare(strict_types=1);

namespace Neo4j\Neo4jBundle\DependencyInjection;

use GraphAware\Bolt\Driver as BoltDriver;
use GraphAware\Neo4j\Client\ClientInterface;
use GraphAware\Neo4j\Client\Connection\Connection;
use GraphAware\Neo4j\OGM\EntityManager;
use GraphAware\Neo4j\Client\HttpDriver\Driver as HttpDriver;
use GraphAware\Neo4j\OGM\EntityManagerInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class Neo4jExtension extends Extension
{
    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = $this->getConfiguration($configs, $container);
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');

        $this->handleConnections($config, $container);
        $clientServiceIds = $this->handleClients($config, $container);

        if ($this->validateEntityManagers($config)) {
            $loader->load('entity_manager.xml');
            $this->handleEntityManagers($config, $container, $clientServiceIds);
            $container->setAlias('neo4j.entity_manager', 'neo4j.entity_manager.default');
            $container->setAlias(EntityManagerInterface::class, 'neo4j.entity_manager.default');
        }

        // add aliases for the default services
        $container->setAlias('neo4j.connection', 'neo4j.connection.default');
        $container->setAlias(Connection::class, 'neo4j.connection.default');
        $container->setAlias('neo4j.client', 'neo4j.client.default');
        $container->setAlias(ClientInterface::class, 'neo4j.client.default');

        // Configure toolbar
        if ($this->isConfigEnabled($container, $config['profiling'])) {
            $loader->load('data-collector.xml');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getConfiguration(array $config, ContainerBuilder $container): Configuration
    {
        return new Configuration($container->getParameter('kernel.debug'));
    }

    /**
     * {@inheritdoc}
     */
    public function getAlias(): string
    {
        return 'neo4j';
    }

    /**
     * @param array            $config
     * @param ContainerBuilder $container
     *
     * @return array with service ids
     */
    private function handleClients(array &$config, ContainerBuilder $container): array
    {
        if (empty($config['clients'])) {
            // Add default entity manager if none set.
            $config['clients']['default'] = ['connections' => ['default']];
        }

        $serviceIds = [];
        foreach ($config['clients'] as $name => $data) {
            $connections = [];
            $serviceIds[$name] = $serviceId = sprintf('neo4j.client.%s', $name);
            foreach ($data['connections'] as $connectionName) {
                if (empty($config['connections'][$connectionName])) {
                    throw new InvalidConfigurationException(sprintf(
                        'Client "%s" is configured to use connection named "%s" but there is no such connection',
                        $name,
                        $connectionName
                    ));
                }
                $connections[] = $connectionName;
            }
            if (empty($connections)) {
                $connections[] = 'default';
            }

            $definition = class_exists(ChildDefinition::class)
                ? new ChildDefinition('neo4j.client.abstract')
                : new DefinitionDecorator('neo4j.client.abstract');

            $container
                ->setDefinition($serviceId, $definition)
                ->setArguments([$connections]);
        }

        return $serviceIds;
    }

    /**
     * @param array            $config
     * @param ContainerBuilder $container
     * @param array            $clientServiceIds
     *
     * @return array
     */
    private function handleEntityManagers(array &$config, ContainerBuilder $container, array $clientServiceIds): array
    {
        $serviceIds = [];
        foreach ($config['entity_managers'] as $name => $data) {
            $serviceIds[] = $serviceId = sprintf('neo4j.entity_manager.%s', $name);
            $clientName = $data['client'];
            if (empty($clientServiceIds[$clientName])) {
                throw new InvalidConfigurationException(sprintf(
                    'EntityManager "%s" is configured to use client named "%s" but there is no such client',
                    $name,
                    $clientName
                ));
            }

            $definition = class_exists(ChildDefinition::class)
                ? new ChildDefinition('neo4j.entity_manager.abstract')
                : new DefinitionDecorator('neo4j.entity_manager.abstract');

            $container
                ->setDefinition($serviceId, $definition)
                ->setArguments([
                    $container->getDefinition($clientServiceIds[$clientName]),
                    empty($data['cache_dir']) ? $container->getParameter('kernel.cache_dir').'/neo4j' : $data['cache_dir'],
                ]);
        }

        return $serviceIds;
    }

    /**
     * @param array            $config
     * @param ContainerBuilder $container
     *
     * @return array with service ids
     */
    private function handleConnections(array &$config, ContainerBuilder $container): array
    {
        $serviceIds = [];
        $firstName = null;
        foreach ($config['connections'] as $name => $data) {
            if (null === $firstName || 'default' === $name) {
                $firstName = $name;
            }
            $def = new Definition(Connection::class);
            $def->addArgument($name);
            $def->addArgument($this->getUrl($data));
            $serviceIds[$name] = $serviceId = 'neo4j.connection.'.$name;
            $container->setDefinition($serviceId, $def);
        }

        // Make sure we got a 'default'
        if ('default' !== $firstName) {
            $config['connections']['default'] = $config['connections'][$firstName];
        }

        // Add connections to connection manager
        $connectionManager = $container->getDefinition('neo4j.connection_manager');
        foreach ($serviceIds as $name => $serviceId) {
            $connectionManager->addMethodCall('registerExistingConnection', [$name, new Reference($serviceId)]);
        }
        $connectionManager->addMethodCall('setMaster', [$firstName]);

        return $serviceIds;
    }

    /**
     * Get URL form config.
     *
     * @param array $config
     *
     * @return string
     */
    private function getUrl(array $config): string
    {
        if (null !== $config['dsn']) {
            return $config['dsn'];
        }

        return sprintf(
            '%s://%s:%s@%s:%d',
            $config['scheme'],
            $config['username'],
            $config['password'],
            $config['host'],
            $this->getPort($config)
        );
    }

    /**
     * Return the correct default port if not manually set.
     *
     * @param array $config
     *
     * @return int
     */
    private function getPort(array $config)
    {
        if (isset($config['port'])) {
            return $config['port'];
        }

        return 'http' == $config['scheme'] ? HttpDriver::DEFAULT_HTTP_PORT : BoltDriver::DEFAULT_TCP_PORT;
    }

    /**
     * Make sure the EntityManager is installed if we have configured it.
     *
     * @param array &$config
     *
     * @return bool true if "graphaware/neo4j-php-ogm" is installed
     *
     * @thorws \LogicException if EntityManagers os not installed but they are configured.
     */
    private function validateEntityManagers(array &$config): bool
    {
        $dependenciesInstalled = class_exists(EntityManager::class);
        $entityManagersConfigured = !empty($config['entity_managers']);

        if ($dependenciesInstalled && !$entityManagersConfigured) {
            // Add default entity manager if none set.
            $config['entity_managers']['default'] = ['client' => 'default'];
        } elseif (!$dependenciesInstalled && $entityManagersConfigured) {
            throw new \LogicException(
                'You need to install "graphaware/neo4j-php-ogm" to be able to use the EntityManager'
            );
        }

        return $dependenciesInstalled;
    }
}