<?php

namespace Bugsnag\Silex;

use Bugsnag\Callbacks\CustomUser;
use Bugsnag\Client;
use Bugsnag\Report;
use Bugsnag\Configuration;
use InvalidArgumentException;
use Silex\Application;
use Symfony\Component\Security\Core\User\UserInterface;

abstract class AbstractServiceProvider
{
    /**
     * The package version.
     *
     * @var string
     */
    const VERSION = '2.4.0';

    /**
     * Make a new bugsnag client instance.
     *
     * @param \Silex\Application $app
     *
     * @return \Bugsnag\Client
     */
    protected function makeClient(Application $app)
    {
        try {
            $config = $app['bugsnag.options'];
        } catch (InvalidArgumentException $e) {
            $config = [];
        }

        $key = isset($config['api_key']) ? $config['api_key'] : getenv('BUGSNAG_API_KEY');

        $guzzle = Client::makeGuzzle(isset($config['endpoint']) ? $config['endpoint'] : null);

        $client = new Client(new Configuration($key), $app['bugsnag.resolver'], $guzzle);

        if (!isset($config['callbacks']) || $config['callbacks']) {
            $client->registerDefaultCallbacks();
        }

        if (!isset($config['user']) || $config['user']) {
            $this->setupUserDetection($client, $app);
        }

        $this->setupPaths($client, isset($config['strip_path']) ? $config['strip_path'] : null, isset($config['project_root']) ? $config['project_root'] : null);

        $env = getenv('SYMFONY_ENV') ?: null;
        $stage = isset($config['release_stage']) ? $config['release_stage'] : null;
        $client->setReleaseStage($stage ?: ($env === 'prod' ? 'production' : $env));
        $client->setHostname(isset($config['hostname']) ? $config['hostname'] : null);

        $client->setFallbackType('Console');
        $client->setAppType(isset($config['app_type']) ? $config['app_type'] : null);
        $client->setAppVersion(isset($config['app_version']) ? $config['app_version'] : null);
        $client->setBatchSending(isset($config['batch_sending']) ? $config['batch_sending'] : true);
        $client->setSendCode(isset($config['send_code']) ? $config['send_code'] : true);

        $client->setNotifier([
            'name' => 'Bugsnag Silex',
            'version' => static::VERSION,
            'url' => 'https://github.com/bugsnag/bugsnag-silex',
        ]);

        if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
            $client->setNotifyReleaseStages($config['notify_release_stages']);
        }
        if (isset($config['filters']) && is_array($config['filters'])) {
            $client->setFilters($config['filters']);
        }

        return $client;
    }

    /**
     * Setup user detection.
     *
     * @param \Bugsnag\Client    $client
     * @param \Silex\Application $app
     *
     * @return void
     */
    protected function setupUserDetection(Client $client, Application $app)
    {
        try {
            $tokens = $app['security.token_storage'];
            $checker = $app['security.authorization_checker'];
        } catch (InvalidArgumentException $e) {
            return;
        }

        $client->registerCallback(new CustomUser(function () use ($tokens, $checker) {
            $token = $tokens->getToken();

            if (!$token || !$checker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
                return;
            }

            $user = $token->getUser();

            if ($user instanceof UserInterface) {
                return ['id' => $user->getUsername()];
            }

            return ['id' => (string) $user];
        }));
    }

    /**
     *
     */
    protected function autoNotify(Client $client, $exception, $callback=null)
    {
        $report = Report::fromPHPThrowable(
            $client->getConfig(),
            $exception
        );
        $report->setUnhandled(true);
        $report->setSeverityReason([
            'type' => 'unhandledExceptionMiddleware',
            'attributes' => [
                'framework' => 'Silex'
            ]
        ]);
        $client->notify($report, $callback);
    }

    /**
     * Setup the client paths.
     *
     * @param \Bugsnag\Client $client
     * @param string|null     $strip
     * @param string|null     $project
     *
     * @return void
     */
    protected function setupPaths(Client $client, $strip, $project)
    {
        if ($strip) {
            $client->setStripPath($strip);

            if (!$project) {
                $client->setProjectRoot("{$strip}/src");
            }

            return;
        }

        $base = realpath(__DIR__.'/../../../../');

        if ($project) {
            if ($base && substr($project, 0, strlen($base)) === $base) {
                $client->setStripPath($base);
            }

            $client->setProjectRoot($project);

            return;
        }

        if ($base) {
            $client->setStripPath($base);

            if ($root = realpath("{$base}/src")) {
                $client->setProjectRoot($root);
            }
        }
    }
}


<?php declare(strict_types=1);

namespace Improved\IteratorPipeline;

use Improved as i;

/**
 * Functional-style operations, such as map-reduce transformations on arrays and iterators.
 * A pipeline uses Generators, meaning it can be used only once.
 */
class Pipeline implements \IteratorAggregate
{
    use Traits\MappingTrait;
    use Traits\FilteringTrait;
    use Traits\SortingTrait;
    use Traits\TypeHandlingTrait;
    use Traits\FindingTrait;
    use Traits\AggregationTrait;

    /**
     * @var iterable
     */
    protected $iterable;

    /**
     * Pipeline constructor.
     *
     * @param iterable $iterable
     */
    public function __construct(iterable $iterable)
    {
        $this->iterable = $iterable;
    }

    /**
     * Define the next step via a callback that returns an array or Traversable object.
     *
     * @param callable $callback
     * @param mixed    ...$args
     * @return self
     */
    public function then(callable $callback, ...$args): self
    {
        $this->iterable = i\type_check(
            $callback($this->iterable, ...$args),
            'iterable',
            new \UnexpectedValueException("Expected step to return an array or Traversable, %s returned")
        );

        return $this->iterable instanceof Pipeline ? $this->iterable : $this;
    }

    /**
     * Get iterator.
     *
     * @return \Iterator
     */
    public function getIterator(): \Iterator
    {
        return i\iterable_to_iterator($this->iterable);
    }

    /**
     * Get iterable as array.
     *
     * @return array
     */
    public function toArray(): array
    {
        return i\iterable_to_array($this->iterable, true);
    }

    /**
     * Traverse over the iterator, not capturing the values.
     * This is particularly useful after `apply()`.
     *
     * @return void
     */
    public function walk(): void
    {
        i\iterable_walk($this->iterable);
    }

    /**
     * Factory method
     *
     * @param iterable $iterable
     * @return static
     */
    final public static function with(iterable $iterable): self
    {
        return $iterable instanceof static ? $iterable : new static($iterable);
    }

    /**
     * Factory method for PipelineBuilder
     *
     * @return PipelineBuilder
     */
    public static function build(): PipelineBuilder
    {
        return new PipelineBuilder();
    }
}


<?php /** @noinspection PhpInternalEntityUsedInspection */ declare(strict_types=1);

namespace Improved\IteratorPipeline;

use Improved as i;
use Improved\Iterator\CombineIterator;
use Improved\IteratorPipeline\PipelineBuilder\Stub;
use Improved\IteratorPipeline\Traits\FilteringTrait;
use Improved\IteratorPipeline\Traits\MappingTrait;
use Improved\IteratorPipeline\Traits\SortingTrait;

/**
 * The `PipelineBuilder` can be used to create a blueprint for pipelines.
 */
class PipelineBuilder
{
    use MappingTrait;
    use FilteringTrait;
    use SortingTrait;

    /**
     * @var array
     */
    protected $steps = [];

    /**
     * Define the next step via a callback that returns an array or Traversable object.
     *
     * @param callable $callback
     * @param mixed    ...$args
     * @return static
     */
    public function then(callable $callback, ...$args): self
    {
        $copy = clone $this;

        if ($callback instanceof self) {
            $copy->steps = array_merge($this->steps, $callback->steps);
        } else {
            $copy->steps[] = [$callback, $args];
        }

        return $copy;
    }

    /**
     * Add a stub, which does nothing but can be replaced later.
     *
     * @param string $name
     * @return static
     * @throw \BadMethodCallException if stub already exists
     */
    public function stub(string $name): self
    {
        $hasStub = i\iterable_has_any($this->steps, function ($step) use ($name) {
            return $step[0] instanceof Stub && $step[0]->getName() === $name;
        });

        if ($hasStub) {
            throw new \BadMethodCallException("Pipeline builder already has '$name' stub");
        }

        return $this->then(new Stub($name));
    }

    /**
     * Get a pipeline builder where a stub is replaced.
     *
     * @param string   $name
     * @param callable $callable
     * @param mixed    ...$args
     * @return static
     */
    public function unstub(string $name, callable $callable, ...$args): self
    {
        $index = i\iterable_find_key($this->steps, function ($step) use ($name) {
            return $step[0] instanceof Stub && $step[0]->getName() === $name;
        });

        if ($index === null) {
            throw new \BadMethodCallException("Pipeline builder doesn't have '$name' stub");
        }

        $clone = clone $this;
        $clone->steps[$index] = [$callable, $args];

        return $clone;
    }

    /**
     * Create a new pipeline
     *
     * @param iterable $iterable
     * @return Pipeline
     */
    public function with(iterable $iterable): Pipeline
    {
        $pipeline = new Pipeline($iterable);

        foreach ($this->steps as [$callback, $args]) {
            $pipeline = $pipeline->then($callback, ...$args);
        }

        return $pipeline;
    }

    /**
     * Invoke the builder.
     *
     * @param iterable $iterable
     * @return array
     */
    public function __invoke(iterable $iterable): array
    {
        return $this->with($iterable)->toArray();
    }

    /**
     * Use another iterator as keys and the current iterator as values.
     *
     * @param iterable $keys  Keys will be turned into an array.
     * @return static
     */
    public function setKeys(iterable $keys)
    {
        $combine = function ($values, $keys) {
            return new CombineIterator($keys, $values);
        };

        return $this->then($combine, i\iterable_to_array($keys));
    }
}


<?php declare(strict_types=1);

namespace Improved\IteratorPipeline\Traits;

use Improved as i;

/**
 * Filtering methods for iterator pipeline.
 */
trait FilteringTrait
{
    /**
     * Define the next step via a callback that returns an array or Traversable object.
     *
     * @param callable $callback
     * @param mixed    ...$args
     * @return static
     */
    abstract public function then(callable $callback, ...$args);

    /**
     * Eliminate elements based on a criteria.
     *
     * @param callable $matcher
     * @return static
     */
    public function filter(callable $matcher)
    {
        return $this->then(i\iterable_filter, $matcher);
    }

    /**
     * Filter out `null` values from iteratable.
     *
     * @return static
     */
    public function cleanup()
    {
        return $this->then(i\iterable_cleanup);
    }

    /**
     * Filter on unique elements.
     *
     * @param callable|null $grouper  If provided, filtering will be based on return value.
     * @return static
     */
    public function unique(?callable $grouper = null)
    {
        return $this->then(i\iterable_unique, $grouper);
    }

    /**
     * Filter our duplicate keys.
     * Unlike associative arrays, the keys of iterators don't have to be unique.
     *
     * @return static
     */
    public function uniqueKeys()
    {
        return $this->then(i\iterable_unique, function ($value, $key) {
            return $key;
        });
    }

    /**
     * Get only the first elements of an iterator.
     *
     * @param int $size
     * @return static
     */
    public function limit(int $size)
    {
        return $this->then(i\iterable_slice, 0, $size);
    }

    /**
     * Get a limited subset of the elements using an offset.
     *
     * @param int      $offset
     * @param int|null $size    size limit
     * @return static
     */
    public function slice(int $offset, ?int $size = null)
    {
        return $this->then(i\iterable_slice, $offset, $size);
    }
}


<?php declare(strict_types=1);

namespace Improved\IteratorPipeline\Traits;

use Improved as i;

/**
 * Methods that change the order of the elements.
 */
trait SortingTrait
{
    /**
     * Define the next step via a callback that returns an array or Traversable object.
     *
     * @param callable $callback
     * @param mixed    ...$args
     * @return static
     */
    abstract public function then(callable $callback, ...$args);

    /**
     * Sort all elements of an iterator.
     *
     * @param callable|int $compare       SORT_* flags as binary set or callback comparator function
     * @param bool         $preserveKeys
     * @return static
     */
    public function sort($compare, bool $preserveKeys = true)
    {
        return $this->then(i\iterable_sort, $compare, $preserveKeys);
    }

    /**
     * Sort all elements of an iterator based on the key.
     *
     * @param callable|int $compare   SORT_* flags as binary set or callback comparator function
     * @return static
     */
    public function sortKeys($compare)
    {
        return $this->then(i\iterable_sort_keys, $compare);
    }

    /**
     * Reverse order of elements of an iterable.
     *
     * @return static
     */
    public function reverse()
    {
        return $this->then(i\iterable_reverse);
    }
}


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

use PHPoole\Collection\Collection as PHPooleCollection;

/**
 * Class Collection.
 */
class Collection extends PHPooleCollection
{
    /**
     * Return a Menu collection (creates it if not exists)
     * {@inheritdoc}
     */
    public function get($id)
    {
        if (!$this->has($id)) {
            $this->add(new Menu($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\Page;

use PHPoole\Exception\Exception;

/**
 * Helper to set and get page variables.
 *
 * @property array properties
 */
trait VariableTrait
{
    abstract public function offsetExists($offset);

    abstract public function offsetGet($offset);

    abstract public function offsetSet($offset, $value);

    abstract public function offsetUnset($offset);

    /**
     * Set variables.
     *
     * @param array $variables
     *
     * @throws Exception
     *
     * @return $this
     */
    public function setVariables($variables)
    {
        if (!is_array($variables)) {
            return $this;
        }
        foreach ($variables as $key => $value) {
            $this->setVariable($key, $value);
        }

        return $this;
    }

    /**
     * Get variables.
     *
     * @return array
     */
    public function getVariables()
    {
        return $this->properties;
    }

    /**
     * Set a variable.
     *
     * @param $name
     * @param $value
     *
     * @throws Exception
     *
     * @return $this
     */
    public function setVariable($name, $value)
    {
        switch ($name) {
            case 'date':
                try {
                    if ($value instanceof \DateTime) {
                        $this->offsetSet('date', $value);
                    } else {
                        if (is_numeric($value)) {
                            $this->offsetSet('date', (new \DateTime())->setTimestamp($value));
                        } else {
                            if (is_string($value)) {
                                $this->offsetSet('date', new \DateTime($value));
                            }
                        }
                    }
                } catch (Exception $e) {
                    throw new Exception(sprintf("Expected date string in page ID: '%s'", $this->getId()));
                }
                break;
            case 'draft':
                if ($value === true) {
                    $this->offsetSet('published', false);
                }
                break;
            default:
                $this->offsetSet($name, $value);
        }

        return $this;
    }

    /**
     * Is variable exist?
     *
     * @param $name
     *
     * @return bool
     */
    public function hasVariable($name)
    {
        return $this->offsetExists($name);
    }

    /**
     * Get a variable.
     *
     * @param string $name
     *
     * @return mixed|null
     */
    public function getVariable($name)
    {
        if ($this->offsetExists($name)) {
            return $this->offsetGet($name);
        }
    }

    /**
     * Unset a variable.
     *
     * @param $name
     *
     * @return $this
     */
    public function unVariable($name)
    {
        if ($this->offsetExists($name)) {
            $this->offsetUnset($name);
        }

        return $this;
    }
}


<?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 Section.
 */
class Section extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();
        $sections = [];

        // collects sections
        /* @var $page Page */
        foreach ($pageCollection as $page) {
            if ($page->getSection() != '') {
                $sections[$page->getSection()][] = $page;
            }
        }
        // adds node pages to collection
        if (count($sections) > 0) {
            $menuWeight = 100;
            foreach ($sections as $section => $pages) {
                if (!$pageCollection->has($section.'/')) {
                    usort($pages, 'PHPoole\Util::sortByDate');
                    $page = (new Page())
                        ->setId(Page::urlize(sprintf('%s/', $section)))
                        ->setPathname(Page::urlize(sprintf('%s', $section)))
                        ->setTitle(ucfirst($section))
                        ->setNodeType(NodeType::SECTION)
                        ->setVariable('pages', $pages)
                        ->setVariable('date', reset($pages)->getDate())
                        ->setVariable('menu', [
                            'main' => ['weight' => $menuWeight],
                        ]);
                    $generatedPages->add($page);
                }
                $menuWeight += 10;
            }
        }

        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\Step;

use PHPoole\Collection\Page\Page;
use PHPoole\Exception\Exception;
use PHPoole\Util;

/**
 * Pages saving.
 */
class SavePages extends AbstractStep
{
    /**
     * {@inheritdoc}
     *
     * @throws Exception
     */
    public function init($options)
    {
        if ($options['dry-run']) {
            $this->process = false;
            call_user_func_array($this->phpoole->getMessageCb(), ['SAVE', 'Dry run']);

            return;
        }

        Util::getFS()->mkdir($this->config->getOutputPath());

        $this->process = true;
    }

    /**
     * {@inheritdoc}
     *
     * @throws Exception
     */
    public function process()
    {
        call_user_func_array($this->phpoole->getMessageCb(), ['SAVE', 'Saving pages']);

        /* @var $page Page */
        $filteredPages = $this->phpoole->getPages()->filter(function (Page $page) {
            return !empty($page->getVariable('rendered'));
        });
        $max = count($filteredPages);

        $count = 0;
        foreach ($filteredPages as $page) {
            $count++;

            $pathname = $this->cleanPath($this->config->getOutputPath().'/'.$this->getPathname($page));

            Util::getFS()->dumpFile($pathname, $page->getVariable('rendered'));

            $message = substr($pathname, strlen($this->config->getDestinationDir()) + 1);
            call_user_func_array($this->phpoole->getMessageCb(), ['SAVE_PROGRESS', $message, $count, $max]);
        }
    }

    /**
     * Return output pathname.
     *
     * @param Page $page
     *
     * @return string
     */
    protected function getPathname(Page $page)
    {
        // force pathname of a file node page (ie: "section/index.md")
        if ($page->getName() == 'index') {
            return $page->getPath().'/'.$this->config->get('output.filename');
        } else {
            // custom extension, ex: 'manifest.json'
            if (!empty(pathinfo($page->getPermalink(), PATHINFO_EXTENSION))) {
                return $page->getPermalink();
            }
            // underscore prefix, ex: '_redirects'
            if (strpos($page->getPermalink(), '_') === 0) {
                return $page->getPermalink();
            }

            return $page->getPermalink().'/'.$this->config->get('output.filename');
        }
    }

    /**
     * Remove unnecessary slashes.
     *
     * @param string $pathname
     *
     * @return string
     */
    protected function cleanPath($pathname)
    {
        return preg_replace('#/+#', '/', $pathname);
    }
}


<?php
/**
 * AntiSpoof.php
 * Username spoofing prevention for MediaWiki
 * Version 0.04
 *
 * Copyright (C) Neil Harris 2006
 * Python->PHP conversion by Brion Vibber <brion@pobox.com>
 *
 * 2006-06-30 Handles non-CJK scripts as per UTR #39 + my extensions
 * 2006-07-01 Now handles Simplified <-> Traditional Chinese rules, as
 *            per JET Guidelines for Internationalized Domain Names,
 *            and the ICANN language registry values for .cn
 * 2006-09-14 Now handles 'rn' etc better, and uses stdin for input
 * 2006-09-18 Added exception handling for nasty cases, eg BiDi violations
 * 2006-09-19 Converted to PHP for easier integration into a MW extension
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 * USA
 */

use UtfNormal\Utils;
use UtfNormal\Validator;
use Wikimedia\Equivset\Equivset;

class AntiSpoof {
	/**
	 * Define script tag codes for various Unicode codepoint ranges
	 * If it does not have a code here, it does not have a script assignment
	 * NB: Braille is not in this list since it is a transliteration system, not a script;
	 * this does not disadvantage blind people, who will use Braille input/output methods
	 * and not raw Braille...
	 * NB: Middle dot is included in SCRIPT_LATIN for use in Catalan
	 * NB: All scripts described by the Unicode Consortium as "Other Scripts" or "Ancient Scripts"
	 * are commented out: these are either not in modern use, or only used for specialized
	 * religious purposes, or only of literary interest
	 */
	private static $script_ranges = [
		[ 0x0020, 0x002F,
			"SCRIPT_ASCII_PUNCTUATION" ], // ASCII Punctuation 1, Hyphen, ASCII Punctuation 2
		[ 0x0030, 0x0039, "SCRIPT_ASCII_DIGITS" ], // ASCII Digits
		[ 0x003A, 0x0040, "SCRIPT_ASCII_PUNCTUATION" ], // Colon, ASCII Punctuation 3
		[ 0x0041, 0x005A, "SCRIPT_LATIN" ], // ASCII Uppercase
		[ 0x005B, 0x0060,
			"SCRIPT_ASCII_PUNCTUATION" ], // ASCII Punctuation 4, Underscore, ASCII Punctuation 5
		[ 0x0061, 0x007A, "SCRIPT_LATIN" ], // ASCII Lowercase
		[ 0x007B, 0x007E, "SCRIPT_ASCII_PUNCTUATION" ], // ASCII Punctuation 5
		[ 0x00B7, 0x00B7, "SCRIPT_LATIN" ], // Middle Dot
		[ 0x00C0, 0x00D6, "SCRIPT_LATIN" ], // Latin-1 Letters 1
		[ 0x00D8, 0x00F6, "SCRIPT_LATIN" ], // Latin-1 Letters 2
		[ 0x00F8, 0x02AF,
			"SCRIPT_LATIN" ], // Latin-1 Letters 3, Latin Extended-A, Latin Extended-B, IPA Extensions
		[ 0x0300, 0x036F, "SCRIPT_COMBINING_MARKS" ], // Combining Diacritical Marks
		[ 0x0370, 0x03E1, "SCRIPT_GREEK" ], // Greek and Coptic (Greek)
		[ 0x03E2, 0x03EF, "SCRIPT_COPTIC_EXTRAS" ], // Greek and Coptic (Coptic-unique)
		[ 0x03F0, 0x03FF, "SCRIPT_GREEK" ], // Greek and Coptic (Greek)
		[ 0x0400, 0x052F, "SCRIPT_CYRILLIC" ], // Cyrillic, Cyrillic Supplement
		[ 0x0530, 0x058F, "SCRIPT_ARMENIAN" ], // Armenian
		[ 0x0590, 0x05FF, "SCRIPT_HEBREW" ], // Hebrew
		[ 0x0600, 0x06FF, "SCRIPT_ARABIC" ], // Arabic
		[ 0x0700, 0x074F, "SCRIPT_SYRIAC" ], // Syriac
		[ 0x0750, 0x077F, "SCRIPT_ARABIC" ], // Arabic Supplement
		[ 0x0780, 0x07BF, "SCRIPT_THAANA" ], // Thaana
		[ 0x0900, 0x097F, "SCRIPT_DEVANAGARI" ], // Devanagari
		[ 0x0980, 0x09FF, "SCRIPT_BENGALI" ], // Bengali
		[ 0x0A00, 0x0A7F, "SCRIPT_GURMUKHI" ], // Gurmukhi
		[ 0x0A80, 0x0AFF, "SCRIPT_GUJARATI" ], // Gujarati
		[ 0x0B00, 0x0B7F, "SCRIPT_ORIYA" ], // Oriya
		[ 0x0B80, 0x0BFF, "SCRIPT_TAMIL" ], // Tamil
		[ 0x0C00, 0x0C7F, "SCRIPT_TELUGU" ], // Telugu
		[ 0x0C80, 0x0CFF, "SCRIPT_KANNADA" ], // Kannada
		[ 0x0D00, 0x0D7F, "SCRIPT_MALAYALAM" ], // Malayalam
		[ 0x0D80, 0x0DFF, "SCRIPT_SINHALA" ], // Sinhala
		[ 0x0E00, 0x0E7F, "SCRIPT_THAI" ], // Thai
		[ 0x0E80, 0x0EFF, "SCRIPT_LAO" ], // Lao
		[ 0x0F00, 0x0FFF, "SCRIPT_TIBETAN" ], // Tibetan
		[ 0x1000, 0x109F, "SCRIPT_MYANMAR" ], // Myanmar
		[ 0x10A0, 0x10FF, "SCRIPT_GEORGIAN" ], // Georgian
		[ 0x1100, 0x11FF, "SCRIPT_HANGUL" ], // Hangul Jamo
		[ 0x1200, 0x139F, "SCRIPT_ETHIOPIC" ], // Ethiopic, Ethiopic Supplement
		[ 0x13A0, 0x13FF, "SCRIPT_CHEROKEE" ], // Cherokee
		[ 0x1400, 0x167F, "SCRIPT_CANADIAN_ABORIGINAL" ], // Unified Canadian Aboriginal Syllabics
		// [ 0x1680, 0x169F, "SCRIPT_OGHAM" ], // Ogham
		// [ 0x16A0, 0x16FF, "SCRIPT_RUNIC" ], // Runic
		[ 0x1700, 0x171F, "SCRIPT_TAGALOG" ], // Tagalog
		[ 0x1720, 0x173F, "SCRIPT_HANUNOO" ], // Hanunoo
		[ 0x1740, 0x175F, "SCRIPT_BUHID" ], // Buhid
		[ 0x1760, 0x177F, "SCRIPT_TAGBANWA" ], // Tagbanwa
		[ 0x1780, 0x17FF, "SCRIPT_KHMER" ], // Khmer
		[ 0x1800, 0x18AF, "SCRIPT_MONGOLIAN" ], // Mongolian
		[ 0x1900, 0x194F, "SCRIPT_LIMBU" ], // Limbu
		[ 0x1950, 0x197F, "SCRIPT_TAI_LE" ], // Tai Le
		[ 0x1980, 0x19DF, "SCRIPT_NEW_TAI_LUE" ], // New Tai Lue
		[ 0x1A00, 0x1A1F, "SCRIPT_BUGINESE" ], // Buginese
		[ 0x1E00, 0x1EFF, "SCRIPT_LATIN" ], // Latin Extended Additional
		[ 0x1F00, 0x1FFF, "SCRIPT_GREEK" ], // Greek Extended
		// [ 0x2C00, 0x2C5F, "SCRIPT_GLAGOLITIC" ], // Glagolitic
		[ 0x2C80, 0x2CFF, "SCRIPT_COPTIC" ], // Coptic
		[ 0x2D00, 0x2D2F, "SCRIPT_GEORGIAN" ], // Georgian Supplement
		[ 0x2D30, 0x2D7F, "SCRIPT_TIFINAGH" ], // Tifinagh
		[ 0x2D80, 0x2DDF, "SCRIPT_ETHIOPIC" ], // Ethiopic Extended
		[ 0x2E80, 0x2FDF, "SCRIPT_DEPRECATED" ], // CJK Radicals Supplement, Kangxi Radicals
		[ 0x3040, 0x309F, "SCRIPT_HIRAGANA" ], // Hiragana
		[ 0x30A0, 0x30FF, "SCRIPT_KATAKANA" ], // Katakana
		[ 0x3100, 0x312F, "SCRIPT_BOPOMOFO" ], // Bopomofo
		[ 0x3130, 0x318F, "SCRIPT_HANGUL" ], // Hangul Compatibility Jamo
		[ 0x31A0, 0x31BF, "SCRIPT_BOPOMOFO" ], // Bopomofo Extended
		[ 0x3400, 0x4DBF, "SCRIPT_HAN" ], // CJK Unified Ideographs Extension A
		[ 0x4E00, 0x9FFF, "SCRIPT_HAN" ], // CJK Unified Ideographs
		[ 0xA000, 0xA4CF, "SCRIPT_YI" ], // Yi Syllables, Yi Radicals
		[ 0xA800, 0xA82F, "SCRIPT_SYLOTI_NAGRI" ], // Syloti Nagri
		[ 0xAC00, 0xD7AF, "SCRIPT_HANGUL" ], // Hangul Syllables
		[ 0xF900, 0xFAFF, "SCRIPT_DEPRECATED" ], // CJK Compatibility Ideographs
		// [ 0x10000, 0x100FF, "SCRIPT_LINEAR_B" ], // Linear B Syllabary, Linear B Ideograms
		// [ 0x10140, 0x1018F, "SCRIPT_GREEK" ], // Ancient Greek Numbers
		// [ 0x10300, 0x1032F, "SCRIPT_OLD_ITALIC" ], // Old Italic
		[ 0x10330, 0x1034F, "SCRIPT_GOTHIC" ], // Gothic
		// [ 0x10380, 0x1039F, "SCRIPT_UGARITIC" ], // Ugaritic
		// [ 0x103A0, 0x103DF, "SCRIPT_OLD_PERSIAN" ], // Old Persian
		// [ 0x10400, 0x1044F, "SCRIPT_DESERET" ], // Deseret
		// [ 0x10450, 0x1047F, "SCRIPT_SHAVIAN" ], // Shavian
		// [ 0x10480, 0x104AF, "SCRIPT_OSMANYA" ], // Osmanya
		// [ 0x10800, 0x1083F, "SCRIPT_CYPRIOT" ], // Cypriot Syllabary
		[ 0x10A00, 0x10A5F, "SCRIPT_KHAROSHTHI" ], // Kharoshthi
		[ 0x20000, 0x2A6DF, "SCRIPT_HAN" ], // CJK Unified Ideographs Extension B
		[ 0x2F800, 0x2FA1F, "SCRIPT_DEPRECATED" ] // CJK Compatibility Ideographs Supplement
	];

	/**
	 * @var Equivset
	 */
	private static $equivset;

	public static function getEquivSet() {
		if ( !self::$equivset ) {
			self::$equivset = new Equivset();
		}

		return self::$equivset;
	}

	/**
	 * @param string $ch
	 * @return string
	 */
	private static function getScriptCode( $ch ) {
		# Linear search: binary chop would be faster...
		foreach ( self::$script_ranges as $range ) {
			if ( $ch >= $range[0] && $ch <= $range[1] ) {
				return $range[2];
			}
		}
		# Otherwise...
		return "SCRIPT_UNASSIGNED";
	}

	/**
	 * From the name of a script, get a script descriptor, if valid,
	 * otherwise return None
	 *
	 * @param string $name
	 * @return null|string
	 */
	private static function getScriptTag( $name ) {
		$name = "SCRIPT_" . strtoupper( trim( $name ) );
		// Linear search
		foreach ( self::$script_ranges as $range ) {
			if ( $name == $range[2] ) {
				return $range[2];
			}
		}
		// Otherwise...
		return null;
	}

	/**
	 * @param array $aList
	 * @param array $bList
	 * @return bool
	 */
	private static function isSubsetOf( $aList, $bList ) {
		return count( array_diff( $aList, $bList ) ) == 0;
	}

	/**
	 * Is this an allowed script mixture?
	 *
	 * @param array $scriptList
	 * @return bool
	 */
	private static function isAllowedScriptCombination( $scriptList ) {
		$allowedScriptCombinations = [
			[ "SCRIPT_COPTIC", "SCRIPT_COPTIC_EXTRAS" ], # Coptic, using old Greek chars
			[ "SCRIPT_GREEK", "SCRIPT_COPTIC_EXTRAS" ], # Coptic, using new Coptic chars
			[ "SCRIPT_HAN", "SCRIPT_BOPOMOFO" ], # Chinese
			[ "SCRIPT_HAN", "SCRIPT_HANGUL" ], # Korean
			[ "SCRIPT_HAN", "SCRIPT_KATAKANA", "SCRIPT_HIRAGANA" ] # Japanese
		];
		foreach ( $allowedScriptCombinations as $allowedCombo ) {
			if ( self::isSubsetOf( $scriptList, $allowedCombo ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Convert string into array of Unicode code points as integers
	 * @param string $str
	 * @return array
	 */
	public static function stringToList( $str ) {
		$ar = [];
		if ( !preg_match_all( '/./us', $str, $ar ) ) {
			return [];
		}
		$out = [];
		foreach ( $ar[0] as $char ) {
			$out[] = Utils::utf8ToCodepoint( $char );
		}
		return $out;
	}

	/**
	 * @param array $list
	 * @return string
	 */
	public static function listToString( $list ) {
		$out = '';
		foreach ( $list as $cp ) {
			$out .= Utils::codepointToUtf8( $cp );
		}
		return $out;
	}

	/**
	 * @param array $a_list
	 * @return string
	 */
	private static function hardjoin( $a_list ) {
		return implode( '', $a_list );
	}

	/**
	 * @param string $testName
	 * @return string
	 */
	public static function normalizeString( $testName ) {
		return self::getEquivSet()->normalize( $testName );
	}

	/**
	 * @param array $text
	 * @param string $script
	 * @return array
	 */
	private static function stripScript( $text, $script ) {
		$scripts = array_map( [ 'AntiSpoof', 'getScriptCode' ], $text );
		$out = [];
		foreach ( $text as $index => $char ) {
			if ( $scripts[$index] !== $script ) {
				$out[] = $char;
			}
		}
		return $out;
	}

	/*
	 * Helper function for checkUnicodeStringStatus: Return an error on a bad character.
	 * @todo I would like to show Unicode character name, but it is not clear how to get it.
	 * @param string $msgId message identifier.
	 * @param number $point codepoint of the bad character.
	 * @return Status
	 */
	private static function badCharErr( $msgId, $point ) {
		$symbol = Utils::codepointToUtf8( $point );
		// Combining marks are combined with the previous character. If abusing character is a
		// combining mark, prepend it with space to show them correctly.
		if ( self::getScriptCode( $point ) == "SCRIPT_COMBINING_MARKS" ) {
			$symbol = ' ' . $symbol;
		}
		$code = sprintf( 'U+%04X', $point );
		if ( preg_match( '/\A\p{C}\z/u', $symbol ) ) {
			$char = wfMessage( 'antispoof-bad-char-non-printable', $code );
		} else {
			$char = wfMessage( 'antispoof-bad-char', $symbol, $code );
		}
		return Status::newFatal( wfMessage( $msgId, $char ) );
	}

	/**
	 * @param string $testName
	 * @return array
	 */
	public static function checkUnicodeString( $testName ) {
		$status = self::checkUnicodeStringStatus( $testName );
		if ( $status->isOK() ) {
			return [ "OK", $status->getValue() ];
		}
		return [ "ERROR", $status->getMessage()->text() ];
	}

	/**
	 * TODO: does too much in one routine, refactor...
	 * @param string $testName
	 * @return Status
	 * @since 1.32
	 */
	public static function checkUnicodeStringStatus( $testName ) {
		global $wgAntiSpoofBlacklist;

		// Start with some sanity checking
		if ( !is_array( $wgAntiSpoofBlacklist ) ) {
			throw new MWException( '$wgAntiSpoofBlacklist should be an array!' );
		}
		if ( !is_string( $testName ) ) {
			return Status::newFatal( 'antispoof-badtype' );
		}

		if ( strlen( $testName ) == 0 ) {
			return Status::newFatal( 'antispoof-empty' );
		}

		foreach ( self::stringToList( $testName ) as $char ) {
			if ( in_array( $char, $wgAntiSpoofBlacklist ) ) {
				return self::badCharErr( 'antispoof-blacklisted', $char );
			}
		}

		// Perform Unicode _compatibility_ decomposition
		$testName = Validator::toNFKD( $testName );
		$testChars = self::stringToList( $testName );

		// Be paranoid: check again, just in case Unicode normalization code changes...
		foreach ( $testChars as $char ) {
			if ( in_array( $char, $wgAntiSpoofBlacklist ) ) {
				return self::badCharErr( 'antispoof-blacklisted', $char );
			}
		}

		// Check for this: should not happen in any valid Unicode string
		if ( self::getScriptCode( $testChars[0] ) == "SCRIPT_COMBINING_MARKS" ) {
			return self::badCharErr( 'antispoof-combining', $testChars[0] );
		}

		// Strip all combining characters in order to crudely strip accents
		// Note: NFKD normalization should have decomposed all accented chars earlier
		$testChars = self::stripScript( $testChars, "SCRIPT_COMBINING_MARKS" );

		$testScripts = array_map( [ 'AntiSpoof', 'getScriptCode' ], $testChars );
		$unassigned = array_search( "SCRIPT_UNASSIGNED", $testScripts );
		if ( $unassigned !== false ) {
			return self::badCharErr( 'antispoof-unassigned', $testChars[$unassigned] );
		}
		$deprecated = array_search( "SCRIPT_DEPRECATED", $testScripts );
		if ( $deprecated !== false ) {
			return self::badCharErr( 'antispoof-deprecated', $testChars[$deprecated] );
		}
		$testScripts = array_unique( $testScripts );

		// We don't mind ASCII punctuation or digits
		$testScripts = array_diff( $testScripts,
			[ "SCRIPT_ASCII_PUNCTUATION", "SCRIPT_ASCII_DIGITS" ] );

		if ( !$testScripts ) {
			return Status::newFatal( 'antispoof-noletters' );
		}

		if ( count( $testScripts ) > 1 && !self::isAllowedScriptCombination( $testScripts ) ) {
			return Status::newFatal( 'antispoof-mixedscripts' );
		}

		// At this point, we should probably check for BiDi violations if they aren't
		// caught above...

		// Squeeze out all punctuation chars
		// TODO: almost the same code occurs twice, refactor into own routine
		$testChars = self::stripScript( $testChars, "SCRIPT_ASCII_PUNCTUATION" );

		$testName = self::listToString( $testChars );

		// Replace characters in confusables set with equivalence chars
		$testName = self::normalizeString( $testName );

		// Do very simple sequence processing: "vv" -> "w", "rn" -> "m"...
		// Not exhaustive, but ups the ante...
		// Do this _after_ canonicalization: looks weird, but needed for consistency
		$testName = str_replace( 'VV', 'W', $testName );
		$testName = str_replace( 'RN', 'M', $testName );

		// Remove all remaining spaces, just in case any have snuck through...
		$testName = self::hardjoin( explode( " ", $testName ) );

		// Reduce repeated char sequences to single character
		// BUG: TODO: implement this

		if ( strlen( $testName ) < 1 ) {
			return Status::newFatal( 'antispoof-tooshort' );
		}

		// Don't ASCIIfy: we assume we are UTF-8 capable on output

		// Prepend version string, for futureproofing if this algorithm changes
		$testName = "v2:" . $testName;

		// And return the canonical version of the name
		return Status::newGood( $testName );
	}
}


<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 */

$IP = getenv( 'MW_INSTALL_PATH' );
if ( $IP === false ) {
	$IP = __DIR__ . '/../../..';
}
require_once "$IP/maintenance/Maintenance.php";

/**
 * Go through all usernames and calculate and record spoof thingies
 */
class BatchAntiSpoof extends Maintenance {

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

		$this->requireExtension( 'AntiSpoof' );
		$this->setBatchSize( 1000 );
	}

	/**
	 * @param array $items
	 */
	protected function batchRecord( $items ) {
		SpoofUser::batchRecord( $this->getDB( DB_MASTER ), $items );
	}

	/**
	 * @return string
	 */
	protected function getTableName() {
		return 'user';
	}

	/**
	 * @return string
	 */
	protected function getUserColumn() {
		return 'user_name';
	}

	/**
	 * @return string Primary key of the table returned by getTableName()
	 */
	protected function getPrimaryKey() {
		return 'user_id';
	}

	/**
	 * @param string $name
	 * @return SpoofUser
	 */
	protected function makeSpoofUser( $name ) {
		return new SpoofUser( $name );
	}

	protected function waitForSlaves() {
		wfWaitForSlaves();
	}

	/**
	 * Do the actual work. All child classes will need to implement this
	 */
	public function execute() {
		$this->output( "Creating username spoofs...\n" );

		$userCol = $this->getUserColumn();
		$iterator = new BatchRowIterator( $this->getDB( DB_MASTER ),
			$this->getTableName(),
			$this->getPrimaryKey(),
			$this->getBatchSize()
		);
		$iterator->setFetchColumns( [ $userCol ] );

		$n = 0;
		foreach ( $iterator as $batch ) {
			$items = [];
			foreach ( $batch as $row ) {
				$items[] = $this->makeSpoofUser( $row->$userCol );
			}

			$n += count( $items );
			$this->output( "...$n\n" );
			$this->batchRecord( $items );
			$this->waitForSlaves();
		}

		$this->output( "$n user(s) done.\n" );
	}
}


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

use PHPoole\Collection\Page\Page;
use PHPoole\Exception\Exception;
use PHPoole\Renderer\Layout;
use PHPoole\Renderer\Twig as Twig;

/**
 * Pages rendering.
 */
class RenderPages extends AbstractStep
{
    /**
     * {@inheritdoc}
     *
     * @throws Exception
     */
    public function init($options)
    {
        if (!is_dir($this->config->getLayoutsPath()) && !$this->config->hasTheme()) {
            throw new Exception(sprintf(
                "'%s' is not a valid layouts directory",
                $this->config->getLayoutsPath()
            ));
        }

        $this->process = true;
    }

    /**
     * {@inheritdoc}
     *
     * @throws Exception
     */
    public function process()
    {
        // prepares renderer
        $this->phpoole->setRenderer(new Twig($this->getAllLayoutsPaths(), $this->config));

        // add globals variables
        $this->addGlobals();

        call_user_func_array($this->phpoole->getMessageCb(), ['RENDER', 'Rendering pages']);

        // collect published pages
        /* @var $page Page */
        $filteredPages = $this->phpoole->getPages()->filter(function (Page $page) {
            return !empty($page->getVariable('published'));
        });
        $max = count($filteredPages);

        // render each page
        $count = 0;
        foreach ($filteredPages as $page) {
            $count++;

            $rendered = $this->phpoole->getRenderer()->render(
                $layout = (new Layout())->finder($page, $this->config),
                ['page' => $page]
            );
            $page->setVariable('rendered', $rendered);
            $this->phpoole->getPages()->replace($page->getId(), $page);

            $message = sprintf('%s (%s)', ($page->getPathname() ?: 'index'), $layout);
            call_user_func_array($this->phpoole->getMessageCb(), ['RENDER_PROGRESS', $message, $count, $max]);
        }
    }

    /**
     * Return an array of layouts directories.
     *
     * @return array Layouts directory
     */
    protected function getAllLayoutsPaths()
    {
        $paths = [];

        // layouts/
        if (is_dir($this->config->getLayoutsPath())) {
            $paths[] = $this->config->getLayoutsPath();
        }
        // <theme>/layouts/
        if ($this->config->hasTheme()) {
            $themes = $this->config->getTheme();
            foreach ($themes as $theme) {
                $paths[] = $this->config->getThemeDirPath($theme);
            }
        }
        // res/layouts/
        if (is_dir($this->config->getInternalLayoutsPath())) {
            $paths[] = $this->config->getInternalLayoutsPath();
        }

        return $paths;
    }

    /**
     * Add globals variables.
     */
    protected function addGlobals()
    {
        // adds global variables
        $this->phpoole->getRenderer()->addGlobal('site', array_merge(
            $this->config->get('site'),
            ['menus' => $this->phpoole->getMenus()],
            ['pages' => $this->phpoole->getPages()->filter(function (Page $page) {
                return $page->getVariable('published');
            })],
            ['time' => time()]
        ));
        $this->phpoole->getRenderer()->addGlobal('cecil', [
            'url'       => sprintf('https://cecil.app/#%s', $this->phpoole->getVersion()),
            'version'   => $this->phpoole->getVersion(),
            'poweredby' => sprintf('Cecil/PHPoole v%s', $this->phpoole->getVersion()),
        ]);
    }
}


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

class GeneratorManager extends \SplPriorityQueue
{
    /**
     * Adds a generator.
     *
     * @param GeneratorInterface $generator
     * @param int                $priority
     *
     * @return self
     */
    public function addGenerator(GeneratorInterface $generator, $priority = 1)
    {
        $this->insert($generator, $priority);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function compare($priority1, $priority2)
    {
        if ($priority1 === $priority2) {
            return 0;
        }

        return $priority1 > $priority2 ? -1 : 1;
    }

    /**
     * Process each generators.
     *
     * @param PageCollection $pageCollection
     * @param \Closure       $messageCallback
     *
     * @return PageCollection
     */
    public function process(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $max = $this->count();

        if ($max > 0) {
            $this->top();
            while ($this->valid()) {
                /* @var GeneratorInterface $generator */
                $generator = $this->current();
                /* @var $generatedPages PageCollection */
                $generatedPages = $generator->generate($pageCollection, $messageCallback);
                foreach ($generatedPages as $page) {
                    /* @var $page \PHPoole\Collection\Page\Page */
                    if ($pageCollection->has($page->getId())) {
                        $pageCollection->replace($page->getId(), $page);
                    } else {
                        $pageCollection->add($page);
                    }
                }
                $message = substr(strrchr(get_class($generator), '\\'), 1).': '.count($generatedPages);
                $count = ($max - $this->key());
                call_user_func_array($messageCallback, ['GENERATE_PROGRESS', $message, $count, $max]);
                $this->next();
            }
        }

        return $pageCollection;
    }
}


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

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

/**
 * Create Pages collection from content iterator.
 */
class CreatePages extends AbstractStep
{
    /**
     * {@inheritdoc}
     */
    public function process()
    {
        $this->phpoole->setPages(new PageCollection());
        if (count($this->phpoole->getContent()) <= 0) {
            return;
        }
        call_user_func_array($this->phpoole->getMessageCb(), ['CREATE', 'Creating pages']);
        $max = count($this->phpoole->getContent());
        $count = 0;
        /* @var $file \Symfony\Component\Finder\SplFileInfo */
        foreach ($this->phpoole->getContent() as $file) {
            $count++;
            /* @var $page Page */
            $page = (new Page($file))->parse();
            $this->phpoole->getPages()->add($page);
            $message = $page->getPathname();
            call_user_func_array($this->phpoole->getMessageCb(), ['CREATE_PROGRESS', $message, $count, $max]);
        }
    }
}


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

use PHPoole\Exception\Exception;
use PHPoole\Util;
use Symfony\Component\Yaml\Yaml;

/**
 * Import (themes) config.
 */
class ImportConfig extends AbstractStep
{
    const THEME_CONFIG_FILE = 'config.yml';

    /**
     * {@inheritdoc}
     *
     * @throws Exception
     */
    public function init($options)
    {
        if ($this->config->hasTheme()) {
            $this->process = true;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function process()
    {
        call_user_func_array($this->phpoole->getMessageCb(), ['CONFIG', 'Importing config']);

        try {
            $themes = array_reverse($this->config->getTheme());
            $count = 0;
            $max = count($themes);
            foreach ($themes as $theme) {
                $count++;
                $themeConfigFile = $this->config->getThemesPath().'/'.$theme.'/'.self::THEME_CONFIG_FILE;
                if (Util::getFS()->exists($themeConfigFile)) {
                    $themeConfig = Yaml::parse(
                        file_get_contents($themeConfigFile)
                    );
                    $this->config->import($themeConfig);
                    $message = sprintf('%s: config imported', $theme);
                } else {
                    $message = sprintf('%s: no config file', $theme);
                }
                call_user_func_array($this->phpoole->getMessageCb(), ['CONFIG_PROGRESS', $message, $count, $max]);
            }
        } catch (Exception $e) {
            echo $e->getMessage()."\n";
        }
    }
}


<?php

namespace Nekland\Bundle\FeedBundle\Loader;

use Nekland\Bundle\FeedBundle\Feed;
use Nekland\Bundle\FeedBundle\Item\GenericItem;

/**
 * Loads Atom-XML and build a Feed object
 *
 * @throws \InvalidArgumentException
 * @author Yohan Giarelli <yohan@giarelli.org>
 * @author Nek' <nek.dev+github@gmail.com>
 */
class AtomLoader implements LoaderInterface
{
    protected static $methodMapping = array(
        'published' 	=> 'setFeedDate',
        'contributor' 	=> 'setAtomContributors',
        'content'		=> 'setFeedDescription',
        'link'          => 'setFeedRoutes'
    );

    /**
     * @throws \InvalidArgumentException
     * @param $feedContent
     * @return \Nekland\Bundle\FeedBundle\Feed
     */
    public function load($feedContent)
    {
        $feed = new Feed(array('class' => 'Nekland\\Bundle\\FeedBundle\\Item\\GenericItem'));
        $xml = simplexml_load_string($feedContent);

        if (false === $xml) {
            throw new \InvalidArgumentException('The given data is not a valid XML string.');
        }

        foreach ($xml->children() as $xmlTag) {
            if ($xmlTag->getName() != 'entry') {
                $this->setParam($xmlTag, $feed);
            } else {
                $this->addItem($xmlTag, $feed);
            }
        }

        return $feed;
    }

    /**
     * Adds an Item to the feed
     *
     * @param \SimpleXMLElement        $element
     * @param \Nekland\Bundle\FeedBundle\Feed $feed
     * @return void
     */
    protected function addItem(\SimpleXMLElement $element, Feed $feed)
    {
        $item = new GenericItem;
        foreach ($element as $subElement) {
            $method = isset(self::$methodMapping[$subElement->getName()]) ?
                    self::$methodMapping[$subElement->getName()] :
                    'setFeed' . ucfirst($subElement->getName());
            $data = '';

            if ($subElement->getName() == 'link') {

                if(($routes = $item->getFeedRoutes()) == null)
                    $routes = array();

                $i = count($routes);

                foreach($subElement->attributes() as $attrName => $attrValue) {
                    if($attrName == 'href') {
                        $routes[$i]['url'] = $attrValue;
                    } else {
                        $routes[$i][$attrName] = $attrValue;
                    }
                }
                $data = $routes;

            } elseif ($subElement->getName() == 'published') {
                $data = \DateTime::createFromFormat(\DateTime::ATOM, (string) $subElement);

            } elseif (count($subElement) === 0 && $subElement->getName() != 'updated') {

                if($subElement->getName() == 'content' || $subElement->getName() == 'title' || $subElement->getName() == 'summary') {
                    $typemethod = 'setAtom' . $subElement->getName() . 'Type';

                    $attributes = $subElement->attributes();
                    if(isset($attributes['type']))
                        $item->$typemethod($attributes['type']);
                    if(isset($attributes['xml:lang']) && $subElement->getName() == 'content') {
                        $item->setAtomContentLanguage($attributes['xml:lang']);
                    }
                }
                $data = (string) $subElement;

            } else {
                $data = $this->extractParam($subElement);
            }
            $reflection = new \ReflectionClass(get_class($item));
            if($reflection->hasMethod($method))
                $item->$method($data);
        }

        $feed->add($item);
    }

    /**
     * Set a feed param
     *
     * @param \SimpleXMLElement        $element
     * @param \Nekland\Bundle\FeedBundle\Feed $feed
     * @return void
     */
    protected function setParam(\SimpleXMLElement $element, Feed $feed)
    {
        if (count($element) === 0) {
            $feed->set($element->getName(), (string)$element);
        } else {
            $feed->set($element->getName(), $this->extractParam($element));
        }
    }

    /**
     * Extract array params
     *
     * @param \SimpleXMLElement $element
     * @return array
     */
    protected function extractParam(\SimpleXMLElement $element)
    {
        $param = array();
        foreach ($element as $subElement) {
            if (count($subElement) === 0) {
                $param[$subElement->getName()] = (string)$subElement;
            } else {
                $param[$subElement->getName()] = $this->extractParam($subElement);
            }
        }

        return $param;
    }

    /**
     * @return string format
     */
    public function getFormat()
    {
        return 'atom';
    }

}


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

use Abc\Bundle\SchedulerBundle\Model\ScheduleInterface;
use Abc\Bundle\SchedulerBundle\Schedule\ProcessorInterface;

/**
 * Checks whether a schedule with a cron expression is due using the library "PHP Cron Expression Parser" by mtdowling.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 * @see https://github.com/mtdowling/cron-expression
 */
class Processor implements ProcessorInterface
{
    /**
     * @var ExpressionFactoryInterface
     */
    protected $expressionFactory;

    /**
     * @param ExpressionFactoryInterface $expressionFactory
     */
    function __construct(ExpressionFactoryInterface $expressionFactory)
    {
        $this->expressionFactory = $expressionFactory;
    }

    /**
     * {@inheritDoc}
     */
    public function process(ScheduleInterface $schedule, \DateTime $currentDateTime = null)
    {
        // ensure that a schedule is not executed twice within in a minute
        $now = ($currentDateTime == null) ? new \DateTime() : $currentDateTime;

        if($schedule->getScheduledAt() != null && $schedule->getScheduledAt()->format('Y-m-d H:i') == $now->format('Y-m-d H:i'))
        {
            return false;
        }

        $cron = $this->expressionFactory->create($schedule->getExpression());

        return $cron->isDue($currentDateTime);
    }
}


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

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;

class Util
{
    /**
     * Symfony\Component\Filesystem.
     *
     * @var Filesystem
     */
    protected static $fs;

    /**
     * Return Symfony\Component\Filesystem instance.
     *
     * @return Filesystem
     */
    public static function getFS()
    {
        if (!self::$fs instanceof Filesystem) {
            self::$fs = new Filesystem();
        }

        return self::$fs;
    }

    /**
     * Runs a Git command on the repository.
     *
     * @param string $command The command.
     *
     * @throws \RuntimeException If the command failed.
     *
     * @return string The trimmed output from the command.
     */
    public static function runGitCommand($command)
    {
        try {
            $process = new Process($command, __DIR__);
            if (0 === $process->run()) {
                return trim($process->getOutput());
            }

            throw new \RuntimeException(
                sprintf(
                    'The tag or commit hash could not be retrieved from "%s": %s',
                    __DIR__,
                    $process->getErrorOutput()
                )
            );
        } catch (\RuntimeException $exception) {
            throw new \RuntimeException('Process error');
        }
    }

    /**
     * Sort array by 'date' item.
     *
     * @param $a
     * @param $b
     *
     * @return int
     */
    public static function sortByDate($a, $b)
    {
        if (!isset($a['date'])) {
            return -1;
        }
        if (!isset($b['date'])) {
            return 1;
        }
        if ($a['date'] == $b['date']) {
            return 0;
        }

        return ($a['date'] > $b['date']) ? -1 : 1;
    }
}


<?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 Homepage.
 */
class Homepage extends AbstractGenerator implements GeneratorInterface
{
    /**
     * {@inheritdoc}
     */
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
    {
        $generatedPages = new PageCollection();

        if (!$pageCollection->has('index')) {
            $filteredPages = $pageCollection->filter(function (Page $page) {
                return $page->getNodeType() === null
                && $page->getSection() == $this->config->get('site.paginate.homepage.section')
                && !empty($page->getBody());
            });
            $pages = $filteredPages->sortByDate()->toArray();

            /* @var $page Page */
            $page = (new Page())
                ->setId('index')
                ->setNodeType(NodeType::HOMEPAGE)
                ->setPathname(Page::urlize(''))
                ->setTitle('Home')
                ->setVariable('pages', $pages)
                ->setVariable('menu', [
                    'main' => ['weight' => 1],
                ]);
            $generatedPages->add($page);
        }

        return $generatedPages;
    }
}


<?php

namespace Nekland\Bundle\FeedBundle;

use Nekland\Bundle\FeedBundle\Item\ItemInterface;
use Nekland\Bundle\FeedBundle\Item\GenericItem;

/**
 * @throws \InvalidArgumentException
 *
 * This class represent the information flux.
 * That's why you can use it like an array, iterate on it...
 *
 * @author Nek' <nek.dev+github@gmail.com>
 * @author Yohan Giarelli <yohan@giarelli.org>
 */
class Feed implements \ArrayAccess, \Countable, \IteratorAggregate
{

    /**
     * Config example:
     *
     * ```php
     *  <php [
     *      'class'       => 'Nekland\BlogBundle\Entity\Article', // (who must implement FeedBundle\Item\RssItem)
     *      'title'       => 'My Rss title',
     *      'description' => 'My Rss description',
     *      'route'       => 'My Rss site route',                 //(home if not defined)
     *  ]
     * ```
     *
     * @var array $config
     */
    protected $config;

    /**
     * @var array|ItemInterface
     */
    protected $items;

    /**
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->config = $config;
        $this->items = array();
    }

    /**
     * @throws \InvalidArgumentException
     * @param ItemInterface $item
     * @return Feed
     */
    public function add(ItemInterface $item)
    {
        $rc = new \ReflectionClass($this->config['class']);
        if (!($rc->isInstance($item) || $item instanceof GenericItem)) {
            throw new \InvalidArgumentException('The class given MUST be an instance of "' . $this->config['class'] . '".');
        }

        $this->items[] = $item;

        $this->autoSlice();

        return $this;
    }

    /**
     * Removes an item by Id
     *
     * @throws \InvalidArgumentException
     * @param $id
     * @return Feed
     */
    public function remove($id)
    {
        $success = false;
        foreach ($this->items as $i => $item) {
            if ($item->getFeedId() === $id) {
                unset($this->items[$i]);
                $success = true;
                break;
            }
        }

        if (false === $success) {
            throw new \InvalidArgumentException('Unknown item');
        }

        return $this;
    }

    /**
     * Replace an Item by an other
     *
     * @throws \InvalidArgumentException
     * @param $id
     * @param ItemInterface $newItem
     * @return Feed
     */
    public function replace($id, ItemInterface $newItem)
    {
        $success = false;
        foreach ($this->items as $i => $item) {
            if ($item->getFeedId() == $id) {
                $this->items[$i] = $newItem;
                $success = true;
                break;
            }
        }

        if (false === $success) {
            throw new \InvalidArgumentException('Unknown item');
        }

        return $this;
    }

    /**
     * Returns the feed filename
     *
     * @param $format
     * @return string
     */
    public function getFilename($format)
    {
        return strtr($this->config['filename'], array('|format|' => $format));
    }

    /**
     * Return a configuration param
     *
     * @param $param
     * @return mixed
     */
    public function get($param, $default = null)
    {
        return isset($this->config[$param]) ? $this->config[$param] : $default;
    }

    /**
     * Set a configuration param
     *
     * @param $param
     * @param $value
     * @return void
     */
    public function set($param, $value)
    {
        $this->config[$param] = $value;
    }

    /**
     * Merge 2 feeds
     *
     * @param Feed $feed
     * @return Feed
     */
    public function merge(Feed $feed)
    {
        $this->items = array();

        foreach ($feed as $item) {
            $this->add($item);
        }

        return $this;
    }

    /**
     * Retrieve an external iterator
     * @return \Iterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->items);
    }

    /**
     * @param $offset
     * @return bool if $offset exists
     */
    public function offsetExists($offset)
    {
        return isset($this->items[$offset]);
    }

    /**
     * @param $offset
     * @return ItemInterface
     */
    public function offsetGet($offset)
    {
        return $this->items[$offset];
    }

    /**
     * @param $offset
     * @param ItemInterface
     * @return void
     */
    public function offsetSet($offset, $value)
    {
        if (!$value instanceof ItemInterface) {
            throw new \InvalidArgumentException('Feed items must implement ItemInterface');
        }
        $this->items[$offset] = $value;
    }

    /**
     * @param $offset
     */
    public function offsetUnset($offset)
    {
        unset($this->items[$offset]);
    }

    /**
     * @return int
     */
    public function count()
    {
        return count($this->items);
    }

    /**
     * @param $config
     * @return Feed
     */
    public function setConfig(array $config)
    {
        $this->config = $config;

        return $this;
    }

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

    /**
     * Auto slice the items to keep the good number of items
     * @return Feed
     */
    protected function autoSlice()
    {
        $maxItems = $this->get('max_items', 10);
        if (count($this) > $maxItems) {
            $this->items = array_slice(
                $this->items,
                count($this) - $maxItems,
                $maxItems
            );
        }

        return $this;
    }
}


<?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\Validator\Constraints;

use Cron\CronExpression as Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class CronExpressionValidator extends ConstraintValidator
{
    /**
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if (null === $value || '' === $value) {
            return;
        }

        if (!Validator::isValidExpression($value)) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{string}}', $value)
                ->addViolation();
        }
    }
}


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

use Abc\Bundle\SchedulerBundle\Event\SchedulerEvent;
use Abc\Bundle\SchedulerBundle\Event\SchedulerEvents;
use Abc\Bundle\SchedulerBundle\Iterator\ScheduleIteratorInterface;
use Abc\Bundle\SchedulerBundle\Model\ScheduleInterface;
use Abc\Bundle\SchedulerBundle\Schedule\Exception\ScheduleException;
use Abc\Bundle\SchedulerBundle\Schedule\Exception\SchedulerException;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Scheduler iterates over schedules and notifies a SchedulerEvent if a schedule is due.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class Scheduler implements SchedulerInterface
{
    /**
     * @var ProcessorRegistryInterface
     */
    protected $registry;

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

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @param ProcessorRegistryInterface $registry
     * @param EventDispatcherInterface   $dispatcher
     * @param LoggerInterface            $logger
     */
    public function __construct(ProcessorRegistryInterface $registry, EventDispatcherInterface $dispatcher, LoggerInterface $logger = null)
    {
        $this->registry   = $registry;
        $this->dispatcher = $dispatcher;
        $this->logger     = $logger == null ? new NullLogger() : $logger;
    }

    /**
     * {@inheritDoc}
     */
    public final function process(ScheduleIteratorInterface $iterator)
    {
        $numOfProcessed = 0;
        $exceptions = array();
        foreach($iterator as $schedule)
        {
            $this->logger->debug('Process schedule {schedule}', array('schedule' => $schedule));

            try
            {
                /**
                 * @var ScheduleInterface $schedule
                 */
                if($this->registry->get($schedule->getType())->process($schedule))
                {
                    $schedule->setScheduledAt(new \DateTime());
                    $iterator->getManager()->save($schedule);

                    $this->dispatcher->dispatch(SchedulerEvents::SCHEDULE, new SchedulerEvent($schedule));

                    $numOfProcessed++;
                }
            }
            catch(\Exception $e)
            {
                $this->logger->error('Failed to process schedule {schedule} ({exception})', array('schedule' => $schedule, 'exception' => $e));
                $exceptions[] = new ScheduleException($schedule, $e);
            }
        }

        if(count($exceptions) > 0)
        {
            throw new SchedulerException($numOfProcessed, $exceptions);
        }

        return $numOfProcessed;
    }
}


<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 */

use Wikimedia\Rdbms\IDatabase;

class SpoofUser {
	/** @var bool */
	private $legal;

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

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

	/** @var null|Status */
	private $error;

	/**
	 * @param string $name
	 */
	public function __construct( $name ) {
		$this->name = strval( $name );
		$status = AntiSpoof::checkUnicodeStringStatus( $this->name );
		$this->legal = $status->isOK();
		if ( $this->legal ) {
			$this->normalized = $status->getValue();
			$this->error = null;
		} else {
			$this->normalized = null;
			$this->error = $status;
		}
	}

	/**
	 * Does the username pass Unicode legality and script-mixing checks?
	 * @return bool
	 */
	public function isLegal() {
		return $this->legal;
	}

	/**
	 * Describe the error.
	 * @return null|string
	 */
	public function getError() {
		return $this->error ? $this->error->getMessage()->text() : null;
	}

	/**
	 * Describe the error.
	 * @return null|Status
	 * @since 1.32
	 */
	public function getErrorStatus() {
		return $this->error;
	}

	/**
	 * Get the normalized key form
	 * @return string|null
	 */
	public function getNormalized() {
		return $this->normalized;
	}

	/**
	 * @return string
	 */
	protected function getTableName() {
		return 'user';
	}

	/**
	 * @return string
	 */
	protected function getUserColumn() {
		return 'user_name';
	}

	/**
	 * Does the username pass Unicode legality and script-mixing checks?
	 *
	 * @return array empty if no conflict, or array containing conflicting usernames
	 */
	public function getConflicts() {
		$dbr = $this->getDBSlave();

		// Join against the user table to ensure that we skip stray
		// entries left after an account is renamed or otherwise munged.
		$spoofedUsers = $dbr->select(
			[ 'spoofuser', $this->getTableName() ],
			[ 'su_name' ], // Same thing due to the join. Saves extra variableness
			[
				'su_normalized' => $this->normalized,
				'su_name = ' . $this->getUserColumn(),
			],
			__METHOD__,
			[
				'LIMIT' => 5
			] );

		$spoofs = [];
		foreach ( $spoofedUsers as $row ) {
			array_push( $spoofs, $row->su_name );
		}
		return $spoofs;
	}

	/**
	 * Record the username's normalized form into the database
	 * for later comparison of future names...
	 * @return bool
	 */
	public function record() {
		return self::batchRecord( $this->getDBMaster(), [ $this ] );
	}

	/**
	 * @return array
	 */
	private function insertFields() {
		return [
			'su_name'       => $this->name,
			'su_normalized' => $this->normalized,
			'su_legal'      => $this->legal ? 1 : 0,
			'su_error'      => $this->getError(),
		];
	}

	/**
	 * Insert a batch of spoof normalization records into the database.
	 * @param IDatabase $dbw
	 * @param SpoofUser[] $items
	 * @return bool
	 */
	public static function batchRecord( IDatabase $dbw, $items ) {
		if ( !count( $items ) ) {
			return false;
		}
		$fields = [];
		/**
		 * @var $item SpoofUser
		 */
		foreach ( $items as $item ) {
			$fields[] = $item->insertFields();
		}
		$dbw->replace(
			'spoofuser',
			[ 'su_name' ],
			$fields,
			__METHOD__ );
		return true;
	}

	/**
	 * @param string $oldName
	 */
	public function update( $oldName ) {
		$that = $this;
		$method = __METHOD__;
		$dbw = $this->getDBMaster();
		// Avoid user rename triggered deadlocks
		$dbw->onTransactionPreCommitOrIdle(
			function () use ( $dbw, $that, $method, $oldName ) {
				if ( $that->record() ) {
					$dbw->delete(
						'spoofuser',
						[ 'su_name' => $oldName ],
						$method
					);
				}
			}
		);
	}

	/**
	 * Remove a user from the spoofuser table
	 */
	public function remove() {
		$this->getDBMaster()->delete(
			'spoofuser',
			[ 'su_name' => $this->name ],
			__METHOD__
		);
	}

	/**
	 * @return IDatabase
	 */
	protected function getDBSlave() {
		return wfGetDB( DB_REPLICA );
	}

	/**
	 * @return IDatabase
	 */
	protected function getDBMaster() {
		return wfGetDB( DB_MASTER );
	}
}


<?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 Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Registers services tagged as expression checkers in the expression checker registry.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class RegisterProcessorPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    protected $registryService;

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

    /**
     * @param string $registryServiceName
     * @param string $tagName
     */
    public function __construct($registryServiceName = 'abc.scheduler.processor_registry', $tagName = 'abc.scheduler.processor')
    {
        $this->registryService = $registryServiceName;
        $this->tagName         = $tagName;
    }

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

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

        foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags)
        {
            foreach ($tags as $tag)
            {
                if(!isset($tag['type']))
                {
                    return;
                }

                $definition->addMethodCall('register',  array($tag['type'], new Reference($id)));
            }
        }
    }
}


<?php
/**
 * This file is part of the PositibeLabs Projects.
 *
 * (c) Pedro Carlos Abreu <pcabreus@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Positibe\Bundle\UniqueViewsBundle\Model;

use Doctrine\ORM\EntityManagerInterface;

/**
 * Class VisitableManager
 * @package Positibe\Bundle\UniqueViewsBundle\Model
 *
 * @author Pedro Carlos Abreu <pcabreus@gmail.com>
 */
class VisitableManager implements VisitableManagerInterface {

    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * {@inheritdoc}
     */
    public function update(VisitableInterface $visitable)
    {
        $visitable->onNewViewed();

        $this->entityManager->persist($visitable);
        $this->entityManager->flush();
    }
}


<?php

/**
 * This class render an xml file
 * Using format Atom 1
 *
 * @author Nek' <nek.dev+github@gmail.com>
 */

namespace Nekland\Bundle\FeedBundle\Renderer;

use Symfony\Component\Routing\RouterInterface;
use Nekland\Bundle\FeedBundle\Feed;
use Nekland\Bundle\FeedBundle\XML\XMLManager;
use Nekland\Bundle\FeedBundle\Item\ItemInterface;

class AtomRenderer 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('atom'));
        if (is_file($filename)) {
            unlink($filename);
        }

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

    private function init(XMLManager $xml, Feed $feed) {
        $root = $xml->getXml()->createElement('feed');
        $root->setAttribute('xmlns', 'http://www.w3.org/2005/Atom');
        $root = $xml->getXml()->appendChild($root);

        $xml->addTextNode('title', $feed->get('title'), $root);

        if($subtitle = $feed->get('subtitle'))
        $xml->addTextNode('subtitle', $subtitle, $root);

        $date = new \DateTime();
        $xml->addTextNode('updated', $date->format(\DateTime::ATOM), $root);

        if($id = $feed->get('id'))
        $xml->addTextNode('id', $id, $root);

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

        if(null !== $feed->get('image')) {
            $image = $feed->get('image');
            $xml->addTextNode('logo', $image['url'], $root);
        }

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

        if(null !== $feed->get('url')) {
            $link = $xml->getXml()->createElement('link');
            $link->setAttribute('href', $feed->get('url'));
            $link = $root->appendChild($link);
        }

    }

    /**
     * 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);

        $type = $this->itemHas($item, 'getAtomContentType') ? $item->getAtomContentType() : null;
        $content = $xml->addTextNode('content', $type === 'xhtml' ? $item->getFeedDescription() : htmlentities($item->getFeedDescription(), ENT_COMPAT, 'UTF-8'), $nodeItem);
        if(!empty($type)){
            $content->setAttribute('type', $type);
        }
        if($this->itemHas($item, 'getAtomContentLanguage')){
            $content->setAttribute('xml:lang', $item->getAtomContentLanguage());
        }

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

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

        $date = new \DateTime();
        $xml->addTextNode('updated', $item->getFeedDate()->format(\DateTime::ATOM), $nodeItem);

        if ($this->itemHas($item, 'getFeedAuthor')) {
            if ($author = $item->getFeedAuthor()) {
                $authorNode = $this->createAuthor($xml, $author);
                $nodeItem->appendChild($authorNode);
            }
        }
        if ($this->itemHas($item, 'getSummary')) {
            $type = $this->itemHas($item, 'getAtomSummaryType') ? $item->getAtomSummaryType() : null;
            $summary = $xml->addTextNode('category', $type === 'xhtml' ? $item->getFeedDescription() : htmlentities($item->getFeedSummary(), ENT_COMPAT, 'UTF-8'), $nodeItem);
            if(!empty($type)){
                $summary->setAttribute('type', $type);
            }
        }
        if ($this->itemHas($item, 'getAtomContributors')) {
            if ($contributors = $item->getAtomContributors($item)) {
                foreach($contributors as $contributor) {
                    $contribNode = $this->createContributor($xml, $contributor);
                    $nodeItem->appendChild($contribNode);
                }
            }
        }

        $routes = $item->getFeedRoutes();
        foreach($routes as $route) {
            $linkNode = $this->createLink($xml, $route);
            $nodeItem->appendChild($linkNode);
        }
    }

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

        return $itemNode;
    }

    /**
     *
     * @param XMLManager $xml
     * @param array      $author
     *
     * @return \DOMNode
     */
    private function createAuthor(XMLManager $xml, array $author) {
        $authorNode = $xml->getXml()->createElement('author');

        if(isset($author['name'])) {
            $xml->addTextNode('name', $author['name'], $authorNode);
        }

        if(isset($author['website'])) {
            $xml->addTextNode('uri', $author['website'], $authorNode);
        }

        if(isset($author['email'])) {
            $xml->addTextNode('email', $author['email'], $authorNode);
        }

        return $authorNode;
    }

    /**
     *
     * @param XMLManager $xml
     * @param array      $contributor
     *
     * @return \DOMNode
     */
    private function createContributor(XMLManager $xml, array $contributor) {
        $contributorNode = $xml->getXml()->createElement('contributor');

        if(isset($author['name'])) {
            $xml->addTextNode('name', $contributor['name'], $contributorNode);
        }

        if(isset($author['website'])) {
            $xml->addTextNode('uri', $contributor['website'], $contributorNode);
        }

        if(isset($author['email'])) {
            $xml->addTextNode('email', $contributor['email'], $contributorNode);
        }

        return $contributorNode;
    }

    /**
     *
     * @param XMLManager $xml
     * @param array      $route
     * @throws \InvalidArgumentException
     *
     * @return \DOMNode
     */
    private function createLink(XMLManager $xml, array $route) {
        $routeNode = $xml->getXml()->createElement('link');
        $url = '';
        if(!empty($route['route']) && is_array($route['route'])) {

            $url = $this->router->generate($route['route'][0], $route['route'][1]);

        } else if(!empty($route['url'])) {

            $url = $route['url'];

        } else {
            throw new \InvalidArgumentException('The method getFeedRoutes must be an array of links. Check the ItemInterface.php for more details.');
        }

        $routeNode->setAttribute('href', $url);

        foreach($route as $attribute => $value) {
            if($attribute != 'route' && $attribute != 'url') {
                $routeNode->setAttribute($attribute, $value);
            }
        }

        return $routeNode;
    }

    private function itemHas(ItemInterface $item, $method) {
        $rc = new \ReflectionClass($item);
        if($rc->hasMethod($method)) {
            return true;
        } else {
            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\Schedule\Timestamp;

use Abc\Bundle\SchedulerBundle\Model\ScheduleInterface;
use Abc\Bundle\SchedulerBundle\Schedule\ProcessorInterface;

/**
 * Checks whether a schedule with a timestamp is due.
 *
 * @author Wojciech Ciolko <wojciech.ciolko@aboutcoders.com>
 */
class Processor implements ProcessorInterface
{
    /**
     * {@inheritDoc}
     */
    public function process(ScheduleInterface $schedule, \DateTime $currentDateTime = null)
    {
        $now = ($currentDateTime == null) ? new \DateTime() : $currentDateTime;

        if($schedule->getScheduledAt() !== null)
        {
            return false;
        }

        if($schedule->getExpression() < $now->getTimestamp())
        {
            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 Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class RegisterConstraintsPass implements CompilerPassInterface
{
    /**
     * @var string
     */
    private $registry;

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

    /**
     * @param string $registry Service name of the definition registry in processed container
     * @param string $tag      The tag name used for jobs
     */
    public function __construct($registry = 'abc.scheduler.constraint_registry', $tag = 'abc.scheduler.constraint')
    {
        $this->registry = $registry;
        $this->tag      = $tag;
    }

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

        $registry = $container->findDefinition($this->registry);
        foreach ($container->findTaggedServiceIds($this->tag) as $id => $tags) {
            $definition = $container->getDefinition($id);
            foreach ($tags as $tag) {
                if (!isset($tag['type'])) {
                    throw new \InvalidArgumentException(sprintf('The service "%s" must define the attribute "type" on "%s" tags.', $id, $this->tag));
                }

                $registry->addMethodCall('register', [$tag['type'], $definition]);
            }
        }
    }
}


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

use PHPoole\Generator\GeneratorManager;

/**
 * Generates virtual pages.
 */
class GeneratePages extends AbstractStep
{
    /**
     * {@inheritdoc}
     */
    public function init($options)
    {
        if (count($this->phpoole->getConfig()->get('generators')) > 0) {
            $this->process = true;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function process()
    {
        if ($this->process) {
            $generatorManager = new GeneratorManager();
            $generators = $this->phpoole->getConfig()->get('generators');
            array_walk($generators, function ($generator, $priority) use ($generatorManager) {
                if (!class_exists($generator)) {
                    $message = sprintf("Unable to load generator '%s'", $generator);
                    call_user_func_array($this->phpoole->getMessageCb(), ['GENERATE_ERROR', $message]);

                    return;
                }
                $generatorManager->addGenerator(new $generator($this->phpoole->getConfig()), $priority);
            });
            call_user_func_array($this->phpoole->getMessageCb(), ['GENERATE', 'Generating pages']);
            $pages = $generatorManager->process($this->phpoole->getPages(), $this->phpoole->getMessageCb());
            $this->phpoole->setPages($pages);
        }
    }
}


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

use Abc\Bundle\SchedulerBundle\Model\ScheduleInterface;
use Abc\Bundle\SchedulerBundle\Model\ScheduleManagerInterface;

/**
 * Iterator based on ScheduleManagerInterface.
 *
 * @author Hannes Schulz <hannes.schulz@aboutcoders.com>
 */
class ScheduleManagerScheduleIterator implements ScheduleIteratorInterface
{

    /**
     * @var ScheduleManagerInterface
     */
    protected $scheduleManager;

    /**
     * @var int
     */
    protected $limit;

    /**
     * @var int
     */
    protected $offset;

    /**
     * @var ScheduleInterface
     */
    protected $current;

    /**
     * @var int
     */
    protected $position;

    /**
     * @var array
     */
    protected $buffer = array();

    /**
     * @param ScheduleManagerInterface $scheduleManager
     * @param int                      $batchSize (default is 100)
     */
    public function __construct(ScheduleManagerInterface $scheduleManager, $batchSize = 100)
    {
        $this->scheduleManager = $scheduleManager;
        $this->limit           = $batchSize;
        $this->offset          = 0;
        $this->position        = 0;
    }

    /**
     * {@inheritdoc}
     */
    public function getManager()
    {
        return $this->scheduleManager;
    }

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        $this->position = 0;
        $this->offset = 0;
        $this->setCurrent();
    }

    /**
     * {@inheritdoc}
     */
    public function valid()
    {
        return $this->current != null;
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return $this->position;
    }

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

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        $this->setCurrent();
        $this->position++;
    }

    /**
     * Assign current pointer
     */
    protected function setCurrent()
    {
        if(count($this->buffer) === 0)
        {
            $this->buffer();
        }

        $this->current = count($this->buffer) > 0 ? array_pop($this->buffer) : null;
    }

    /**
     * Fill the inner buffer
     */
    protected function buffer()
    {
        $this->buffer = $this->scheduleManager->findSchedules($this->limit, $this->offset);

        $this->offset += count($this->buffer);
    }
}


<?php
/**
 * This file is part of the PositibeLabs Projects.
 *
 * (c) Pedro Carlos Abreu <pcabreus@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Positibe\Bundle\UniqueViewsBundle\Counter;

use Positibe\Bundle\UniqueViewsBundle\Model\VisitableManager;
use Positibe\Bundle\UniqueViewsBundle\Model\VisitableInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

/**
 * Class SessionCounter
 * @package Positibe\Bundle\UniqueViewsBundle\Counter
 *
 * @todo Hacer un sistema de eventos para las posibles distintas acciones que se quieran hacer cuando se ve un elemento
 *
 * @author Pedro Carlos Abreu <pcabreus@gmail.com>
 */
class SessionCounter implements UniqueViewsCounterInterface
{
    const SESSION_ARRAY_VARIABLE = 'unique_views';

    private $session;
    private $visitableManager;

    /**
     * SessionCounter constructor.
     * @param SessionInterface $session
     * @param VisitableManager $visitableManager
     */
    public function __construct(SessionInterface $session, VisitableManager $visitableManager)
    {
        $this->session = $session;
        $this->visitableManager = $visitableManager;
    }

    /**
     * {@inheritdoc}
     */
    public function count(VisitableInterface $visitable)
    {
        $uniqueViews = $this->getUniqueViewsVariable();

        if ($uniqueViews === null) {
            $uniqueViews = array();

            $this->saveVisitableType($uniqueViews, $visitable);

            $this->visitableManager->update($visitable);
        } elseif (!isset($uniqueViews[$visitable->getVisitableType()])) {
            $this->saveVisitableType($uniqueViews, $visitable);

            $this->visitableManager->update($visitable);
        } elseif (!isset($uniqueViews[$visitable->getVisitableType()][$visitable->getVisitableId()])) {
            $this->saveVisitableId($uniqueViews, $visitable);

            $this->visitableManager->update($visitable);
        }
    }

    /**
     * @param array $uniqueViews
     * @param VisitableInterface $visitable
     */
    private function saveVisitableId(array $uniqueViews, VisitableInterface $visitable)
    {
        $uniqueViews[$visitable->getVisitableType()][$visitable->getVisitableId()] = $visitable->getVisitableId();
        $this->session->set(self::SESSION_ARRAY_VARIABLE, $uniqueViews);
    }

    /**
     * @param array $uniqueViews
     * @param VisitableInterface $visitable
     */
    private function saveVisitableType(array $uniqueViews, VisitableInterface $visitable)
    {
        $uniqueViews[$visitable->getVisitableType()] = array();
        $this->saveVisitableId($uniqueViews, $visitable);
    }

    /**
     * @return mixed
     */
    private function getUniqueViewsVariable()
    {
        return $this->session->get(self::SESSION_ARRAY_VARIABLE);
    }

}