<?php
/**
 * @link http://psesd.org/
 *
 * @copyright Copyright (c) 2015 Puget Sound ESD
 * @license http://psesd.org/license/
 */

namespace cascade\controllers;

use cascade\components\types\Module as TypeModule;
use cascade\components\types\Relationship;
use cascade\components\web\browser\Response as BrowseResponse;
use cascade\components\web\ObjectViewEvent;
use cascade\models\DeleteForm;
use cascade\models\ObjectFamiliarity;
use cascade\models\Registry;
use cascade\models\Relation;
use canis\base\exceptions\HttpException;
use canis\helpers\ArrayHelper;
use canis\web\Controller;
use Yii;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;

/**
 * ObjectController [[@doctodo class_description:cascade\controllers\ObjectController]].
 *
 * @author Jacob Morrison <email@ofjacob.com>
 */
class ObjectController extends Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                    [
                        'allow' => false,
                        'roles' => ['?'],
                    ],
                ],
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'index' => ['get'],
                    'view' => ['get'],
                    'create' => ['get', 'post'],
                    'setPrimary' => ['get'],
                    'link' => ['get', 'post'],
                    'setPrimary' => ['get'],
                    'update' => ['get', 'post'],
                    'updateField' => ['post'],
                    'delete' => ['get', 'post'],
                    'watch' => ['get'],
                    'unwatch' => ['get'],
                    'widget' => ['get', 'post'],
                    'unwatch' => ['get'],
                    'search' => ['get'],
                    'profile' => ['get'],
                    'browseHierarchy' => ['get'],
                    'activity' => ['get'],
                ],
            ],
        ];
    }

    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\\web\\ErrorAction',
            ],
        ];
    }

    /**
     * [[@doctodo method_description:actionBrowse]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionBrowse()
    {
        if (!isset($_GET['type'])
            || !($this->params['typeItem'] = $typeItem = Yii::$app->collectors['types']->getOne($_GET['type']))
            || !($this->params['type'] = $typeItem->object)) {
            throw new HttpException(400, "Invalid object type");
        }
        Yii::$app->response->view = 'browse';
    }

    /**
     * [[@doctodo method_description:actionBrowseHierarchy]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     * @return [[@doctodo return_type:actionBrowseHierarchy]] [[@doctodo return_description:actionBrowseHierarchy]]
     *
     */
    public function actionBrowseHierarchy()
    {
        if (!Yii::$app->request->isAjax) {
            throw new HttpException(400, "Bad Request");
        }
        $package = [];
        $defaultParams = [];
        $requestParams = array_merge($defaultParams, $_GET);

        if (empty($requestParams['requests'])) {
            Yii::$app->response->data = $package;

            return;
        }

        if (isset($requestParams['modules']) && !is_array($requestParams['modules'])) {
            $requestParams['modules'] = implode(',', $requestParams['modules']);
        }

        $modules = isset($requestParams['modules']) ? (array) $requestParams['modules'] : false;
        $baseInstructions = ['ignore' => [], 'ignoreChildren' => [], 'ignoreParents' => []];
        if (!empty($searchParams['ignore'])) {
            $baseInstructions['ignore'] = (array) $searchParams['ignore'];
        }
        if (!empty($searchParams['ignoreChildren'])) {
            $baseInstructions['ignoreChildren'] = (array) $searchParams['ignoreChildren'];
        }
        if (!empty($searchParams['ignoreParents'])) {
            $baseInstructions['ignoreParents'] = (array) $searchParams['ignoreParents'];
        }
        $baseInstructions['modules'] = $modules;
        $requests = [];
        foreach ($requestParams['requests'] as $requestId => $request) {
            if (isset($request['task']) && !isset($request['handler'])) {
                // we're doing something new
                switch ($request['task']) {
                    case 'root':
                        $request['handler'] = 'types';
                    break;
                    case 'stack':
                        $request = BrowseResponse::parseStack(array_merge($baseInstructions, $request));
                    break;
                }
            }

            if ($request && isset($request['handler'])) {
                $requests[$requestId] = $request;
            }
        }
        $package = BrowseResponse::handleRequests($requests, $baseInstructions, true)->package();
        Yii::$app->response->data = $package;
    }

    /**
     * [[@doctodo method_description:actionSearch]].
     *
     * @return [[@doctodo return_type:actionSearch]] [[@doctodo return_description:actionSearch]]
     */
    public function actionSearch()
    {
        $package = [];
        $defaultParams = ['typeFilters' => []];
        $searchParams = array_merge($defaultParams, $_GET);

        if (empty($searchParams['term'])) {
            Yii::$app->response->data = $package;
            return;
        }

        if (isset($searchParams['modules']) && !is_array($searchParams['modules'])) {
            $searchParams['modules'] = implode(',', $searchParams['modules']);
        }

        $modules = isset($searchParams['modules']) ? (array) $searchParams['modules'] : array_keys(Yii::$app->collectors['types']->getAll());
        $params = ['ignore' => [], 'ignoreChildren' => [], 'ignoreParents' => []];
        if (!empty($searchParams['ignore'])) {
            $params['ignore'] = (array) $searchParams['ignore'];
        }
        if (!empty($searchParams['ignoreChildren'])) {
            $params['ignoreChildren'] = (array) $searchParams['ignoreChildren'];
        }
        if (!empty($searchParams['ignoreParents'])) {
            $params['ignoreParents'] = (array) $searchParams['ignoreParents'];
        }
        $params['action'] = 'read';
        $params['modules'] = $modules;
        $term = $searchParams['term'];
        $params['limit'] = 8;
        $scores = [];
        foreach ($modules as $module) {
            $moduleItem = Yii::$app->collectors['types']->getOne($module);
            if (!$moduleItem || !($moduleObject = $moduleItem->object)) {
                continue;
            }
            if (!(isset($searchParams['modules']) && in_array($moduleItem->systemId, $searchParams['modules'])) && empty($moduleItem->object->searchWeight)) {
                continue;
            }
            if (in_array('authority', $searchParams['typeFilters']) && $moduleItem->object->getBehavior('Authority') === null) {
                continue;
            }
            if (in_array('dashboard', $searchParams['typeFilters']) && !$moduleItem->object->hasDashboard) {
                continue;
            }
            $moduleResults = $moduleObject->search($term, $params);
            foreach ($moduleResults as $r) {
                $package[] = $r;
            }
        }
        ArrayHelper::multisort($package, 'scoreSort', SORT_DESC);
        foreach (array_slice($package, 0, 30) as $key => $result) {
            $package[$key] = $result->toArray();
        }
        Yii::$app->response->data = $package;
    }

    /**
     * [[@doctodo method_description:actionPhoto]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionPhoto()
    {
        if (empty($_GET['id']) || !($object = $this->params['object'] = Registry::getObject($_GET['id'], false)) || !($typeItem = $this->params['typeItem'] = $object->objectTypeItem)) {
            throw new HttpException(404, "Unknown object.");
        }
        if (!$object->can('read')) {
            throw new HttpException(403, "Unable to access object.");
        }
        Yii::$app->request->object = $object;

        if ($object->getBehavior('Photo') === null) {
            throw new HttpException(404, "No profile photo available (A)");
        }
        $options = [];
        if (isset($_GET['size'])) {
            $options['width'] = $_GET['size'];
            $options['height'] = $_GET['size'];
        }
        if (isset($_GET['width'])) {
            $options['width'] = $_GET['width'];
        }
        if (isset($_GET['height'])) {
            $options['height'] = $_GET['height'];
        }
        if (isset($_GET['rotate'])) {
            $options['rotate'] = $_GET['rotate'];
        }
        if (!$object->serve($options)) {
            throw new HttpException(404, "No profile photo available (B)");
        }
    }

    /**
     * [[@doctodo method_description:actionView]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     * @return [[@doctodo return_type:actionView]] [[@doctodo return_description:actionView]]
     *
     */
    public function actionView()
    {
        if (empty($_GET['id']) || !($object = $this->params['object'] = Registry::getObject($_GET['id'], false)) || !($typeItem = $this->params['typeItem'] = $object->objectTypeItem)) {
            throw new HttpException(404, "Unknown object.");
        }
        if (!$object->can('read')) {
            throw new HttpException(403, "Unable to access object.");
        }
        $action = isset($_GET['subaction']) ? $_GET['subaction'] : 'view';
        Yii::$app->request->object = $object;
        $object->loadChildParentIds();
        $type = $this->params['type'] = $object->objectType;
        $viewEvent = new ObjectViewEvent(['object' => $object, 'action' => $action]);
        $type->trigger(TypeModule::EVENT_VIEW_OBJECT, $viewEvent);
        //Yii::$app->collectors['widgets']->lazy = true;

        if ($viewEvent->handled) {
            if ($viewEvent->accessed) {
                ObjectFamiliarity::accessed($object);
            }

            return;
        }

        if (empty($_GET['h']) or !($relatedObject = $this->params['relatedObject'] = Registry::getObject($_GET['h'], false)) or !($relatedTypeItem = $this->params['relatedTypeItem'] = $relatedObject->objectTypeItem)) {
            $relatedObject = null;
        } elseif (!$object->can('read')) {
            $relatedObject = null;
        }

        if (!$type->hasDashboard) {
            $relatedObjectOptions = [];
            $relatedObjects = $object->queryRelations(false)->all();
            foreach ($relatedObjects as $relation) {
                if ($relation->child_object_id === $object->primaryKey) {
                    $relatedTest = Registry::getObject($relation->parent_object_id, false);
                } else {
                    $relatedTest = Registry::getObject($relation->child_object_id, false);
                }
                if (!$relatedTest || !$relatedTest->objectType->hasDashboard || !$relatedTest->can('read')) {
                    continue;
                }
                $relatedObjectOptions[$relatedTest->primaryKey] = ['descriptor' => $relatedTest->descriptor, 'url' => $relatedTest->getUrl('view', ['h' => $object->primaryKey], false)];
            }
            if (isset($relatedObject) && isset($relatedObjectOptions[$relatedObject->primaryKey])) {
                $this->redirect($relatedObjectOptions[$relatedObject->primaryKey]['url']);

                return;
            } elseif (sizeof($relatedObjectOptions) === 1) {
                $relatedObject = array_pop($relatedObjectOptions);
                $this->redirect($relatedObject['url']);

                return;
            } else {
                $this->params['options'] = $relatedObjectOptions;
                Yii::$app->response->view = 'viewOptions';

                return;
            }
            throw new HttpException(400, "Bad request");
        }
        $this->params['highlight'] = $relatedObject;

        ObjectFamiliarity::accessed($object);
        Yii::$app->response->view = 'view';

        $sections = $this->params['sections'] = $typeItem->getSections($object);
        $this->params['active'] = $this->params['default'] = null;
        foreach ($sections as $section) {
            if ($section->priority > 0) {
                $this->params['active'] = $this->params['default'] = $section->systemId;
                break;
            }
        }
        if (!empty($_GET['section'])) {
            $this->params['active'] = $_GET['section'];
        }
    }

    /**
     * [[@doctodo method_description:actionActivity]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionActivity()
    {
        if (empty($_GET['id']) || !($object = $this->params['object'] = Registry::getObject($_GET['id'], false)) || !($typeItem = $this->params['typeItem'] = $object->objectTypeItem)) {
            throw new HttpException(404, "Unknown object.");
        }
        if (!$object->can('read')) {
            throw new HttpException(403, "Unable to access object.");
        }
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = ['title' => 'Activity for ' . $object->descriptor , 'width' => '100%', 'isForm' => false];
        Yii::$app->response->view = 'activity';
        Yii::$app->request->object = $object;
    }

    /**
     * [[@doctodo method_description:_checkParams]].
     *
     * @param [[@doctodo param_type:params]] $params   [[@doctodo param_description:params]]
     * @param array                          $required [[@doctodo param_description:required]] [optional]
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     * @return [[@doctodo return_type:_checkParams]] [[@doctodo return_description:_checkParams]]
     *
     */
    public function _checkParams($params, $required = [])
    {
        if (in_array('type', $required) && isset($params['type'])) {
            if (!$params['type']->hasDashboard) {
                $required[] = 'relatedObject';
                $required[] = 'relationship';
                $required[] = 'relatedType';
                $required[] = 'relationshipRole';
            }
        }

        $required = array_unique($required);
        foreach ($required as $r) {
            if (empty($params[$r])) {
                throw new HttpException(400, "Invalid request ({$r} is required)");
            }
        }

        return true;
    }

    /**
     * [[@doctodo method_description:_parseParams]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     * @return [[@doctodo return_type:_parseParams]] [[@doctodo return_description:_parseParams]]
     *
     */
    public function _parseParams()
    {
        $paramSource = $_GET;
        $p = [];

        // primary object
        $p['objectId'] = ArrayHelper::getValue($paramSource, 'id', false);
        if ($p['objectId']) {
            $p['object'] = Registry::getObject($p['objectId'], false);
            if (empty($p['object'])) {
                throw new HttpException(404, "Unknown object '{$p['objectId']}.");
            }
        }

        // object type
        $p['typeName'] = ArrayHelper::getValue($paramSource, 'type', false);
        if ($p['typeName']) {
            $p['typeItem'] = Yii::$app->collectors['types']->getOne($p['typeName']);
            if (isset($p['typeItem']->object)) {
                $p['type'] = $p['typeItem']->object;
            }
        } elseif (isset($p['object'])) {
            $p['typeItem'] = $p['object']->objectTypeItem;
            $p['type'] = $p['object']->objectType;
        }

        if (empty($p['type'])) {
            throw new HttpException(404, "Unknown object type.");
        }

        // related object
        $p['relatedObjectId'] = ArrayHelper::getValue($paramSource, 'related_object_id', false);
        if ($p['relatedObjectId']) {
            $p['relatedObject'] = Registry::getObject($p['relatedObjectId'], false);
            if (empty($p['relatedObject'])) {
                throw new HttpException(404, "Unknown related object.");
            }
            $p['relatedType'] = $p['relatedObject']->objectType;
        }

        // relation
        $p['objectRelationName'] = ArrayHelper::getValue($paramSource, 'object_relation', false);
        if ($p['objectRelationName'] && isset($p['relatedType'])) {
            list($p['relationship'], $p['relationshipRole']) = $p['relatedType']->getRelationship($p['objectRelationName']);
            if (!empty($p['relationship']) && !empty($p['relatedObject']) && !empty($p['object'])) {
                if ($p['relationshipRole'] === 'child') {
                    $p['parentObject'] = $p['relatedObject'];
                    $p['childObject'] = $p['object'];
                } else {
                    $p['parentObject'] = $p['object'];
                    $p['childObject'] = $p['relatedObject'];
                }
                $p['relation'] = $p['relationship']->getModel($p['parentObject'], $p['childObject']);
            }
            if (empty($p['relationship'])) {
                throw new HttpException(404, "Unknown type relationship {$p['objectRelationName']}");
            }
        }

        return $p;
    }

    /**
     * [[@doctodo method_description:actionCreate]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionCreate()
    {
        $p = $this->_parseParams();
        $this->_checkParams($p, ['type']);
        $this->params = &$p;

        if (!Yii::$app->gk->canGeneral('create', $p['type']->primaryModel)) {
            throw new HttpException(403, "You do not have access to create {$p['type']->title->getPlural(true)}");
        }

        Yii::$app->response->view = 'create';
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = ['title' => 'Create ' . $p['type']->title->getSingular(true) , 'width' => '800px'];

        $p['primaryModel'] = $p['type']->getModel();
        $relations = [];
        if (isset($p['relatedObject'])) {
            $fields = $p['primaryModel']->getFields();
            $companionNiceId = $p['relationship']->getCompanionNiceId($p['relationshipRole']);
            if (isset($fields[$companionNiceId])) {
                if ($p['relationshipRole'] === 'child') {
                    $relationField = 'parent_object_id';
                } else {
                    $relationField = 'child_object_id';
                }
                $p['primaryModel']->setIndirectObject($p['relatedObject']);
                $fields[$companionNiceId]->model->{$relationField} = $p['relatedObject']->primaryKey;
                $relations[$fields[$companionNiceId]->model->tabularId] = $fields[$companionNiceId]->model;
            } else {
                // \d(array_keys($fields));
                // \d($p['objectRelationName']);
                throw new HttpException(403, "Invalid relationship!");
            }
        }

        $p['primaryModel']->setRelationModels($relations);
        if (!empty($_POST)) {
            $p['primaryModel']->load($_POST);
            if ($p['primaryModel']->getBehavior('Storage') !== null) {
                $p['primaryModel']->loadPostFile();
            }
            if (!$p['primaryModel']->save()) {
                Yii::$app->response->error = 'Unable to create object!';
            } else {
                Yii::$app->response->task = 'status';
                Yii::$app->response->success = '<em>' . $p['primaryModel']->descriptor . '</em> was created successfully.';
                if (isset($p['relatedType'])) {
                    $primaryModelClass = get_class($p['primaryModel']);
                    Yii::$app->response->trigger = [
                        ['refresh', '.model-' . $primaryModelClass::baseClassName()],
                    ];
                } else {
                    Yii::$app->response->redirect = $p['primaryModel']->getUrl('view');
                }
            }
        }

        if (!($p['form'] = $p['type']->getForm($p['primaryModel'], ['relationSettings' => false]))) {
            throw new HttpException(403, "There is nothing to create for {$p['type']->title->getPlural(true)}");
        }
        $p['form']->ajax = true;
    }

    /**
     * [[@doctodo method_description:actionUpdate]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionUpdate()
    {
        $p = $this->_parseParams();
        $this->_checkParams($p, ['type', 'object']);
        $this->params = &$p;

        if (!$p['object']->can('update')) {
            throw new HttpException(403, "You do not have access to update the {$p['type']->title->getPlural(true)} '{$p['object']->descriptor}'");
        }

        if (isset($p['relatedObject']) && !$p['relatedObject']->can('update')) {
            throw new HttpException(403, "You do not have access to update '{$p['relatedObject']->descriptor}'");
        }

        Yii::$app->response->view = 'create';
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = ['title' => 'Update ' . $p['type']->title->getSingular(true) , 'width' => '800px'];

        $p['primaryModel'] = $p['type']->getModel($p['object']);
        if (isset($p['relatedObject'])) {
            $p['primaryModel']->setIndirectObject($p['relatedObject']);
        }
        if (!empty($_POST)) {
            $p['primaryModel']->load($_POST);
            if ($p['primaryModel']->getBehavior('Storage') !== null) {
                $p['primaryModel']->loadPostFile();
            }
            if (!$p['primaryModel']->save()) {
                // \d($p['primaryModel']->errors);
                Yii::$app->response->error = 'Unable to update object!';
            } else {
                Yii::$app->response->task = 'status';
                Yii::$app->response->success = '<em>' . $p['primaryModel']->descriptor . '</em> was updated successfully.';
                if (isset($p['relatedType'])) {
                    $primaryModelClass = get_class($p['primaryModel']);
                    Yii::$app->response->trigger = [
                        ['refresh', '.model-' . $primaryModelClass::baseClassName()],
                    ];
                } else {
                    Yii::$app->response->redirect = $p['primaryModel']->getUrl('view');
                }
            }
        }

        if (!($p['form'] = $p['type']->getForm($p['primaryModel'], ['relationSettings' => false]))) {
            throw new HttpException(403, "There is nothing to update for {$p['type']->title->getPlural(true)}");
        }
        $p['form']->ajax = true;
    }

    /**
     * [[@doctodo method_description:actionSetPrimary]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionSetPrimary()
    {
        $p = $this->_parseParams();
        $this->_checkParams($p, ['type', 'object', 'relation']);
        $this->params = &$p;

        if (!$p['object']->can('update')) {
            throw new HttpException(403, "You do not have access to update the {$p['type']->title->getPlural(true)} '{$p['object']->descriptor}'");
        }

        Yii::$app->response->view = false;
        Yii::$app->response->task = 'status';

        if (!$p['relation']->setPrimary($p['relationshipRole'])) {
            Yii::$app->response->error = 'Unable to set relationship as primary!';
        } else {
            Yii::$app->response->task = 'status';
            Yii::$app->response->success = '<em>' . $p['object']->descriptor . '</em> was set as primary!';

            $primaryModelClass = get_class($p['object']);
            Yii::$app->response->trigger = [
                ['refresh', '.model-' . $primaryModelClass::baseClassName()],
            ];
        }
    }

    /**
     * [[@doctodo method_description:actionLink]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionLink()
    {
        $p = $this->_parseParams();
        $this->_checkParams($p, ['type', 'relatedObject', 'relatedType']);
        $this->params = &$p;

        if (isset($p['relatedObject']) && !$p['relatedObject']->can('update')) {
            throw new HttpException(403, "You do not have access to update '{$p['relatedObject']->descriptor}'");
        }

        Yii::$app->response->view = 'create';
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = ['title' => 'Link ' . $p['relatedType']->title->getSingular(true) , 'width' => '800px'];

        $p['primaryModel'] = $p['relatedType']->getModel($p['relatedObject']);
        $relations = [];
        $relationSettings = ['template' => 'hierarchy'];
        if (isset($p['relatedObject'])) {
            $fields = $p['primaryModel']->getFields();
            $niceId = $p['relationship']->getNiceId($p['relationshipRole']);
            if (isset($fields[$niceId])) {
                if ($p['relationshipRole'] === 'child') {
                    $relationField = 'parent_object_id';
                } else {
                    $relationField = 'child_object_id';
                }
                if (isset($p['relation'])) {
                    $tabularId = $fields[$niceId]->model->tabularId;
                    $handler = $fields[$niceId]->model->_moduleHandler;
                    $fields[$niceId]->model = $p['relation'];
                    $relationSettings['lockFields'] = ['object_id'];
                    $fields[$niceId]->model->tabularId = $handler;
                    $fields[$niceId]->model->_moduleHandler = $handler;
                } else {
                    $fields[$niceId]->resetModel();
                    $fields[$niceId]->model->{$relationField} = $p['relatedObject']->primaryKey;
                }
                $relations[$fields[$niceId]->model->tabularId] = $fields[$niceId]->model;
            } else {
                throw new HttpException(403, "Invalid relationship!");
            }
        }

        $p['primaryModel']->setRelationModels($relations);
        if (!empty($_POST)) {
            $p['primaryModel']->load($_POST);
            if (!$p['primaryModel']->save()) {
                Yii::$app->response->error = 'Unable to create object!';
            } else {
                Yii::$app->response->task = 'status';
                Yii::$app->response->success = '<em>' . $p['primaryModel']->descriptor . '</em> was created successfully.';
                if (isset($p['relatedType'])) {
                    $primaryModelClass = $p['type']->primaryModel;
                    Yii::$app->response->trigger = [
                        ['refresh', '.model-' . $primaryModelClass::baseClassName()],
                    ];
                } else {
                    Yii::$app->response->redirect = $p['primaryModel']->getUrl('view');
                }
            }
        }
        if (!($p['form'] = $p['relatedType']->getForm($p['primaryModel'], ['relationSettings' => $relationSettings, 'subform' => $p['objectRelationName']]))) {
            throw new HttpException(403, "There is nothing to create for {$p['type']->title->getPlural(true)}");
        }
        $p['form']->ajax = true;
    }

    /**
     * [[@doctodo method_description:actionUpdateField]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionUpdateField()
    {
        Yii::$app->response->task = 'status';
        if (empty($_POST['attribute']) || empty($_POST['object']) || !($object = $this->params['object'] = Registry::getObject($_POST['object'], false)) || !($typeItem = $this->params['typeItem'] = $object->objectTypeItem)) {
            throw new HttpException(404, "Unknown object.");
        }
        $relatedObject = false;
        if (!empty($_POST['relatedObject']) && (!($relatedObject = $this->params['relatedObject'] = Registry::getObject($_POST['relatedObject'], false)) || !($relatedTypeItem = $this->params['typeItem'] = $relatedObject->objectTypeItem))) {
            throw new HttpException(404, "Unknown related object.");
        }
        if (!$object->can('update')) {
            throw new HttpException(403, "Unable to update object.");
        }
        if (in_array($_POST['attribute'], ['id', 'created', 'created_user_id', 'modified', 'modified_user_id', 'archived', 'archived_user_id'])) {
            throw new HttpException(403, "Invalid attribute!");
        }
        $object->attributes = [$_POST['attribute'] => $_POST['value']];
        if ($relatedObject) {
            $object->indirectObject = $relatedObject;
        }
        if ($object->save()) {
            Yii::$app->response->success = $object->descriptor . ' was updated';
        } else {
            Yii::$app->response->error = 'Unable to update ' . $object->descriptor;
        }
    }

    /**
     * [[@doctodo method_description:actionAccess]].
     */
    public function actionAccess()
    {
        $subform = null;
        $p = $this->_parseParams();
        $this->_checkParams($p, ['object', 'type']);
        $this->params = &$p;

        $primaryModel = $p['type']->primaryModel;
        $this->params['errors'] = [];
        Yii::$app->response->view = 'access';
        $taskOptions = ['title' => 'Access for ' . $p['type']->title->getSingular(true)];
        $lookAtPost = false;
        if ($p['object']->can('manageAccess')) {
            $lookAtPost = true;
            $taskOptions['title'] = 'Manage ' . $taskOptions['title'];
            $taskOptions['isForm'] = false;
        }
        $this->params['access'] = $access = $p['object']->objectAccess;
        $this->params['disableFields'] = !$lookAtPost;
        $taskOptions['isForm'] = $lookAtPost;
        $objectRoles = $access->roleObjects;
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = $taskOptions;
        if ($lookAtPost && !empty($_POST['roles'])) {
            $result = $access->save($_POST['roles']);
            if (!empty($result['errors'])) {
                if (is_array($result['errors'])) {
                    $this->params['errors'] = $result['errors'];
                    Yii::$app->response->error = 'An error occurred while saving the object\'s sharing settings.';
                } else {
                    Yii::$app->response->error = $result['errors'];
                }
                foreach ($result['data'] as $requestorId => $roleId) {
                    $objectRole = $access->getRoleObject($requestorId, $roleId);
                    if (!isset($objectRoles[$requestorId])
                            || (isset($objectRoles[$requestorId]['role'])
                                    && $objectRoles[$requestorId]['role']->object->primaryKey !== $roleId)
                    ) {
                        $objectRoles[$requestorId] = $objectRole;
                    }
                }
            } else {
                Yii::$app->response->task = 'status';
                Yii::$app->response->success = 'Access has been updated.';
                if (empty($p['relatedObject'])) {
                    Yii::$app->response->refresh = true;
                } else {
                    $primaryModel = $p['type']->primaryModel;
                    Yii::$app->response->trigger = [
                        ['refresh', '.model-' . $primaryModel::baseClassName()],
                    ];
                }
            }
        }
        $this->params['objectRoles'] = $objectRoles;
    }

    /**
     * [[@doctodo method_description:actionDelete]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionDelete()
    {
        $subform = null;
        $p = $this->_parseParams();
        $this->_checkParams($p, ['object', 'type']);
        $this->params = &$p;
        $primaryModel = $p['type']->primaryModel;

        $this->params['model'] = new DeleteForm();
        if (isset($p['relation'])) {
            $this->params['model']->object = $p['object'];
            $this->params['model']->relationship = $p['relationship'];
            $this->params['model']->relationModel = $p['relation'];
            $this->params['model']->relationshipWith = $p['relatedObject'];
            $this->params['model']->object->indirectObject = $p['relatedObject'];
            $primaryObject = $p['object'];
        } else {
            $primaryObject = $p['object'];
            $this->params['model']->object = $p['object'];
        }

        if (empty($this->params['model']->possibleTargets)) {
            throw new HttpException(403, "You are not authorized to perform this action.");
        }

        Yii::$app->response->view = 'delete';
        Yii::$app->response->task = 'dialog';
        Yii::$app->response->taskOptions = ['title' => 'Delete ' . $p['type']->title->getSingular(true) , 'isConfirmDeletion' => true];

        if (!empty($_POST['DeleteForm'])) {
            Yii::$app->response->task = 'status';
            $targetDescriptor = $this->params['model']->targetDescriptor;
            $this->params['model']->attributes = $_POST['DeleteForm'];
            if (!$this->params['model']->handle()) {
                Yii::$app->response->error =  'Could not ' . $this->params['model']->targetLabel['long'];
            } else {
                Yii::$app->response->success = ucfirst($this->params['model']->targetLabel['past']) . '.';
                if (isset($this->params['model']->targetLabel['response']) && empty($p['relation'])) {
                    switch ($this->params['model']->targetLabel['response']) {
                        case 'home':
                            Yii::$app->response->redirect = '/';
                        break;
                        case 'refresh':
                            Yii::$app->response->refresh = true;
                        break;
                    }
                } else {
                    Yii::$app->response->trigger = [
                        ['refresh', '.model-' . $p['object']::baseClassName()],
                    ];
                }
            }
        }
    }

    /**
     * [[@doctodo method_description:actionWatch]].
     *
     * @throws HttpException [[@doctodo exception_description:HttpException]]
     */
    public function actionWatch()
    {
        if (empty($_GET['id']) or !($object = $this->params['object'] = Registry::getObject($_GET['id'], false)) or !($typeItem = $this->params['typeItem'] = $object->objectTypeItem)) {
            throw new HttpException(404, "Unknown object.");
        }
        if (!$object->can('read')) {
            throw new HttpException(403, "Unable to access object.");
        }
        Yii::$app->request->object = $object;

        $watching = empty($_GET['stop']);
        if ($object->watch($watching)) {
            Yii::$app->response->task = 'trigger';
            if ($watching) {
                Yii::$app->response->success = 'You are now watching ' . $object->descriptor . '.';
                Yii::$app->response->trigger = [
                    ['startedWatching'],
                ];
            } else {
                Yii::$app->response->success = 'You stopped watching ' . $object->descriptor . '.';
                Yii::$app->response->trigger = [
                    ['stoppedWatching'],
                ];
            }
        } else {
            Yii::$app->response->error = 'Unable update the watching status of this object.';
        }
    }

    /**
     * [[@doctodo method_description:actionWidget]].
     */
    public function actionWidget()
    {
        $package = [];
        $renderWidgets = [];
        if (!empty($_POST['widgets'])) {
            $renderWidgets = $_POST['widgets'];
            $baseState = ['fetch' => 0];
        } elseif (!empty($_GET['widgets'])) {
            $renderWidgets = $_GET['widgets'];
            $baseState = ['fetch' => 1];
            ob_start();
            ob_implicit_flush(false);
        }
        $sectionCount = count($renderWidgets);
        if (isset($_GET['sectionCount'])) {
            $sectionCount = (int) $_GET['sectionCount'];
        }
        if (isset($_POST['sectionCount'])) {
            $sectionCount = (int) $_POST['sectionCount'];
        }
        if (!empty($renderWidgets)) {
            foreach ($renderWidgets as $i => $widget) {
                $w = [];
                if (empty($widget['state'])) {
                    $widget['state'] = [];
                }
                if (empty($widget['data'])) {
                    $widget['data'] = [];
                }
                if (!isset($widget['data']['sectionCount'])) {
                    $widget['data']['sectionCount'] = $sectionCount;
                }
                $w['rendered'] = Yii::$app->widgetEngine->build($widget['name'], $widget['data'], [], array_merge($baseState, $widget['state']));
                $w['id'] =  Yii::$app->widgetEngine->lastBuildId;
                $package[$i] = $w;
            }
        }
        //sleep(3);
        $this->params['widgets'] = $package;
        //var_dump($package);exit;
        $this->json();
    }
}


<?php
/**
 * This file is part of the RedKite CMS Application and it is distributed
 * under the GPL LICENSE Version 2.0. To use this application you must leave
 * intact this copyright notice.
 *
 * Copyright (c) RedKite Labs <info@redkite-labs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * For extra documentation and help please visit http://www.redkite-labs.com
 *
 * @license    GPL LICENSE Version 2.0
 *
 */

namespace RedKiteCms\Rendering\Controller\Cms;

use RedKiteCms\FilesystemEntity\Page;
use RedKiteCms\Rendering\Controller\BaseController;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class FrontendController is the object assigned to implement the action to render the CMS frontend
 *
 * @author  RedKite Labs <webmaster@redkite-labs.com>
 * @package RedKiteCms\Rendering\Controller\Cms
 */
abstract class FrontendController extends BaseController
{
    /**
     * @type \RedKiteCms\Configuration\ConfigurationHandler
     */
    protected $configuration;

    /**
     * Implements the action to render the CMS frontend
     * @param array $options
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function show(array $options)
    {
        $resolver = new OptionsResolver();
        $this->configureOptions($resolver);
        $this->options = $resolver->resolve($options);

        $this->configuration = $this->options["red_kite_cms_config"];
        $this->options["template_assets"]->boot('prod');
        $page = $this->renderPage();
        $template = $this->fetchTemplate($page);
        $params = $this->configureRendererOptions($page);

        return $options["twig"]->render($template, $params);
    }

    /**
     * Configures the options for the resolver
     * @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
     */
    protected function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired(
            array(
                'username',
                'root_dir',
                'request',
                'twig',
                'red_kite_cms_config',
                'plugin_manager',
                'page',
                'template_assets',
                'page_renderer',
            )
        );

        $resolver->setAllowedTypes(
            array(
                'username' => array(
                    'string',
                    'null'
                ),
                'root_dir' => 'string',
                'request' => '\Symfony\Component\HttpFoundation\Request',
                'twig' => '\Twig_Environment',
                'red_kite_cms_config' => '\RedKiteCms\Configuration\ConfigurationHandler',
                'plugin_manager' => '\RedKiteCms\Plugin\PluginManager',
                'page' => '\RedKiteCms\FilesystemEntity\Page',
                'template_assets' => '\RedKiteCms\Rendering\TemplateAssetsManager\TemplateAssetsManager'
            )
        );
    }

    /**
     * Renders the page
     *
     * @return \RedKiteCms\FilesystemEntity\Page
     */
    protected function renderPage()
    {
        $page = $this->options["page"];
        $request = $this->options["request"];
        $username = $this->options["username"];
        $pageOptions = array(
            'page' => $request->get('page'),
            'language' => $request->get('_locale'),
            'country' => $request->get('country'),
        );
        $page->render($this->configuration->siteDir(), $pageOptions, $username);

        return $page;
    }

    /**
     * Returns the template to render
     * @param \RedKiteCms\Rendering\Controller\Cms\Page $page
     *
     * @return string
     */
    protected function fetchTemplate(Page $page)
    {
        $themeName = $this->configuration->theme();
        $pageAttributes = $page->getPageAttributes();
        $pageName = $pageAttributes["template"];

        return sprintf('%s/Resources/views/%s.html.twig', $themeName, $pageName);
    }

    /**
     * Configures the backend options
     * @param \RedKiteCms\FilesystemEntity\Page $page
     *
     * @return array
     */
    protected function configureRendererOptions(Page $page)
    {
        $slots = $this->renderSlots($page);
        $baseTemplate = $this->configuration->baseTemplate();
        $templateAssetsManager = $this->initTemplateAssetsManager();
        $seo = $page->getSeoAttributes();

        return array_merge(
            array(
                'page' => $page->getPageName(),
                'language' => $page->getLanguage(),
                'country' => $page->getCountry(),
                'metatitle' => $seo["title"],
                'metadescription' => $seo["description"],
                'metakeywords' => $seo["keywords"],
                'base_template' => $baseTemplate,
                'template_assets_manager' => $templateAssetsManager,
            ),
            $slots
        );
    }

    protected function renderSlots(Page $page)
    {
        $this->options['page_renderer']->render($page);

        return array(
            "slots" => $this->options['page_renderer']->getSlots(),
        );
    }

    protected function initTemplateAssetsManager()
    {
        $templateAssetsManager = $this->options["template_assets"];
        $pluginManager = $this->options["plugin_manager"];
        $templateAssetsManager
            ->frontend()
            ->add($pluginManager->getAssets())
        ;

        return $templateAssetsManager;
    }
}


<?php

namespace Gaufrette\Core\Operator;

use Gaufrette\Core\Adapter;
use Gaufrette\Core\File;

final class ContentOperator extends AbstractOperator implements CanLoad, CanSave
{
    /**
     * {@inheritdoc}
     */
    public function supports(File $file, Adapter $adapter)
    {
        return $this->adapterHasBehavior($adapter, 'Gaufrette\Core\Adapter\KnowsContent');
    }

    /**
     * {@inheritdoc}
     */
    public function load(File $file, Adapter $adapter)
    {
        $file->setContent($adapter->readContent($file->getName()));
    }

    /**
     * {@inheritdoc}
     */
    public function save(File $file, Adapter $adapter)
    {
        $adapter->writeContent($file->getName(), $file->getContent());
    }
}


<?php
namespace Lab123\Odin\Traits;

trait FieldsRules
{

    /**
     * The params attributes.
     *
     * @var array
     */
    protected $fields = [];

    /**
     * Convert the model's attributes to an array.
     *
     * @return array
     */
    public function attributesToArray()
    {
        $array = parent::attributesToArray();

        return $this->transform($array);
    }

    /**
     * Transform attributes model.
     *
     * @return array
     */
    public function transform(array $array)
    {
        $transformation = $this->getTransformation();
        $transformed = [];

        if (key_exists('public_id', $array)) {
            $transformed['id'] = $array['public_id'];
        }

        foreach ($transformation as $name => $new_name) {
            if (! key_exists($name, $array)) {
                continue;
            }

            $transformed[$new_name] = $array[$name];
        }

        /*$transformed = array(
            'uri' => $this->getResourceData()
        ) + $transformed;*/

        return $transformed;
    }

    /**
     * Transform Front to Model
     *
     * @return array
     */
    public function transformFromFront(array $array)
    {
        $transformation = $this->getTransformation();
        $fillables = $this->getFillable();
        $transformed = [];

        /* Add fillables to array transformed */
        foreach ($fillables as $name) {
            if (! key_exists($name, $array)) {
                continue;
            }

            $transformed[$name] = $array[$name];
        }

        /* Add transform fields to array transformed */
        foreach ($transformation as $name => $new_name) {
            if (! key_exists($new_name, $array)) {
                continue;
            }

            $transformed[$name] = $array[$new_name];
        }

        return $transformed;
    }

    /**
     * Get the rules attributes for the model.
     *
     * @return array
     */
    public function getRules()
    {
        $rules = [];
        foreach ($this->fields as $field => $extra) {

            if (is_int($field)) {
                continue;
            }
            $rule = (key_exists('rules', $extra)) ? $extra['rules'] : '';

            $rules[$field] = $rule;
        }

        return $rules;
    }

    /**
     * Get the transformation attributes for the model.
     *
     * @return array
     */
    public function getTransformation()
    {
        $transforms = [];
        foreach ($this->fields as $field => $extra) {
            if (is_int($field)) {
                $transforms[$extra] = $extra;
                continue;
            }

            $transform = (key_exists('transform', $extra)) ? $extra['transform'] : $field;

            if ($transform === false) {
                continue;
            }

            $transforms[$field] = $transform;
        }

        return $transforms;
    }
}


<?php namespace SKAgarwal\Reflection;

use ReflectionClass;
use SKAgarwal\Reflection\Exceptions\NotFoundException;

/**
 * For easy testing of provate\protected methods
 * For easy testing of provate\protected properties
 *
 * trait ReflectableTrait
 *
 * @package Tests\Traits
 */
trait ReflectableTrait
{
    /**
     * @var ReflectionClass
     */
    protected $reflection;

    /**
     * Object of the class to be reflected.
     *
     * @var
     */
    protected $classObj;

    /**
     * Single use reflcetion.
     *
     * @var ReflectionClass
     */
    protected $reflectionOn;

    /**
     * Single use class object.
     *
     * @var
     */
    protected $classObjOn;

    /**
     * For checking if called after the on() method.
     *
     * @var bool
     */
    private $isCalledAfterOn = false;

    /**
     * Check if the dynamic method is called on given type.
     *
     * @param $type
     * @param $name
     *
     * @return int
     */
    private function is($type, $name)
    {
        return preg_match("/^{$type}/", $name);
    }

    /**
     * Extract the dynamic name from given type
     *
     * @param $from
     * @param $name
     *
     * @return string
     */
    private function extract($type, $name)
    {
        return lcfirst(preg_replace("/{$type}/", '', $name, 1));
    }

    /**
     * $classObj and $reflection properties should be defined.
     *
     * @throws NotFoundException
     */
    private function checkClassObjAndReflectionProperties()
    {
        if ($this->isCalledAfterOn) {
            if ((!$this->classObjOn || !$this->reflectionOn)) {
                throw new NotFoundException("No class object reflected");
            }
        }
        elseif ((!$this->classObj || !$this->reflection)) {
            throw new NotFoundException("No class object reflected");
        }
    }

    /**
     * check if property or method
     * is private or protected.
     *
     * @param $object ReflectionMethod / ReflectionProperty
     *
     * @return bool
     */
    private function setAccessibleOn($object)
    {
        if ($object->isPrivate() || $object->isProtected()) {
            $object->setAccessible(true);
        }
    }

    /**
     * get the Reflection and ClassObject
     *
     * @return array
     */
    private function getReflectionAndClassObject()
    {
        if ($this->isCalledAfterOn) {

            $this->isCalledAfterOn = false;
            $classObj = $this->classObjOn;
            $reflection = $this->reflectionOn;

            unset($this->classObjOn);
            unset($this->reflectionOn);

            return [$reflection, $classObj];
        }

        return [$this->reflection, $this->classObj];
    }

    /**
     * Getting the reflection.
     *
     * @param $classObj Object of the class the reflection to be created.
     */
    public function reflect($classObj)
    {
        $this->classObj = $classObj;
        $this->reflection = new ReflectionClass($this->classObj);
    }

    /**
     * Getting the reflection.
     *
     * @param $classObj Object of the class the reflection to be created.
     *
     * @return $this
     */
    public function on($classObj)
    {
        $this->classObjOn = $classObj;
        $this->reflectionOn = new ReflectionClass($classObj);
        $this->isCalledAfterOn = true;

        return $this;
    }

    /**
     * Call to public/private/protected methods.
     *
     * @param       $method    Method name to be called (case sensitive)
     * @param array $arguments Arguments to be passed to the method
     *
     * @return $this
     * @throws NotFoundException
     */
    public function call($method, $arguments = [])
    {
        $this->checkClassObjAndReflectionProperties();

        list($reflection, $classObj) = $this->getReflectionAndClassObject();
        $method = $reflection->getMethod($method);
        $this->setAccessibleOn($method);

        return $method->invokeArgs($classObj, $arguments);
    }

    /**
     * Get value of public/private/protected properties.
     *
     * @param $name Property name to be accessed (Case sensitive).
     *
     * @return mixed
     * @throws NotFoundException
     */
    public function get($name)
    {
        $this->checkClassObjAndReflectionProperties();

        list($reflection, $classObj) = $this->getReflectionAndClassObject();

        $property = $reflection->getProperty($name);
        $this->setAccessibleOn($property);

        return $property->getValue($classObj);
    }

    /**
     * Set value of public/private/protected properties.
     *
     * @param $name
     * @param $value
     *
     * @throws NotFoundException
     */
    public function set($name, $value)
    {
        $this->checkClassObjAndReflectionProperties();

        list($reflection, $classObj) = $this->getReflectionAndClassObject();

        $property = $reflection->getProperty($name);
        $this->setAccessibleOn($property);

        $property->setValue($classObj, $value);
    }

    /**
     * @param       $method
     * @param array $arguments
     *
     * @return ReflectableTrait
     * @throws NotFoundException
     */
    public function __call($method, $arguments = [])
    {
        if ($this->is('call', $method)) {
            $methodName = $this->extract('call', $method);

            return $this->call($methodName, $arguments);
        }

        throw new NotFoundException("Method '{$method}' not found.");
    }

    /**
     * @param $name
     *
     * @return mixed
     * @throws NotFoundException
     */
    public function __get($name)
    {
        if ($this->is('get', $name)) {
            $name = $this->extract('get', $name);

            return $this->get($name);
        }

        throw new NotFoundException("Property '{$name}' not found.");
    }

    /**
     * @param $name
     * @param $value
     *
     * @throws NotFoundException
     */
    public function __set($name, $value)
    {
        if ($this->is('set', $name)) {
            $name = $this->extract('set', $name);

            $this->set($name, $value);
        } else {
            throw new NotFoundException("Property '{$name}' not found.");
        }
    }
}


<?php
/**
 * File SeekableTrait.php
 *
 * @author Edward Pfremmer <epfremme@nerdery.com>
 */
namespace Epfremme\Collection\Traits;

/**
 * Trait SeekableTrait
 *
 * @property array|mixed[] $elements
 * @package Epfremme\Collection\Traits
 * @uses \SeekableIterator
 */
trait SeekableTrait
{
    /**
     * Seek to a position within the collection
     *
     * @param $position
     * @return void
     */
    public function seek($position)
    {
        if (!array_key_exists($position, $this->elements)) {
            throw new \InvalidArgumentException(
                sprintf('Position %s does not exist in collection', $position)
            );
        }

        reset($this->elements);

        while (key($this->elements) !== $position) {
            next($this->elements);
        }
    }
}


<?php

/*
 * This file is part of the FuelPHP Doctrine package.
 *
 * (c) Indigo Development Team
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Fuel\Doctrine;

use Indigo\Fuel\Dependency\Container as DiC;
use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Driver\DriverChain;
use Doctrine\Common\EventManager;
use Doctrine\Common\EventSubscriber;
use Gedmo\DoctrineExtensions;

/**
 * Entity Manager Facade
 *
 * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
 */
class Manager
{
	use \Indigo\Core\Helper\Config;

	/**
	 * Entity Manager
	 *
	 * @var EntityManager
	 */
	protected $entityManager;

	/**
	 * Creates a new Manager
	 *
	 * @param array $config
	 */
	public function __construct(array $config)
	{
		$this->config = $config;

		if ($this->getConfig('mapping.auto', false))
		{
			$this->autoLoadMappingInfo();
		}
	}

	/**
	 * Creates a new Entity Manager
	 *
	 * @return EntityManager
	 */
	protected function createEntityManager()
	{
		// Cache can be null in case of auto setup
		if ($cache = $this->getConfig('cache_driver', 'array'))
		{
			$cache = 'doctrine.cache.'.$cache;

			$cache = DiC::resolve($cache);
		}

		// Auto or manual setup
		if ($this->getConfig('auto_config', false))
		{
			$dev = $this->getConfig('dev_mode', \Fuel::$env === \Fuel::DEVELOPMENT);
			$proxy_dir = $this->getConfig('proxy_dir');

			$config = Setup::createConfiguration($dev, $proxy_dir, $cache);
		}
		else
		{
			$config = new Configuration;

			$config->setProxyDir($this->getConfig('proxy_dir'));
			$config->setProxyNamespace($this->getConfig('proxy_namespace'));
			$config->setAutoGenerateProxyClasses($this->getConfig('auto_generate_proxy_classes', false));

			if ($cache)
			{
				$config->setMetadataCacheImpl($cache);
				$config->setQueryCacheImpl($cache);
				$config->setResultCacheImpl($cache);
			}
		}

		// Ugly hack for autoloading annotations
		$config->newDefaultAnnotationDriver(array());

		$this->registerMapping($config);

		$conn = DiC::multiton('dbal', $this->getConfig('dbal'), [$this->getConfig('dbal')]);
		$evm = $conn->getEventManager();

		$this->registerBehaviors($evm, $config);

		return $this->entityManager = EntityManager::create($conn, $config, $evm);
	}

	/**
	 * Returns the Entity Manager
	 *
	 * @return EntityManager
	 */
	public function getEntityManager()
	{
		if ($this->entityManager === null)
		{
			return $this->createEntityManager();
		}

		return $this->entityManager;
	}

	/**
	 * Returns mapping config
	 *
	 * @return array
	 */
	public function getMappings()
	{
		return $this->getConfig('mappings', array());
	}

	/**
	 * Sets a mapping configuration
	 *
	 * @param string $mappingName
	 * @param array  $mappingConfig
	 *
	 * @return this
	 */
	public function setMappings($mappingName, array $mappingConfig = null)
	{
		if (is_array($mappingName) === false)
		{
			$mappingName = array($mappingName => $mappingConfig);
		}

		\Arr::set($this->config['mappings'], $mappingName);

		return $this;
	}

	/**
	 * Returns default config path
	 *
	 * @return string
	 */
	public function getConfigPath()
	{
		return $this->getConfig('mapping.config_path', 'config'.DS.'doctrine'.DS);
	}

	/**
	 * Returns default class path
	 *
	 * @return string
	 */
	public function getClassPath()
	{
		return $this->getConfig('mapping.class_path', 'classes'.DS);
	}

	/**
	 * Returns default object name
	 *
	 * @return string
	 */
	public function getObjectName()
	{
		return $this->getConfig('mapping.object_name', 'Entity');
	}

	/**
	 * Generates mapping information for packages, modules and the app
	 *
	 * @return array
	 */
	protected function autoLoadMappingInfo()
	{
		$mappings = array();

		foreach (\Package::loaded() as $package => $path)
		{
			$mappings[] = $package . '::package';
		}

		foreach (\Module::loaded() as $module => $path)
		{
			$mappings[] = $module . '::module';
		}

		$mappings[] = 'app';

		$mappings = array_fill_keys($mappings, array('is_component' => true));

		$this->setMappings($mappings);
	}

	/**
	 * Registers mapping in a Configuration
	 *
	 * @param Configuration $config
	 */
	public function registerMapping(Configuration $config)
	{
		$driverChain = new DriverChain;
		$aliasMap = array();
		$drivers = array();

		$this->parseMappingInfo();

		// Get actual drivers
		foreach ($this->getMappings() as $mappingName => $mappingConfig)
		{
			if (empty($mappingConfig['prefix']))
			{
				$mappingConfig['prefix'] = '__DEFAULT__';
			}

			$drivers[$mappingConfig['type']][$mappingConfig['prefix']] = $mappingConfig['dir'];

			if (isset($mappingConfig['alias']))
			{
				$aliasMap[$mappingConfig['alias']] = $mappingConfig['prefix'];
			}
		}

		foreach ($drivers as $driverType => $driverPaths)
		{
			if ($driverType === 'annotation')
			{
				$driver = $config->newDefaultAnnotationDriver($driverPaths, false);
				// Annotations are needed to be registered, thanks Doctrine
				// $driver = new AnnotationDriver(
				// 	new CachedReader(
				// 		new AnnotationReader,
				// 		$config->getMetadataCacheImpl()
				// 	),
				// 	$driverPaths
				// );
			}
			else
			{
				$paths = $driverPaths;

				if (strpos($driverType, 'simplified') === 0)
				{
					$paths = array_flip($driverPaths);
				}

				$driver = DiC::resolve($driverType, [$paths]);
			}

			foreach ($driverPaths as $prefix => $driverPath)
			{
				if ($prefix === '__DEFAULT__' or count($this->config['mappings']) === 1)
				{
					$driverChain->setDefaultDriver($driver);
				}
				else
				{
					$driverChain->addDriver($driver, $prefix);
				}
			}
		}

		$config->setMetadataDriverImpl($driverChain);
		$config->setEntityNamespaces($aliasMap);
	}

	/**
	 * Parses mapping info
	 *
	 * @return array
	 */
	public function parseMappingInfo()
	{
		$mappings = array();

		foreach ($this->getMappings() as $mappingName => $mappingConfig)
		{
			// This is from symfony DoctrineBundle, should be reviewed
			if (is_array($mappingConfig) === false or \Arr::get($mappingConfig, 'mapping', true) === false)
			{
				continue;
			}

			$mappingConfig = array_replace(array(
				'dir'    => false,
				'type'   => false,
				'prefix' => false,
			), $mappingConfig);

			if (isset($mappingConfig['is_component']) === false)
			{
				$mappingConfig['is_component'] = false;

				if (is_dir($mappingConfig['dir']) === false)
				{
					$mappingConfig['is_component'] = (\Package::loaded($mappingName) or \Module::loaded($mappingName));
				}
			}

			if ($mappingConfig['is_component'])
			{
				$mappingConfig = $this->getComponentDefaults($mappingName, $mappingConfig);
			}

			if (empty($mappingConfig))
			{
				continue;
			}

			$mappings[$mappingName] = $mappingConfig;
		}

		$this->config['mappings'] = $mappings;
	}

	/**
	 * Returns default settings for components
	 *
	 * @param string $mappingName
	 * @param array  $mappingConfig
	 *
	 * @return array
	 */
	protected function getComponentDefaults($mappingName, array $mappingConfig)
	{
		if (strpos($mappingName, '::'))
		{
			list($componentName, $componentType) = explode('::', $mappingName);
		}
		else
		{
			$componentName = $mappingName;

			$componentType = $this->detectComponentType($componentName);

			if ($componentType === false and $componentName === 'app')
			{
				$componentType = 'app';
			}
		}

		if (($componentPath = $this->getComponentPath($componentName, $componentType)) === false)
		{
			return false;
		}

		$configPath = $mappingConfig['dir'];

		if ($configPath === false)
		{
			$configPath = $this->getConfigPath();
		}

		if ($mappingConfig['type'] === false)
		{
			$mappingConfig['type'] = $this->detectMetadataDriver($componentPath, $configPath);
		}

		if ($mappingConfig['type'] === false)
		{
			return false;
		}

		if ($mappingConfig['dir'] === false)
		{
			if (in_array($mappingConfig['type'], array('annotation', 'staticphp')))
			{
				$mappingConfig['dir'] = $this->getClassPath().$this->getObjectName();
			}
			else
			{
				$mappingConfig['dir'] = $configPath;
			}
		}

		if (is_array($mappingConfig['dir']))
		{
			foreach ($mappingConfig['dir'] as &$path)
			{
				$path = $componentPath . $path;
			}
		}
		else
		{
			$mappingConfig['dir'] = $componentPath . $mappingConfig['dir'];
		}

		if ($mappingConfig['prefix'] === false)
		{
			$mappingConfig['prefix'] = $this->detectComponentNamespace($componentName, $componentType);
		}

		// Set this to false to prevent reinitialization on subsequent load calls
		$mappingConfig['is_component'] = false;

		return $mappingConfig;
	}

	/**
	 * Detects which metadata driver to use for the supplied directory
	 *
	 * @param string       $dir        A directory path
	 * @param string|array $configPath Config path or paths
	 *
	 * @return string|null A metadata driver short name, if one can be detected
	 */
	protected function detectMetadataDriver($dir, $configPath)
	{
		foreach ((array) $configPath as $cPath)
		{
			$path = $dir.DS.$cPath.DS;

			if (($files = glob($path.'*.dcm.xml')) && count($files))
			{
				return 'xml';
			}
			elseif (($files = glob($path.'*.orm.xml')) && count($files))
			{
				return 'simplified_xml';
			}
			elseif (($files = glob($path.'*.dcm.yml')) && count($files))
			{
				return 'yml';
			}
			elseif (($files = glob($path.'*.orm.yml')) && count($files))
			{
				return 'simplified_yml';
			}
			elseif (($files = glob($path.'*.php')) && count($files))
			{
				return 'php';
			}
		}

		if (is_dir($dir.DS.$this->getClassPath().$this->getObjectName()))
		{
			return 'annotation';
		}

		return false;
	}

	/**
	 * Registers subscribers to Event Manager
	 *
	 * @param EventManager $evm
	 */
	protected function registerBehaviors(EventManager $evm, Configuration $config)
	{
		$reader = new AnnotationReader;

		if ($cache = $config->getMetadataCacheImpl())
		{
			$reader = new CachedReader($reader, $cache);
		}

		foreach ($this->getConfig('behaviors', array()) as $behavior)
		{
			if ($class = DiC::resolve('doctrine.behavior.'.$behavior))
			{
				$class->setAnnotationReader($reader);

				$this->configureBehavior($behavior, $class);

				$evm->addEventSubscriber($class);
			}
		}

		if ($mapping = $config->getMetadataDriverImpl())
		{
			$type = 'registerMappingIntoDriverChainORM';

			if ($this->getConfig('behavior.superclass', false))
			{
				$type = 'registerAbstractMappingIntoDriverChainORM';
			}

			DoctrineExtensions::$type(
				$mapping,
				$reader
			);
		}
	}

	/**
	 * Configures Behavior Subscriber
	 *
	 * @param  string          $behavior
	 * @param  EventSubscriber $es
	 */
	protected function configureBehavior($behavior, EventSubscriber $es)
	{
		switch ($behavior) {
			case 'translatable':
				$es->setTranslatableLocale(\Config::get('language', 'en'));
				$es->setDefaultLocale(\Config::get('language_fallback', 'en'));
				break;
		}
	}
}


<?php

namespace Dothiv\Bundle\ContentfulBundle\Output;

use Dothiv\Bundle\ContentfulBundle\DothivContentfulBundleEvents;
use Dothiv\Bundle\ContentfulBundle\Event\ContentfulViewEvent;
use Dothiv\Bundle\ContentfulBundle\Adapter\ContentfulContentAdapter;
use Dothiv\Bundle\ContentfulBundle\Item\ContentfulAsset;
use Dothiv\Bundle\ContentfulBundle\Item\ContentfulEntry;
use Dothiv\Bundle\ContentfulBundle\Item\ContentfulItem;
use PhpOption\None;
use PhpOption\Option;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class ViewBuilder
{
    /**
     * @var string
     */
    private $defaultLocale;

    /**
     * @var ContentfulContentAdapter
     */
    private $contentAdapter;

    /**
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * @param string                   $defaultLocale
     * @param ContentfulContentAdapter $contentAdapter
     * @param EventDispatcherInterface $dispatcher
     */
    public function __construct($defaultLocale, ContentfulContentAdapter $contentAdapter, EventDispatcherInterface $dispatcher)
    {
        $this->contentAdapter = $contentAdapter;
        $this->defaultLocale  = $defaultLocale;
        $this->dispatcher     = $dispatcher;
    }

    /**
     * @param ContentfulEntry $entry
     * @param string          $locale
     *
     * @return object
     */
    public function buildView(ContentfulEntry $entry, $locale)
    {
        $spaceId                         = $entry->getSpaceId();
        $fields                          = $this->localize($entry, $locale);
        $fields['cfMeta']                = array();
        $fields['cfMeta']['url']         = $entry->getContentfulUrl();
        $fields['cfMeta']['contentType'] = $this->contentAdapter->getContentTypeById($spaceId, $entry->getContentTypeId())->getName();
        $fields['cfMeta']['itemName']    = $entry->getName();
        $fields['cfMeta']['itemId']      = $entry->getId();
        $fields['cfMeta']['createdAt']   = $entry->getCreatedAt();
        $fields['cfMeta']['updatedAt']   = $entry->getUpdatedAt();
        $view                            = $this->createView($fields, $spaceId, $locale);
        return $view;
    }

    /**
     * @param ContentfulItem $entry
     * @param                $locale
     *
     * @return array
     */
    protected function localize(ContentfulItem $entry, $locale)
    {
        $fields = array();
        foreach ($entry->getFields() as $k => $v) {
            $localValue = isset($v[$locale]) ? $v[$locale] : null;
            if ($localValue === null) {
                if (isset($v[$this->defaultLocale])) {
                    $localValue = $v[$this->defaultLocale];
                }
            }
            if (is_null($localValue)) {
                continue;
            }
            if (is_scalar($localValue) && trim($localValue) === "") {
                continue;
            }
            $fields[$k] = $localValue;
        }
        return $fields;
    }

    /**
     * @param mixed  $value
     * @param string $spaceId
     * @param string $locale
     *
     * @return Option
     */
    protected function getValue($value, $spaceId, $locale)
    {
        if (is_scalar($value)) {
            return Option::fromValue($value);
        }
        if (is_object($value)) {
            // TODO: Generalize?
            if ($value instanceof \DateTime) {
                return Option::fromValue($value);
            }
        }
        if (is_array($value)) {
            if ($this->isLink($value)) {
                /** @var ContentfulItem $entry */
                $optionalEntry = $this->contentAdapter->findByTypeAndId($spaceId, $value['sys']['linkType'], $value['sys']['id']);
                if ($optionalEntry->isEmpty()) {
                    return None::create();
                }
                $entry = $optionalEntry->get();

                $fields                       = $this->localize($entry, $locale);
                $fields['cfMeta']             = array();
                $fields['cfMeta']['itemType'] = $value['sys']['linkType'];
                $fields['cfMeta']['itemId']   = $value['sys']['id'];
                $fields['cfMeta']['url']      = $entry->getContentfulUrl();
                if ($entry instanceof ContentfulEntry) {
                    /** @var ContentfulEntry $entry */
                    $fields['cfMeta']['contentType'] = $this->contentAdapter->getContentTypeById($spaceId, $entry->getContentTypeId())->getName();
                    $fields['cfMeta']['itemName']    = $entry->getName();
                    $fields['cfMeta']['createdAt']   = $entry->getCreatedAt();
                    $fields['cfMeta']['updatedAt']   = $entry->getUpdatedAt();
                }
                if ($entry instanceof ContentfulAsset) {
                    /** @var ContentfulAsset $entry */
                    $fields['cfMeta']['itemName']    = $fields['title'];
                    $fields['cfMeta']['contentType'] = 'Asset';
                    $fields['cfMeta']['createdAt']   = $entry->getCreatedAt();
                    $fields['cfMeta']['updatedAt']   = $entry->getUpdatedAt();
                }
                return Option::fromValue($this->createView($fields, $spaceId, $locale));
            } else {
                $newValue = array();
                foreach ($value as $k => $v) {
                    $vv = $this->getValue($v, $spaceId, $locale);
                    if ($vv->isDefined()) {
                        $newValue[$k] = $vv->get();
                    }

                }
                return Option::fromValue($newValue);
            }
        }
    }

    protected function createView(array $fields, $spaceId, $locale)
    {
        $view = new \stdClass();
        foreach ($fields as $k => $v) {
            $v = $this->getValue($v, $spaceId, $locale);
            if ($v->isDefined()) {
                $view->$k = $v->get();
            }
        }
        $view = $this->dispatcher->dispatch(DothivContentfulBundleEvents::CONTENTFUL_VIEW_CREATE, new ContentfulViewEvent($view))->getView();
        return $view;
    }

    protected function isLink(array $value)
    {
        if (!isset($value['sys'])) return false;
        $sys = $value['sys'];
        if (!isset($sys['type'])) return false;
        if ($sys['type'] != 'Link') return false;
        return true;
    }

    /**
     * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    public function getEventDispatcher()
    {
        return $this->dispatcher;
    }
}


<?php

/**
 * Used to implement Action Controllers for use with the Front Controller.
 *
 * @link Benri_Controller_Abstract.html Benri_Controller_Abstract
 */
class Benri_Rest_Controller_Action extends Benri_Controller_Action_Abstract
{
    /**
     * Request data.
     * @var StdClass
     */
    protected $_input;

    /**
     * Response data.
     * @var mixed
     */
    protected $_data;

    /**
     * Initialize object.
     */
    public function init()
    {
        parent::init();

        Zend_Controller_Front::getInstance()
            ->registerPlugin(new Zend_Controller_Plugin_PutHandler())
            ->registerPlugin(new Benri_Controller_Plugin_CORS())
            ->registerPlugin(new Benri_Controller_Plugin_RequireUserAgentHeader())
            ->registerPlugin(new Benri_Controller_Plugin_OptionsRequest());

        try {
            $this->getHelper('layout')->disableLayout();
        } catch (Zend_Controller_Action_Exception $e) {
            // If the Layout helper isn't enabled, just ignore and continue.
        }

        $this->getHelper('viewRenderer')->setNoRender(true);

        $this->_input = new StdClass();
    }

    /**
     * Post-dispatch routines.
     *
     * Common usages for `postDispatch()` include rendering content in a
     * sitewide template, link url correction, setting headers, etc.
     */
    public function postDispatch()
    {
        $respond  = false;
        $response = (object) [];

        if ($this->_data) {
            $response->data = $this->_data;
        }

        if ($this->_errors) {
            $response->errors = $this->_errors;
        }

        if ($this->_messages) {
            $response->messages = $this->_messages;
        }

        $this
            ->getResponse()
            ->setHeader('Content-Type', 'application/json; charset=utf-8', true)
            ->setBody(json_encode($response, JSON_NUMERIC_CHECK | JSON_HEX_AMP));
    }

    /**
     * Pre-dispatch routines.
     */
    public function preDispatch()
    {
        $error   = null;
        $request = $this->getRequest();

        if (!$request->isGet() && !$request->isHead()) {
            // read data from the request body.
            $this->_input = json_decode($request->getRawBody());

            if (JSON_ERROR_NONE === json_last_error()) {
                return;
            }

            // Sending invalid JSON will result in a `400 Bad Request` response.
            $this
                ->getResponse()
                ->setHttpResponseCode(400)
                ->setHeader('Content-Type', 'text/plain; charset=utf-8', true)
                ->setBody(json_last_error_msg())
                ->sendResponse();

            exit(400);
        }
    }

    /**
     * General method to save models (Benri_Db_Table_Row).
     *
     * @param Benri_Db_Table_Row
     * @return Benri_Controller_Rest
     */
    protected function _saveModel(Benri_Db_Table_Row &$model, $data = null)
    {
        return parent::_saveModel($model, $data ?: $this->_input);
    }
}


<?php

namespace Maba\OAuthCommerceAccountsClient;

use Maba\OAuthCommerceAccountsClient\Entity\Transaction;
use Maba\OAuthCommerceClient\BaseClient;
use Maba\OAuthCommerceClient\Command;
use Guzzle\Http\Url;
use Maba\OAuthCommerceClient\Entity\AccessToken;

class AccountsClient extends BaseClient
{
    /**
     * @param Transaction $transaction
     *
     * @return Command<Transaction>
     */
    public function createTransaction(Transaction $transaction)
    {
        return $this->createCommand()
            ->setRequest($this->client->post('transaction'))
            ->setBodyEntity($transaction, 'urlencoded')
            ->setResponseClass('Maba\OAuthCommerceAccountsClient\Entity\Transaction')
        ;
    }

    /**
     * @param string                                       $transactionKey
     * @param \Maba\OAuthCommerceClient\Entity\AccessToken $accessToken
     *
     * @return Command<Transaction>
     */
    public function getTransaction($transactionKey, AccessToken $accessToken = null)
    {
        return $this->createCommand()
            ->setAccessToken($accessToken)
            ->setRequest($this->client->get('transaction/' . $transactionKey))
            ->setResponseClass('Maba\OAuthCommerceAccountsClient\Entity\Transaction')
        ;
    }

    /**
     * @param string                                       $transactionKey
     * @param \Maba\OAuthCommerceClient\Entity\AccessToken $accessToken
     *
     * @return Command<Transaction>
     */
    public function confirmTransaction($transactionKey, AccessToken $accessToken)
    {
        return $this->createCommand()
            ->setAccessToken($accessToken)
            ->setRequest($this->client->put('transaction/' . $transactionKey . '/confirm'))
            ->setResponseClass('Maba\OAuthCommerceAccountsClient\Entity\Transaction')
        ;
    }

    /**
     * @param string                                       $transactionKey
     * @param \Maba\OAuthCommerceClient\Entity\AccessToken $accessToken
     *
     * @return Command<Transaction>
     */
    public function reserveTransaction($transactionKey, AccessToken $accessToken)
    {
        return $this->createCommand()
            ->setAccessToken($accessToken)
            ->setRequest($this->client->put('transaction/' . $transactionKey . '/reserve'))
            ->setResponseClass('Maba\OAuthCommerceAccountsClient\Entity\Transaction')
        ;
    }

    /**
     * @param string                                       $transactionKey
     * @param \Maba\OAuthCommerceClient\Entity\AccessToken $accessToken
     *
     * @return Command<Transaction>
     */
    public function revokeTransaction($transactionKey, AccessToken $accessToken)
    {
        return $this->createCommand()
            ->setAccessToken($accessToken)
            ->setRequest($this->client->delete('transaction/' . $transactionKey))
            ->setResponseClass('Maba\OAuthCommerceAccountsClient\Entity\Transaction')
        ;
    }

}