<?php

namespace Nekland\Bundle\FeedBundle\Renderer;

use Symfony\Component\Routing\RouterInterface;

use Nekland\Bundle\FeedBundle\XML\XMLManager;
use Nekland\Bundle\FeedBundle\Feed;
use Nekland\Bundle\FeedBundle\Item\ItemInterface;
use Nekland\Bundle\FeedBundle\Item\ExtendedItemInterface;

/**
 * This class render an xml file
 * Using format RSS 2.0
 *
 * @author Nek' <nek.dev+github@gmail.com>
 * @author Yohan Giarelli <yohan@giarelli.org>
 */

class RssRenderer implements RendererInterface
{
    /**
     * @var RouterInterface
     */
    protected $router;

    /**
     * @var string
     */
    protected $basePath;

    public function __construct(RouterInterface $router, $basePath)
    {
        $this->router = $router;
        $this->basePath = $basePath;
    }

    /**
     * Renders the feed
     *
     * @param \Nekland\Bundle\FeedBundle\Feed $feed
     * @return void
     */
    public function render(Feed $feed)
    {
        $filename = sprintf('%s/%s', $this->basePath, $feed->getFilename('rss'));
        if (is_file($filename)) {
            unlink($filename);
        }

        $xml = new XMLManager($filename);
        $this->init($xml, $feed);
        $this->writeItems($xml, $feed);
        $xml->save();
    }

    /**
     * Build the feed properties
     *
     * @param \Nekland\Bundle\FeedBundle\XML\XMLManager $xml
     * @param \Nekland\Bundle\FeedBundle\Feed           $feed
     * @return void
     */
    private function init(XMLManager $xml, Feed $feed)
    {
        $root = $xml->getXml()->createElement('rss');
        $root->setAttribute('version', '2.0');
        $root = $xml->getXml()->appendChild($root);

        $channel = $xml->getXml()->createElement('channel');
        $channel = $root->appendChild($channel);

        $xml->addTextNode('description', $feed->get('description'), $channel);

        $xml->addTextNode('pubDate', $feed->get('pubDate', new \DateTime())->format(\DateTime::RSS), $channel);
        $date = new \DateTime();
        $xml->addTextNode('lastBuildDate', $date->format(\DateTime::RSS), $channel);

        $xml->addTextNode('link', $feed->get('url'), $channel);
        $xml->addTextNode('title', $feed->get('title'), $channel);
        $xml->addTextNode('language', $feed->get('language'), $channel);

        if (null !== $feed->get('copyright')) {
            $xml->addTextNode('copyright', $feed->get('copyright'), $channel);
        }

        if (null !== $feed->get('managingEditor')) {
            $xml->addTextNode('managingEditor', $feed->get('managingEditor'), $channel);
        }

        if (null !== $feed->get('generator')) {
            $xml->addTextNode('generator', $feed->get('generator'), $channel);
        }

        if (null !== $feed->get('webMaster')) {
            $xml->addTextNode('webMaster', $feed->get('webMaster'), $channel);
        }

        $image = $feed->get('image', array());
        if (isset($image['url']) && isset($image['title']) && isset($image['link'])) {

            $imageNode = $xml->getXml()->createElement('image');
            $imageNode = $channel->appendChild($imageNode);
            $xml->addTextNode('url', $image['url'], $imageNode);
            $xml->addTextNode('title', $image['url'], $imageNode);
            $xml->addTextNode('link', $image['url'], $imageNode);

            if (isset($image['height'])) {
                $xml->addTextNode('height', $image['height'], $imageNode);
            }
            if (isset($image['width'])) {
                $xml->addTextNode('width', $image['width'], $imageNode);
            }
        }

        if (null !== $feed->get('ttl')) {
            $xml->addTextNode('ttl', $feed->get('ttl'), $channel);
        }

    }

    /**
     * Write Feed Items
     *
     * @param \Nekland\Bundle\FeedBundle\XML\XMLManager $xml
     * @param \Nekland\Bundle\FeedBundle\Feed           $feed
     * @return void
     */
    private function writeItems(XMLManager $xml, Feed $feed)
    {
        foreach ($feed as $item) {
            $this->writeItem($xml, $item);
        }
    }

    /**
     * Write an ItemInterface into the feed
     *
     * @param \Nekland\Bundle\FeedBundle\XML\XMLManager     $xml
     * @param \Nekland\Bundle\FeedBundle\Item\ItemInterface $item
     * @return void
     */
    private function writeItem(XMLManager $xml, ItemInterface $item)
    {
        $nodeItem = $this->createItem($xml);
        $xml->addTextNode('title', $item->getFeedTitle(), $nodeItem);
        $xml->addTextNode('description', $item->getFeedDescription(), $nodeItem);

        $id=$item->getFeedId();
        if(empty($id))
            throw new \InvalidArgumentException('The method « getFeedId » MUST return a not empty value.');
        $xml->addTextNode('guid', $id, $nodeItem);

        $xml->addTextNode('link', $this->getRoute($item), $nodeItem);

        $xml->addTextNode('pubDate', $item->getFeedDate()->format(\DateTime::RSS), $nodeItem);

        if ($this->itemHas($item, 'getFeedAuthor')) {
            if ($author = $this->getAuthor($item)) {
                $xml->addTextNode('author', $author, $nodeItem);
            }
        }
        if ($this->itemHas($item, 'getFeedCategory')) {
            $xml->addTextNode('category', $item->getFeedCategory(), $nodeItem);
        }
        if ($this->itemHas($item, 'getFeedCommentRoute')) {
            if ($comments = $this->getComments($item)) {
                $xml->addTextNode('comments', $comments, $nodeItem);
            }
        }
        if ($this->itemHas($item, 'getFeedEnclosure')) {
            if ($enclosure = $this->getEnclosure($item, $xml)) {
                $nodeItem->appendChild($enclosure);
            }
        }
    }

    /**
     * Create an Item node
     *
     * @param \Nekland\Bundle\FeedBundle\XML\XMLManager $xml
     * @return \DOMElement
     */
    private function createItem(XMLManager $xml)
    {
        $itemNode = $xml->getXml()->createElement('item');
        $channelNode = $xml->getXml()->getElementsByTagName('channel')->item(0);
        $channelNode->appendChild($itemNode);

        return $itemNode;
    }

    /**
     * Extract the author email
     *
     * @param \Nekland\Bundle\FeedBundle\Item\ExtendedItemInterface $item
     * @return string|null
     */
    private function getAuthor(ItemInterface $item)
    {
        $authorData = $item->getFeedAuthor();
        $author = '';
        if(isset($authorData['name'])) {
            $author .= $authorData['name'];
        }
        if (isset($authorData['email'])) {
            $author .= empty($author) ? $authorData['email'] : ' ' . $authorData['email'];
        }

        if(!empty($author)) {
            return $author;
        }

        return null;
    }

    /**
     * Extracts the Comments URI
     *
     * @param \Nekland\Bundle\FeedBundle\Item\ExtendedItemInterface $item
     * @return null|string
     */
    private function getComments(ItemInterface $item)
    {
        $commentRoute = $item->getFeedCommentRoute();
        if (!$commentRoute) {
            return null;
        } elseif (is_array($commentRoute)) {
            return $this->router->generate($commentRoute[0], $commentRoute[1]);
        } else {
            return $this->router->generate($commentRoute);
        }
    }

    /**
     * Extract enclosure
     *
     * @param \Nekland\Bundle\FeedBundle\Item\ExtendedItemInterface $item
     * @param \Nekland\Bundle\FeedBundle\XML\XMLManager             $xml
     * @return \DOMElement|null
     */
    private function getEnclosure(ExtendedItemInterface $item, XMLManager $xml)
    {
        $enc = $item->getFeedEnclosure();
        if (is_array($enc)) {
            $enclosure = $xml->getXml()->createElement('enclosure');
            foreach ($enc as $key => $value) {
                $enclosure->setAttribute($key, $value);
            }

            return $enclosure;
        }

        return null;
    }

    private function getRoute(ItemInterface $item) {
        $route = $item->getFeedRoutes();
        //var_dump($item); exit();
        //var_dump($route); exit();
        if(!isset($route[0]) || !is_array($route[0])) {
            throw new \InvalidArgumentException('The « getFeedRoutes » method have to return an array of routes.');
        }
        $route = $route[0];
        if(!empty($route['route']) && is_array($route['route'])) {

            return $this->router->generate($route['route'][0], $route['route'][1], true);
        } else if(!empty($route['url'])) {

            return $route['url'];
        } else {
            return null;
        }
    }

    private function itemHas(ItemInterface $item, $method) {
        $rc = new \ReflectionClass($item);
        if($rc->hasMethod($method)) {
            return true;
        } else {
            return false;
        }
    }

}


<?php

namespace Nekland\Bundle\FeedBundle\XML;

class XMLManager
{
    private $filename;
    protected $xml;
    private $new;

    public function __construct($filename)
    {
        $this->filename = $filename;

        if ($this->new = !file_exists($this->filename)) {

            $this->xml = new \DomDocument('1.0', 'utf-8');
        } else {

            $this->xml = new \DomDocument();
            $this->xml->load($this->filename);
        }
    }

    public function addTextNode($nodeName, $content, \DOMNode $parentNode)
    {
        $node = $this->xml->createElement($nodeName);
        $node = $parentNode->appendChild($node);

        if ($this->hasXMLSyntaxMarkers($content)) {
            $node_text = $this->xml->createCDATASection($content);
        } else {
            $node_text = $this->xml->createTextNode($content);
        }

        $node->appendChild($node_text);

        return $node;
    }

    public function getFilename()
    {
        return $this->filename;
    }

    public function getXml()
    {
        return $this->xml;
    }

    public function isNew()
    {
        return $this->new;
    }

    public function save()
    {
        return $this->xml->save($this->filename);
    }

    /**
     * Check if the content has any XML syntax markers that would result in
     * invalid XML if inserted into an XML document. If it does, the content
     * should either be wrapped in a CDATA section or have all of it's
     * special XML characters converted into entities.
     *
     * @param string $content
     * @return boolean
     */
    protected function hasXMLSyntaxMarkers($content)
    {
        foreach (array('<', '&') as $char) {
            if (strpos($content, $char) !== false) {
                return true;
            }
        }

        return false;
    }
}


<?php
/*
* This file is part of the scheduler-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\SchedulerBundle\DependencyInjection\Compiler;

use Abc\Bundle\SchedulerBundle\Event\SchedulerEvents;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Registers event listeners and subscribers for schedule events.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class RegisterListenersPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    private $dispatcherService;

    /**
     * @var string
     */
    private $listenerTag;

    /**
     * @var string
     */
    private $subscriberTag;

    /**
     * Constructor.
     *
     * @param string $dispatcherServiceName Service name of the event dispatcher within the processed container
     * @param string $listenerTag
     * @param string $subscriberTag
     */
    public function __construct($dispatcherServiceName = 'event_dispatcher', $listenerTag = 'abc.scheduler.event_listener', $subscriberTag = 'abc.scheduler.event_subscriber')
    {
        $this->dispatcherService = $dispatcherServiceName;
        $this->listenerTag       = $listenerTag;
        $this->subscriberTag     = $subscriberTag;
    }

    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        if(!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService))
        {
            return;
        }

        $definition = $container->findDefinition($this->dispatcherService);

        foreach($container->findTaggedServiceIds($this->listenerTag) as $id => $tags)
        {
            $def = $container->getDefinition($id);

            if(!$def->isPublic())
            {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
            }

            if($def->isAbstract())
            {
                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
            }

            foreach($tags as $tag)
            {
                $priority = isset($tag['priority']) ? $tag['priority'] : 0;

                if(!isset($tag['method']))
                {
                    throw new \InvalidArgumentException(sprintf('Service "%s" must define the "method" attribute on "%s" tags.', $id, $this->listenerTag));
                }

                $definition->addMethodCall('addListenerService', array(SchedulerEvents::SCHEDULE, array($id, $tag['method']), $priority));
            }
        }

        foreach($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes)
        {
            $def = $container->getDefinition($id);
            if(!$def->isPublic())
            {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
            }

            // We must assume that the class value has been correctly filled, even if the service is created by a factory
            $class = $def->getClass();

            $refClass  = new \ReflectionClass($class);
            $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
            if(!$refClass->implementsInterface($interface))
            {
                throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
            }

            $definition->addMethodCall('addSubscriberService', array($id, $class));
        }
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Generator;

use PHPoole\Collection\Collection as PageCollection;
use PHPoole\Collection\Page\Page;

/**
 * Class Alias.
 */
class Alias extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();

        /* @var $page Page */
        foreach ($pageCollection as $page) {
            $aliases = [];
            if ($page->hasVariable('aliases')) {
                $aliases = $page->getVariable('aliases');
            }
            if ($page->hasVariable('alias')) {
                $aliases[] = $page->getVariable('alias');
            }
            if (!empty($aliases)) {
                foreach ($aliases as $alias) {
                    /* @var $aliasPage Page */
                    $aliasPage = (new Page())
                        ->setId($alias.'/redirect')
                        ->setPathname(Page::urlize($alias))
                        ->setTitle($alias)
                        ->setLayout('redirect.html')
                        ->setVariable('destination', $page->getPermalink())
                        ->setDate($page->getDate());
                    $generatedPages->add($aliasPage);
                }
            }
        }

        return $generatedPages;
    }
}


<?php
/*
* This file is part of the scheduler-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\SchedulerBundle\Schedule;

/**
 * ProcessorRegistry
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class ProcessorRegistry implements ProcessorRegistryInterface
{
    /**
     * @var array
     */
    private $registry = [];

    /**
     * {@inheritDoc}
     */
    public function register($type, ProcessorInterface $processor)
    {
        $this->registry[$type] = $processor;
    }

    /**
     * {@inheritDoc}
     */
    public function has($type)
    {
        return isset($this->registry[$type]);
    }

    /**
     * {@inheritDoc}
     */
    public function get($type)
    {
        if(!array_key_exists($type, $this->registry))
        {
            throw new \InvalidArgumentException(sprintf('A processor for type "%s" is not registered', $type));
        }

        return $this->registry[$type];
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Generator;

use PHPoole\Collection\Collection as PageCollection;
use PHPoole\Collection\Page\Page;

/**
 * Class PagesFromConfig.
 */
class PagesFromConfig extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();

        $fmPages = $this->config->get('site.fmpages');
        foreach ($fmPages as $file => $frontmatter) {
            $page = (new Page())
                ->setId(Page::urlize(sprintf('%s', $file)))
                ->setPathname(Page::urlize(sprintf('%s', $file)));
            $page->setVariables($frontmatter);
            if (!empty($frontmatter['layout'])) {
                $page->setLayout($frontmatter['layout']);
            }
            if (!empty($frontmatter['permalink'])) {
                $page->setPermalink($frontmatter['permalink']);
            }
            $generatedPages->add($page);
        }

        return $generatedPages;
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Collection\Taxonomy;

use PHPoole\Collection\Collection as PHPooleCollection;

/**
 * Class Collection.
 */
class Collection extends PHPooleCollection
{
    /**
     * Return a Vocabulary collection (creates it if not exists)
     * {@inheritdoc}
     */
    public function get($id)
    {
        if (!$this->has($id)) {
            $this->add(new Vocabulary($id));
        }

        return $this->items[$id];
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Generator;

use PHPoole\Collection\Collection as PageCollection;
use PHPoole\Collection\Page\Page;

/**
 * Class TitleReplace.
 */
class TitleReplace extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();

        $filteredPages = $pageCollection->filter(function (Page $page) {
            return null !== $page->getTitle();
        });

        /* @var $page Page */
        foreach ($filteredPages as $page) {
            $alteredPage = clone $page;
            $alteredPage->setTitle(strtoupper($page->getTitle()));
            $generatedPages->add($alteredPage);
        }

        return $generatedPages;
    }
}


<?php
/*
* This file is part of the scheduler-bundle package.
*
* (c) Hannes Schulz <hannes.schulz@aboutcoders.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Abc\Bundle\SchedulerBundle\Command;

use Abc\Bundle\SchedulerBundle\Iterator\IteratorRegistryInterface;
use Abc\Bundle\SchedulerBundle\Schedule\Exception\SchedulerException;
use Abc\Bundle\SchedulerBundle\Schedule\SchedulerInterface;
use Abc\ProcessControl\ControllerInterface;
use Abc\ProcessControl\NullController;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class SchedulerProcessCommand extends ContainerAwareCommand
{
    /**
     * {@inheritDoc}
     */
    protected function configure()
    {
        $this
            ->setName('abc:scheduler:process')
            ->setDescription('Process schedules')
            ->addOption('iteration', 'i', InputOption::VALUE_OPTIONAL, 'Run n iterations before exiting', false);
    }

    /**
     * {@inheritDoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $i                = 0;
        $startMemoryUsage = memory_get_usage(true);

        try {
            do {
                if ($i > 0) {
                    usleep(5000000);
                }

                $i++;
                $this->iterate($input, $output, $startMemoryUsage);
            } while (!$this->getProcessController()->doStop() && (!$input->getOption('iteration') || $i < (int)$input->getOption('iteration')));

            $output->writeln('End of iteration cycle');
        } catch (\Exception $e) {
            $output->writeln(sprintf("<error>KO - %s</error>", $e->getMessage()));
        }
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     * @param                 $startMemoryUsage
     */
    protected function iterate(InputInterface $input, OutputInterface $output, $startMemoryUsage)
    {
        foreach ($this->getIteratorRegistry()->all() as $name => $iterator) {
            try {
                $numOfProcessed = $this->getScheduler()->process($iterator);

                if ($numOfProcessed > 0) {
                    $output->writeln(
                        sprintf(
                            "<comment>processed %s schedules of iterator %s</comment>",
                            $numOfProcessed,
                            $name
                        )
                    );
                }
            } catch (SchedulerException $e) {
                $output->writeln(sprintf("<error>Failed to process %s out of %s schedules</error>", count($e->getScheduleExceptions()), $e->getNumOfProcessed()));
            }
        }
    }

    /**
     * @return SchedulerInterface
     */
    protected function getScheduler()
    {
        return $this->getContainer()->get('abc.scheduler.scheduler');
    }

    /**
     * @return IteratorRegistryInterface
     */
    protected function getIteratorRegistry()
    {
        return $this->getContainer()->get('abc.scheduler.iterator_registry');
    }

    /**
     * @return ControllerInterface
     */
    public function getProcessController()
    {
        if(!$this->getContainer()->has('abc.process_control.controller') || !$this->getContainer()->get('abc.process_control.controller') instanceof ControllerInterface) {
            return new NullController();
        }

        return $this->getContainer()->get('abc.process_control.controller');
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Generator;

use PHPoole\Collection\Collection as PageCollection;
use PHPoole\Collection\Page\Page;
use PHPoole\Page\NodeType;

/**
 * Class Pagination.
 */
class Pagination extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();

        $filteredPages = $pageCollection->filter(function (Page $page) {
            return in_array($page->getNodeType(), [NodeType::HOMEPAGE, NodeType::SECTION]);
        });

        /* @var $page Page */
        foreach ($filteredPages as $page) {
            if ($this->config->get('site.paginate.disabled')) {
                return $generatedPages;
            }

            $paginateMax = intval($this->config->get('site.paginate.max'));
            $paginatePath = $this->config->get('site.paginate.path');
            $pages = $page->getVariable('pages');
            $path = $page->getPathname();

            // paginate
            if (count($pages) > $paginateMax) {
                $paginateCount = ceil(count($pages) / $paginateMax);
                for ($i = 0; $i < $paginateCount; $i++) {
                    $pagesInPagination = array_slice($pages, ($i * $paginateMax), $paginateMax);
                    $alteredPage = clone $page;
                    // first page, if homepage id = 'index'
                    if ($i == 0) {
                        $alteredPage
                            ->setId(Page::urlize(sprintf('%s/%s', $path, $path == '' ? 'index' : '')))
                            ->setPathname(Page::urlize(sprintf('%s', $path)))
                            ->setVariable('aliases', [
                                sprintf('%s/%s/%s', $path, $paginatePath, 1),
                            ]);
                    } else {
                        $alteredPage
                            ->setId(Page::urlize(sprintf('%s/%s/%s/', $path, $paginatePath, $i + 1)))
                            ->setPathname(Page::urlize(sprintf('%s/%s/%s', $path, $paginatePath, $i + 1)))
                            ->unVariable('menu');
                    }
                    // pagination
                    $pagination = ['pages' => $pagesInPagination];
                    if ($i > 0) {
                        $pagination += ['prev' => Page::urlize(sprintf('%s/%s/%s', $path, $paginatePath, $i))];
                    }
                    if ($i < $paginateCount - 1) {
                        $pagination += ['next' => Page::urlize(sprintf('%s/%s/%s', $path, $paginatePath, $i + 2))];
                    }
                    $alteredPage
                        ->setVariable('pagination', $pagination)
                        ->setVariable('date', reset($pagination['pages'])->getDate());

                    $generatedPages->add($alteredPage);
                }
            }
        }

        return $generatedPages;
    }
}


<?php
/*
 * Copyright (c) Arnaud Ligny <arnaud@ligny.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PHPoole\Generator;

use PHPoole\Collection\Collection as PageCollection;
use PHPoole\Collection\Page\Page;
use PHPoole\Collection\Taxonomy\Collection as TaxonomyCollection;
use PHPoole\Collection\Taxonomy\Term as Term;
use PHPoole\Collection\Taxonomy\Vocabulary as Vocabulary;
use PHPoole\Exception\Exception;
use PHPoole\Page\NodeType;

/**
 * Class Taxonomy.
 */
class Taxonomy extends AbstractGenerator implements GeneratorInterface
{
    /* @var TaxonomyCollection */
    protected $taxonomies;
    /* @var PageCollection */
    protected $pageCollection;
    /* @var PageCollection */
    protected $generatedPages;

    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $this->pageCollection = $pageCollection;
        $this->generatedPages = new PageCollection();

        if ($this->config->get('site.taxonomies')) {
            // is taxonomies disabled
            if ($this->config->get('site.taxonomies.disabled')) {
                return $this->generatedPages;
            }

            // prepares taxonomies collection
            $this->taxonomies = new TaxonomyCollection('taxonomies');
            // adds each vocabulary collection to the taxonomies collection
            foreach ($this->config->get('site.taxonomies') as $vocabulary) {
                if ($vocabulary != 'disable') {
                    $this->taxonomies->add(new Vocabulary($vocabulary));
                }
            }

            // collects taxonomies from pages
            $this->collectTaxonomiesFromPages();

            // creates node pages
            $this->createNodePages();
        }

        return $this->generatedPages;
    }

    /**
     * Collects taxonomies from pages.
     */
    protected function collectTaxonomiesFromPages()
    {
        /* @var $page Page */
        foreach ($this->pageCollection as $page) {
            foreach (array_keys($this->config->get('site.taxonomies')) as $plural) {
                if (isset($page[$plural])) {
                    // converts a list to an array if necessary
                    if (!is_array($page[$plural])) {
                        $page->setVariable($plural, [$page[$plural]]);
                    }
                    foreach ($page[$plural] as $term) {
                        // adds each terms to the vocabulary collection
                        $this->taxonomies->get($plural)
                            ->add(new Term($term));
                        // adds each pages to the term collection
                        $this->taxonomies
                            ->get($plural)
                            ->get($term)
                            ->add($page);
                    }
                }
            }
        }
    }

    /**
     * Creates node pages.
     */
    protected function createNodePages()
    {
        /* @var $terms Vocabulary */
        foreach ($this->taxonomies as $plural => $terms) {
            if (count($terms) > 0) {
                /*
                 * Creates $plural/$term pages (list of pages)
                 * ex: /tags/tag-1/
                 */
                /* @var $pages PageCollection */
                foreach ($terms as $term => $pages) {
                    if (!$this->pageCollection->has($term)) {
                        $pages = $pages->sortByDate()->toArray();
                        $page = (new Page())
                            ->setId(Page::urlize(sprintf('%s/%s/', $plural, $term)))
                            ->setPathname(Page::urlize(sprintf('%s/%s', $plural, $term)))
                            ->setTitle(ucfirst($term))
                            ->setNodeType(NodeType::TAXONOMY)
                            ->setVariable('pages', $pages)
                            ->setVariable('date', $date = reset($pages)->getDate())
                            ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
                            ->setVariable('pagination', ['pages' => $pages]);
                        $this->generatedPages->add($page);
                    }
                }
                /*
                 * Creates $plural pages (list of terms)
                 * ex: /tags/
                 */
                $page = (new Page())
                    ->setId(Page::urlize($plural))
                    ->setPathname(strtolower($plural))
                    ->setTitle($plural)
                    ->setNodeType(NodeType::TERMS)
                    ->setVariable('plural', $plural)
                    ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
                    ->setVariable('terms', $terms)
                    ->setVariable('date', $date);

                // add page only if a template exist
                try {
                    $this->generatedPages->add($page);
                } catch (Exception $e) {
                    echo $e->getMessage()."\n";
                    // do not add page
                    unset($page);
                }
            }
        }
    }
}


<?php

namespace Orchestra\View\Console;

use InvalidArgumentException;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\Console\Command as BaseCommand;

class OptimizeCommand extends BaseCommand
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'theme:optimize';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Pre-cache themes views in the application.';

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $this->info('Compiling views');

        $this->compileViews();
    }

    /**
     * Compile all view files.
     *
     * @return void
     */
    protected function compileViews(): void
    {
        foreach ($this->laravel['view']->getFinder()->getPaths() as $path) {
            $this->compileViewsInPath($path);
        }
    }

    /**
     * Compile all views files in path.
     *
     * @param  string  $path
     *
     * @return void
     */
    protected function compileViewsInPath(string $path): void
    {
        foreach ($this->laravel['files']->allFiles($path) as $file) {
            try {
                $engine = $this->laravel['view']->getEngineFromPath($file);
            } catch (InvalidArgumentException $e) {
                continue;
            }

            $this->compileSingleViewFile($engine, $file);
        }
    }

    /**
     * Compile single view file.
     *
     * @param  mixed  $engine
     * @param  string  $file
     *
     * @return void
     */
    protected function compileSingleViewFile($engine, string $file): void
    {
        if ($engine instanceof CompilerEngine) {
            $engine->getCompiler()->compile($file);
        }
    }
}


<?php

namespace PhpDocReader;

/**
 * Class ConfigurationBuilder
 * @author  Lucas A. de Araújo <lucas@painapp.com.br>
 * @package PhpDocReader
 */
class ConfigurationBuilder
{
    /**
     * Cria um arquivo de configuração a
     * partir de um comentário.
     * @param $comment
     * @return array
     */
    public static function build($comment)
    {
        $json = self::getJson($comment);
        $config = self::compile($json);
        return $config;
    }

    /**
     * @param $comment
     * @return array
     */
    private static function getJson($comment)
    {
        preg_match_all("/@configure (.*?)\n/", $comment, $matches);
        return $matches[1];
    }

    /**
     * Junta todas configurações em uma array
     * @param array $config
     * @return array
     */
    private static function compile(array $config)
    {
        $fullConfig = [];

        foreach ($config as $json) {
            $jsonConfig = (array)json_decode($json);
            $fullConfig = array_merge($fullConfig, $jsonConfig);
        }

        return $fullConfig;
    }
}


<?php

namespace Orchestra\View\Console;

use Illuminate\Console\Command as BaseCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

abstract class Command extends BaseCommand
{
    /**
     * Execute the console command.
     *
     * @param  \Symfony\Component\Console\Input\InputInterface  $input
     * @param  \Symfony\Component\Console\Output\OutputInterface  $output
     *
     * @return mixed
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $result = parent::execute($input, $output);

        $this->finish();

        return $result;
    }

    /**
     * Finish the console command.
     *
     * @return void
     */
    protected function finish(): void
    {
        // Save any changes to orchestra/memory
        $this->laravel['orchestra.memory']->finish();
    }
}


<?php

namespace Orchestra\Extension\Console;

use Illuminate\Console\ConfirmableTrait;

class ResetCommand extends ExtensionCommand
{
    use ConfirmableTrait;

    /**
     * The console command signature.
     *
     * @var string
     */
    protected $signature = 'extension:reset
        {name : Extension name.}
        {--force : Force the operation to run when in production.}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Reset an extension.';

    /**
     * {@inheritdoc}
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return;
        }

        $name = $this->argument('name');

        $this->laravel['orchestra.extension.finder']->detect();

        $reset = $this->laravel['orchestra.extension']->reset($name);

        if ((bool) $reset) {
            $this->info("Extension [{$name}] has been reset.");
        } else {
            $this->error("Unable to reset extension [{$name}].");
        }
    }
}


<?php

/*
 * Copyright 2013 Johannes M. Schmitt <schmittjoh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Scrutinizer\Util;

use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

abstract class DiffUtils
{
    const MAX_SIZE = 1024 * 1024 * 512;

    /**
     * Generates a DIFF.
     *
     * @param string $before
     * @param string $after
     * @return string
     */
    public static function generate($before, $after)
    {
        if ($before === $after) {
            return '';
        }

        if (strlen($before) > self::MAX_SIZE || strlen($after) > self::MAX_SIZE) {
            return '';
        }

        $beforeTmpFile = tempnam(sys_get_temp_dir(), 'diff');
        file_put_contents($beforeTmpFile, $before);

        $afterTmpFile = tempnam(sys_get_temp_dir(), 'diff');
        file_put_contents($afterTmpFile, $after);

        $proc = new Process('git diff --no-index --exit-code -- '.escapeshellarg($beforeTmpFile).' '.escapeshellarg($afterTmpFile));
        $proc->run();

        @unlink($beforeTmpFile);
        @unlink($afterTmpFile);

        switch ($proc->getExitCode()) {
            // $before and $after were deemed identical. Should normally never
            // be reached as we already check for this above.
            case 0:
                return '';

            // The first four lines contain information about the files which were being compared.
            // We can savely trim them as we only generate the diff for one file.
            case 1:
                $output = explode("\n", $proc->getOutput());

                return implode("\n", array_slice($output, 4));

            default:
                throw new ProcessFailedException($proc);
        }
    }

    public static function reverseApply($newContent, $diff)
    {
        return self::apply($newContent, $diff, true);
    }

    public static function apply($original, $diff, $reverse = false)
    {
        if ('' === trim($diff)) {
            return $original;
        }

        // Diffs must end with a new line, otherwise the command throws an error.
        if ("\n" !== substr($diff, -1)) {
            $diff .= "\n";
        }

        $oFile = tempnam(sys_get_temp_dir(), 'diff-apply');
        file_put_contents($oFile, $original);

        $pFile = tempnam(sys_get_temp_dir(), 'diff-patch');
        file_put_contents($pFile, "--- a/".basename($oFile)."\n+++ b/".basename($oFile)."\n".$diff);

        $proc = new Process(sprintf('cd %s && git apply'.($reverse ? ' --reverse' : '').' --no-index --verbose %s', escapeshellarg(dirname($pFile)), escapeshellarg(basename($pFile))));
        if (0 !== $proc->run()) {
            throw new ProcessFailedException($proc);
        }

        $appliedContent = file_get_contents($oFile);
        unlink($oFile);
        unlink($pFile);

        return $appliedContent;
    }

    /**
     * Parser for Github's DIFF format.
     *
     * @param string $diff
     * @return array an array of chunks
     */
    public static function parse($diff)
    {
        if ('' === $diff) {
            return array();
        }

        $lines = explode("\n", $diff);
        $nbLines = count($lines);
        $curLine = 0;
        $chunks = array();

        while ($curLine < $nbLines) {
            $chunks[] = self::parseChunk($lines, $nbLines, $curLine);
        }

        return $chunks;
    }

    // This is more or less a port of https://github.com/mojombo/grit/blob/master/lib/grit/diff.rb
    public static function diff($repositoryPath, $baseSha, $headSha, array $paths = array())
    {
        $proc = new Process(sprintf('cd %s && git diff %s %s -- %s',
                    escapeshellarg($repositoryPath),
                    escapeshellarg($baseSha),
                    escapeshellarg($headSha),
                    implode(' ', array_map('escapeshellarg', $paths))));

        if (0 !== $proc->run()) {
            throw new ProcessFailedException($proc);
        }

        return self::parseDiffs($proc->getOutput());
    }

    public static function parseDiffs($diffOutput)
    {
        if (empty($diffOutput)) {
            return array();
        }

        $match = function($regex, $line) {
            if ( ! preg_match($regex, $line, $match)) {
                throw new \RuntimeException(sprintf('"%s" did not match "%s".', $regex, $line));
            }

            return $match;
        };

        $lines = explode("\n", $diffOutput);

        $diffs = array();

        while ($lines) {
            list(, $aPath, $bPath) = $match('#^diff --git a/(.+?) b/(.+)$#', array_shift($lines));

            $aMode = $bMode = null;
            if (0 === strpos('old mode', $lines[0])) {
                list(, $aMode) = $match('#^old mode (\d+)#', array_shift($lines));
                list(, $bMode) = $match('#^new mode (\d+)#', array_shift($lines));
            }

            if ( ! $lines || 0 === strpos($lines[0], 'diff --git')) {
                $diffs[] = array(
                    'a_path' => $aPath,
                    'b_path' => $bPath,
                    'a_sha' => null,
                    'b_sha' => null,
                    'a_mode' => $aMode,
                    'b_mode' => $bMode,
                    'is_new' => false,
                    'is_deleted' => false,
                    'is_renamed' => false,
                    'diff' => null,
                    'sim_index' => null,
                );

                continue;
            }

            $simIndex = null;
            $newFile = $deletedFile = $renamedFile = false;

            if (0 === strpos($lines[0], 'new file')) {
                list(, $bMode) = $match('#new file mode (.+)$#', array_shift($lines));
                $newFile = true;
            } else if (0 === strpos($lines[0], 'deleted file')) {
                list(, $aMode) = $match('#deleted file mode (.+)$#', array_shift($lines));
                $deletedFile = true;
            } else if (0 === strpos($lines[0], 'similarity index')) {
                list(, $simIndex) = $match('#similarity index (\d+)%#', array_shift($lines));
                $renamedFile = true;
                array_shift($lines); // Shift away the ``rename from/to ...`` lines.
            }

            $m = $match('#^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$#', array_shift($lines));
            $m[3] = isset($m[3]) ? trim($m[3]) : null;
            list(, $aSha, $bSha, $bMode) = $m;

            $binaryFile = false;
            $diffLines = array();
            while ($lines && 0 !== strpos($lines[0], 'diff --git')) {
                if (0 === strpos($lines[0], '+++ ') || 0 === strpos($lines[0], '--- ')) {
                    array_shift($lines);

                    continue;
                }

                if (0 === strpos($lines[0], 'Binary files ')) {
                    array_shift($lines);
                    $binaryFile = true;

                    continue;
                }

                $diffLines[] = array_shift($lines);
            }

            $diffs[] = array(
                'a_path' => $aPath,
                'b_path' => $bPath,
                'a_sha' => $aSha,
                'b_sha' => $bSha,
                'a_mode' => $aMode,
                'b_mode' => $bMode,
                'is_new' => $newFile,
                'is_deleted' => $deletedFile,
                'is_renamed' => $renamedFile,
                'diff' => $binaryFile ? null : implode("\n", $diffLines),
                'sim_index' => $simIndex,
            );
        }

        return $diffs;
    }

    private static function parseChunk(array $lines, $nbLines, &$curLine)
    {
        if (!preg_match('/^@@ \-([0-9]+)(,[0-9]+)? \+([0-9]+)(?:,([0-9]+))? @@/', $lines[$curLine], $match)) {
            throw new \RuntimeException("Invalid chunk header: ".$lines[$curLine]);
        }

        $chunk = array(
            'raw_header' => $lines[$curLine],
            'original_start_index' => (int) $match[1],
            'original_size' => empty($match[2]) ? 1 : (int) substr($match[2], 1),
            'new_start_index' => (int) $match[3],
            'new_size' => isset($match[4]) ? (int) $match[4] : 1,
            'diff' => array()
        );

        while ($nbLines > ++$curLine) {
            if ('' === $lines[$curLine]) {
                $curLine += 1;
                break;
            }

            if ('@' === $lines[$curLine][0]) {
                break;
            }

            if (' ' === $lines[$curLine][0]) {
                $type = 'common';
            } else if ('-' === $lines[$curLine][0]) {
                $type = 'removed';
            } else if ('+' === $lines[$curLine][0]) {
                $type = 'added';
            } else if ('\\' === $lines[$curLine][0]) {
                $type = 'escaped';
            } else {
                throw new \RuntimeException(sprintf('Invalid line start "%s" of line "%s".', $lines[$curLine][0], $lines[$curLine]));
            }

            $chunk['diff'][] = array(
                'type' => $type,
                'content' => substr($lines[$curLine], 1),
                'raw' => $lines[$curLine],
            );
        }

        return $chunk;
    }

    private final function __construct() { }
}


<?php

namespace Orchestra\Extension\Bootstrap;

use Illuminate\Contracts\Foundation\Application;

class LoadExtension
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $app->make('orchestra.extension')
                ->attach($app->make('orchestra.memory')->makeOrFallback())
                ->boot();
    }
}


<?php

namespace Orchestra\Extension\Concerns;

use Closure;
use Illuminate\Support\Collection;

trait Dispatchable
{
    /**
     * Booted indicator.
     *
     * @var bool
     */
    protected $booted = false;

    /**
     * The status checker implementation.
     *
     * @var \Orchestra\Contracts\Extension\StatusChecker
     */
    protected $status;

    /**
     * Boot active extensions.
     *
     * @return $this
     */
    public function boot()
    {
        // Extension should be activated only if we're not running under
        // safe mode (or debug mode). This is to ensure that developer have
        // a way to disable broken extension without tempering the database.
        if (! ($this->booted() || $this->status->is('safe'))) {
            // Avoid extension booting being called more than once.
            $this->booted = true;

            $this->registerActiveExtensions();

            // Boot are executed once all extension has been registered. This
            // would allow extension to communicate with other extension
            // without having to known the registration dependencies.
            $this->dispatcher->boot();

            $this->events->dispatch('orchestra.extension: booted');
        }

        return $this;
    }

    /**
     * Boot active extensions.
     *
     * @return bool
     */
    public function booted(): bool
    {
        return $this->booted;
    }

    /**
     * Shutdown all extensions.
     *
     * @return $this
     */
    public function finish()
    {
        $this->extensions->each(function ($options, $name) {
            $this->dispatcher->finish($name, $options);
        });

        $this->extensions = new Collection();
        $this->booted = false;

        return $this;
    }

    /**
     * Register all active extension to dispatcher.
     *
     * @return void
     */
    protected function registerActiveExtensions(): void
    {
        $available = $this->memory->get('extensions.available', []);
        $active = $this->memory->get('extensions.active', []);

        // Loop all active extension and merge the configuration with
        // available config. Extension registration is handled by dispatcher
        // process due to complexity of extension boot process.

        Collection::make($active)->filter(function ($options, $name) use ($available) {
            return isset($available[$name]);
        })->map(function ($options, $name) use ($available) {
            $options['config'] = \array_merge(
                (array) $available[$name]['config'] ?? [],
                (array) $options['config'] ?? []
            );

            return $options;
        })->each(function ($options, $name) {
            $this->extensions->put($name, $options);
            $this->dispatcher->register($name, $options);
        });
    }

    /**
     * Create an event listener or execute it directly.
     *
     * @param  \Closure|null  $callback
     *
     * @return void
     */
    public function after(Closure $callback = null): void
    {
        if ($this->booted() || $this->status->is('safe')) {
            $this->app->call($callback);

            return;
        }

        $this->events->listen('orchestra.extension: booted', $callback);
    }
}


<?php

namespace Orchestra\Extension\Processors;

use Illuminate\Support\Fluent;
use Orchestra\Contracts\Extension\Command\Deactivator as Command;
use Orchestra\Contracts\Extension\Listener\Deactivator as Listener;

class Deactivator extends Processor implements Command
{
    /**
     * Deactivate an extension.
     *
     * @param  \Orchestra\Contracts\Extension\Listener\Deactivator  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function deactivate(Listener $listener, Fluent $extension)
    {
        if (! $this->factory->started($extension->get('name'))
            && ! $this->factory->activated($extension->get('name'))
        ) {
            return $listener->abortWhenRequirementMismatched();
        }

        $this->factory->deactivate($extension->get('name'));

        return $listener->deactivationHasSucceed($extension);
    }

    /**
     * Execute processor using invoke.
     *
     * @param  \Orchestra\Contracts\Extension\Listener\Deactivator  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function __invoke(Listener $listener, Fluent $extension)
    {
        return $this->deactivate($listener, $extension);
    }
}


<?php

namespace Orchestra\Extension;

use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\ServiceProvider;
use Orchestra\Contracts\Foundation\Application;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcherContract;

class ProviderRepository
{
    /**
     * Application instance.
     *
     * @var \Orchestra\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The event dispatcher implementation.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher
     */
    protected $events;

    /**
     * The filesystem instance.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $files;

    /**
     * List of compiled services.
     *
     * @var array
     */
    protected $compiled = [];

    /**
     * List of cached manifest.
     *
     * @var array
     */
    protected $manifest = [];

    /**
     * The path to the manifest file.
     *
     * @var string
     */
    protected $manifestPath;

    /**
     * Construct a new finder.
     *
     * @param  \Orchestra\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @param  \Illuminate\Filesystem\Filesystem  $files
     */
    public function __construct(Application $app, EventDispatcherContract $events, Filesystem $files)
    {
        $this->app = $app;
        $this->events = $events;
        $this->files = $files;

        $this->manifestPath = $this->app->getCachedExtensionServicesPath();
    }

    /**
     * Load available service providers.
     *
     * @param  array  $provides
     *
     * @return void
     */
    public function provides(array $provides)
    {
        $this->compiled = Collection::make($provides)->mapWithKeys(function ($provider) {
            $options = $this->manifest[$provider] ?? $this->recompileProvider($provider);

            $this->loadDeferredServiceProvider($provider, $options);
            $this->loadEagerServiceProvider($provider, $options);
            $this->loadQueuedServiceProvider($provider, $options);

            return [$provider => Arr::except($options, ['instance'])];
        })->all();
    }

    /**
     * Recompile provider by reviewing the class configuration.
     *
     * @param  string  $provider
     *
     * @return array
     */
    protected function recompileProvider($provider)
    {
        $instance = $this->app->resolveProvider($provider);

        $type = $instance->isDeferred() ? 'Deferred' : 'Eager';

        return $this->{"register{$type}ServiceProvider"}($provider, $instance);
    }

    /**
     * Load the service provider manifest JSON file.
     *
     * @return array
     */
    public function loadManifest()
    {
        $this->manifest = [];

        // The service manifest is a file containing a JSON representation of every
        // service provided by the application and whether its provider is using
        // deferred loading or should be eagerly loaded on each request to us.
        if ($this->files->exists($this->manifestPath)) {
            return $this->manifest = $this->files->getRequire($this->manifestPath);
        }

        return $this->manifest;
    }

    /**
     * Determine if the manifest should be compiled.
     *
     * @return bool
     */
    public function shouldRecompile()
    {
        return \array_keys($this->manifest) != \array_keys($this->compiled);
    }

    /**
     * Write the service manifest file to disk.
     *
     * @return array
     */
    public function writeManifest()
    {
        if ($this->shouldRecompile()) {
            $this->writeManifestFile($this->compiled);
        }
    }

    /**
     * Write an empty service manifest file to disk.
     *
     * @return array
     */
    public function writeFreshManifest()
    {
        $this->writeManifestFile($this->manifest = []);
    }

    /**
     * Write the manifest file.
     *
     * @param  array  $manifest
     *
     * @return void
     */
    protected function writeManifestFile(array $manifest = [])
    {
        $this->files->put($this->manifestPath, '<?php return '.\var_export($manifest, true).';');
    }

    /**
     * Register deferred service provider.
     *
     * @param  string  $provider
     * @param  \Illuminate\Support\ServiceProvider  $instance
     *
     * @return void
     */
    protected function registerDeferredServiceProvider($provider, ServiceProvider $instance)
    {
        return [
            'instance' => $instance,
            'eager' => false,
            'when' => $instance->when(),
            'deferred' => \array_fill_keys($instance->provides(), $provider),
        ];
    }

    /**
     * Register eager service provider.
     *
     * @param  string  $provider
     * @param  \Illuminate\Support\ServiceProvider  $instance
     *
     * @return void
     */
    protected function registerEagerServiceProvider($provider, ServiceProvider $instance)
    {
        return [
            'instance' => $instance,
            'eager' => true,
            'when' => [],
            'deferred' => [],
        ];
    }

    /**
     * Load deferred service provider.
     *
     * @param  string  $provider
     * @param  array  $options
     *
     * @return void
     */
    protected function loadDeferredServiceProvider($provider, array $options)
    {
        if ($options['eager']) {
            return;
        }

        $this->app->addDeferredServices($options['deferred']);
    }

    /**
     * Load eager service provider.
     *
     * @param  string  $provider
     * @param  array  $options
     *
     * @return void
     */
    protected function loadEagerServiceProvider($provider, array $options)
    {
        if (! $options['eager']) {
            return;
        }

        $this->app->register($options['instance'] ?? $provider);
    }

    /**
     * Load queued service provider.
     *
     * @param  string  $provider
     * @param  array  $options
     *
     * @return void
     */
    protected function loadQueuedServiceProvider($provider, array $options)
    {
        foreach ($options['when'] as $listen) {
            $this->events->listen($listen, function () use ($provider, $options) {
                $this->app->register($options['instance'] ?? $provider);
            });
        }
    }
}


<?php

namespace Orchestra\Extension\Console;

use Illuminate\Support\Fluent;
use Illuminate\Console\ConfirmableTrait;
use Orchestra\Extension\Processors\Activator as Processor;
use Orchestra\Contracts\Extension\Listener\Activator as Listener;

class ActivateCommand extends ExtensionCommand implements Listener
{
    use ConfirmableTrait;

    /**
     * The console command signature.
     *
     * @var string
     */
    protected $signature = 'extension:activate
        {name : Extension name.}
        {--force : Force the operation to run when in production.}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Activate an extension.';

    /**
     * Execute the console command.
     *
     * @param  \Orchestra\Extension\Processors\Activator  $activator
     *
     * @return void
     */
    public function handle(Processor $activator)
    {
        if (! $this->confirmToProceed()) {
            return;
        }

        return $activator($this, new Fluent(['name' => $this->argument('name')]));
    }

    /**
     * Response when extension activation has failed.
     *
     * @param  \Illuminate\Support\Fluent  $extension
     * @param  array  $errors
     *
     * @return mixed
     */
    public function activationHasFailed(Fluent $extension, array $errors)
    {
        $this->error("Unable to activate extension [{$extension->get('name')}].");
    }

    /**
     * Response when extension activation has succeed.
     *
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function activationHasSucceed(Fluent $extension)
    {
        $this->refreshRouteCache();

        $this->info("Extension [{$extension->get('name')}] activated.");
    }
}


<?php

namespace Orchestra\Extension\Processors;

use Illuminate\Support\Fluent;
use Orchestra\Contracts\Extension\Factory;
use Orchestra\Contracts\Extension\Command\Activator as Command;
use Orchestra\Contracts\Extension\Listener\Activator as Listener;

class Activator extends Processor implements Command
{
    /**
     * Activate an extension.
     *
     * @param  \Orchestra\Contracts\Extension\Listener\Activator  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function activate(Listener $listener, Fluent $extension)
    {
        if ($this->factory->started($extension->get('name'))) {
            return $listener->abortWhenRequirementMismatched();
        }

        return $this->execute($listener, 'activation', $extension, function (Factory $factory, $name) {
            $factory->activate($name);
        });
    }

    /**
     * Execute processor using invoke.
     *
     * @param  \Orchestra\Contracts\Extension\Listener\Activator  $listener
     * @param  \Illuminate\Support\Fluent  $extension
     *
     * @return mixed
     */
    public function __invoke(Listener $listener, Fluent $extension)
    {
        return $this->activate($listener, $extension);
    }
}


<?php
/*
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

namespace MpaCustomDoctrineHydrator\Factory;

use DoctrineModule\Stdlib\Hydrator\DoctrineObject;
use Locale;
use MpaCustomDoctrineHydrator\Services\EntityAttacher;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class EntityAttacherFactory implements FactoryInterface
{
    /**
     * Create service
     *
     * @param  ServiceLocatorInterface $serviceLocator
     * @return EntityAttacher
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $entityManager = $serviceLocator->get('doctrine.entitymanager.orm_default');

        return new EntityAttacher(
            $entityManager,
            $serviceLocator->get('Config')['mpacustomdoctrinehydrator']['formats'][Locale::getDefault()],
            new DoctrineObject($entityManager)
        );
    }
}


<?php

namespace Orchestra\Extension\Config;

use Orchestra\Memory\MemoryManager;
use Illuminate\Contracts\Config\Repository as Config;

class Repository
{
    /**
     * Config instance.
     *
     * @var \Illuminate\Contracts\Config\Repository
     */
    protected $config;

    /**
     * Memory instance.
     *
     * @var \Orchestra\Memory\MemoryManager
     */
    protected $memory;

    /**
     * Construct a new Config Repository instance.
     *
     * @param  \Illuminate\Contracts\Config\Repository  $config
     * @param  \Orchestra\Memory\MemoryManager  $memory
     */
    public function __construct(Config $config, MemoryManager $memory)
    {
        $this->config = $config;
        $this->memory = $memory;
    }

    /**
     * Map configuration to allow orchestra to store it in database.
     *
     * @param  string  $name
     * @param  array   $aliases
     *
     * @return bool
     */
    public function map(string $name, array $aliases): bool
    {
        $memory = $this->memory->make();
        $meta = $memory->get("extension_{$name}", []);

        foreach ($aliases as $current => $default) {
            isset($meta[$current]) && $this->config->set($default, $meta[$current]);

            $meta[$current] = $this->config->get($default);
        }

        $memory->put("extension_{$name}", $meta);

        return true;
    }
}


<?php

namespace Orchestra\View;

use Illuminate\Support\Collection;
use Illuminate\View\FileViewFinder as LaravelViewFinder;

class FileViewFinder extends LaravelViewFinder
{
    const HINT_PATH_DELIMITER = '::';

    /**
     * {@inheritdoc}
     */
    protected function findNamespacedView($name)
    {
        list($namespace, $view) = $this->parseNamespaceSegments($name);

        // Prepend global view paths to namespace hints path. This would
        // allow theme to take priority if such view exist.

        return $this->findInPaths($view, Collection::make($this->paths)->map(function ($path) use ($namespace) {
            return "{$path}/packages/{$namespace}";
        })->merge($this->hints[$namespace])->all());
    }

    /**
     * Set the active view paths.
     *
     * @param  array  $paths
     *
     * @return $this
     */
    public function setPaths($paths)
    {
        $this->paths = $paths;

        return $this;
    }
}


<?php

namespace Orchestra\Extension\Console;

use Illuminate\Console\ConfirmableTrait;

class RefreshCommand extends ExtensionCommand
{
    use ConfirmableTrait;

    /**
     * The console command signature.
     *
     * @var string
     */
    protected $signature = 'extension:refresh
        {name : Extension name.}
        {--force : Force the operation to run when in production.}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Refresh an extension.';

    /**
     * {@inheritdoc}
     */
    public function handle()
    {
        if (! $this->confirmToProceed()) {
            return;
        }

        $name = $this->argument('name');

        $refresh = $this->laravel['orchestra.extension']->refresh($name);

        if ((bool) $refresh) {
            $this->info("Extension [{$name}] refreshed.");
        } else {
            $this->error("Unable to refresh extension [{$name}].");
        }
    }
}


<?php

namespace Orchestra\Extension\Concerns;

use Orchestra\Extension\RouteGenerator;
use Illuminate\Contracts\Foundation\Application;

trait DomainAware
{
    /**
     * Register domain awareness from configuration.
     *
     * @return void
     */
    public function registerDomainAwareness()
    {
        $this->app->resolving(RouteGenerator::class, function (RouteGenerator $generator, Application $app) {
            $generator->setBaseUrl($app->make('config')->get('app.url'));
        });
    }
}


<?php

namespace Orchestra\Extension;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Orchestra\Memory\Memorizable;
use Illuminate\Support\Collection;
use Illuminate\Contracts\Container\Container;
use Orchestra\Extension\Bootstrap\LoadExtension;
use Orchestra\Contracts\Extension\Finder as FinderContract;
use Orchestra\Contracts\Extension\Factory as FactoryContract;
use Orchestra\Contracts\Extension\Dispatcher as DispatcherContract;
use Orchestra\Contracts\Extension\UrlGenerator as UrlGeneratorContract;
use Orchestra\Contracts\Extension\StatusChecker as StatusCheckerContract;

class Factory implements FactoryContract
{
    use Concerns\Dispatchable,
        Concerns\Operation,
        Memorizable;

    /**
     * Application instance.
     *
     * @var \Illuminate\Contracts\Container\Container
     */
    protected $app;

    /**
     * The event dispatcher implementation.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher
     */
    protected $events;

    /**
     * Dispatcher instance.
     *
     * @var \Orchestra\Contracts\Extension\Dispatcher
     */
    protected $dispatcher;

    /**
     * List of extensions.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $extensions;

    /**
     * List of routes.
     *
     * @var array
     */
    protected $routes = [];

    /**
     * Construct a new Application instance.
     *
     * @param  \Illuminate\Contracts\Container\Container  $app
     * @param  \Orchestra\Contracts\Extension\Dispatcher  $dispatcher
     * @param  \Orchestra\Contracts\Extension\StatusChecker  $status
     */
    public function __construct(
        Container $app,
        DispatcherContract $dispatcher,
        StatusCheckerContract $status
    ) {
        $this->app = $app;
        $this->events = $this->app->make('events');
        $this->dispatcher = $dispatcher;
        $this->extensions = new Collection();
        $this->status = $status;
    }

    /**
     * Detect all extensions.
     *
     * @return \Illuminate\Support\Collection
     */
    public function detect(): Collection
    {
        $this->events->dispatch('orchestra.extension: detecting');

        return \tap($this->finder()->detect(), function ($extensions) {
            $this->memory->put('extensions.available', $extensions->map(function ($item) {
                return Arr::except($item, ['description', 'author', 'url', 'version']);
            })->all());
        });
    }

    /**
     * Get extension finder.
     *
     * @return \Orchestra\Contracts\Extension\Finder
     */
    public function finder(): FinderContract
    {
        return $this->app->make('orchestra.extension.finder');
    }

    /**
     * Get an option for a given extension.
     *
     * @param  string  $name
     * @param  string  $option
     * @param  mixed   $default
     *
     * @return mixed
     */
    public function option(string $name, string $option, $default = null)
    {
        if (! $this->extensions->has($name)) {
            return \value($default);
        }

        return Arr::get($this->extensions->get($name), $option, $default);
    }

    /**
     * Check whether an extension has a writable public asset.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function permission(string $name): bool
    {
        $finder = $this->finder();
        $memory = $this->memory;
        $basePath = \rtrim($memory->get("extensions.available.{$name}.path", $name), '/');
        $path = $finder->resolveExtensionPath("{$basePath}/public");

        return $this->isWritableWithAsset($name, $path);
    }

    /**
     * Publish an extension.
     *
     * @param  string
     *
     * @return void
     */
    public function publish(string $name): void
    {
        $this->app->make('orchestra.publisher.migrate')->extension($name);
        $this->app->make('orchestra.publisher.asset')->extension($name);

        $this->events->dispatch('orchestra.publishing', [$name]);
        $this->events->dispatch("orchestra.publishing: {$name}");
    }

    /**
     * Register an extension.
     *
     * @param  string  $name
     * @param  string  $path
     *
     * @return bool
     */
    public function register(string $name, string $path): bool
    {
        return $this->finder()->registerExtension($name, $path);
    }

    /**
     * Get extension route handle.
     *
     * @param  string   $name
     * @param  string   $default
     *
     * @return \Orchestra\Contracts\Extension\UrlGenerator
     */
    public function route(string $name, string $default = '/'): UrlGeneratorContract
    {
        // Boot the extension.
        ! $this->booted() && $this->app->make(LoadExtension::class)->bootstrap($this->app);

        if (! isset($this->routes[$name])) {

            // All route should be manage via `orchestra/extension::handles.{name}`
            // config key, except for orchestra/foundation.
            $key = "orchestra/extension::handles.{$name}";

            $prefix = $this->app->make('config')->get($key, $default);

            $this->routes[$name] = $this->app->make('orchestra.extension.url')->handle($prefix);
        }

        return $this->routes[$name];
    }

    /**
     * Check whether an extension has a writable public asset.
     *
     * @param  string  $name
     * @param  string  $path
     *
     * @return bool
     */
    protected function isWritableWithAsset(string $name, string $path): bool
    {
        $files = $this->app->make('files');
        $publicPath = $this->app['path.public'];
        $targetPath = "{$publicPath}/packages/{$name}";

        if (Str::contains($name, '/') && ! $files->isDirectory($targetPath)) {
            list($vendor) = \explode('/', $name);
            $targetPath = "{$publicPath}/packages/{$vendor}";
        }

        $isWritable = $files->isWritable($targetPath);

        if ($files->isDirectory($path) && ! $isWritable) {
            return false;
        }

        return true;
    }
}


<?php

namespace Orchestra\Extension\Concerns;

use Illuminate\Support\Arr;

trait Operation
{
    /**
     * Activate an extension.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function activate(string $name): bool
    {
        return $this->activating($name);
    }

    /**
     * Activating an extension.
     *
     * @param  string  $name
     *
     * @return bool
     */
    protected function activating(string $name): bool
    {
        if (\is_null($active = $this->refresh($name))) {
            return false;
        }

        $this->extensions->put($name, $active[$name]);
        $this->publish($name);

        $this->dispatcher->activating($name, $active[$name]);

        return true;
    }

    /**
     * Check whether an extension is active.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function activated(string $name): bool
    {
        return \is_array($this->memory->get("extensions.active.{$name}"));
    }

    /**
     * Check whether an extension is available.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function available(string $name): bool
    {
        return \is_array($this->memory->get("extensions.available.{$name}"));
    }

    /**
     * Deactivate an extension.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function deactivate(string $name): bool
    {
        $memory = $this->memory;
        $active = $memory->get('extensions.active', []);

        if (! isset($active[$name])) {
            return false;
        }

        $memory->put('extensions.active', Arr::except($active, $name));
        $this->dispatcher->deactivating($name, $active[$name]);

        return true;
    }

    /**
     * Refresh extension configuration.
     *
     * @param  string  $name
     *
     * @return array|null
     */
    public function refresh(string $name): ?array
    {
        $memory = $this->memory;
        $available = $memory->get('extensions.available', []);
        $active = $memory->get('extensions.active', []);

        if (! isset($available[$name])) {
            return null;
        }

        // Append the activated extension to active extensions, and also
        // publish the extension (migrate the database and publish the
        // asset).
        if (! \is_null($handles = $active[$name]['config']['handles'] ?? null)) {
            Arr::set($available, "{$name}.config.handles", $handles);
        }

        $active[$name] = $available[$name];

        $memory->put('extensions.active', $active);

        return $active;
    }

    /**
     * Reset extension.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function reset(string $name): bool
    {
        $memory = $this->memory;
        $default = $memory->get("extensions.available.{$name}", []);

        $memory->put("extensions.active.{$name}", $default);

        if ($memory->has("extension_{$name}")) {
            $memory->put("extension_{$name}", []);
        }

        return true;
    }

    /**
     * Check if extension is started.
     *
     * @param  string  $name
     *
     * @return bool
     */
    public function started(string $name): bool
    {
        return $this->extensions->has($name);
    }

    /**
     * Publish an extension.
     *
     * @param  string
     *
     * @return void
     */
    abstract public function publish(string $name): void;
}


<?php
/*
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

namespace MpaCustomDoctrineHydrator\Form\Annotation;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use DoctrineORMModule\Form\Annotation\ElementAnnotationsListener as DoctrineElementAnnotationsListener;
use Zend\EventManager\EventInterface;
use Zend\Form\FormElementManager;
use Zend\Validator\Callback;

class ElementAnnotationsListener extends DoctrineElementAnnotationsListener
{
    protected $objectManager;
    protected $formElementManager;

    /**
     * @param ObjectManager      $objectManager
     * @param FormElementManager $formElementManager
     */
    public function __construct(ObjectManager $objectManager, FormElementManager $formElementManager)
    {
        $this->objectManager      = $objectManager;
        $this->formElementManager = $formElementManager;
    }

    /**
     * Exclude GENERATOR_TYPE_IDENTITY && GENERATOR_TYPE_CUSTOM
     * Because most of the time they are custom auto-incrementers
     *
     * @param  EventInterface $event
     * @internal
     * @return bool
     */
    public function handleExcludeField(EventInterface $event)
    {
        /** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata */
        $metadata    = $event->getParam('metadata');
        $identifiers = $metadata->getIdentifierFieldNames();

        return in_array($event->getParam('name'), $identifiers) &&
               ($metadata->generatorType === ClassMetadata::GENERATOR_TYPE_IDENTITY ||
                $metadata->generatorType === ClassMetadata::GENERATOR_TYPE_CUSTOM);
    }

    /**
     * @param EventInterface $event
     * @internal
     */
    public function handleFilterField(EventInterface $event)
    {
        $metadata = $event->getParam('metadata');
        if (!$metadata || !$metadata->hasField($event->getParam('name'))) {
            return;
        }

        $this->prepareEvent($event);

        $inputSpec = $event->getParam('inputSpec');

        switch ($metadata->getTypeOfField($event->getParam('name'))) {
            case 'bool':
            case 'boolean':
                $inputSpec['filters'][] = ['name' => 'Boolean'];
                break;
            case 'bigint':
            case 'integer':
            case 'smallint':
                /**
                 * empty string return null, '0' is a valid number
                 * and we don't want an empty field to return 0
                 */
                $inputSpec['filters'][] = ['name' => 'StringTrim'];
                $inputSpec['filters'][] = [
                    'name'    => 'Null',
                    'options' => [
                        'type' => 'string'
                    ],
                ];
                break;
            case 'date':
                /**
                 * grab the filters of the Date Element
                 */
                $filters = $this->formElementManager->get('Date')->getInputSpecification()['filters'];
                foreach ($filters as $filter) {
                    $inputSpec['filters'][] = $filter;
                }
                break;
            case 'datetime':
                /**
                 * grab the filters of the DateTime Element
                 */
                $filters = $this->formElementManager->get('DateTime')->getInputSpecification()['filters'];
                foreach ($filters as $filter) {
                    $inputSpec['filters'][] = $filter;
                }
                break;
            case 'time':
                /**
                 * grab the filters of the Time Element
                 */
                $filters = $this->formElementManager->get('Time')->getInputSpecification()['filters'];
                foreach ($filters as $filter) {
                    $inputSpec['filters'][] = $filter;
                }
                break;
            case 'datetimetz':
                $inputSpec['filters'][] = ['name' => 'StringTrim'];
                break;
            case 'string':
            case 'text':
                /**
                 * empty string return null, but we allow '0'
                 */
                $inputSpec['filters'][] = ['name' => 'StringTrim'];
                $inputSpec['filters'][] = [
                    'name'    => 'Null',
                    'options' => [
                        'type' => 'string'
                    ],
                ];
        }
    }

    /**
     * @param EventInterface $event
     * @internal
     */
    public function handleValidatorField(EventInterface $event)
    {
        $mapping  = $this->getFieldMapping($event);
        $metadata = $event->getParam('metadata');
        if (!$mapping) {
            return;
        }

        $this->prepareEvent($event);

        $inputSpec = $event->getParam('inputSpec');

        switch ($metadata->getTypeOfField($event->getParam('name'))) {
            case 'bool':
            case 'boolean':
                $inputSpec['validators'][] = [
                    'name'    => 'InArray',
                    'options' => ['haystack' => ['0', '1']]
                ];
                break;
            case 'float':
                $inputSpec['validators'][] = ['name' => 'Float'];
                break;
            case 'bigint':
            case 'integer':
            case 'smallint':
                $inputSpec['validators'][] = ['name' => 'Digits'];
                break;
            /**
             * here we provide a callback, because StringLength
             * validator shocks with null values
             */
            case 'string':
                if (isset($mapping['length'])) {
                    $inputSpec['validators'][] = [
                        'name'    => 'Callback',
                        'options' => [
                            'callback'        => [$this, 'stringLengthValidatorCallback'],
                            'callbackOptions' => [
                                [
                                    'length' => $mapping['length']
                                ],
                            ],
                            'messages'        => [
                                Callback::INVALID_VALUE => 'Maximum allowed text size exceeded',
                            ],
                        ],
                    ];
                }
                break;
            /**
             * grab the validators of the Date Element
             */
            case 'date':
                $validators = $this->formElementManager->get('Date')->getInputSpecification()['validators'];
                foreach ($validators as $validator) {
                    $inputSpec['validators'][] = $validator;
                }
                break;
            /**
             * grab the validators of the DateTime Element
             */
            case 'datetime':
                $validators = $this->formElementManager->get('DateTime')->getInputSpecification()['validators'];
                foreach ($validators as $validator) {
                    $inputSpec['validators'][] = $validator;
                }
                break;
            /**
             * grab the validators of the Time Element
             */
            case 'time':
                $validators = $this->formElementManager->get('Time')->getInputSpecification()['validators'];
                foreach ($validators as $validator) {
                    $inputSpec['validators'][] = $validator;
                }
                break;
        }
    }

    /**
     * @param  string $value
     * @param  array  $context
     * @param  array  $options
     * @return bool
     */
    public function stringLengthValidatorCallback($value, $context, $options)
    {
        if (null !== $value && strlen($value) > $options['length']) {
            return false;
        }

        return true;
    }
}


<?php
/*
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

namespace MpaCustomDoctrineHydrator\Factory;

use Locale;
use MpaCustomDoctrineHydrator\Form\Element\Time;
use Zend\Form\FormElementManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class TimeElementFactory implements FactoryInterface
{
    /**
     * Create service
     *
     * @param  ServiceLocatorInterface $serviceLocator
     * @return Time
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        /** @var $serviceLocator FormElementManager */
        $parentLocator = $serviceLocator->getServiceLocator();
        $config        = $parentLocator->get('Config')['mpacustomdoctrinehydrator']['formats'][Locale::getDefault()];

        $formElement = new Time();
        $formElement->setFormat($config['time_format']);
        $formElement->setAttribute('placeholder', $config['time_placeholder']);

        return $formElement;
    }
}


<?php

namespace Orchestra\View\Theme;

use Illuminate\Support\Manager;
use Illuminate\Support\Collection;
use Orchestra\Contracts\Theme\Theme as ThemeContract;

class ThemeManager extends Manager
{
    /**
     * Create an instance of the orchestra theme driver.
     *
     * @return \Orchestra\Contracts\Theme\Theme
     */
    protected function createOrchestraDriver(): ThemeContract
    {
        $theme = new Theme(
            $this->app, $this->app->make('events'), $this->app->make('files')
        );

        return $theme->initiate();
    }

    /**
     * {@inheritdoc}
     */
    public function getDefaultDriver()
    {
        return 'orchestra';
    }

    /**
     * Detect available themes.
     *
     * @return \Illuminat\Support\Collection
     */
    public function detect(): Collection
    {
        return $this->app->make('orchestra.theme.finder')->detect();
    }
}


<?php

namespace Orchestra\Extension;

use Closure;
use Illuminate\Support\Fluent;
use Illuminate\Contracts\Foundation\Application;
use Orchestra\Contracts\Html\Form\Builder as FormBuilder;

abstract class Plugin
{
    /**
     * Extension name.
     *
     * @var string
     */
    protected $extension;

    /**
     * Configuration.
     *
     * @var array
     */
    protected $config = [];

    /**
     * Menu handler.
     *
     * @var object|null
     */
    protected $menu;

    /**
     * Validation rules.
     *
     * @var array
     */
    protected $rules = [];

    /**
     * Sidebar placeholders.
     *
     * @var array
     */
    protected $sidebar = [];

    /**
     * Bootstrap plugin.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $this->bootstrapConfiguration($app);

        $this->bootstrapForm($app);

        $this->bootstrapMenuHandler($app);

        $this->bootstrapSidebarPlaceholders($app);

        $this->bootstrapValidationRules($app);
    }

    /**
     * Bootstrap configuration.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    protected function bootstrapConfiguration(Application $app)
    {
        if (empty($this->extension) || empty($this->config)) {
            return;
        }

        $app->make('orchestra.extension.config')->map($this->extension, $this->config);
    }

    /**
     * Bootstrap the form.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    protected function bootstrapForm(Application $app)
    {
        $this->attachListenerOn($app, 'form', function (Fluent $model, FormBuilder $form) {
            $this->form($model, $form);
        });
    }

    /**
     * Bootstrap menu handler.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    protected function bootstrapMenuHandler(Application $app)
    {
        if (\is_null($this->menu)) {
            return;
        }

        $app->make('events')->listen('orchestra.ready: admin', $this->menu);
    }

    /**
     * Bootstrap sidebar placeholder.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    protected function bootstrapSidebarPlaceholders(Application $app)
    {
        $widget = $app->make('orchestra.widget');
        $placeholder = $widget->make('placeholder.orchestra.extensions');

        $this->attachListenerOn($app, 'form', function () use ($placeholder) {
            foreach ($this->sidebar as $name => $view) {
                $placeholder->add($name)->value(\view($view));
            }
        });
    }

    /**
     * Bootstrap validation rules.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     *
     * @return void
     */
    protected function bootstrapValidationRules(Application $app)
    {
        $this->attachListenerOn($app, 'validate', function (Fluent $rules) {
            foreach ($this->rules as $name => $validation) {
                $rules[$name] = $validation;
            }
        });
    }

    /**
     * Attach event listener.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  string  $event
     * @param  \Closure  $callback
     *
     * @return void
     */
    protected function attachListenerOn(Application $app, $event, Closure $callback)
    {
        $app->make('events')->listen("orchestra.{$event}: extension.{$this->extension}", $callback);
    }

    /**
     * Setup the form.
     *
     * @param  \Illuminate\Support\Fluent  $model
     * @param  \Orchestra\Contracts\Html\Form\Builder  $form
     *
     * @return void
     */
    abstract protected function form(Fluent $model, FormBuilder $form);
}


<?php

namespace Orchestra\View\Theme;

use Illuminate\Support\Collection;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Container\Container;
use Orchestra\Contracts\Theme\Theme as ThemeContract;

class Theme implements ThemeContract
{
    /**
     * Application instance.
     *
     * @var \Illuminate\Contracts\Container\Container
     */
    protected $app;

    /**
     * The event dispatcher implementation.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher
     */
    protected $dispatcher;

    /**
     * The filesystem implementation.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $files;

    /**
     * Theme filesystem path.
     *
     * @var string
     */
    protected $path;

    /**
     * Theme cascading filesystem path.
     *
     * @var string
     */
    protected $cascadingPath;

    /**
     * URL path of theme.
     *
     * @var string
     */
    protected $absoluteUrl;

    /**
     * Relative URL path of theme.
     *
     * @var string
     */
    protected $relativeUrl;

    /**
     * Boot indicator.
     *
     * @var bool
     */
    protected $booted = false;

    /**
     * Resolve indicator.
     *
     * @var bool
     */
    protected $resolved = false;

    /**
     * Theme name.
     *
     * @var string
     */
    protected $theme = null;

    /**
     * Setup a new theme container.
     *
     * @param  \Illuminate\Contracts\Container\Container  $app
     * @param  \Illuminate\Contracts\Events\Dispatcher  $dispatcher
     * @param  \Illuminate\Filesystem\Filesystem  $files
     */
    public function __construct(Container $app, Dispatcher $dispatcher, Filesystem $files)
    {
        $this->app = $app;
        $this->dispatcher = $dispatcher;
        $this->files = $files;

        $this->path = $app['path.public'].'/themes';
        $this->cascadingPath = $app['path.base'].'/resources/themes';
    }

    /**
     * Start theme engine, this should be called from application booted
     * or whenever we need to overwrite current active theme per request.
     *
     * @return $this
     */
    public function initiate()
    {
        $baseUrl = $this->app->make('request')->root();

        // Register relative and absolute URL for theme usage.
        $this->absoluteUrl = \rtrim($baseUrl, '/').'/themes';
        $this->relativeUrl = \trim(\str_replace($baseUrl, '/', $this->absoluteUrl), '/');

        return $this;
    }

    /**
     * Set the theme, this would also load the theme manifest.
     *
     * @param  string|null  $theme
     *
     * @return void
     */
    public function setTheme(?string $theme): void
    {
        if (! \is_null($this->theme)) {
            $this->resolved && $this->resetViewPaths();

            $this->dispatcher->dispatch("orchestra.theme.unset: {$this->theme}");
        }

        $this->theme = $theme;
        $this->dispatcher->dispatch("orchestra.theme.set: {$this->theme}");

        if ($this->resolved) {
            $this->resolved = false;
            $this->resolving();
        }
    }

    /**
     * Get the theme.
     *
     * @return string
     */
    public function getTheme(): string
    {
        return $this->theme;
    }

    /**
     * Boot and Load theme starter files.
     *
     * @return bool
     */
    public function boot(): bool
    {
        if ($this->booted) {
            return false;
        }

        $this->booted = true;

        $themePath = $this->getThemePath();
        $autoload = $this->getThemeAutoloadFiles($themePath);

        foreach ($autoload as $file) {
            $file = \ltrim($file, '/');
            $this->files->requireOnce("{$themePath}/{$file}");
        }

        $this->dispatcher->dispatch("orchestra.theme.boot: {$this->theme}");

        return true;
    }

    /**
     * Resolving the theme.
     *
     * @return bool
     */
    public function resolving(): bool
    {
        if ($this->resolved) {
            return false;
        }

        $this->resolved = true;

        $this->dispatcher->dispatch('orchestra.theme.resolving', [$this, $this->app]);

        $this->setViewPaths();

        return true;
    }

    /**
     * Get theme path.
     *
     * @return string
     */
    public function getThemePath(): string
    {
        return "{$this->path}/{$this->theme}";
    }

    /**
     * Get cascading theme path.
     *
     * @return string
     */
    public function getCascadingThemePath(): string
    {
        return "{$this->cascadingPath}/{$this->theme}";
    }

    /**
     * Get theme paths.
     *
     * @return array
     */
    public function getThemePaths(): array
    {
        return [
            $this->getCascadingThemePath(),
            $this->getThemePath(),
        ];
    }

    /**
     * Get available theme paths.
     *
     * @return array
     */
    public function getAvailableThemePaths(): array
    {
        return Collection::make($this->getThemePaths())->filter(function ($path) {
            return $this->files->isDirectory($path);
        })->values()->all();
    }

    /**
     * URL helper for the theme.
     *
     * @param  string  $url
     *
     * @return string
     */
    public function to(string $url = ''): string
    {
        return "{$this->absoluteUrl}/{$this->theme}/{$url}";
    }

    /**
     * Relative URL helper for theme.
     *
     * @param  string  $url
     *
     * @return string
     */
    public function asset(string $url = ''): string
    {
        return "/{$this->relativeUrl}/{$this->theme}/{$url}";
    }

    /**
     * Get theme autoload files from manifest.
     *
     * @param  string $themePath
     *
     * @return array
     */
    protected function getThemeAutoloadFiles(string $themePath): array
    {
        $manifest = new Manifest($this->files, $themePath);

        return $manifest->autoload ?? [];
    }

    /**
     * Set theme paths to view file finder paths.
     *
     * @return void
     */
    protected function setViewPaths(): void
    {
        $viewFinder = $this->app->make('view.finder');

        $themePaths = $this->getAvailableThemePaths();

        if (! empty($themePaths)) {
            $viewFinder->setPaths(\array_merge($themePaths, $viewFinder->getPaths()));
        }
    }

    /**
     * Reset theme paths to view file finder paths.
     *
     * @return void
     */
    protected function resetViewPaths(): void
    {
        $viewFinder = $this->app->make('view.finder');

        $paths = $viewFinder->getPaths();

        foreach ($this->getThemePaths() as $themePath) {
            ($paths[0] === $themePath) && \array_shift($paths);
        }

        $viewFinder->setPaths($paths);
    }
}


<?php

namespace Orchestra\Extension\Console;

use Illuminate\Console\Command;

class DetectCommand extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'extension:detect';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Detect available extensions in the application.';

    /**
     * {@inheritdoc}
     */
    public function handle()
    {
        $provider = $this->laravel->make('orchestra.extension');
        $extensions = $provider->detect();

        if ($this->option('quiet')) {
            return;
        }

        if ($extensions->isEmpty()) {
            return $this->line('<comment>No extension detected!</comment>');
        }

        $header = ['Extension', 'Version', 'Activate'];

        $this->table($header, $extensions->map(function ($options, $name) use ($provider) {
            return [
                $name,
                $option['version'],
                $provider->started($name) ? '    ✓' : '',
            ];
        })->all());
    }
}