<?php

namespace OpenOrchestra\ModelBundle\Repository;

use Doctrine\Common\Collections\Collection;
use OpenOrchestra\ModelBundle\Repository\RepositoryTrait\StatusableTrait;
use OpenOrchestra\ModelBundle\Repository\RepositoryTrait\UseTrackableTrait;
use OpenOrchestra\ModelInterface\Model\NodeInterface;
use OpenOrchestra\ModelInterface\Model\ReadNodeInterface;
use OpenOrchestra\ModelInterface\Model\StatusableInterface;
use OpenOrchestra\ModelInterface\Repository\NodeRepositoryInterface;
use OpenOrchestra\ModelInterface\Repository\FieldAutoGenerableRepositoryInterface;
use OpenOrchestra\ModelBundle\Repository\RepositoryTrait\AutoPublishableTrait;
use OpenOrchestra\Pagination\Configuration\PaginateFinderConfiguration;
use Solution\MongoAggregation\Pipeline\Stage;
use OpenOrchestra\Repository\AbstractAggregateRepository;
use MongoRegex;

/**
 * Class NodeRepository
 */
class NodeRepository extends AbstractAggregateRepository implements FieldAutoGenerableRepositoryInterface, NodeRepositoryInterface
{
    use AutoPublishableTrait;
    use UseTrackableTrait;
    use StatusableTrait;

    /**
     * @param string $entityId
     *
     * @return NodeInterface
     */
    public function findVersionByDocumentId($entityId)
    {
        return $this->find(new \MongoId($entityId));
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function getFooterTree($language, $siteId)
    {
        return $this->getTreeByLanguageAndFieldAndSiteId($language, 'inFooter', $siteId);
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function getMenuTree($language, $siteId)
    {
        return $this->getTreeByLanguageAndFieldAndSiteId($language, 'inMenu', $siteId);
    }

    /**
     * @param string $nodeId
     * @param int    $nbLevel
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function getSubMenu($nodeId, $nbLevel, $language, $siteId)
    {
        $node = $this->findOnePublished($nodeId, $language, $siteId);
        $list = array();

        if ($node instanceof ReadNodeInterface) {
            $list[] = $node;
            $list = array_merge($list, $this->getTreeParentIdLevelAndLanguage($node->getNodeId(), $nbLevel, $language, $siteId));
        }

        return $list;
    }

    /**
     * @param string $nodeId
     * @param string $language
     * @param string $siteId
     *
     * @return mixed
     */
    public function findOnePublished($nodeId, $language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $filter = array(
            'status.publishedState' => true,
            'deleted' => false,
            'nodeId' => $nodeId,
        );
        $qa->match($filter);

        return $this->singleHydrateAggregateQuery($qa);
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function findAllPublishedSpecialPage($language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $filter = array(
            'status.publishedState' => true,
            'deleted' => false,
            'specialPageName' => array('$exists' => true),
        );
        $qa->match($filter);

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function findAllSpecialPage($language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $filter = array(
            'deleted' => false,
            'specialPageName' => array('$exists' => true),
        );
        $qa->match($filter);

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     * @param string $language
     * @param string $name
     *
     * @return int
     */
    public function countOtherNodeWithSameSpecialPageName($nodeId, $siteId, $language, $name)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(
            array(
                'nodeId'   => array('$ne' => $nodeId),
                'deleted'  => false,
                'specialPageName' => $name
            )
        );

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string   $nodeId
     * @param string   $language
     * @param string   $siteId
     * @param string   $version
     *
     * @return mixed
     */
    public function findVersionNotDeleted($nodeId, $language, $siteId, $version)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(
            array(
                'nodeId'  => $nodeId,
                'deleted' => false,
                'version' => $version,
            )
        );
        return $this->singleHydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $language
     * @param string $siteId
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function findNotDeletedSortByUpdatedAt($nodeId, $language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(array(
            'nodeId' => $nodeId,
            'deleted'=> false,
        ));
        $qa->sort(array('updatedAt' => -1));

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $language
     * @param string $siteId
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function countNotDeletedVersions($nodeId, $language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(array(
            'nodeId' => $nodeId,
            'deleted'=> false,
        ));

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $language
     * @param string $siteId
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function findPublishedSortedByVersion($nodeId, $language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(
            array(
                'nodeId'  => $nodeId,
                'status.publishedState' => true,
            )
        );
        $qa->sort(array('createdAt' => -1));

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     *
     * @return mixed
     */
    public function findByNodeAndSite($nodeId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(array('nodeId' => $nodeId));

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     * @param string $areaId
     *
     * @return array
     */
    public function findByNodeIdAndSiteIdWithBlocksInArea($nodeId, $siteId, $areaId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(array(
                'nodeId' => $nodeId,
                'areas.'.$areaId.'.blocks.0' => array('$exists' => true),
        ));

        return $this->findLastVersionInLanguage($qa);
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     *
     * @return mixed
     */
    public function findOneByNodeAndSite($nodeId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(array('nodeId' => $nodeId));

        return $this->singleHydrateAggregateQuery($qa);
    }

    /**
     * @param string $parentId
     * @param string $siteId
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function findByParent($parentId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(array('parentId' => $parentId));

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $path
     * @param string $siteId
     * @param string $language
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     *
     * @return array
     */
    public function findNodeIdByIncludedPathSiteIdAndLanguage($path, $siteId, $language)
    {
        $qb = $this->createQueryBuilder()->hydrate(false);
        $qb->field('siteId')->equals($siteId)
           ->field('language')->equals($language)
           ->field('deleted')->equals(false)
           ->field('path')->equals(new MongoRegex('/^'.$path.'(\/.*)?$/'))
           ->distinct('nodeId');

        return $qb->getQuery()->execute()->toArray();
    }

    /**
     * @param string $path
     * @param string $siteId
     * @param string $language
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     *
     * @return array
     */
    public function findNodeIdByIncludedPathSiteId($path, $siteId)
    {
        $qb = $this->createQueryBuilder();
        $qb->field('siteId')->equals($siteId)
            ->field('path')->equals(new MongoRegex('/^'.$path.'(\/.*)?$/'));

        return $qb->getQuery()->execute()->toArray();
    }

    /**
     * @param string $nodeId
     * @param string $language
     * @param string $siteId
     *
     * @return mixed
     */
    public function findInLastVersion($nodeId, $language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(
            array(
                'nodeId'  => $nodeId,
                'deleted' => false,
            )
        );
        $qa->sort(array('createdAt' => -1));

        return $this->singleHydrateAggregateQuery($qa);
    }

    /**
     * @param string $siteId
     * @param string $type
     *
     * @return array
     */
    public function findLastVersionByType($siteId, $type = NodeInterface::TYPE_DEFAULT)
    {
        return $this->prepareFindLastVersion($type, $siteId, false);
    }

    /**
     * @param string $siteId
     * @param string $language
     *
     * @return array
     */
    public function findLastVersionByLanguage($siteId, $language)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'siteId' => $siteId,
                'deleted' => false,
                'language' => $language
            )
        );

        return $this->findLastVersion($qa);
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    public function findAllRoutePattern($language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->project(array(
            'routePattern' => true,
            'nodeId' => true,
            'parentId' => true,
        ));

        return $qa->getQuery()->aggregate()->toArray();
    }

    /**
     * @param string $siteId
     * @param string $language
     * @param string $parentId
     *
     * @return array
     */
    public function findTreeNode($siteId, $language, $parentId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        if (NodeInterface::ROOT_PARENT_ID !== $parentId ) {
            $qa->match(array('path' => new MongoRegex('/'.$parentId.'(\/.*)?$/')));
        }
        $qa->match(array('deleted' => false));

        $qa->sort(array('createdAt' => 1));
        $elementName = 'node';
        $qa->group(array(
            '_id' => array('nodesId' => '$nodeId'),
            'version' => array('$last' => '$version'),
            'order' => array('$last' => '$order'),
            'nodeId' => array('$last' => '$nodeId'),
            $elementName => array('$last' => '$$ROOT')
        ));
        $qa->sort(array('order' => 1));
        $nodes = $qa->getQuery()->aggregate()->toArray();

        return $this->generateTree($nodes, $parentId);
    }

    /**
     * @param string $siteId
     * @param string $type
     *
     * @return array
     */
    public function findPublishedByType($siteId, $type = NodeInterface::TYPE_DEFAULT)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'siteId' => $siteId,
                'deleted' => false,
                'nodeType' => $type,
                'status.publishedState' => true,
            )
        );

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $path
     * @param string $siteId
     * @param string $language
     *
     * @return array
     */
    public function findPublishedByPathAndLanguage($path, $siteId, $language)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'path' => new MongoRegex('/^'.$path.'(\/.*)?$/'),
                'status.publishedState' => true,
                'deleted' => false,
                'language' => $language,
            )
        );

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $path
     * @param string $siteId
     * @param string $language
     *
     * @return mixed
     */
    public function findSubTreeByPath($path, $siteId, $language)
    {
        $qa = $this->buildTreeRequest($language, $siteId);
        $qa->match(array('path' => new \MongoRegex('/'.preg_quote($path).'.+/')));

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param PaginateFinderConfiguration $configuration
     * @param string                      $siteId
     * @param string                      $language
     *
     * @return array
     */
    public function findForPaginate(PaginateFinderConfiguration $configuration, $siteId, $language)
    {
        $elementName = 'node';
        $order = $configuration->getOrder();
        $qa = $this->createQueryWithFilterAndLastVersion($configuration, $siteId, $language, $elementName, $order);

        $qa->skip($configuration->getSkip());
        $qa->limit($configuration->getLimit());

        return $this->hydrateAggregateQuery($qa, $elementName);
    }

    /**
     * @param PaginateFinderConfiguration $configuration
     * @param string                      $siteId
     * @param string                      $language
     * @param string                      $blockId
     *
     * @return array
     */
    public function findWithBlockUsedForPaginate(PaginateFinderConfiguration $configuration, $siteId, $language, $blockId)
    {
        $qb = $this->createQueryBuilder();
        $qb->field('language')->equals($language);
        $qb->field('siteId')->equals($siteId);

        $functionFilter = $this->generateFunctionFilterBlock($blockId);
        $qb->where($functionFilter);

        $order = $configuration->getOrder();
        if (!empty($order)) {
            $qb->sort($order);
        }

        $qb->skip($configuration->getSkip());
        $qb->limit($configuration->getLimit());

        return $qb->getQuery()->execute();
    }

    /**
     * @param string  $siteId
     * @param string  $language
     *
     * @return int
     */
    public function count($siteId, $language)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(array('deleted' => false));
        $elementName = 'node';
        $qa->sort(array('createdAt' => 1));
        $qa->group(array(
            '_id' => array('nodeId' => '$nodeId'),
            $elementName => array('$last' => '$$ROOT')
        ));

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string  $siteId
     * @param string  $language
     * @param string  $blockId
     *
     * @return int
     */
    public function countWithBlockUsed($siteId, $language, $blockId)
    {
        $qb = $this->createQueryBuilder();
        $qb->field('language')->equals($language);
        $qb->field('siteId')->equals($siteId);

        $functionFilter = $this->generateFunctionFilterBlock($blockId);
        $qb->where($functionFilter);

        return $qb->getQuery()->execute()->count();
    }

    /**
     * @param PaginateFinderConfiguration $configuration
     * @param string                      $siteId
     * @param string                      $language
     *
     * @return int
     */
    public function countWithFilter(PaginateFinderConfiguration $configuration, $siteId, $language)
    {
        $elementName = 'node';
        $qa = $this->createQueryWithFilterAndLastVersion($configuration, $siteId, $language, $elementName);

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string $siteId
     * @param string $nodeId
     * @param int    $order
     * @param string $parentId
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    public function updateOrderOfBrothers($siteId, $nodeId, $order, $parentId)
    {
        $this->createQueryBuilder()
            ->updateMany()
            ->field('nodeId')->notEqual($nodeId)
            ->field('siteId')->equals($siteId)
            ->field('parentId')->equals($parentId)
            ->field('order')->gte($order)
            ->field('order')->inc(1)
            ->getQuery()
            ->execute();
    }

    /**
     * @param string $blockId
     *
     * @return \MongoCode
     */
    protected function generateFunctionFilterBlock($blockId)
    {
        return new \MongoCode(
            'function() {
                for (var areaIndex in this.areas)
                    for (var key in this.areas[areaIndex].blocks)
                        if (this.areas[areaIndex].blocks[key].$id == "'.$blockId.'")
                            return this;
            }'
        );
    }

    /**
     * @param PaginateFinderConfiguration $configuration
     * @param string                      $siteId
     * @param string                      $language
     * @param string                      $elementName
     * @param array                       $order
     *
     * @return Stage
     */
    protected function createQueryWithFilterAndLastVersion(
        PaginateFinderConfiguration $configuration,
        $siteId,
        $language,
        $elementName,
        $order = array()
    ){
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(array('deleted' => false));
        $filters = $this->getFilterSearch($configuration);
        if (!empty($filters)) {
            $qa->match($filters);
        }

        $qa->sort(array('createdAt' => 1));

        $group = array(
            '_id' => array('nodeId' => '$nodeId'),
            $elementName => array('$last' => '$$ROOT')
        );
        $groupOrder = array();
        foreach ($order as $name => $dir) {
            $nameOrder = str_replace('.', '_', $name);
            $groupOrder[$nameOrder] = $dir;
            $group[$nameOrder] = array('$last' => '$'.$name);
        }
        $qa->group($group);

        if (!empty($groupOrder)) {
            $qa->sort($groupOrder);
        }

        return $qa;
    }

    /**
     * @param string $parentId
     * @param int    $nbLevel
     * @param string $language
     * @param string $siteId
     *
     * @return array
     */
    protected function getTreeParentIdLevelAndLanguage($parentId, $nbLevel, $language, $siteId)
    {
        $result = array();

        if ($nbLevel >= 0) {
            $qa = $this->buildTreeRequest($language, $siteId);
            $qa->match(array('parentId' => $parentId));

            $nodes = $this->hydrateAggregateQuery($qa);
            $result = $nodes;

            if (is_array($nodes)) {
                foreach ($nodes as $node) {
                    $temp = $this->getTreeParentIdLevelAndLanguage($node->getNodeId(), $nbLevel-1, $language, $siteId);
                    $result = array_merge($result, $temp);
                }
            }
        }

        return $result;
    }

    /**
     * @param Stage $qa
     *
     * @return array
     */
    protected function findLastVersion(Stage $qa)
    {
        $elementName = 'node';
        $qa->sort(array('createdAt' => 1));
        $qa->group(array(
            '_id' => array('nodeId' => '$nodeId'),
            $elementName => array('$last' => '$$ROOT')
        ));

        return $this->hydrateAggregateQuery($qa, $elementName, 'getNodeId');
    }

    /**
     * @param Stage $qa
     *
     * @return array
     */
    protected function findLastVersionInLanguage(Stage $qa)
    {
        $elementName = 'node';
        $qa->sort(array('createdAt' => 1));
        $qa->group(array(
            '_id' => array('nodeId' => '$nodeId', 'language' => '$language'),
            $elementName => array('$last' => '$$ROOT')
        ));

        return $this->hydrateAggregateQuery($qa, $elementName);
    }

    /**
     * @param string $nodeId
     *
     * @return boolean
     */
    public function testUniquenessInContext($nodeId)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(array('nodeId' => $nodeId));

        return $this->countDocumentAggregateQuery($qa) > 0;
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return ReadNodeInterface
     */
    public function findPublishedByLanguageAndSiteId($language, $siteId)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'siteId'=> $siteId,
                'language'=> $language,
                'status.publishedState' => true,
                'nodeType' => NodeInterface::TYPE_DEFAULT
            )
        );

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param StatusableInterface $element
     *
     * @return array
     */
    public function findPublished(StatusableInterface $element)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($element->getSiteId(), $element->getLanguage());
        $filter = array(
            'status.publishedState' => true,
            'deleted' => false,
            'nodeId' => $element->getNodeId(),
        );
        $qa->match($filter);

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $parentId
     * @param string $routePattern
     * @param string $nodeId
     * @param string $siteId
     *
     * @return array
     */
    public function findByParentAndRoutePattern($parentId, $routePattern, $nodeId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'parentId'     => $parentId,
                'routePattern' => $routePattern,
                'nodeId'       => array('$ne' => $nodeId),
            )
        );

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $parentId
     * @param int    $order
     * @param string $nodeId
     * @param string $siteId
     *
     * @return bool
     */
    public function hasOtherNodeWithSameParentAndOrder($parentId, $order, $nodeId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'parentId' => $parentId,
                'order'    => $order,
                'nodeId'   => array('$ne' => $nodeId),
                'deleted'  => false,
                'nodeType' => NodeInterface::TYPE_DEFAULT
            )
        );
        $node = $this->singleHydrateAggregateQuery($qa);

        return $node instanceof NodeInterface;
    }

    /**
     * @param string $parentId
     * @param int    $order
     * @param string $siteId
     *
     * @return bool
     */
    public function hasNodeWithSameParentAndOrder($parentId, $order, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'parentId' => $parentId,
                'order'    => $order,
                'deleted'  => false,
                'nodeType' => NodeInterface::TYPE_DEFAULT
            )
        );
        $node = $this->singleHydrateAggregateQuery($qa);

        return $node instanceof NodeInterface;
    }

    /**
     * @return ReadNodeInterface
     */
    public function findLastPublished()
    {
        $qb = $this->createQueryBuilder();
        $qb->field('status.publishedState')->equals(true);
        $qb->field('deleted')->equals(false);
        $qb->sort('updatedAt', 'desc');

        return $qb->getQuery()->getSingleResult();
    }

    /**
     * @param string $nodeType
     * @param int    $skip
     * @param int    $limit
     *
     * @return array
     */
    public function findAllPublishedByTypeWithSkipAndLimit($nodeType, $skip, $limit)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'nodeType' => $nodeType,
                'status.publishedState' => true,
                'deleted' => false
            )
        );
        $qa->sort(array('createdAt' => 1));
        $qa->skip($skip);
        $qa->limit($limit);

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $nodeType
     *
     * @return int
     */
    public function countAllPublishedByType($nodeType)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'nodeType' => $nodeType,
                'status.publishedState' => true,
                'deleted' => false
            )
        );

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string       $id
     * @param string       $siteId
     * @param array|null   $eventTypes
     * @param boolean|null $published
     * @param int|null     $limit
     * @param array|null   $sort
     *
     * @return array
     */
    public function findByHistoryAndSiteId($id, $siteId, array $eventTypes = null, $published = null, $limit = null, array $sort = null)
    {
        $qa = $this->createAggregationQuery();
        $filter = array(
            'nodeType' => NodeInterface::TYPE_DEFAULT,
            'histories.user.$id' => new \MongoId($id),
            'siteId' => $siteId,
            'deleted' => false
        );
        if (null !== $eventTypes) {
            $filter['histories.eventType'] = array('$in' => $eventTypes);
        }
        if (null !== $published) {
            $filter['status.publishedState'] = $published;
        }
        $qa->match($filter);

        if (null !== $sort) {
            $qa->sort($sort);
        }

        if (null !== $limit) {
            $qa->limit($limit);
        }

        return $this->hydrateAggregateQuery($qa);
    }

    /**
     * @param string $blockId
     *
     * @return boolean
     */
    public function countBlockUsed($blockId)
    {
        $qb = $this->createQueryBuilder();
        $function = new \MongoCode(
            'function() {
                for (var areaIndex in this.areas)
                    for (var key in this.areas[areaIndex].blocks)
                        if (this.areas[areaIndex].blocks[key].$id == "'.$blockId.'")
                            return this;
            }'
        );
        $qb->where($function);

        return $qb->getQuery()->execute()->count();
    }

    /**
     * @param string  $blockId
     * @param string  $areaName
     * @param string  $nodeId
     * @param string  $siteId
     * @param string  $language
     * @param string  $version
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    public function removeBlockInArea($blockId, $areaName, $nodeId, $siteId, $language, $version)
    {
        $qb = $this->createQueryBuilder();
        $qb->updateMany()
            ->field('nodeId')->equals($nodeId)
            ->field('siteId')->equals($siteId)
            ->field('language')->equals($language)
            ->field('version')->equals($version)
            ->field('areas.'.$areaName.'.blocks.$id')->equals(new \MongoId($blockId))
            ->field('areas.'.$areaName.'.blocks')->pull(array('$id' => new \MongoId($blockId)))
            ->getQuery()
            ->execute();
    }

    /**
     * @param string $referenceNodeId
     * @param string $nodeId
     * @param string $siteId
     * @param string $entityType
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    public function updateUseReference($referenceNodeId, $nodeId, $siteId, $entityType)
    {
        $this->createQueryBuilder()
            ->updateMany()
            ->field('nodeId')->equals($nodeId)
            ->field('siteId')->equals($siteId)
            ->field('useReferences.'.$entityType.'.'.$referenceNodeId)->set($referenceNodeId)
            ->getQuery()
            ->execute();
    }

    /**
     * @param array $nodeIds
     *
     * @throws \Exception
     */
    public function removeNodeVersions(array $nodeIds)
    {
        $nodeMongoIds = array();
        foreach ($nodeIds as $nodeId) {
            $nodeMongoIds[] = new \MongoId($nodeId);
        }

        $qb = $this->createQueryBuilder();
        $qb->remove()
            ->field('id')->in($nodeMongoIds)
            ->getQuery()
            ->execute();
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     *
     * @throws \Exception
     */
    public function softDeleteNode($nodeId, $siteId)
    {
        $qb = $this->createQueryBuilder();
        $qb->updateMany()
            ->field('nodeId')->equals($nodeId)
            ->field('siteId')->equals($siteId)
            ->field('deleted')->set(true)
            ->getQuery()
            ->execute();
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     * @param string $parentId
     * @param string $path
     *
     * @throws \Exception
     */
    public function restoreDeletedNode($nodeId, $siteId, $parentId = null , $path = null)
    {
        $qb = $this->createQueryBuilder();
        $qb->updateMany()
            ->field('nodeId')->equals($nodeId)
            ->field('siteId')->equals($siteId)
            ->field('deleted')->set(false);

        if (null !== $parentId && null !== $path) {
            $qb->field('path')->set($path)
               ->field('parentId')->set($parentId);
        }

        $qb->getQuery()->execute();
    }

    /**
     * @param string $parentId
     * @param string $siteId
     *
     * @return int
     */
    public function countByParentId($parentId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'parentId' => $parentId,
                'deleted'  => false,
            )
        );

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string $parentId
     * @param string $siteId
     *
     * @return int
     */
    public function countById($id, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'nodeId' => $id,
                'deleted'  => false,
            )
        );

        return $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param string $nodeId
     * @param string $siteId
     *
     * @return int
     */
    public function hasNodeIdWithoutAutoUnpublishToState($nodeId, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(
            array(
                'nodeId'  => $nodeId,
                'status.autoUnpublishToState' => false
            )
        );

        return 0 !== $this->countDocumentAggregateQuery($qa);
    }

    /**
     * @param $siteId
     *
     * @return Collection
     *
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    public function findWithUseReferences($siteId)
    {
        $where = "function() { return this.useReferences && Object.keys(this.useReferences).length > 0; }";
        $qb = $this->createQueryBuilder();
        $qb->field('siteId')->equals($siteId)
           ->field('useReferences')->where($where);

        return $qb->getQuery()->execute();
    }

    /**
     * @param PaginateFinderConfiguration $configuration
     *
     * @return array
     */
    protected function getFilterSearch(PaginateFinderConfiguration $configuration) {
        $filter = array();
        $name = $configuration->getSearchIndex('name');
        if (null !== $name && $name !== '') {
            $filter['name'] = new MongoRegex('/.*'.$name.'.*/i');
        }

        $inMenu = $configuration->getSearchIndex('inMenu');
        if (null !== $inMenu && $inMenu !== '') {
            $filter['inMenu'] = (boolean) $inMenu;
        }

        $status = $configuration->getSearchIndex('status');
        if (null !== $status && $status !== '') {
            $filter['status.name'] = $status;
        }

        return $filter;
    }

    /**
     * @param array  $nodes
     * @param string $parentId
     *
     * @return array
     */
    protected function generateTree(array $nodes, $parentId)
    {
        if (empty($nodes)) {
            return array();
        }

        $nodesRoot = array_filter($nodes, function($node, $key) use ($parentId) {
            $property = 'nodeId';
            if (NodeInterface::ROOT_PARENT_ID === $parentId) {
                $property = 'parentId';
            }
            if ($parentId !== $node['node'][$property]) {
                return false;
            }
            return true;
        }, ARRAY_FILTER_USE_BOTH);

        $tree = array();
        foreach ($nodesRoot as $nodeRoot) {
            $tree[] = array('node' => $nodeRoot['node'], 'child' => $this->getChildren($nodeRoot['nodeId'], $nodes));
        }
        uasort($tree, array($this, 'sortTree'));
        $tree = array_values($tree);

        return $tree;
    }

    /**
     * @param string $parentId
     * @param array  $nodes
     *
     * @return array
     */
    protected function getChildren($parentId, array $nodes)
    {
        $children = array();
        foreach ($nodes as $position => $node) {
            if ($parentId === $node['node']['parentId']) {
                unset($nodes[$position]);
                $children[] = array('node' => $node['node'], 'child' => $this->getChildren($node['nodeId'], $nodes));
            }
        }
        uasort($children, array($this, 'sortTree'));
        $children = array_values($children);

        return $children;
    }

    /**
     * @param ReadNodeInterface $node1
     * @param ReadNodeInterface $node2
     *
     * @return int
     */
    protected function sortTree($node1, $node2)
    {
        $order1 = $node1['node']['order'];
        $order2 = $node2['node']['order'];

        if ($order1 == $order2 || $order1 == -1 || $order2 == -1) {
            return 0;
        }

        return ($order1 < $order2) ? -1 : 1;
    }

    /**
     * @param string $siteId
     *
     * @return Stage
     */
    protected function createAggregationQueryBuilderWithSiteId($siteId)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(array('siteId' => $siteId));

        return $qa;
    }

    /**
     * @param string $language
     * @param string $siteId
     *
     * @return Stage
     */
    protected function buildTreeRequest($language, $siteId)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language);
        $qa->match(
            array(
                'status.publishedState' => true,
                'deleted' => false,
            )
        );

        return $qa;
    }

    /**
     * @param string $siteId
     * @param string $language
     *
     * @return Stage
     */
    protected function createAggregationQueryBuilderWithSiteIdAndLanguage($siteId, $language)
    {
        $qa = $this->createAggregationQueryBuilderWithSiteId($siteId);
        $qa->match(array('language' => $language));

        return $qa;
    }

    /**
     * @param string $type
     * @param string $siteId
     * @param bool   $deleted
     *
     * @return array
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    protected function prepareFindLastVersion($type, $siteId, $deleted)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'siteId' => $siteId,
                'deleted' => $deleted,
                'nodeType' => $type
            )
        );

        return $this->findLastVersion($qa);
    }

    /**
     * @param string $language
     * @param string $field
     * @param string $siteId
     *
     * @return array
     * @throws \Doctrine\ODM\MongoDB\MongoDBException
     */
    protected function getTreeByLanguageAndFieldAndSiteId($language, $field, $siteId)
    {
        $qa = $this->createAggregationQuery();
        $qa->match(
            array(
                'siteId' => $siteId,
                'language' => $language,
                'status.publishedState' => true,
                'deleted' => false,
                $field => true
            )
        );

        return $this->hydrateAggregateQuery($qa);
    }
}


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

namespace cascade\components\web\widgets\decorator;

use canis\helpers\Html;
use yii\bootstrap\Nav;

/**
 * PanelDecorator [[@doctodo class_description:cascade\components\web\widgets\decorator\PanelDecorator]].
 *
 * @author Jacob Morrison <email@ofjacob.com>
 */
class PanelDecorator extends Decorator
{
    /**
     * @var [[@doctodo var_type:panelCssClass]] [[@doctodo var_description:panelCssClass]]
     */
    public $panelCssClass = 'panel';
    /**
     * @var [[@doctodo var_type:panelStateCssClass]] [[@doctodo var_description:panelStateCssClass]]
     */
    public $panelStateCssClass = 'panel-default';
    /**
     * @var [[@doctodo var_type:gridCellClass]] [[@doctodo var_description:gridCellClass]]
     */
    public $gridCellClass = 'canis\web\grid\Cell';

    /**
     * [[@doctodo method_description:generatePanelTitle]].
     *
     * @return [[@doctodo return_type:generatePanelTitle]] [[@doctodo return_description:generatePanelTitle]]
     */
    public function generatePanelTitle()
    {
        $parts = [];
        if ($this->owner->title) {
            $menu = $icon = null;
            $titleMenu = $this->owner->generateTitleMenu();
            if ($titleMenu) {
                $menu = $titleMenu;
            }
            if (!empty($this->owner->icon)) {
                $icon = Html::tag('i', '', ['class' => 'panel-icon ' . $this->owner->icon]) . Html::tag('span', '', ['class' => 'break']);
            }
            $parts[] = Html::tag('div', Html::tag('h2', $icon . $this->owner->parseText($this->owner->title)) . $menu, ['class' => 'panel-heading']);
        }
        if (empty($parts)) {
            return false;
        }

        return implode("", $parts);
    }

    /**
     * @inheritdoc
     */
    public function generateStart()
    {
        Html::addCssClass($this->owner->htmlOptions, $this->owner->panelCssClass);
        Html::addCssClass($this->owner->htmlOptions, $this->owner->panelStateCssClass);

        return parent::generateStart();
    }

    /**
     * [[@doctodo method_description:generateHeader]].
     *
     * @return [[@doctodo return_type:generateHeader]] [[@doctodo return_description:generateHeader]]
     */
    public function generateHeader()
    {
        $parts = [];
        $title = $this->owner->generatePanelTitle();
        if ($title) {
            $parts[] = $title;
        }
        $parts[] = Html::beginTag('div', ['class' => 'panel-body']);

        return implode("", $parts);
    }

    /**
     * [[@doctodo method_description:generateTitleMenu]].
     *
     * @return [[@doctodo return_type:generateTitleMenu]] [[@doctodo return_description:generateTitleMenu]]
     */
    public function generateTitleMenu()
    {
        $menu = $this->owner->getHeaderMenu();
        if (empty($menu)) {
            return false;
        }
        $this->backgroundifyMenu($menu);

        return Nav::widget([
            'items' => $menu,
            'encodeLabels' => false,
            'options' => ['class' => 'pull-right nav-pills'],
        ]);
    }

    /**
     * [[@doctodo method_description:backgroundifyMenu]].
     *
     * @param [[@doctodo param_type:items]] $items [[@doctodo param_description:items]]
     *
     * @return [[@doctodo return_type:backgroundifyMenu]] [[@doctodo return_description:backgroundifyMenu]]
     */
    protected function backgroundifyMenu(&$items)
    {
        if (!is_array($items)) {
            return;
        }
        foreach ($items as $k => $v) {
            if (!isset($items[$k]['linkOptions'])) {
                $items[$k]['linkOptions'] = [];
            }
            if (!isset($items[$k]['linkOptions']['data-background'])) {
                $items[$k]['linkOptions']['data-handler'] = 'background';
            }
            if (isset($items[$k]['items'])) {
                $this->backgroundifyMenu($items[$k]['items']);
            }
        }
    }

    /**
     * [[@doctodo method_description:generateFooter]].
     *
     * @return [[@doctodo return_type:generateFooter]] [[@doctodo return_description:generateFooter]]
     */
    public function generateFooter()
    {
        $parts = [];
        $parts[] = Html::endTag('div'); // panel-body

        return implode("", $parts);
    }

    /**
     * Get panel title.
     *
     * @return [[@doctodo return_type:getPanelTitle]] [[@doctodo return_description:getPanelTitle]]
     */
    public function getPanelTitle()
    {
        return $this->owner->parseText($this->owner->title);
    }
}


<?php
namespace app\manage\controller;

use think\Request;
use core\manage\logic\JobLogic;

class Job extends Base
{

    /**
     * 任务列表
     *
     * @param Request $request
     * @return string
     */
    public function index(Request $request)
    {
        $this->site_title = '任务列表';
        $job_logic = JobLogic::instance();

        // 队列列表
        $queue_list = $job_logic->getJobQueue();
        $this->assign('queue_list', $queue_list);

        $map = [];

        // 队列
        $queue = $request->param('queue', '');
        if (! empty($queue)) {
            $map['queue'] = $queue;
        }
        $this->assign('queue', $queue);

        // 关键词
        $keyword = $request->param('keyword', '');
        if (! empty($keyword)) {
            $map['payload'] = [
                'exp',
                'like \'%' . str_replace([
                    '"',
                    '\\'
                ], [
                    '',
                    '%'
                ], json_encode($keyword)) . '%\''
            ];
        }
        $this->assign('keyword', $keyword);

        // 总数
        $total_count = $job_logic->model->where($map)->count();
        $this->assign('total_count', $total_count);

        // 列表
        $list = $job_logic->model->where($map)->paginate($this->rows_num);
        $list_new = [];
        foreach ($list as $vo) {
            $vo['payload'] = var_export(json_decode($vo['payload'], true), true);
            $list_new[] = $vo;
        }
        $this->assign('list', $list_new);
        $this->assign('page', $list->render());

        return $this->fetch();
    }

    /**
     * 任务状态
     *
     * @param Request $request
     * @return mixed
     */
    public function modifyStatus(Request $request)
    {
        $id = $request->param('id');
        if (empty($id)) {
            return $this->error('任务ID为空');
        }

        $status = $request->param('value', 0);
        if ($status == 0) {
            $data = [
                'reserved' => 0,
                'reserved_at' => null
            ];
        } elseif ($status == 1) {
            $data = [
                'reserved' => 1,
                'reserved_at' => time()
            ];
        } else {
            $time = $request->param('time', 10);
            $data = [
                'reserved' => 0,
                'reserved_at' => null,
                'available_at' => time() + $time
            ];
        }
        JobLogic::model()->saveById($data, $id);

        return $this->success('操作成功', 'javascript:history.go(0);');
    }

    /**
     * 删除任务
     *
     * @param Request $request
     * @return mixed
     */
    public function delJob(Request $request)
    {
        $job_id = $request->param('job_id');
        if (empty($job_id)) {
            return $this->error('任务ID为空');
        }

        $res = JobLogic::model()->del($job_id);
        if ($res) {
            return $this->success('删除任务成功', 'history.go(0)');
        } else {
            return $this->error('删除任务失败');
        }
    }
}


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

namespace cascade\components\web\widgets;

use cascade\components\web\widgets\section\Section;
use Yii;

/**
 * Collector [[@doctodo class_description:cascade\components\web\widgets\Collector]].
 *
 * @author Jacob Morrison <email@ofjacob.com>
 */
class Collector extends \canis\base\collector\Module
{
    /**
     * @var [[@doctodo var_type:producedWidgets]] [[@doctodo var_description:producedWidgets]]
     */
    public $producedWidgets = [];
    /**
     * @var [[@doctodo var_type:lastBuildId]] [[@doctodo var_description:lastBuildId]]
     */
    public $lastBuildId;

    /**
     * @var [[@doctodo var_type:_lazy]] [[@doctodo var_description:_lazy]]
     */
    protected $_lazy  = false;

    /**
     * Get lazy.
     *
     * @return [[@doctodo return_type:getLazy]] [[@doctodo return_description:getLazy]]
     */
    public function getLazy()
    {
        return $this->_lazy;
    }

    /**
     * Set lazy.
     *
     * @param [[@doctodo param_type:lazy]] $lazy [[@doctodo param_description:lazy]]
     */
    public function setLazy($lazy)
    {
        $this->_lazy = $lazy;
    }

    /**
     * @inheritdoc
     */
    public function getCollectorItemClass()
    {
        return Item::className();
    }

    /**
     * @inheritdoc
     */
    public function getModulePrefix()
    {
        return 'Widget';
    }

    /**
     * [[@doctodo method_description:build]].
     *
     * @param cascade\components\web\widgets\section\Section $section          [[@doctodo param_description:section]]
     * @param [[@doctodo param_type:widgetName]]             $widgetName       [[@doctodo param_description:widgetName]]
     * @param array                                          $instanceSettings [[@doctodo param_description:instanceSettings]] [optional]
     *
     * @return [[@doctodo return_type:build]] [[@doctodo return_description:build]]
     */
    public function build(Section $section = null, $widgetName, $instanceSettings = [])
    {
        if (is_object($widgetName)) {
            $widget = $widgetName;
        } else {
            $widget = $this->getOne($widgetName);
        }
        $widgetObject = $widget->object;
        if (is_null($widgetObject)) {
            return false;
        }
        if (isset($section)) {
            $widgetObject->attachDecorator($section->widgetDecoratorClass);
            $widgetObject->section = $section;
        }
        $widgetObject->owner = $widget->owner;
        Yii::configure($widgetObject, $instanceSettings);
        $cell = $widgetObject->cell;

        $this->lastBuildId = $widgetObject->getWidgetId();
        $this->producedWidgets[$widgetObject->widgetId] = ['widget' => $widgetObject->systemId, 'id' => $widgetObject->widgetId, 'params' => $widgetObject->recreateParams];

        return $cell;
    }

    /**
     * Get location.
     *
     * @param unknown $location
     * @param unknown $owner    (optional)
     *
     * @return unknown
     */
    public function getLocation($location, $owner = null)
    {
        $bucket = $this->getBucket('locations:' . $location);
        if (is_null($owner)) {
            return $bucket->toArray();
        } else {
            $result = [];
            foreach ($bucket as $key => $widget) {
                if ($widget->owner === $owner) {
                    $result[$key] = $widget;
                }
            }

            return $result;
        }
    }
}


<?php

namespace Chill\PersonBundle\Entity;

/*
 * Chill is a software for social workers
 *
 * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
 * <http://www.champs-libres.coop>, <info@champs-libres.coop>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

use Symfony\Component\Validator\ExecutionContextInterface;
use Chill\MainBundle\Entity\Country;
use Chill\PersonBundle\Entity\MaritalStatus;
use Doctrine\Common\Collections\ArrayCollection;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\Address;
use Doctrine\Common\Collections\Criteria;

/**
 * Person
 */
class Person implements HasCenterInterface {
    /** @var integer  The person's id */
    private $id;

    /** @var string The person's first name */
    private $firstName;

    /** @var string The person's last name */
    private $lastName;

    /** @var \DateTime The person's birthdate */
    private $birthdate; //to change in birthdate

    /** @var string The person's place of birth */
    private $placeOfBirth = '';

    /** @var \Chill\MainBundle\Entity\Country The person's country of birth */
    private $countryOfBirth;

    /** @var \Chill\MainBundle\Entity\Country The person's nationality */
    private $nationality;

    /** @var string The person's gender */
    private $gender;

    const MALE_GENDER = 'man';
    const FEMALE_GENDER = 'woman';

    /** @var \Chill\PersonBundle\Entity\MaritalStatus The marital status of the person */
    private $maritalStatus;

    //TO-ADD : address

    /** @var string The person's email */
    private $email = '';

    /** @var string The person's phonenumber */
    private $phonenumber = '';

    //TO-ADD : caseOpeningDate

    //TO-ADD nativeLanguag

    /**
     * @var \Doctrine\Common\Collections\ArrayCollection The person's spoken
     * languages (ArrayCollection of Languages)
     */
    private $spokenLanguages;

    /** @var \Chill\MainBundle\Entity\Center The person's center */
    private $center;

    /**
     * @var \Doctrine\Common\Collections\ArrayCollection The person's
     * accompanying periods (when the person was accompanied by the center)*/
    private $accompanyingPeriods; //TO-CHANGE in accompanyingHistory

    /** @var string A remark over the person */
    private $memo = ''; // TO-CHANGE in remark

    /**
     * @var boolean
     */
    private $proxyAccompanyingPeriodOpenState = false; //TO-DELETE ?

    /** @var array Array where customfield's data are stored */
    private $cFData;

    /**
     *
     * @var \Doctrine\Common\Collections\Collection
     */
    private $addresses;

    public function __construct(\DateTime $opening = null) {
        $this->accompanyingPeriods = new ArrayCollection();
        $this->spokenLanguages = new ArrayCollection();
        $this->addresses = new ArrayCollection();

        if ($opening === null) {
            $opening = new \DateTime();
        }

        $this->open(new AccompanyingPeriod($opening));
    }

    /**
     * @param \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingPeriod
     * @uses AccompanyingPeriod::setPerson
     */
    public function addAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) {
        $accompanyingPeriod->setPerson($this);
        $this->accompanyingPeriods->add($accompanyingPeriod);
    }

    public function removeAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) {
        $this->accompanyingPeriods->remove($accompanyingPeriod);
    }

    /**
     * set the Person file as open at the given date.
     *
     * For updating a opening's date, you should update AccompanyingPeriod instance
     * directly.
     *
     * For closing a file, @see this::close
     *
     * To check if the Person and its accompanying period is consistent, use validation.
     *
     * @param \Chill\PersonBundle\Entity\AccompanyingPeriod $accompanyingPeriod
     */
    public function open(AccompanyingPeriod $accompanyingPeriod) {
        $this->proxyAccompanyingPeriodOpenState = true;
        $this->addAccompanyingPeriod($accompanyingPeriod);
    }

    /**
     *
     * Set the Person file as closed at the given date.
     *
     * For update a closing date, you should update AccompanyingPeriod instance
     * directly.
     *
     * To check if the Person and its accompanying period are consistent, use validation.
     *
     * @param accompanyingPeriod
     * @throws \Exception if two lines of the accompanying period are open.
     */
    public function close(AccompanyingPeriod $accompanyingPeriod = null)
    {
        $this->proxyAccompanyingPeriodOpenState = false;
    }

    /**
     *
     * @return null|AccompanyingPeriod
     */
    public function getCurrentAccompanyingPeriod() {
        if ($this->proxyAccompanyingPeriodOpenState === false) {
            return null;
        }

        foreach ($this->accompanyingPeriods as $period) {
            if ($period->isOpen()) {
                return $period;
            }
        }
    }

    /**
     *
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public function getAccompanyingPeriods() {
        return $this->accompanyingPeriods;
    }

    /**
     * Get the accompanying periods of a give person with the
     * chronological order.
     *
     * @return AccompanyingPeriod[]
     */
    public function getAccompanyingPeriodsOrdered() {
        $periods = $this->getAccompanyingPeriods()->toArray();

        //order by date :
        usort($periods, function($a, $b) {
            $dateA = $a->getOpeningDate();
            $dateB = $b->getOpeningDate();

            if ($dateA == $dateB) {
                $dateEA = $a->getClosingDate();
                $dateEB = $b->getClosingDate();

                if ($dateEA == $dateEB) {
                    return 0;
                }

                if ($dateEA < $dateEB) {
                    return -1;
                } else {
                    return +1;
                }
            }

            if ($dateA < $dateB) {
                return -1 ;
            } else {
                return 1;
            }
        });

        return $periods;
    }

    public function isOpen() {
        return $this->proxyAccompanyingPeriodOpenState;
    }

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set firstName
     *
     * @param string $firstName
     * @return Person
     */
    public function setFirstName($firstName)
    {
        $this->firstName = $firstName;

        return $this;
    }

    /**
     * Get firstName
     *
     * @return string
     */
    public function getFirstName()
    {
        return $this->firstName;
    }

    /**
     * Set lastName
     *
     * @param string $lastName
     * @return Person
     */
    public function setLastName($lastName)
    {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * Get lastName
     *
     * @return string
     */
    public function getLastName()
    {
        return $this->lastName;
    }

    /**
     * Set birthdate
     *
     * @param \DateTime $birthdate
     * @return Person
     */
    public function setBirthdate($birthdate)
    {
        $this->birthdate = $birthdate;

        return $this;
    }

    /**
     * Get birthdate
     *
     * @return \DateTime
     */
    public function getBirthdate()
    {
        return $this->birthdate;
    }

    /**
     * Set placeOfBirth
     *
     * @param string $placeOfBirth
     * @return Person
     */
    public function setPlaceOfBirth($placeOfBirth)
    {
        if ($placeOfBirth === null) {
            $placeOfBirth = '';
        }

        $this->placeOfBirth = $placeOfBirth;

        return $this;
    }

    /**
     * Get placeOfBirth
     *
     * @return string
     */
    public function getPlaceOfBirth()
    {
        return $this->placeOfBirth;
    }

    /**
     * Set gender
     *
     * @param string $gender
     * @return Person
     */
    public function setGender($gender)
    {
        $this->gender = $gender;

        return $this;
    }

    /**
     * Get gender
     *
     * @return string
     */
    public function getGender()
    {
        return $this->gender;
    }

    /**
     * return gender as a Numeric form.
     * This is used for translations
     * @return int
     */
    public function getGenderNumeric() {
        if ($this->getGender() == self::FEMALE_GENDER) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * Set memo
     *
     * @param string $memo
     * @return Person
     */
    public function setMemo($memo)
    {
        if ($memo === null) {
            $memo = '';
        }

        if ($this->memo !== $memo) {
            $this->memo = $memo;
        }

        return $this;
    }

    /**
     * Get memo
     *
     * @return string
     */
    public function getMemo()
    {
        return $this->memo;
    }

    /**
     * Set maritalStatus
     *
     * @param \Chill\PersonBundle\Entity\MaritalStatus $maritalStatus
     * @return Person
     */
    public function setMaritalStatus(MaritalStatus $maritalStatus = null)
    {
        $this->maritalStatus = $maritalStatus;
        return $this;
    }

    /**
     * Get maritalStatus
     *
     * @return \Chill\PersonBundle\Entity\MaritalStatus
     */
    public function getMaritalStatus()
    {
        return $this->maritalStatus;
    }

    /**
     * Set email
     *
     * @param string $email
     * @return Person
     */
    public function setEmail($email)
    {
        if ($email === null) {
            $email = '';
        }

        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set countryOfBirth
     *
     * @param Chill\MainBundle\Entity\Country $countryOfBirth
     * @return Person
     */
    public function setCountryOfBirth(Country $countryOfBirth = null)
    {
        $this->countryOfBirth = $countryOfBirth;
        return $this;
    }

    /**
     * Get countryOfBirth
     *
     * @return Chill\MainBundle\Entity\Country
     */
    public function getCountryOfBirth()
    {
        return $this->countryOfBirth;
    }

    /**
     * Set nationality
     *
     * @param Chill\MainBundle\Entity\Country $nationality
     * @return Person
     */
    public function setNationality(Country $nationality = null)
    {
        $this->nationality = $nationality;

        return $this;
    }

    /**
     * Get nationality
     *
     * @return Chill\MainBundle\Entity\Country
     */
    public function getNationality()
    {
        return $this->nationality;
    }

    public function getLabel() {
        return $this->getFirstName()." ".$this->getLastName();
    }

    /**
     * Get center
     *
     * @return \Chill\MainBundle\Entity\Center
     */
    public function getCenter()
    {
        return $this->center;
    }

    /**
     * Set the center
     *
     * @param \Chill\MainBundle\Entity\Center $center
     * @return \Chill\PersonBundle\Entity\Person
     */
    public function setCenter(\Chill\MainBundle\Entity\Center $center)
    {
        $this->center = $center;
        return $this;
    }

    /**
     * Set cFData
     *
     * @param array $cFData
     *
     * @return Report
     */
    public function setCFData($cFData)
    {
        $this->cFData = $cFData;

        return $this;
    }

    /**
     * Get cFData
     *
     * @return array
     */
    public function getCFData()
    {
        if ($this->cFData === null) {
            $this->cFData = [];
        }
        return $this->cFData;
    }

    /**
     * Set phonenumber
     *
     * @param string $phonenumber
     * @return Person
     */
    public function setPhonenumber($phonenumber = '')
    {
        $this->phonenumber = $phonenumber;

        return $this;
    }

    /**
     * Get phonenumber
     *
     * @return string
     */
    public function getPhonenumber()
    {
        return $this->phonenumber;
    }

    public function __toString()
    {
        return $this->getLabel();
    }

    /**
     * Set spokenLanguages
     *
     * @param type $spokenLanguages
     * @return Person
     */
    public function setSpokenLanguages($spokenLanguages)
    {
        $this->spokenLanguages = $spokenLanguages;

        return $this;
    }

    /**
     * Get spokenLanguages
     *
     * @return ArrayCollection
     */
    public function getSpokenLanguages()
    {
        return $this->spokenLanguages;
    }

    public function addAddress(Address $address)
    {
        $this->addresses[] = $address;

        return $this;
    }

    public function removeAddress(Address $address)
    {
        $this->addresses->removeElement($address);
    }

    /**
     * By default, the addresses are ordered by date, descending (the most
     * recent first)
     *
     * @return \Chill\MainBundle\Entity\Address[]
     */
    public function getAddresses()
    {
        return $this->addresses;
    }

    public function getLastAddress(\DateTime $date = null)
    {
        if ($date === null) {
            $date = new \DateTime('now');
        }

        $addresses = $this->getAddresses();

        if ($addresses == null) {

            return null;
        }

        return $addresses->first();
    }

    /**
     * Validation callback that checks if the accompanying periods are valid
     *
     * This method add violation errors.
     */
    public function isAccompanyingPeriodValid(ExecutionContextInterface $context)
    {
        $r = $this->checkAccompanyingPeriodsAreNotCollapsing();

        if ($r !== true) {
            if ($r['result'] === self::ERROR_PERIODS_ARE_COLLAPSING) {
                $context->addViolationAt('accompanyingPeriods',
                    'Two accompanying periods have days in commun',
                    array());
            }

            if ($r['result'] === self::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD) {
                $context->addViolationAt('accompanyingPeriods',
                    'A period is opened and a period is added after it',
                    array());
            }
        }
    }

    const ERROR_PERIODS_ARE_COLLAPSING = 1; // when two different periods
    // have days in commun
    const ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD = 2; // where there exist
    // a period opened and another one after it

    /**
     * Function used for validation that check if the accompanying periods of
     * the person are not collapsing (i.e. have not shared days) or having
     * a period after an open period.
     *
     * @return true | array True if the accompanying periods are not collapsing,
     * an array with data for displaying the error
     */
    public function checkAccompanyingPeriodsAreNotCollapsing()
    {
        $periods = $this->getAccompanyingPeriodsOrdered();
        $periodsNbr = sizeof($periods);
        $i = 0;

        while($i < $periodsNbr - 1) {
            $periodI = $periods[$i];
            $periodAfterI = $periods[$i + 1];

            if($periodI->isOpen()) {
                return array(
                    'result' => self::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD,
                    'dateOpening' => $periodAfterI->getOpeningDate(),
                    'dateClosing' => $periodAfterI->getClosingDate(),
                    'date' => $periodI->getOpeningDate()
                );
            } elseif ($periodI->getClosingDate() >= $periodAfterI->getOpeningDate()) {
                return array(
                    'result' => self::ERROR_PERIODS_ARE_COLLAPSING,
                    'dateOpening' => $periodI->getOpeningDate(),

                    'dateClosing' => $periodI->getClosingDate(),
                    'date' => $periodAfterI->getOpeningDate()
                );
            }
            $i++;
        }

        return true;
    }
}


<?php

namespace nadir2\core;

/**
 * This is a class of controller wrapper, an instance of which perfoms delegated
 * function - it calls target controller after the succesful auth or calls onFail
 * auth class functionality in other case.
 * @author Leonid Selikhov
 */
class CtrlWrapper
{
    /** @var \nadir2\core\AbstractWebCtrl The target controller object. */
    protected $ctrl = null;

    /**
     * The constructor assigns the object-wrapper with controller object.
     * @param \nadir2\core\AbstractWebCtrl $ctrl The controller object.
     */
    public function __construct(AbstractWebCtrl $ctrl)
    {
        $this->ctrl = $ctrl;
    }

    /**
     * The method calls user auth checking, on successful complition of which
     * it invokes the target controller and the onFail method of Auth class in
     * other case.
     * @param string $actionName The action name of target controller.
     * @param mixed[] $actionArgs The action parameters.
     */
    protected function processAuth(string $actionName, array $actionArgs): void
    {
        // Lambda
        $callAction = function (string $actionName, array $actionArgs) {
            if (empty($actionArgs)) {
                $this->ctrl->{$actionName}();
            } else {
                (new \ReflectionMethod($this->ctrl, $actionName))
                    ->invokeArgs($this->ctrl, $actionArgs);
            }
        };

        if (class_exists('\extensions\core\Auth')) {
            $auth = new \extensions\core\Auth($this->ctrl->getRequest());
            $auth->run();
            if ($auth->isValid()) {
                $callAction($actionName, $actionArgs);
            } else {
                $auth->onFail();
            }
        } else {
            $callAction($actionName, $actionArgs);
        }
    }

    /**
     * This is the method-interseptor of method calling of the target controller.
     * @param string $actionName The action name of target controller.
     * @param mixed[] $actionArgs The action parameters.
     */
    public function __call(string $actionName, array $actionArgs): void
    {
        $this->processAuth($actionName, $actionArgs);
    }
}


<?php
/**
 * @author Semenov Alexander <semenov@skeeks.com>
 * @link http://skeeks.com/
 * @copyright 2010 SkeekS (СкикС)
 * @date 29.08.2016
 */
namespace skeeks\cms\importCsvContent;

use skeeks\cms\importCsv\handlers\CsvHandler;
use skeeks\cms\importCsv\helpers\CsvImportRowResult;
use skeeks\cms\importCsv\ImportCsvHandler;
use skeeks\cms\importCsvContent\widgets\MatchingInput;
use skeeks\cms\models\CmsContent;
use skeeks\cms\models\CmsContentElement;
use skeeks\cms\models\CmsContentElementProperty;
use skeeks\cms\models\CmsContentProperty;
use skeeks\cms\models\CmsContentPropertyEnum;
use skeeks\cms\relatedProperties\PropertyType;
use skeeks\cms\relatedProperties\propertyTypes\PropertyTypeElement;
use skeeks\cms\relatedProperties\propertyTypes\PropertyTypeList;
use yii\base\Exception;
use yii\helpers\ArrayHelper;
use yii\helpers\Json;
use yii\widgets\ActiveForm;

/**
 * @property CmsContent $cmsContent
 *
 * Class CsvContentHandler
 *
 * @package skeeks\cms\importCsvContent
 */
class ImportCsvContentHandler extends ImportCsvHandler
{
    public $content_id = null;
    public $unique_field = null;

    public function getAvailableFields()
    {
        $element = new CmsContentElement([
            'content_id' => $this->cmsContent->id
        ]);

        $fields = [];

        foreach ($element->attributeLabels() as $key => $name)
        {
            $fields['element.' . $key] = $name;
        }

        $element->relatedPropertiesModel->initAllProperties();
        foreach ($element->relatedPropertiesModel->attributeLabels() as $key => $name)
        {
            $fields['property.' . $key] = $name . " [свойство]";
        }

        $fields['image'] = 'Ссылка на главное изображение';

        return array_merge(['' => ' - '], $fields);
    }

    /**
     * @return null|CmsContent
     */
    public function getCmsContent()
    {
        if (!$this->content_id)
        {
            return null;
        }

        return CmsContent::findOne($this->content_id);
    }

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

        $this->name = \Yii::t('skeeks/importCsvContent', '[CSV] Import content items');
    }

    /**
     * Соответствие полей
     * @var array
     */
    public $matching = [];

    public function rules()
    {
        return ArrayHelper::merge(parent::rules(), [

            ['content_id' , 'required'],
            ['content_id' , 'integer'],

            ['unique_field' , 'string'],

            [['matching'], 'safe'],
            [['matching'], function($attribute) {
                /*if (!in_array('element.name', $this->$attribute))
                {
                    $this->addError($attribute, "Укажите соответствие названия");
                }*/
            }]
        ]);
    }

    public function attributeLabels()
    {
        return ArrayHelper::merge(parent::attributeLabels(), [
            'content_id'        => \Yii::t('skeeks/importCsvContent', 'Контент'),
            'matching'          => \Yii::t('skeeks/importCsvContent', 'Preview content and configuration compliance'),
            'unique_field'      => \Yii::t('skeeks/importCsvContent', 'Уникальная колонка'),
        ]);
    }

    /**
     * @param ActiveForm $form
     */
    public function renderConfigForm(ActiveForm $form)
    {
        parent::renderConfigForm($form);

        echo $form->field($this, 'content_id')->listBox(
            array_merge(['' => ' - '], CmsContent::getDataForSelect()), [
            'size' => 1,
            'data-form-reload' => 'true'
        ]);

        if ($this->content_id && $this->rootFilePath && file_exists($this->rootFilePath))
        {
            echo $form->field($this, 'matching')->widget(
                \skeeks\cms\importCsv\widgets\MatchingInput::className(),
                [
                    'columns' => $this->getAvailableFields()
                ]
            );

            echo $form->field($this, 'unique_field')->listBox(
                array_merge(['' => ' - '], $this->getAvailableFields()), [
                'size' => 1,
            ]);
        }
    }

    protected function _initModelByField(CmsContentElement &$cmsContentElement, $fieldName, $value)
    {
        if (strpos("field_" . $fieldName, 'element.'))
        {
            $realName = str_replace("element.", "", $fieldName);
            $cmsContentElement->{$realName} = $value;

        } else if (strpos("field_" . $fieldName, 'property.'))
        {

            $realName = str_replace("property.", "", $fieldName);
            $property = $cmsContentElement->relatedPropertiesModel->getRelatedProperty($realName);

            if ($property->property_type == PropertyType::CODE_ELEMENT)
            {
                if ($property = $cmsContentElement->relatedPropertiesModel->getRelatedProperty($realName))
                {
                    $content_id = $property->handler->content_id;

                    $brand = CmsContentElement::find()
                                ->where(['content_id' => $content_id])
                                ->andWhere(['name' => $value])
                                ->one()
                            ;

                    if (!$brand)
                    {
                        $brand = new CmsContentElement();
                        $brand->name = $value;
                        $brand->content_id = $content_id;
                        $brand->save();
                    }

                    if ($brand && !$brand->isNewRecord)
                    {
                        $cmsContentElement->relatedPropertiesModel->setAttribute($realName, $brand->id);
                    }

                }

            } else if($property->property_type == PropertyType::CODE_LIST)
            {
                if ($property = $cmsContentElement->relatedPropertiesModel->getRelatedProperty($realName))
                {
                    if ( $enum = $property->getEnums()->andWhere(['value' => $value])->one() )
                    {

                    } else
                    {
                        $enum = new CmsContentPropertyEnum();
                        $enum->value        = $value;
                        $enum->property_id  = $property->id;
                        $enum->save();
                    }

                    if ($enum && !$enum->isNewRecord)
                    {
                        $cmsContentElement->relatedPropertiesModel->setAttribute($realName, $enum->id);
                    }
                }
            } else
            {
                $cmsContentElement->relatedPropertiesModel->setAttribute($realName, $value);
            }
        }
    }

    /**
     * @param $code
     *
     * @return int|null
     */
    public function getColumnNumber($code)
    {
        if (in_array($code, $this->matching))
        {
            foreach ($this->matching as $number => $codeValue)
            {
                if ($codeValue == $code)
                {
                    return (int) $number;
                }
            }
        }

        return null;
    }

    /**
     * @param $code
     * @param array $row
     *
     * @return null
     */
    public function getValue($code, $row = [])
    {
        $number = $this->getColumnNumber($code);

        if ($number !== null)
        {
            return $row[$number];
        }

        return null;
    }

    /**
     * @param $fieldName
     * @param $uniqueValue
     * @param null $contentId
     *
     * @return array|null|\yii\db\ActiveRecord
     */
    public function getElement($fieldName, $uniqueValue, $contentId = null)
    {
        $element = null;

        if (!$contentId)
        {
            $contentId = $this->content_id;
        }

        if (strpos("field_" . $fieldName, 'element.'))
        {
            $realName = str_replace("element.", "", $fieldName);
            $element = CmsContentElement::find()->where([$realName => $uniqueValue])->one();

        } else if (strpos("field_" . $fieldName, 'property.'))
        {
            $realName = str_replace("property.", "", $fieldName);

            /**
             * @var $property CmsContentProperty
             */
            $property = CmsContentProperty::find()->where(['code' => $realName])->one();
            $query = CmsContentElement::find();
            CmsContentElement::filterByProperty($query, $property, $uniqueValue);

            //print_r($query->createCommand()->rawSql);die;
            $element = $query->one();
        }

        return $element;
    }
    /**
     * @param $number
     * @param $row
     *
     * @return array|null|CmsContentElement|\yii\db\ActiveRecord
     * @throws Exception
     */
    protected function _initElement($number, $row, $contentId = null, $className = null)
    {
        if (!$contentId)
        {
            $contentId    =   $this->content_id;
        }

        if (!$className)
        {
            $className = CmsContentElement::className();
        }

        if (!$this->unique_field)
        {
            $element                = new $className();
            $element->content_id    =   $contentId;
        } else
        {
            $uniqueValue = trim($this->getValue($this->unique_field, $row));
            if ($uniqueValue)
            {
                if (strpos("field_" . $this->unique_field, 'element.'))
                {
                    $realName = str_replace("element.", "", $this->unique_field);
                    $element = CmsContentElement::find()->where([$realName => $uniqueValue])->one();

                } else if (strpos("field_" . $this->unique_field, 'property.'))
                {
                    $realName = str_replace("property.", "", $this->unique_field);

                    /**
                     * @var $property CmsContentProperty
                     */
                    $property = CmsContentProperty::find()->where(['code' => $realName])->one();
                    $query = $className::find();
                    $className::filterByProperty($query, $property, $uniqueValue);

                    $element = $query->one();
                    //print_r($query->createCommand()->rawSql);die;

                    /*$element = $className::find()

                        ->joinWith('relatedElementProperties map')
                        ->joinWith('relatedElementProperties.property property')

                        ->andWhere(['property.code'     => $realName])
                        ->andWhere(['map.value'         => $uniqueValue])

                        ->joinWith('cmsContent as ccontent')
                        ->andWhere(['ccontent.id'        => $contentId])

                        ->one()
                    ;*/
                }
            } else
            {
                throw new Exception('Не задано уникальное значение');
            }

            if (!$element)
            {
                $element                = new $className();
                $element->content_id    =   $contentId;
            }
        }

        return $element;
    }

    /**
     * @param $number
     * @param $row
     *
     * @return CsvImportRowResult
     */
    public function import($number, $row)
    {
        $result = new CsvImportRowResult();

        try
        {
            $isUpdate = false;
            $element = $this->_initElement($number, $row);

            foreach ($this->matching as $number => $fieldName)
            {
                //Выбрано соответствие
                if ($fieldName)
                {
                    $this->_initModelByField($element, $fieldName, $row[$number]);
                }
            }

            if (!$element->isNewRecord)
            {
                $isUpdate = true;
            }

            $element->validate();
            $element->relatedPropertiesModel->validate();

            if (!$element->errors && !$element->relatedPropertiesModel->errors)
            {
                $element->save();

                if (!$element->relatedPropertiesModel->save())
                {
                    throw new Exception('Не сохранены дополнительные данные');
                };

                $imageUrl = $this->getValue('image', $row);
                if ($imageUrl && !$element->image)
                {
                    try
                    {
                        $file = \Yii::$app->storage->upload($imageUrl, [
                            'name' => $element->name
                        ]);

                        $element->link('image', $file);

                    } catch (\Exception $e)
                    {
                        //\Yii::error('Not upload image to: ' . $cmsContentElement->id . " ({$realUrl})", 'import');
                    }
                }

                $result->message        =   $isUpdate === true ? "Элемент обновлен" : 'Элемент создан' ;

                $element->relatedPropertiesModel->initAllProperties();
                $rp = Json::encode($element->relatedPropertiesModel->toArray());
                $rp = '';
                $result->html           =   <<<HTML
Элемент: url}" data-pjax="0" target="_blank">{$element->id} $rp
HTML;

            } else
            {
                $result->success        =   false;
                $result->message        =   'Ошибка';
                $result->html           =   Json::encode($element->errors) . "
" . Json::encode($element->relatedPropertiesModel->errors);
            }

            $result->data           =   $this->matching;

            unset($element->relatedPropertiesModel);
            unset($element);

        } catch (\Exception $e)
        {
            $result->success        =   false;
            $result->message        =   $e->getMessage();
        }

        return $result;
    }

    public function memoryUsage($usage, $base_memory_usage) {
        return \Yii::$app->formatter->asSize($usage - $base_memory_usage);
    }

    public function execute()
    {
        ini_set("memory_limit","8192M");
        set_time_limit(0);

        $base_memory_usage = memory_get_usage();
        $this->memoryUsage(memory_get_usage(), $base_memory_usage);

        $rows = $this->getCsvColumnsData($this->startRow, $this->endRow);
        $results = [];
        $totalSuccess = 0;
        $totalErrors = 0;

        $this->result->stdout("\tCSV import: c {$this->startRow} по {$this->endRow}\n");

        foreach ($rows as $number => $data)
        {
            $result = $this->import($number, $data);
            if ($result->success)
            {
                $this->result->stdout("\tСтрока: {$number}: {$result->message}\n");
                $totalSuccess++;
            } else
            {
                $this->result->stdout("\tСтрока: {$number}: ошибка: {$result->message}\n");
                $totalErrors++;
            }

            unset($result);
            echo $this->memoryUsage(memory_get_usage(), $base_memory_usage);

            //$results[$number] = $result;
        }

        return $this->result;
    }
}


<?php

declare (strict_types=1);

namespace Coshi\Variator\Variation\Type;

use Coshi\Variator\ConfigResolver;
use Coshi\Variator\Variation\AbstractVariation;

class IntVariation extends AbstractVariation
{
    /**
     * @var array|int
     */
    private $min;

    /**
     * @var array|int
     */
    private $max;

    /**
     * @var int
     */
    private $current;

    /**
     * @var array
     */
    private $minCallback;

    /**
     * @var array
     */
    private $maxCallback;

    public function __construct(string $name, array $parameters, ConfigResolver $configResolver)
    {
        parent::__construct($name, $parameters, $configResolver);

        if (is_array($parameters['max'])) {
            $this->maxCallback = $configResolver->resolveCallback($parameters['max']);
        } else {
            $this->max = (int) $parameters['max'];
        }

        if (is_array($parameters['min'])) {
            $this->minCallback = $configResolver->resolveCallback($parameters['min']);
        } else {
            $this->min = (int) $parameters['min'];
        }
    }

    /**
     * {@inheritdoc}
     */
    public static function validateParameters(array $parameters) : bool
    {
        if (!isset($parameters['max'], $parameters['min'])) {
            throw new \InvalidArgumentException('Min and max parameters should be defined');
        }

        if (!is_numeric($parameters['max']) && !is_array($parameters['max'])) {
            throw new \InvalidArgumentException('Max parameter should be numeric or callable');
        }

        if (!is_numeric($parameters['min']) && !is_array($parameters['min'])) {
            throw new \InvalidArgumentException('Min parameter should be numeric or callable');
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function dependsOn(string $name) : bool
    {
        if (!parent::dependsOn($name)) {
            return false;
        }

        if (is_array($this->maxCallback)) {
            return $this->containsArgumentsPattern($this->maxCallback['arguments'], $name);
        }

        if (is_array($this->minCallback)) {
            return $this->containsArgumentsPattern($this->minCallback['arguments'], $name);
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public static function validateValue($value) : bool
    {
        return is_numeric($value);
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return 0;
    }

    /**
     * {@inheritdoc}
     */
    public function getType() : string
    {
        return 'int';
    }

    /**
     * {@inheritdoc}
     */
    public function getCurrent()
    {
        return (int) $this->current;
    }

    /**
     * {@inheritdoc}
     */
    public function pushForward()
    {
        ++$this->current;
    }

    /**
     * {@inheritdoc}
     */
    public function isValid() : bool
    {
        return $this->current <= $this->max;
    }

    protected function doRewind()
    {
        if (is_array($this->maxCallback)) {
            $this->max = (int) $this->configResolver->call($this->maxCallback);
        }

        if (is_array($this->minCallback)) {
            $this->min = (int) $this->configResolver->call($this->minCallback);
        }
        $this->current = $this->min;
    }
}


<?php

namespace Cobak78\GeoHash;

/**
 * Class GeoHash
 * @package Cobak78\GeoHash
 */
class GeoHash
{
    /**
     * @param  float  $lat1
     * @param  float  $lon1
     * @param  float  $lat2
     * @param  float  $lon2
     * @param  string $unit
     * @return float
     */
    public function distance(
        float $lat1,
        float $lon1,
        float $lat2,
        float $lon2,
        string $unit = 'K'
    ) {
        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;

        switch (strtoupper($unit)) {
            case "K":
            case "KM":
                return ($miles * 1.609344);
            case "MN":
            case "N":
                return ($miles * 0.8684);
            default:
                return $miles;
        }
    }

    /**
     * geoBoundBox: [ top_left => [ lat => x, lon => y ], bottom_right => [ lat => x, lon => y ]
     *
     * @param  array      $geoBoundBox
     * @param  int        $squares
     * @return mixed
     * @throws \Exception
     */
    public function getGeoHashPrecision(array $geoBoundBox, int $squares)
    {
        $remainder = $squares % 2;
        $xQuotient = $squares / 2;
        $yQuotient = $xQuotient / 2;

        if (0 !== $remainder) {
            throw new \InvalidArgumentException('geoHash divisions must be multiple of 2');
        }

        // get x distance
        $xDist = $this->distance($geoBoundBox['top_left']['lat'], $geoBoundBox['top_left']['lon'], $geoBoundBox['bottom_right']['lat'], $geoBoundBox['top_left']['lon']);
        $xDist = $xDist * 1000;
        // get y distance
        $yDist = $this->distance($geoBoundBox['top_left']['lat'], $geoBoundBox['top_left']['lon'], $geoBoundBox['top_left']['lat'], $geoBoundBox['bottom_right']['lon']);
        $yDist = $yDist * 1000;

        // geohash distances
        $xGeohashDist = $xDist / $xQuotient;
        $yGeohashDist = $yDist / $yQuotient;

        return $this->getPrecisionFromArea($xGeohashDist, $yGeohashDist);

    }

    private function getPrecisionFromArea($width, $height)
    {
        // return
        $precisionMap = [
            1 => [5009400, 4992000.6],
            2 => [1252300, 624100],
            3 => [156500, 156000],
            4 => [39100, 19500],
            5 => [4900, 4900],
            6 => [1200, 609.4],
            7 => [152.9, 152.4],
            8 => [38.2, 19],
            9 => [4.8, 4.8],
            10 => [1.2, 0.595],
            11 => [0.14, 0.149],
            12 => [0.037, 0.019]
        ];

        foreach ($precisionMap as $key => $value) {
            if ($width > $value[0] || $height > $value[1]) {
                return $key;
            }
        }

        return 12;
    }
}


<?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\Deploy;

use RedKiteCms\Content\BlockManager\BlockManagerApprover;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class SaveAllPagesController is the object assigned to save the website in production
 *
 * @author  RedKite Labs <webmaster@redkite-labs.com>
 * @package RedKiteCms\Rendering\Controller\Page
 */
abstract class SaveAllPagesController extends BaseSavePageController
{
    /**
     * Implements the action to save the website
     * @param array $options
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function save(array $options)
    {
        $serializer = $options["serializer"];
        $deployer = $options["deployer"];
        $languages = $options["configuration_handler"]->languages();

        $blockManager = new BlockManagerApprover($serializer, new OptionsResolver());
        $deployer
            ->contributor($options["username"])
            ->saveAllPages($blockManager, $languages);

        $this->buldSitemap($options);
        $this->removeCache($options);

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


<?php

namespace OpenOrchestra\ModelBundle\DataFixtures\MongoDB;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use OpenOrchestra\ModelBundle\Document\Status;

/**
 * Class AbstractLoadStatus
 */
abstract class AbstractLoadStatus extends AbstractFixture implements OrderedFixtureInterface
{
    /**
     * @param string $enName
     * @param string $frName
     * @param string $name
     * @param string $color
     * @param bool   $publishedState
     * @param bool   $initialState
     * @param bool   $blockedEdition
     * @param bool   $outOfWorkflow
     * @param bool   $autoPublishFromState
     * @param bool   $autoUnpublishToState
     * @param bool   $translationState
     *
     * @return Status
     */
    protected function loadStatus(
        $enName,
        $frName,
        $name,
        $color,
        $publishedState = false,
        $initialState = false,
        $blockedEdition = false,
        $outOfWorkflow = false,
        $autoPublishFromState = false,
        $autoUnpublishToState = false,
        $translationState = false
    ) {
        $value = new Status();
        $value->setName($name);
        $value->setPublishedState($publishedState);
        $value->setInitialState($initialState);
        $value->setAutoPublishFromState($autoPublishFromState);
        $value->setAutoUnpublishToState($autoUnpublishToState);
        $value->addLabel('en', $enName);
        $value->addLabel('fr', $frName);
        $value->setDisplayColor($color);
        $value->setBlockedEdition($blockedEdition);
        $value->setOutOfWorkflow($outOfWorkflow);
        $value->setTranslationState($translationState);

        $this->addReference('status-' . $name, $value);

        return $value;
    }
}


<?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\Content\SlotsManager;

use RedKiteCms\Configuration\ConfigurationHandler;
use RedKiteCms\Exception\General\RuntimeException;

/**
 * Class SlotsManagerFactory is the factory object assigned to build a slot manager from repeat status
 *
 * @author  RedKite Labs <webmaster@redkite-labs.com>
 * @package RedKiteCms\Content\SlotsManager
 */
class SlotsManagerFactory implements SlotsManagerFactoryInterface
{
    /**
     * @type \RedKiteCms\Configuration\ConfigurationHandler
     */
    private $configurationHandler;

    /**
     * Constructor
     *
     * @param \RedKiteCms\Configuration\ConfigurationHandler $configurationHandler
     */
    public function __construct(ConfigurationHandler $configurationHandler)
    {
        $this->configurationHandler = $configurationHandler;
    }

    /**
     *{@inheritdoc}
     */
    public function createSlotManager($repeat)
    {
        $repeatedNormalized = ucfirst($repeat);
        $slotsManager = 'RedKiteCms\Content\SlotsManager\SlotsManager' . $repeatedNormalized;
        if (!class_exists($slotsManager)) {
            throw new RuntimeException(
                sprintf("SlotsManagerFactory cannot instantiate the SlotsManager%s object", $repeatedNormalized)
            );
        }

        $dataDir = $this->configurationHandler->dataDir();
        $siteName = $this->configurationHandler->siteName();

        return new $slotsManager($dataDir, $siteName);
    }
}


<?php
/*
 * This file is part of the infotech/document-generator package.
 *
 * (c) Infotech, Ltd
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Infotech\DocumentGenerator\DataStructure\MetadataSource;

use Infotech\DocumentGenerator\DataSource\FetcherInterface;
use Infotech\DocumentGenerator\DataStructure\Definition\StructureDefinitionInterface;

abstract class MetadataFetcher implements FetcherInterface
{
    /**
     * @var StructureDefinitionInterface
     */
    private $structure;

    public function __construct(StructureDefinitionInterface $structure)
    {
        $this->structure = $structure;
    }

    /**
     * @return \Infotech\DocumentGenerator\DataStructure\Definition\StructureDefinitionInterface
     */
    public function getStructure()
    {
        return $this->structure;
    }

    /**
     * Get placeholder substitutions from origin
     *
     * @param mixed $origin original data or identifier to fetch strings
     *
     * @return array Placeholders to data strings map
     */
    public function getData($origin)
    {
        return array_reduce(
            $this->structure->getChildren(),
            function ($acc, $childDef) {
                return array_merge($acc, array($childDef['name'] => $childDef['name']));
            },
            $this->getMetadataValues()
        );
    }

    /**
     * @return array
     */
    abstract public function getMetadataValues();
}