<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

namespace Core\Collection;

use Doctrine\Common\Collections\Collection;
use Closure;

class IdentityWrapper implements Collection
{

    /**
     * @var Collection
     */
    protected $collection;

    /**
     * @var callable
     */
    protected $identityExtractor;

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

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

    /**
     * {@inheritDoc}
     */
    public function add($element)
    {
        return $this->collection->add($element);
    }

    /**
     * {@inheritDoc}
     */
    public function clear()
    {
        $this->collection->clear();
    }

    /**
     * {@inheritDoc}
     */
    public function contains($element)
    {
        return $this->collection->contains($element);
    }

    /**
     * {@inheritDoc}
     */
    public function isEmpty()
    {
        return $this->collection->isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    public function remove($key)
    {
        $element = $this->getElement($key);

        if ($element !== false && $this->collection->removeElement($element)) {
            return $element;
        }
    }

    /**
     * {@inheritDoc}
     */
    public function removeElement($element)
    {
        return $this->collection->removeElement($element);
    }

    /**
     * {@inheritDoc}
     */
    public function containsKey($key)
    {
        return $this->getElement($key) !== false;
    }

    /**
     * {@inheritDoc}
     */
    public function get($key)
    {
        $element = $this->getElement($key);

        return $element !== false ? $element : null;
    }

    /**
     * {@inheritDoc}
     */
    public function getKeys()
    {
        return $this->collection->map(function ($element) {
            return $this->getKey($element);
        })->toArray();
    }

    /**
     * {@inheritDoc}
     */
    public function getValues()
    {
        return $this->collection->getValues();
    }

    /**
     * {@inheritDoc}
     */
    public function set($key, $value)
    {
        if ($this->getElement($key) !== false) {
            $this->collection->set($this->collection->indexOf($value), $value);
        } else {
            $this->collection->add($value);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function toArray()
    {
        $array = [];

        foreach ($this->collection as $element) {
            $array[$this->getKey($element)] = $element;
        }

        return $array;
    }

    /**
     * {@inheritDoc}
     */
    public function first()
    {
        return $this->collection->first();
    }

    /**
     * {@inheritDoc}
     */
    public function last()
    {
        return $this->collection->last();
    }

    /**
     * {@inheritDoc}
     */
    public function key()
    {
        $element = $this->collection->current();

        if ($element !== false) {
            return $this->getKey($element);
        }
    }

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

    /**
     * {@inheritDoc}
     */
    public function next()
    {
        return $this->collection->next();
    }

    /**
     * {@inheritDoc}
     */
    public function exists(Closure $p)
    {
        foreach ($this->collection as $element) {
            $key = $this->getKey($element);
            if ($p($key, $element)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritDoc}
     */
    public function filter(Closure $p)
    {
        return new static($this->collection->filter($p));
    }

    /**
     * {@inheritDoc}
     */
    public function forAll(Closure $p)
    {
        foreach ($this->collection as $element) {
            $key = $this->getKey($element);
            if (!$p($key, $element)) {
                return false;
            }
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public function map(Closure $func)
    {
        $mapped = $this->collection->map($func);

        // validate mapped elements
        array_map($this->getIdentityExtractor(), $mapped->toArray());

        return new static($mapped);
    }

    /**
     * {@inheritDoc}
     */
    public function partition(Closure $p)
    {
        $array = $this->toArray();
        $this->collection->clear();

        foreach ($array as $key => $element) {
            $this->collection->set($key, $element);
        }

        $partitions = $this->collection->partition($p);

        return [new static($partitions[0]), new static($partitions[1])];
    }

    /**
     * {@inheritDoc}
     */
    public function indexOf($element)
    {
        return $this->collection->indexOf($element) === false ? false : $this->getKey($element);
    }

    /**
     * {@inheritDoc}
     */
    public function slice($offset, $length = null)
    {
        $array = [];

        foreach ($this->collection->slice($offset, $length) as $element) {
            $array[$this->getKey($element)] = $element;
        }

        return $array;
    }

    /**
     * {@inheritDoc}
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->toArray());
    }

    /**
     * {@inheritDoc}
     */
    public function offsetExists($offset)
    {
        return (bool)$this->getElement($offset);
    }

    /**
     * {@inheritDoc}
     */
    public function offsetGet($offset)
    {
        return $this->get($offset);
    }

    /**
     * {@inheritDoc}
     */
    public function offsetSet($offset, $value)
    {
        if (!isset($offset)) {
            return $this->add($value);
        }

        $this->set($offset, $value);
    }

    /**
     * {@inheritDoc}
     */
    public function offsetUnset($offset)
    {
        return $this->remove($offset);
    }

    /**
     * @param callable $identityExtractor
     * @return IdentityWrapper
     */
    public function setIdentityExtractor(callable $identityExtractor)
    {
        $this->identityExtractor = $identityExtractor;

        return $this;
    }

    /**
     *
     * @return \Core\Collection\callable
     */
    protected function getIdentityExtractor()
    {
        if (!isset($this->identityExtractor)) {
            // default identity extractor
            $this->identityExtractor = function ($element) {
                if (!is_callable([$element, 'getId'])) {
                    throw new \LogicException('$element must have getId() method');
                }

                return $element->getId();
            };
        }

        return $this->identityExtractor;
    }

    /**
     * @param mixed $element
     * @return mixed
     */
    protected function getKey($element)
    {
        return call_user_func($this->getIdentityExtractor(), $element);
    }

    /**
     * @param mixed $element
     * @return mixed
     */
    protected function getElement($key)
    {
        return $this->collection->filter(function ($element) use ($key) {
            return $this->getKey($element) == $key;
        })->first();
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Listener;

use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * Lazy loads listeners.
 *
 * Registered listeners will be created and/or invoked by this aggregate,
 * if and only if the associated event is triggered from the event manager
 * this aggregate is attached to.
 *
 * If you do not retrieve an instance through the service manager you have to inject
 * the service manager instance yourself.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.20
 */
class DeferredListenerAggregate implements ListenerAggregateInterface
{

    /**
     * @var ServiceLocatorInterface
     */
    protected $serviceManager;

    /**
     * Registered listener specifications.
     *
     * @var array
     */
    protected $listenerSpecs = [];

    /**
     * Listener handles from the event manager,
     *
     * @var array
     */
    protected $listeners = [];

    /**
     * Creates an instance.
     *
     * Calls {@link setListeners()}.
     *
     * @param ServiceLocatorInterface $serviceManager
     * @param array $specs
     */
    public function __construct(ServiceLocatorInterface $serviceManager, $specs = [])
    {
        $this->serviceManager = $serviceManager;
        $this->setListeners($specs);
    }

    /**
     * Alias for setListeners().
     *
     * @param array $hooks
     *
     * @codeCoverageIgnore
     * @deprecated since 0.25 use setListeners() instead.
     * @see setListeners
     * @return self
     */
    public function setHooks(array $hooks)
    {
        return $this->setListeners($hooks);
    }

    /**
     * Alias for setListener().
     *
     * @param      $event
     * @param      $service
     * @param null $method
     * @param int  $priority
     *
     * @codeCoverageIgnore
     * @deprecated since 0.25 use setListener() instead.
     * @see setListener
     * @return self
     */
    public function setHook($event, $service, $method = null, $priority = 0)
    {
        return $this->setListener($event, $service, $method, $priority);
    }

    /**
     * Adds multiple listener specifications.
     *
     * $specs must be an array of arrays in the following format:
     *

     * [
     *      [
     *          'event' => <eventName>,
     *          'service' => <serviceNameOrFQCN>,
     *          { 'method' => <callbackMethodName>, }
     *          { 'priority' => <priority> }
     *      ],
     *      ...
     * ]
     *

     *
     * @param array $specs
     *
     * @see setListener
     * @return self
     * @throws \DomainException if a specification array does not contain the keys 'event' or 'service'.
     * @since 0.25
     */
    public function setListeners(array $specs)
    {
        foreach ($specs as $spec) {
            if (!isset($spec['event']) || !isset($spec['service'])) {
                throw new \DomainException('Listener specification must be an array with the keys "event" and "service".');
            }
            $method = isset($spec['method']) ? $spec['method'] : null;
            $priority = isset($spec['priority']) ? $spec['priority'] : 0;

            $this->setListener($spec['event'], $spec['service'], $method, $priority);
        }

        return $this;
    }

    /**
     * Adds a listener specification.
     *
     * @param string     $event
     * @param string     $service
     * @param null|string|int $method
     * @param int  $priority
     *
     * @return self
     * @since 0.25
     */
    public function setListener($event, $service, $method = null, $priority = 0)
    {
        if (is_int($method)) {
            $priority = $method;
            $method = null;
        }

        $name = uniqid();

        $this->listenerSpecs[$name] = [
            'event' => $event,
            'service' => $service,
            'method' => $method,
            'priority' => $priority,
            'instance' => null,
        ];

        return $this;
    }

    /**
     * Attachs listener creation and invokation callback.
     *
     * For each specification this aggregate attachs itself to the event manager on the
     * specified event to be able to react upon triggering.
     *
     * @param EventManagerInterface $events
     *
     * @return void
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        foreach ($this->listenerSpecs as $name => $spec) {
            $this->listeners[] = $events->attach($spec['event'], array($this, "do$name"), $spec['priority']);
        }
    }

    /**
     * Detach all listener creation and invokation callbacks.
     *
     * @param EventManagerInterface $events
     *
     * @return boolean
     */
    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $i => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$i]);
            }
        }

        return empty($this->listeners);
    }

    /**
     * Callback for creation and invokation of listeners.
     *
     * @param string $method
     * @param array $args
     *
     * @return mixed The return value of the listener invokation.
     *
     * @throws \UnexpectedValueException if listener specification could not be found or listener could not be created.
     * @throws \BadMethodCallException if the method called does not start with "do".
     */
    public function __call($method, $args)
    {
        if (0 !== strpos($method, 'do')) {
            throw new \BadMethodCallException('Unknown method "' . $method . '"');
        }

        $name = substr($method, 2);
        $spec = isset($this->listenerSpecs[$name]) ? $this->listenerSpecs[$name] : false;

        if (!$spec) {
            throw new \UnexpectedValueException('No deferred listener specification found.');
        }

        $service = $spec['service'];
        $method  = $spec['method'];
        $listener = $spec['instance'];

        if (!$listener) {
            if ($this->serviceManager->has($service)) {
                $listener = $this->serviceManager->get($service);
            } else {
                if (!class_exists($service, true)) {
                    throw new \UnexpectedValueException(sprintf(
                        'Cannot create deferred listener "%s", because the class does not exist.',
                        $service
                    ));
                }

                $listener = new $service();
            }

            $this->listenerSpecs[$name]['instance'] = $listener;
        }

        if ($method && method_exists($listener, $method)) {
            return call_user_func_array([ $listener, $method ], $args);
        }

        if (is_callable($listener)) {
            return call_user_func_array($listener, $args);
        }

        throw new \UnexpectedValueException(sprintf(
            'Deferred listener %s%s is not callable.',
            get_class($listener),
            $method ? ' has no method "' . $method . '" and ' : ''
        ));
    }

    /**
     * @param ServiceLocatorInterface $serviceManager
     * @return DeferredListenerAggregate
     */
    public static function factory(ServiceLocatorInterface $serviceManager)
    {
        return new static($serviceManager);
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** Core forms view helpers */
namespace Core\Form\View\Helper;

use Core\Form\ViewPartialProviderInterface;
use Core\Form\ExplicitParameterProviderInterface;
use Core\Form\Element\ViewHelperProviderInterface;
use Core\Form\Container;
use Core\Form\WizardContainer;
use Zend\Form\View\Helper\AbstractHelper;
use Core\Form\SummaryForm;

/**
 * Helper for rendering form wizard containers
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
class FormWizardContainer extends AbstractHelper
{

    /**
     * Invoke as function.
     *
     * Proxies to {@link render()} or returns self.
     *
     * @param  null|Container $container
     * @param string $layout
     * @param array $parameter
     * @return FormContainer|string
     */
    public function __invoke(Container $container = null, $layout = Form::LAYOUT_HORIZONTAL, $parameter = array())
    {
        if (!$container) {
            return $this;
        }

        return $this->render($container, $layout, $parameter);
    }

    /**
     * Renders the forms of a container.
     *
     * @param WizardContainer $container
     * @param string $layout
     * @param array $parameter
     * @return string
     */
    public function render(WizardContainer $container, $layout = Form::LAYOUT_HORIZONTAL, $parameter = array())
    {
        $content = '';

        $content .= $container->renderPre($this->getView());

        $tabsNav = '';
        $tabsContent = '';
        $containerParams = [
            'pager' => true,
            'finish_label' => 'Finish',
            'finish_href' => 'javascript:;',
            'finish_enabled' => true,
        ];

        if (isset($parameter['wizard'])) {
            $containerParams = array_merge($containerParams, $parameter['wizard']);
            unset($parameter['wizard']);
        }

        $translate = $this->getView()->plugin('translate');
        $formContainer = $this->getView()->plugin('formContainer');

        if ($container instanceof ViewPartialProviderInterface) {
            return $this->getView()->partial($container->getViewPartial(), array('element' => $container));
        }

        $containerId = $container->getAttribute('id');
        if (!$containerId) {
            $containerId = 'wizardcontainer-' . strtolower(str_replace('\\', '-', get_class($container)));
        }

        foreach ($container as $tabElement) {
            $tabId = $containerId . '-' . strtolower($tabElement->getName());
            $tabsNav .= '
' . $translate($tabElement->getLabel()) . '
';
            $tabsContent .= '
'
                          . $formContainer($tabElement, $layout, $parameter)
                          . '
';
        }

        $content .= '
'
                  . '
' . $tabsNav . '
'
                  . '
' . $tabsContent . '
';
        if ($containerParams['pager']) {
            $content .='
'
                  . '
← ' . $translate('previous') . '
'
                  . '
' . $translate('Next') . ' →
'
                  . '
'
                  . (
                      false !== $containerParams['finish_label']
                     ? ''
                       . $translate($containerParams['finish_label']) . ' •'
                     : ''
                    )
                  . '

';
        }
        $content .= '
';

        $content .= $container->renderPost($this->getView());

        return $content;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Entity\Tree;

use Core\Entity\ClonePropertiesTrait;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Core\Entity\Collection\ArrayCollection;
use Core\Entity\EntityTrait;
use Doctrine\Common\Collections\Collection;

/**
 * Main base class for attached or embedded tree leafs.
 *
 * @ODM\MappedSuperclass
 * @ODM\HasLifecycleCallbacks
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.29
 */
abstract class AbstractLeafs implements LeafsInterface
{
    use EntityTrait, ClonePropertiesTrait;

    private $cloneProperties = [ '!items' ];

    /**
     * The leafs.
     *
     * @ODM\ReferenceMany(discriminatorField="_entity", storeAs="dbRef", strategy="set", sort={"priority"="asc"}, cascade={"persist"})
     * @var Collection
     */
    private $items;

    /**
     * The item values of all attached leafs.
     *
     * To make the attached leafs searchable via mongo queries.
     *
     * @ODM\Field(type="collection")
     * @var array
     */
    private $values;

    public function getItems()
    {
        if (!$this->items) {
            $this->setItems(new ArrayCollection());
        }

        return $this->items;
    }

    public function setItems(Collection $items)
    {
        $this->items = $items;
        $this->updateValues();

        return $this;
    }

    /**
     * Get the item values of the attached leafs.
     *
     * @return array
     */
    public function getValues()
    {
        return $this->values;
    }

    /**
     * Updates the item values.
     *
     * @ODM\PrePersist
     * @ODM\PreUpdate
     */
    public function updateValues()
    {
        $values = [];
        /* @var NodeInterface $item */
        foreach ($this->getItems() as $item) {
            if (!is_null($item)) {
                $values[] = $item->getValueWithParents();
            }
        }

        $this->values = $values;
    }

    /**
     * Get a list of the names of all attached leafs.
     *
     * Will prefix the leaf name with its parents, unless the parent is
     * the root node.
     *
     * @return string
     */
    public function __toString()
    {
        $items = [];

        /* @var NodeInterface $item */
        foreach ($this->getItems() as $item) {
            $parent = $item->getParent();
            $nameParts = [];
            while ($parent) {
                $nextParent = $parent->getParent();
                if ($nextParent) {
                    $nameParts[] = $parent->getName();
                }
                $parent = $nextParent;
            }
            $nameParts[] = $item->getName();
            $items[]     = join(' | ', $nameParts);
        }

        $items = join(', ', $items);

        return $items;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 * @author    weitz@cross-solution.de
 */

namespace Core\Listener;

use Interop\Container\ContainerInterface;
use Zend\EventManager\SharedEventManagerInterface;
use Core\Listener\Events\NotificationEvent;
use Zend\Mvc\MvcEvent;
use Zend\EventManager\EventManager;
use Zend\View\Model\JsonModel;

/**
 *
 * @todo [MG] This must be refactored! It's a violation of the encapsulation principle.
 *       I mean... an event manager which is a listener for itself? What a mess!
 */
class NotificationListener extends EventManager
{

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

    /**
     * @var bool
     */
    protected $hasRunned = true;

    /**
     * @param SharedEventManagerInterface $events
     *
     * @return $this
     */
    public function attachShared(SharedEventManagerInterface $events)
    {
        $events->attach('*', NotificationEvent::EVENT_NOTIFICATION_ADD, array($this,'add'), 1);
        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_DISPATCH, array($this,'renderJSON'), -240);
        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_DISPATCH, array($this,'renderHTML'), -250);
        // Sometimes the Dispatch-Event is not reached, for instance with a route-direct
        // but also for Events, that are happening after the Dispatch
        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_FINISH, array($this,'renderHTML'), -250);
        return $this;
    }

    /**
     * @param SharedEventManagerInterface $events
     *
     * @return $this
     */
    public function detachShared(SharedEventManagerInterface $events)
    {
        return $this;
    }

    /**
     * @param NotificationEvent $event
     * @return \Core\Listener\NotificationListener
     */
    public function add($event)
    {
        //@TODO: [ZF3] $event->getNotification() is not working
        $target = $event->getTarget();
        if ($target instanceof NotificationEvent) {
            $notification = $target->getNotification();
            $this->notifications[] = $notification;
            $this->hasRunned = false;
        }

        return $this;
    }

    /**
     * Special handling json
     * @param MvcEvent $event
     */
    public function renderJSON(MvcEvent $event)
    {
        if (!$this->hasRunned) {
            $valueToPlainStati = array(1 => 'error', 2 => 'error', 3 => 'error', 4 => 'error', 5 => 'success', 6 => 'info', 7 => 'info');
            $viewModel = $event->getViewModel();
            if ($viewModel instanceof JsonModel) {
                if (!empty($this->notifications)) {
                    $jsonNotifications = $viewModel->getVariable('notifications', array());
                    foreach ($this->notifications as $notification) {
                        $status = 'info';
                        if (array_key_exists($notification->getPriority(), $valueToPlainStati)) {
                            $status = $valueToPlainStati[$notification->getPriority()];
                        }
                        $jsonNotifications[] = array(
                            'text' => $notification->getNotification(),
                            'status' => $status
                        );
                    }
                    $viewModel->setVariable('notifications', $jsonNotifications);
                }
                $this->hasRunned = true;
            }
        }
        return;
    }

    /**
     * @return \Core\Listener\NotificationListener
     */
    public function reset()
    {
        $this->hasRunned = false;
        return $this;
    }

    /**
     * @param MvcEvent $event
     * @return \Core\Listener\NotificationListener
     */
    public function renderHTML(MvcEvent $event)
    {
        if (!$this->hasRunned) {
            $nEvent = new NotificationEvent();
            $nEvent->setNotifications($this->notifications);
            $this->trigger(NotificationEvent::EVENT_NOTIFICATION_HTML, $nEvent);
            $this->notifications = array();
            $this->hasRunned = true;
        }
        return $this;
    }

    /**
     * @param ContainerInterface $container
     *
     * @return NotificationListener
     */
    public static function factory(ContainerInterface $container)
    {
        $sharedManager = $container->get('Application')->getEventManager()->getSharedManager();
        $instance = new NotificationListener($sharedManager);
        $instance->setEventPrototype(new NotificationEvent());
        return $instance;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2018 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Controller\Plugin;

use Core\Entity\EntityInterface;
use Core\EventManager\EventManager;
use Core\Repository\RepositoryService;
use Core\Service\EntityEraser\DependencyResultEvent;
use Core\Service\EntityEraser\EntityEraserEvents;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;

/**
 * Plugin to load entities, check their dependencies and delete these entities with all their dependencies.
 *
 * It's meant to use three steps:
 *
 * 1. Load the entities to delete.
 *    Using the event manager "Core/EntityEraser/Load/Events", it loads entities to delete by
 *    triggering the event with the event named after a passed in key.
 *    This way all kind of entities can be loaded (mostly relevant for the console action "purge".)
 *
 * 2. Check dependencies.
 *    Using the event manager "Core/EntityEraser/Dependencies/Events", it triggers an event for
 *    each entity to delete. This way any listener can add dependencies to the DependencyResultCollection.
 *    These dependencies can then be used to show which entities will be affected in which way, if the entities
 *    will be deleted.
 *
 * 3. Delete the entities.
 *    Using the event manager "Core/EntityEraser/Dependencies/Events", for each entity to delete, the event
 *    DependencyResultEvent::DELETE will be fired. Allowing listeners to act acording to the deletion of the entity.
 *    After that, each entity will be removed from the database.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @todo write test
 */
class EntityEraser extends AbstractPlugin
{
    /**
     * @var EntityEraserEvents
     */
    private $entityEraserEvents;

    /**
     * EventManager for the loading of entities.
     *
     * @var EventManager
     */
    private $loadEntitiesEvents;

    /**
     * RepositoryService
     *
     * @var \Core\Repository\RepositoryService
     */
    private $repositories;

    /**
     * Array of options to be passed along to listeners as event parameters.
     *
     * @var array
     */
    private $options = [];

    /**
     * EntityEraser constructor.
     *
     * @param EntityEraserEvents $entityEraserEvents
     * @param EventManager       $loadEntitiesEvents
     * @param RepositoryService  $repositories
     */
    public function __construct(EntityEraserEvents $entityEraserEvents, EventManager $loadEntitiesEvents, RepositoryService $repositories)
    {
        $this->entityEraserEvents = $entityEraserEvents;
        $this->loadEntitiesEvents = $loadEntitiesEvents;
        $this->repositories = $repositories;
    }

    /**
     * Set options to be passed along to listeners as event parameters.
     *
     * @param array $options
     */
    public function setOptions(array $options)
    {
        $this->options = $options;
    }

    /**
     * Loads entities to be deleted.
     *
     * Triggers an event on the "Core/EntityEraser/Load/Events" event manager.
     *
     * @param string      $entity Used as event name.
     * @param string|null $id
     *
     * @return array|\Traversable|null
     */
    public function loadEntities($entity, $id = null)
    {
        $params = $this->options;
        $params['id'] = $id;
        $params['repositories'] = $this->repositories;

        $event = $this->loadEntitiesEvents->getEvent($entity, $this, $params);
        $responses = $this->loadEntitiesEvents->triggerEventUntil(
            function ($response) {
                return (is_array($response) || $response instanceof \Traversable) && count($response);
            },
            $event
        );

        $entities = $responses->last();

        return $entities;
    }

    /**
     * Checks dependencies for an entity.
     *
     * Triggers the DependencyResultEvent::CHECK_DEPENDENCIES event on the
     * "Core/EntityEraser/Dependencies/Events" event manager.
     *
     * @param EntityInterface $entity
     *
     * @return \Core\Service\EntityEraser\DependencyResultCollection
     * @uses triggerEvent
     */
    public function checkDependencies(EntityInterface $entity)
    {
        return $this->triggerEvent(DependencyResultEvent::CHECK_DEPENDENCIES, $entity);
    }

    /**
     * Deletes an entity.
     *
     * Triggers the DependencyResultEvent::DELETE event on the
     * "Core/EntityEraser/Dependencies/Events" event manager.
     *
     * Removes the entity from the database.
     *
     * @param EntityInterface $entity
     *
     * @return \Core\Service\EntityEraser\DependencyResultCollection
     * @uses triggerEvent
     */
    public function erase(EntityInterface $entity)
    {
        $dependencies = $this->triggerEvent(DependencyResultEvent::DELETE, $entity);

        foreach ($dependencies as $result) {
            if ($result->isDelete()) {
                foreach ($result->getEntities() as $dependendEntity) {
                    $this->repositories->remove($dependendEntity);
                }
            }
        }

        $this->repositories->remove($entity);

        return $dependencies;
    }

    /**
     * Helper function to trigger a DependecyResultEvent.
     *
     * @param string $name
     * @param EntityInterface $entity
     *
     * @return \Core\Service\EntityEraser\DependencyResultCollection
     */
    private function triggerEvent($name, EntityInterface $entity)
    {
        $params = $this->options;
        $params['entity'] = $entity;
        $params['repositories'] = $this->repositories;

        /* @var DependencyResultEvent $event */
        $event = $this->entityEraserEvents->getEvent($name, $this, $params);

        $this->entityEraserEvents->triggerEvent($event);
        $dependencies = $event->getDependencyResultCollection();

        return $dependencies;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** PermissionsResourceEventsSubscriber.php */
namespace Core\Repository\DoctrineMongoODM\Event;

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\EventSubscriber;
use Doctrine\ODM\MongoDB\Events;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Core\Entity\PermissionsResourceInterface;
use Core\Entity\PermissionsInterface;
use Core\Entity\PermissionsAwareInterface;

/**
 * how to use:
 * derive a class from this, put in the repository
 *
 * Class AbstractUpdatePermissionsSubscriber
 * @package Core\Repository\DoctrineMongoODM\Event
 */
abstract class AbstractUpdatePermissionsSubscriber implements EventSubscriber
{
    protected $repositoryName;
    protected $queuedEntities;

    public function getSubscribedEvents()
    {
        return array(Events::postRemove, Events::postUpdate, 'postCommit');
    }

    public function postUpdate(LifecycleEventArgs $args)
    {
        $this->updatePermissions($args, true);
    }

    public function postRemove(LifecycleEventArgs $args)
    {
        $this->updatePermissions($args, PermissionsInterface::PERMISSION_NONE);
    }

    public function postCommit(EventArgs $args)
    {
        if (!$this->queuedEntities) {
            return;
        }

        $dm = $args->get('documentManager'); /* @var $dm \Doctrine\ODM\MongoDB\DocumentManager */
        foreach ($this->queuedEntities as $entity) {
            $dm->persist($entity);
        }

        $this->queuedEntities = null;
        $dm->flush();
    }

    protected function updatePermissions(LifecycleEventArgs $args, $permission)
    {
        $resource = $args->getDocument();
        if (!$resource instanceof PermissionsResourceInterface) {
            return;
        }

        $entities = $this->getEntities($args);

        foreach ($entities as $entity) {
            if (!$entity instanceof PermissionsAwareInterface) {
                continue;
            }
            $permissions = $entity->getPermissions();
            $permissions->grant($resource, $permission);
            $this->queuedEntities[] = $entity;
        }

        //$args->getDocumentManager()->flush($resource);
    }

    /**
     * Fetch entities from the database which permissions are assigned the referral document.
     *
     * @param LifecycleEventArgs $args
     *
     * @return Collection
     */
    protected function getEntities($args)
    {
        $dm             = $args->getDocumentManager();
        $resource       = $args->getDocument();
        $repositoryName = $this->getRepositoryName();
        $resourceId     = $resource->getPermissionsResourceId();
        $repository     = $dm->getRepository($repositoryName);

        $criteria = array(
            'permissions.assigned.' . $resourceId => array(
                '$exists' => true
            )
        );

        $entities = $repository->findBy($criteria);

        return $entities;
    }

    protected function getRepositoryName()
    {
        if (0 === strpos($this->repositoryName, '\\')) {
            return $this->repositoryName;
        }

        if (null === $this->repositoryName) {
            throw new \RuntimeException('RepositoryName is missing. Define $this->repositoryName.');
        }

        $nameParts = explode('/', $this->repositoryName);
        if (2 > count($nameParts)) {
            $nameParts[1] = $nameParts[0];
        }

        $namespace  = $nameParts[0];
        $entityName = $nameParts[1];

        $this->repositoryName = "\\$namespace\\Entity\\$entityName";
        return $this->repositoryName;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license    MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Factory;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Stdlib\AbstractOptions;

/**
 * Creates options instances from configuration specifications.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author Anthonius Munthi <me@itstoni.com>
 * @since  0.23
 */
class OptionsAbstractFactory implements AbstractFactoryInterface
{

    /**
     * Simple mode handles only skalar values, arrays and POPOs.
     */
    const MODE_SIMPLE = 'simple';

    /**
     * Nested mode is able to handle other options instances as an option value.
     */
    const MODE_NESTED = 'nested';

    /**
     * The configuration specification of all creatable options instances.
     *
     * The format is
     * <pre>
     * [
     *      'ServiceNameOfTheOptionsInstance' => [
     *          'class' => 'FQCN of the options class.',
     *          'mode' => SIMPLE or NESTED (optional, default SIMPLE)
     *          'options' => [
     *              'simpleSkalar' => 'value',
     *              'simpleArray' => [ 'someArray' ],
     *              'nestedOptions' => [
     *                  '__class__' => 'FQCN of the nested options class.',
     *                  'someValue' => 'skalar'
     *              ],
     *          ],
     *      ],
     * ];
     *
     * @var array
     */
    protected $optionsConfig;

    /**
     * Create an object
     *
     * @param  ContainerInterface $container
     * @param  string             $requestedName
     * @param  null|array         $options
     *
     * @return object
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $config = $this->getOptionsConfig($requestedName);

        if (!isset($config['class']) && !class_exists($requestedName)) {
            throw new \InvalidArgumentException(sprintf(
                'Missing index "class" from the config array for options "%s"',
                $requestedName
            ));
        }

        $className = isset($config['class']) ? $config['class'] : $requestedName;
        $mode      = isset($config['mode']) ? $config['mode'] : self::MODE_SIMPLE;
        $options   = isset($config['options']) ? $config['options'] : (isset($config[0]) ? $config[0] : []);

        if (self::MODE_SIMPLE == $mode) {
            return new $className($options);
        }

        if (self::MODE_NESTED == $mode) {
            return $this->createNestedOptions($className, $options);
        }

        throw new \InvalidArgumentException(sprintf('Unknown mode "%s".', $mode));
    }

    /**
     * Can the factory create an instance for the service?
     *
     * @param  ContainerInterface $container
     * @param  string             $requestedName
     *
     * @return bool
     */
    public function canCreate(ContainerInterface $container, $requestedName)
    {
        // Load options config specifications the first time this method is called.
        if (null === $this->optionsConfig) {
            $mainConfig          = $container->get('config');
            $this->optionsConfig = isset($mainConfig['options']) ? $mainConfig['options'] : [];
        }
        return false !== $this->getOptionsConfig($requestedName);
    }

    /**
     * Determines if we can create an options instance with name.
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @param string                  $name
     * @param string                  $requestedName
     *
     * @return bool
     */
    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
    {
        return $this->canCreate($serviceLocator, $requestedName);
    }

    /**
     * Creates an options instance  with name.
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @param string                  $name
     * @param string                  $requestedName
     *
     * @return AbstractOptions
     * @throws \InvalidArgumentException
     */
    public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
    {
        return $this($serviceLocator, $requestedName);
    }

    /**
     * Creates a nested options instance.
     *
     * @param string $className
     * @param array  $options
     *
     * @return AbstractOptions
     */
    protected function createNestedOptions($className, $options)
    {
        $class = new $className();

        foreach ($options as $key => $spec) {
            if (is_array($spec) && array_key_exists('__class__', $spec)) {
                $nestedClassName = $spec['__class__'];
                unset($spec['__class__']);
                $spec = $this->createNestedOptions($nestedClassName, $spec);
            }

            $class->{$key} = $spec;
        }

        return $class;
    }

    /**
     * Gets the configuration for a specific options instance.
     *
     * Returns FALSE if configuration cannot be found.
     *
     * @param string $fullName
     *
     * @return array|bool
     */
    protected function getOptionsConfig($fullName)
    {
        if (array_key_exists($fullName, $this->optionsConfig)) {
            return $this->optionsConfig[$fullName];
        }

        return false;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license       MIT
 */

/**  */
namespace Core\Form\Element;

use Doctrine\Common\Collections\Collection;
use Zend\Form\Element\File;
use Zend\InputFilter\InputProviderInterface;
use Zend\Validator\Callback;
use Zend\Validator\File\MimeType;
use Zend\Validator\File\Size;
use Zend\View\Helper\HelperInterface;
use Zend\Form\FormInterface;

/**
 * File upload formular element.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
class FileUpload extends File implements
    ViewHelperProviderInterface,
    InputProviderInterface
{
    /**
     * The view helper name.
     *
     * @var string
     */
    protected $helper = 'formFileUpload';

    /**
     * Is this element a multiple file upload
     *
     * @var bool
     */
    protected $isMultiple = false;

    /**
     * The form which contains this element
     *
     * @var FormInterface
     */
    protected $form;

    public function setViewHelper($helper)
    {
        if (is_object($helper) && !$helper instanceof HelperInterface) {
            throw new \InvalidArgumentException('Expects helper to be either a service name or an instance of "Zend\View\Helper\HelperInterface"');
        }

        $this->helper = $helper;

        return $this;
    }

    public function getViewHelper()
    {
        return $this->helper;
    }

    /**
     * Sets the form instance of the form which contains this element.
     * This instance is needed in
     * {@link fileCountValidationCallback()} and
     * {@link getFileEntity()}
     *
     * @param \Zend\Form\FormInterface $form
     *
     * @return self
     */
    public function setForm(FormInterface $form)
    {
        $this->form = $form;

        return $this;
    }

    /**
     * Sets the maximum file size.
     *
     * @param int $bytes
     *
     * @return self
     */
    public function setMaxSize($bytes)
    {
        return $this->setAttribute('data-maxsize', $bytes);
    }

    /**
     * Sets the allowed mime types.
     * The types can be passed either as an array or a comma separated list.
     *
     * @param array|string $types
     *
     * @return self
     */
    public function setAllowedTypes($types)
    {
        if (is_array($types)) {
            $types = implode(',', $types);
        }

        return $this->setAttribute('data-allowedtypes', $types);
    }

    /**
     * Sets the maximum files count.
     *
     * @param int $count
     *
     * @return self
     */
    public function setMaxFileCount($count)
    {
        return $this->setAttribute('data-maxfilecount', (int) $count);
    }

    /**
     * Sets if this element allows multiple files to be selected.
     *
     * @param boolean $flag
     *
     * @return self
     */
    public function setIsMultiple($flag)
    {
        $this->isMultiple = (bool) $flag;
        if ($flag) {
            $this->setAttribute('multiple', true);
        } else {
            $this->removeAttribute('multiple');
        }

        return $this;
    }

    public function prepareElement(FormInterface $form)
    {
        $form->setAttribute('class', ($this->isMultiple() ? 'multi' : 'single') . '-file-upload');
        $form->setAttribute('data-is-empty', null === $this->getValue());
        parent::prepareElement($form);
    }

    public function getInputSpecification()
    {
        $validators = array();
        $mimetypes  = $this->getAllowedTypes();
        $fileCount  = $this->getMaxFileCount();

        if ($mimetypes) {
            $mimeTypeValidator = new MimeType();
            $mimeTypeValidator->setMagicFile(false)
                              ->disableMagicFile(true)
                              ->setMimeType($this->getAllowedTypes());

            $validators[] = $mimeTypeValidator;
        }

        $validators[] = new Size($this->getMaxSize());

        if (0 < $fileCount) {
            $validators[] = new Callback(array($this, 'fileCountValidationCallback'));
        }

        return array(
            'name'       => $this->getName(),
            'required'   => false,
            'validators' => $validators,
        );
    }

    /**
     * Gets if this element allows multiple files to be selected.
     *
     * @return boolean
     */
    public function isMultiple()
    {
        return $this->isMultiple;
    }

    /**
     * Gets the allowed mimetypes
     *
     * @param bool $asArray if true, the types are returned as an array,
     *                      if false, the types are returned as comma separated string.
     *
     * @return string|array
     */
    public function getAllowedTypes($asArray = false)
    {
        $types = $this->getAttribute('data-allowedtypes');

        if ($asArray) {
            return explode(',', $types);
        }

        return $types;
    }

    /**
     * Gets the maximum files count.
     *
     * @return int
     */
    public function getMaxFileCount()
    {
        $count = $this->getAttribute('data-maxfilecount');

        return $count;
    }

    /**
     * Gets the maximum file size
     *
     * @return int
     */
    public function getMaxSize()
    {
        return $this->getAttribute('data-maxsize');
    }

    /**
     * Gets the file entity bound to the containing form,
     * Returns <i>NULL</i>, if the entity cannot be retrieved or is not set in the form.
     *
     * @return null|Collection|\Core\Entity\FileInterface
     */
    public function getFileEntity()
    {
        if (!$this->form || !($object = $this->form->getObject())) {
            return null;
        }

        if ($this->isMultiple()) {
            return $object;
        }

        $entityName = $this->getName();

        try {
            $fileEntity = $object->{"get" . $entityName}();
        } catch (\OutOfBoundsException $e) {
            return null;
        }

        return $fileEntity;
    }

    /**
     * Callback for file count validation.
     *
     * @internal
     *      This function gets the value passed in as variable,
     *      but we do not need it.
     * @return bool
     */
    public function fileCountValidationCallback()
    {
        if ($this->form && ($object = $this->form->getObject())) {
            if ($this->getMaxFileCount() - 1 < count($object)) {
                return false;
            }
        }

        return true;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2019 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Queue\Controller;

use Core\Queue\MongoQueue;
use SlmQueue\Queue\QueuePluginManager;
use Zend\Mvc\Console\Controller\AbstractConsoleController;

/**
 * Console controller for list queue jobs.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
class MongoQueueListController extends AbstractConsoleController
{
    /**
     * The queue plugin manager
     *
     * @var QueuePluginManager
     */
    private $queuePluginManager;

    /**
     * MongoQueueListController constructor.
     *
     * @param QueuePluginManager $queuePluginManager
     */
    public function __construct(QueuePluginManager $queuePluginManager)
    {
        $this->queuePluginManager = $queuePluginManager;
    }

    /**
     * List jobs from a queue
     *
     * @return string
     */
    public function listAction()
    {
        $queue   = $this->params()->fromRoute('queue');
        $queue   = $this->queuePluginManager->get($queue);

        if (!$queue instanceOf MongoQueue) {
            return 'Unsupported queue type.';
        }

        $statusMap = function($stat) {
            static $map = [
                'pending' => MongoQueue::STATUS_PENDING,
                'running' => MongoQueue::STATUS_RUNNING,
                'failed'  => MongoQueue::STATUS_FAILED,
            ];

            return $map[$stat] ?? $map['pending'];
        };

        $console = $this->getConsole();
        $jobs    = $queue->listing([
            'limit'  => (int) $this->params()->fromRoute('limit', 0),
            'status' => $statusMap($this->params()->fromRoute('status')),
        ]);

        if (!$jobs) {
            return 'Queue is empty.';
        }

        $lineTmpl = '%-20s %s';
        foreach ($jobs as $job) {
            $console->writeLine(get_class($job['job']) . ' [ ' . $job['job']->getId() . ' ]');

            foreach (['created', 'executed', 'scheduled'] as $key) {
                $console->writeLine(sprintf($lineTmpl, ucFirst($key), $job[$key]->toDateTime()->format('Y-m-d H:i:s')));
            }
            $console->writeLine(sprintf($lineTmpl, 'Tries', $job['tried']));
            $console->writeLine();
            $console->writeLine();
        }

        return '';
    }

}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

namespace Core\Form\View\Helper;

use Zend\Form\View\Helper\FormRow as ZendFormRow;
use Zend\Form\ElementInterface;
use Core\Form\ViewPartialProviderInterface;
use Zend\Form\Element\Button;

class FormRow extends ZendFormRow
{
    protected $layout;
    protected $shouldWrap = true;
    protected $labelSpanWidth = 3;

    public function setShouldWrap($flag)
    {
        $this->shouldWrap = (bool) $flag;
        return $this;
    }

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

    /**
     * Utility form helper that renders a label (if it exists), an element and errors
     *
     * @param ElementInterface $element
     * @return string
     * @throws \Zend\Form\Exception\DomainException
     */
    public function render(ElementInterface $element, $ignoreViewPartial = false)
    {
        $labelAttributes = $element->getLabelAttributes();
        $labelWidth = $element->getOption('labelWidth');
        if (!$labelWidth) {
            $labelWidth = $this->labelSpanWidth;
        }

        if ($element instanceof ViewPartialProviderInterface && !$ignoreViewPartial) {
            return $this->getView()->partial(
                $element->getViewPartial(),
                                             array(
                                                 'element' => $element,
                                                 'labelWitdh' => $labelWidth,
                                                 'label_attributes' => $labelAttributes
                                                            )
            );
        }

        $escapeHtmlHelper    = $this->getEscapeHtmlHelper();
        $labelHelper         = $this->getLabelHelper();
        $elementHelper       = $this->getElementHelper();
        $elementErrorsHelper = $this->getElementErrorsHelper();

        $inputErrorClass = $this->getInputErrorClass();
        $elementErrors   = $elementErrorsHelper->render($element);

        // general Class
        $form_row_class = 'row';
        if ($this->layout == Form::LAYOUT_HORIZONTAL) {
            $form_row_class = 'form-group';
        }

        if (($elementRowClass = $element->getOption('rowClass')) != '') {
            $form_row_class .= ' '.$elementRowClass;
        }

        // Does this element have errors ?
        if (!empty($elementErrors) && !empty($inputErrorClass) && $this->layout != Form::LAYOUT_BARE) {
            $classAttributes = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : '');
            $classAttributes = $classAttributes . $inputErrorClass;

            $element->setAttribute('class', $classAttributes);
        }

        if (!$element->hasAttribute('id')) {
            $elementId = preg_replace(
                array('~[^A-Za-z0-9_-]~', '~--+~', '~^-|-$~'),
                array('-'             , '-',     ''),
                $element->getName()
            );
            $element->setAttribute('id', $elementId);
        } else {
            $elementId = $element->getAttribute('id');
        }
        /*
         * add form-control class to all form elements, but "submit" or "reset" and Buttons!
         */
        if ($element->getAttribute('type') != 'submit'
            && $element->getAttribute('type') != 'reset'
            && $element->getAttribute('type') != 'checkbox'
            && !$element instanceof Button
        ) {
            $element->setAttribute('class', $element->getAttribute('class').' form-control ');
        }

        $elementString = $elementHelper->render($element, $ignoreViewPartial);
        $desc = $element->getOption('description', false);
        if ($desc && $this->layout != Form::LAYOUT_BARE) {
            if (null !== ($translator = $this->getTranslator())) {
                $desc = $translator->translate(
                                 $desc,
                                 $this->getTranslatorTextDomain()
                             );
            }
            $elementString .= sprintf(
                '
%s
',
                $elementId,
                $desc
            );
        }

        if (!$element instanceof \Zend\Form\Element\Hidden
            && !$element instanceof \Zend\Form\Element\Button
            && $this->layout != Form::LAYOUT_BARE
        ) {
            $elementString .= sprintf(
                '
%s
',
                $elementId,
                $elementErrors
            );
        }

        // moved label here so we can change it in the ElementViewHelper
        $label           = $element->getLabel();
        if (isset($label) && '' !== $label && !$element instanceof \Zend\Form\Element\Button) {
            // Translate the label
            if (null !== ($translator = $this->getTranslator())) {
                $label = $translator->translate(
                    $label,
                    $this->getTranslatorTextDomain()
                );
            }

            $label = $escapeHtmlHelper($label);
            $labelAttributes = $element->getLabelAttributes();

            if (empty($labelAttributes)) {
                $labelAttributes = $this->labelAttributes;
            }

            // Multicheckbox elements have to be handled differently as the HTML standard does not allow nested
            // labels. The semantic way is to group them inside a fieldset
            $type = $element->getAttribute('type');
            if ($type === 'multi_checkbox' || $type === 'radio') {
                $markup = sprintf(
                    '<fieldset><legend>%s</legend>%s</fieldset>',
                    $label,
                    $elementString
                );
            } else {
                if ($this->layout == Form::LAYOUT_BARE) {
                    $markup = $elementString;
                } else {
                    if (!isset($labelAttributes['for'])) {
                        $labelAttributes['for'] = $elementId;
                    }
                    if (Form::LAYOUT_HORIZONTAL == $this->layout) {
                        if (!isset($labelAttributes['class'])) {
                            $labelAttributes['class'] = '';
                        }
                        $labelAttributes['class'] .= ' control-label';
                    }
                    $element->setLabelAttributes($labelAttributes);

                    $label = $labelHelper($element, $label);
                    if ($this->shouldWrap) {
                        $spanWidth = 12 - $labelWidth;
                        $elementString = sprintf(
                            '
%s
',
                            $spanWidth,
                            $elementErrors ? " $inputErrorClass" : '',
                            $elementString
                        );
                        $label = sprintf(
                            '
%s
',
                            $labelWidth,
                            $label
                        );
                    }
                    $markup = $label . $elementString;
                }
            }
        } else {
            if ($this->shouldWrap
                && !$element instanceof \Zend\Form\Element\Hidden
                && !$element instanceof \Zend\Form\Element\Button
                && $this->layout != Form::LAYOUT_BARE
                ) {
                $elementString = sprintf(
                    '
%s
',
                    $elementString
                );
            }
            $markup = $elementString;
        }
        if ($this->shouldWrap
            && !$element instanceof \Zend\Form\Element\Hidden
            && !$element instanceof \Zend\Form\Element\Button
            && $this->layout != Form::LAYOUT_BARE
            ) {
            $markup = sprintf('
%s
', $markup);
        }

        return $markup;
    }

    /**
     * Invoke helper as functor
     *
     * Proxies to {@link render()}.
     *
     * @param null|ElementInterface $element
     * @param null|string           $labelPosition
     * @param bool                  $renderErrors
     * @return string|FormRow
     */
    public function __invoke(ElementInterface $element = null, $labelPosition = null, $renderErrors = null, $layout = null)
    {
        if (!$element) {
            return $this;
        }

        if ($labelPosition !== null) {
            $this->setLabelPosition($labelPosition);
        } else {
            $this->setLabelPosition(self::LABEL_PREPEND);
        }

        if ($renderErrors !== null) {
            $this->setRenderErrors($renderErrors);
        }

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

        return $this->render($element);
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

namespace Core\Form;

/**
 * Base YAWIK form.
 *
 * This form adds a base fieldset and a button fieldset.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
class BaseForm extends Form
{

    /**
     * Base fieldset
     * Can be a string representing a FormElementManager key (aka element type),
     * an array holding specification according to {@add()}.
     *
     * @var string|array
     */

    /**
     * {@inheritDoc}
     * @see \Zend\Form\Element::init()
     * @uses addBaseFieldset()
     * @uses addButtonsFieldset()
     */
    public function init()
    {
        if (empty($this->baseFieldset)) {
            throw new \InvalidArgumentException('For the Form ' . get_class($this) . ' there is no Basefieldset');
        }
        $this->addBaseFieldset();
        $this->addButtonsFieldset();
    }

    /**
     * Adds the base fieldset.
     *
     *
     */
    protected function addBaseFieldset()
    {
        if (null === $this->baseFieldset) {
            return;
        }

        $fs = $this->baseFieldset;
        if (!is_array($fs)) {
            $fs = array(
                'type' => $fs,
            );
        }
        if (!isset($fs['options']['use_as_base_fieldset'])) {
            $fs['options']['use_as_base_fieldset'] = true;
        }
        $this->add($fs);
    }

    /**
     * Adds the buttons fieldset.
     */
    protected function addButtonsFieldset()
    {
        $this->add(
            array(
            'type' => 'DefaultButtonsFieldset'
            )
        );
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** FileAccessAssertion.php */
namespace Core\Acl;

use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Resource\ResourceInterface;
use Zend\Permissions\Acl\Role\RoleInterface;
use Core\Entity\FileInterface;
use Auth\Entity\UserInterface;
use Core\Entity\PermissionsInterface;

/**
* ensures that attachments can be viewed only by persons who have access to the application.
* eg. a recruiter may only see an attached file of an application, if he owns the application.
*/

class FileAccessAssertion implements AssertionInterface
{
    /* (non-PHPdoc)
     * @see \Zend\Permissions\Acl\Assertion\AssertionInterface::assert()
     */
    public function assert(
        Acl $acl,
        RoleInterface $role = null,
        ResourceInterface $resource = null,
        $privilege = null
    ) {
        if (!$role instanceof UserInterface || !$resource instanceof FileInterface) {
            return false;
        }

        $privilege = $privilege ?: PermissionsInterface::PERMISSION_VIEW;
        return $resource->getPermissions()->isGranted($role, $privilege)
            || $resource->getPermissions()->isGranted('all', $privilege)
            || UserInterface::ROLE_ADMIN == $role->getRole();
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** Permissions.php */
namespace Core\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Auth\Entity\UserInterface;
use Core\Entity\Collection\ArrayCollection;

/**
 * Manages permissions for an entity.
 *
 *
 * @method boolean isAllGranted($userOrId)    shortcut for isGranted($userOrId, self::PERMISSION_ALL)
 * @method boolean isNoneGranted($userOrId)   shortcut for isGranted($userOrId, self::PERMISSION_NONE)
 * @method boolean isChangeGranted($userOrId) shortcut for isGranted($userOrId, self::PERMISSION_CHANGE)
 * @method boolean isViewGranted($userOrId)   shortcut for isGranted($userOrId, self::PERMISSION_VIEW)
 * @method $this grantAll($resource)          shortcut for grant($resource, self::PERMISSION_ALL)
 * @method $this grantNone($resource)         shortcut for grant($resource, self::PERMISSION_NONE)
 * @method $this grantChange($resource)       shortcut for grant($resource, self::PERMISSION_CHANGE)
 * @method $this grantView($resource)         shortcut for grant($resource, self::PERMISSION_VIEW)
 * @method $this revokeAll($resource)         shortcut for grant($resource, self::PERMISSION_ALL)
 * @method $this revokeNone($resource)        shortcut for grant($resource, self::PERMISSION_NONE)
 * @method $this revokeChange($resource)      shortcut for grant($resource, self::PERMISSION_CHANGE)
 * @method $this revokeView($resource)        shortcut for grant($resource, self::PERMISSION_VIEW)
 *
 * @ODM\EmbeddedDocument
 * @ODM\HasLifeCycleCallbacks
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
class Permissions implements PermissionsInterface
{
    /**
     * The type of this Permissions
     *
     * default is the Fully qualified class name.
     *
     * @ODM\Field(type="string")
     * @var string
     * @since 0,18
     */
    protected $type;

    /**
     * Ids of users, which have view access.
     *
     * @var array
     * @ODM\Field(type="collection")
     * @ODM\Index
     */
    protected $view = array();

    /**
     * Ids of users, which have change access.
     *
     * @var array
     * @ODM\Field(type="collection")
     * @ODM\Index
     */
    protected $change = array();

    /**
     * Specification of assigned resources.
     *
     * As of 0.18, the format is:
     * <pre>
     * array(
     *  resourceId => array(
     *    permission => array(userId,...),
     *    ...
     *  ),
     *  ...
     * );
     * </pre>
     *
     * @var array
     * @ODM\Field(type="hash")
     */
    protected $assigned = array();

    /**
     * Collection of all assigned resources.
     *
     * @var Collection
     * @ODM\ReferenceMany(discriminatorField="_resource")
     */
    protected $resources;

    /**
     * Flag, wether this permissions has changed or not.
     *
     * @var bool
     */
    protected $hasChanged = false;

    /**
     * Creates a Permissions instance.
     *
     * @param string|null $type The type identifier, defaults to FQCN.
     */
    public function __construct($type = null)
    {
        $this->type = $type ?: get_class($this);
    }

    /**
     * Clones resources in a new ArrayCollection.
     * Needed because PHP does not deep cloning objects.
     * (That means, references stay references pointing to the same
     *  object than the parent.)
     */
    public function __clone()
    {
        $resources = new ArrayCollection();
        if ($this->resources) {
            foreach ($this->resources as $r) {
                $resources->add($r);
            }
        }
        $this->resources = $resources;
    }

    /**
     * Provides magic methods.
     *
     * - is[View|Change|None|All]Granted($user)
     * - grant[View|Change|None|All]($user)
     * - revoke[View|Change|None|All($user)
     *
     * @param string $method
     * @param array $params
     *
     * @return self|bool
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function __call($method, $params)
    {
        if (1 != count($params)) {
            throw new \InvalidArgumentException('Missing required parameter.');
        }

        if (preg_match('~^is(View|Change|None|All)Granted$~', $method, $match)) {
            $permission = constant('self::PERMISSION_' . strtoupper($match[1]));
            return $this->isGranted($params[0], $permission);
        }

        if (preg_match('~^grant(View|Change|None|All)$~', $method, $match)) {
            $permission = constant('self::PERMISSION_' . strtoupper($match[1]));
            return $this->grant($params[0], $permission);
        }

        if (preg_match('~^revoke(View|Change|None|All)$~', $method, $match)) {
            $permission = constant('self::PERMISSION_' . strtoupper($match[1]));
            return $this->revoke($params[0], $permission);
        }

        throw new \BadMethodCallException('Unknown method "' . $method . '"');
    }

    /**
     * Gets the permission type
     *
     * @return string
     * @since 0.24
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Grants a permission to a user or resource.
     *
     * {@inheritDoc}
     *
     * @param bool $build Should the view and change arrays be rebuild?
     */
    public function grant($resource, $permission = null, $build = true)
    {
        if (is_array($resource)) {
            foreach ($resource as $r) {
                $this->grant($r, $permission, false);
            }
            if ($build) {
                $this->build();
            }
            return $this;
        }

        //new \Doctrine\ODM\MongoDB\
        true === $permission
        || (null === $permission && $resource instanceof PermissionsResourceInterface)
        || $this->checkPermission($permission);

        $resourceId = $this->getResourceId($resource);

        if (true === $permission) {
            $permission = $this->getFrom($resource);
        }

        if (self::PERMISSION_NONE == $permission) {
            if ($resource instanceof PermissionsResourceInterface) {
                $refs = $this->getResources();
                if ($refs->contains($resource)) {
                    $refs->removeElement($resource);
                }
            }
            unset($this->assigned[$resourceId]);
        } else {
            if ($resource instanceof PermissionsResourceInterface) {
                $spec = $resource->getPermissionsUserIds($this->type);
                if (!is_array($spec) || !count($spec)) {
                    $spec = array();
                } elseif (is_numeric(key($spec))) {
                    $spec = array($permission => $spec);
                }
            } else {
                $spec = array($permission => $resource instanceof UserInterface ? array($resource->getId()) : array($resource));
            }

            $this->assigned[$resourceId] = $spec;

            if ($resource instanceof PermissionsResourceInterface) {
                try {
                    $refs = $this->getResources();
                    if (!$refs->contains($resource)) {
                        $refs->add($resource);
                    }
                } catch (\Exception $e) {
                };
            }
        }

        if ($build) {
            $this->build();
        }
        $this->hasChanged = true;
        return $this;
    }

    /**
     * Revokes a permission from a user or resource.
     *
     * {@inheritDoc}
     *
     * @param bool $build Should the view and change arrays be rebuild?
     *
     * @return $this|PermissionsInterface
     */
    public function revoke($resource, $permission = null, $build = true)
    {
        if (self::PERMISSION_NONE == $permission || !$this->isAssigned($resource)) {
            return $this;
        }

        if (self::PERMISSION_CHANGE == $permission) {
            return $this->grant($resource, self::PERMISSION_VIEW, $build);
        }

        return $this->grant($resource, self::PERMISSION_NONE, $build);
    }

    public function clear()
    {
        $this->view      = array();
        $this->change    = array();
        $this->assigned  = array();
        $this->resources = null;

        return $this;
    }

    public function inherit(PermissionsInterface $permissions, $build = true)
    {
        // Override permissions type temporarly to get the right permissions back
        // from resources which may be aware of the permissions type.
        // Maybe this must be controllable by an additional parameter, but for now
        // we make this default.
        $oldType = $this->type;
        $this->type = $permissions->getType();

        /* @var $permissions Permissions */
        $assigned  = $permissions->getAssigned();
        $resources = $permissions->getResources();

        /*
         * Grant resource references permissions.
         */
        foreach ($resources as $resource) {
            /* @var $resource PermissionsResourceInterface */
            $permission = $permissions->getFrom($resource);

            $this->grant($resource, $permission, false);
            unset($assigned[$resource->getPermissionsResourceId()]);
        }
        /*
         * Merge remaining user permissions (w/o resource references)
         */
        $this->assigned = array_merge($this->assigned, $assigned);
        if ($build) {
            $this->build();
        }
        $this->hasChanged= true;

        // restore orginial permissions type
        $this->type = $oldType;

        return $this;
    }

    /**
     * Builds the user id lists.
     *
     * This will make database queries faster and also the calls to {@link isGranted()} will be faster.
     *
     * @return self
     */
    public function build()
    {
        $view = $change = array();
        foreach ($this->assigned as $resourceId => $spec) {
            /* This is needed to convert old permissions to the new spec format
             * introduced in 0.18
             * TODO: Remove this line some versions later.
             */
            // @codeCoverageIgnoreStart
            if (isset($spec['permission'])) {
                $spec = array($spec['permission'] => $spec['users']);
                $this->assigned[$resourceId] = $spec;
            }
            // @codeCoverageIgnoreEnd

            foreach ($spec as $perm => $userIds) {
                if (self::PERMISSION_ALL == $perm || self::PERMISSION_CHANGE == $perm) {
                    $change = array_merge($change, $userIds);
                }
                $view = array_merge($view, $userIds);
            }
        }

        $this->change = array_unique($change);
        $this->view   = array_unique($view);
        return $this;
    }

    private function checkIsGranted($userId, $permission)
    {
        if (!$userId) {
            return false;
        }

        if (self::PERMISSION_NONE == $permission) {
            return !in_array($userId, $this->view);
        }

        if (self::PERMISSION_ALL == $permission || self::PERMISSION_CHANGE == $permission) {
            return in_array($userId, $this->change);
        }

        // Now there's only PERMISSION_VIEW left to check.
        return in_array($userId, $this->view);
    }

    public function isGranted($userOrId, $permission)
    {
        if ($userOrId instanceof UserInterface) {
            $id = $userOrId->getId();
            $role = $userOrId->getRole();
        } else {
            $id = (string) $userOrId;
            $role = null;
        }

        $this->checkPermission($permission);

        return $this->checkIsGranted($id, $permission)
               || ($this->isAssigned($role) && $this->checkIsGranted($role, $permission))
               || ($this->isAssigned('all') && $this->checkIsGranted('all', $permission));
    }

    public function isAssigned($resource)
    {
        $resourceId = $this->getResourceId($resource);
        return isset($this->assigned[$resourceId]);
    }

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

    /**
     * Gets the assigned specification.
     *
     * This is only needed when inheriting this permissions object into another.
     *
     * @return array
     */
    public function getAssigned()
    {
        return $this->assigned;
    }

    /**
     * Gets the resource collection.
     *
     * This is only needed when inheriting.
     *
     * @internal
     *      The PrePersist hook is needed, because eventually
     *      this method is called during the onFlush event by
     *      an UpdateFilePermission-Listener. Generating
     *      an ArrayCollection in this state leads to a
     *      fatal error deep in Doctrine.
     *
     *      This PrePersist hook assures, that there is
     *      a prefilled ArrayCollection during the
     *      changeset computation.
     *
     * @ODM\PrePersist
     *
     * @return Collection
     */
    public function getResources()
    {
        if (!$this->resources) {
            $this->resources = new ArrayCollection();
        }
        return $this->resources;
    }

    public function getFrom($resource)
    {
        $resourceId = $this->getResourceId($resource);

        if (!isset($this->assigned[$resourceId])) {
            return self::PERMISSION_NONE;
        }

        $spec = $this->assigned[$resourceId];

        return 1 == count($spec) ? key($spec) : null;
    }

    /**
     * Gets/Generates the resource id.
     *
     * @param string|UserInterface|PermissionsResourceInterface $resource
     *
     * @return string
     */
    protected function getResourceId($resource)
    {
        if ($resource instanceof PermissionsResourceInterface) {
            return $resource->getPermissionsResourceId();
        }

        if ($resource instanceof UserInterface) {
            return 'user:' . $resource->getId();
        }

        return 'user:' . $resource;
    }

    /**
     * Checks a valid permission.
     *
     * @param string $permission
     *
     * @throws \InvalidArgumentException
     */
    protected function checkPermission($permission)
    {
        $perms = array(
            self::PERMISSION_ALL,
            self::PERMISSION_CHANGE,
            self::PERMISSION_NONE,
            self::PERMISSION_VIEW,
        );
        if (!in_array($permission, $perms)) {
            throw new \InvalidArgumentException(
                'Invalid permission. Must be one of ' . implode(', ', $perms)
            );
        }
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Repository;

use Core\Entity\EntityInterface;

/**
 * Enables an implementing repository to handle draftable entities.
 *
 * @method EntityInterface create(array $data = [], bool $persist = false)
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.26
 */
trait DraftableEntityAwareTrait
{
    public function findBy(array $criteria, array $sort = null, $limit = null, $skip = null)
    {
        if (!array_key_exists('isDraft', $criteria)) {
            $criteria['isDraft'] = false;
        } elseif (null === $criteria['isDraft']) {
            unset($criteria['isDraft']);
        }

        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        return parent::findBy($criteria, $sort, $limit, $skip);
    }

    /**
     * Find entities in draft mode.
     *
     * Sets the key 'isDraft' with the value true and proxies to parent::findBy()
     *
     * @param array $criteria
     * @param array|null $sort
     * @param null|int  $limit
     * @param null|int  $skip
     *
     * @return \MongoCursor
     */
    public function findDraftsBy(array $criteria, array $sort = null, $limit = null, $skip = null)
    {
        $criteria['isDraft'] = true;

        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        return parent::findBy($criteria, $sort, $limit, $skip);
    }

    public function findOneBy(array $criteria)
    {
        if (!array_key_exists('isDraft', $criteria)) {
            $criteria['isDraft'] = false;
        } elseif (null === $criteria['isDraft']) {
            unset($criteria['isDraft']);
        }
        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        return parent::findOneBy($criteria);
    }

    /**
     * Finds one entity in draft mode.
     *
     * Sets the key 'isDraft' to true and proxies to parent::findOneBy()
     *
     * @param array $criteria
     *
     * @return \MongoCursor
     */
    public function findOneDraftBy(array $criteria)
    {
        $criteria['isDraft'] = true;

        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        return parent::findOneBy($criteria);
    }

    /**
     * Creates a query builder.
     *
     * Prepopulate the constraint for 'isDraft' according to $findDrafts:
     * - true: queryBuilder will return only drafts
     * - false: queryBuilder will return only non drafts.
     * - null: queryBuilder will return all entities matching the additional constraints.
     *
     * @param bool|null $findDrafts
     *
     * @return \Doctrine\MongoDB\Query\Builder
     */
    public function createQueryBuilder($findDrafts = false)
    {
        /* @var \Doctrine\MongoDB\Query\Builder $qb */
        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        $qb = parent::createQueryBuilder();

        if (null !== $findDrafts) {
            $qb->field('isDraft')->equals($findDrafts);
        }
        return $qb;
    }

    /**
     * Creates an entity in draft mode.
     *
     * @param array $data
     * @param bool  $persist
     *
     * @return EntityInterface
     */
    public function createDraft(array $data = null, $persist = false)
    {
        $data['isDraft'] = true;

        return $this->create($data, $persist);
    }
}


<?php
/**
 * @author Philip Bergman <philip@zicht.nl>
 * @copyright Zicht online <http://zicht.nl>
 */
namespace Zicht\Bundle\PageBundle\Security;

use Zicht\Bundle\PageBundle\Model\PageInterface;
use Zicht\Bundle\PageBundle\Model\ViewValidationInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

/**
 * Class PageViewValidation
 *
 * @package Zicht\Bundle\PageBundle\Security
 */
class PageViewValidation implements ViewValidationInterface
{
    /** @var null|AuthorizationCheckerInterface */
    protected $auth;

    /**
     * PageViewValidation constructor.
     *
     * @param AuthorizationCheckerInterface|null $auth
     */
    public function __construct(AuthorizationCheckerInterface $auth = null)
    {
        $this->auth = $auth;
    }

    /**
     * Similar to PageController it will return true when auth === null
     *
     * @param PageInterface $page
     * @return bool
     */
    protected function isAllowed(PageInterface $page)
    {
        if (null !== $this->auth) {
            return $this->auth->isGranted('VIEW', $page);
        }
        return true;
    }

    /**
     * @{inheritDoc}
     */
    public function validate(PageInterface $page)
    {
        if (false === $this->isAllowed($page)) {
            throw new AccessDeniedException("Page {$page->getId()} is not accessible to the current user");
        }
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Options\Exception;

/**
 * Exception is thrown, if a required option value is missing.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.20
 */
class MissingOptionException extends \RuntimeException implements ExceptionInterface
{
    /**
     * Target option class or FQCN.
     *
     * @var string|object
     */
    protected $target;

    /**
     * The option key.
     *
     * @var string
     */
    protected $optionKey;

    /**
     * Creates an instance.
     *
     * @param string     $optionKey
     * @param int        $target
     * @param \Exception $previous
     */
    public function __construct($optionKey, $target, \Exception $previous = null)
    {
        $this->optionKey = $optionKey;
        $this->target    = $target;

        $message = sprintf(
            'Missing value for option "%s" in "%s"',
            $optionKey,
            $this->getTargetFQCN()
        );

        parent::__construct($message, 0, $previous);
    }

    /**
     * Gets the option key.
     *
     * @return string
     */
    public function getOptionKey()
    {
        return $this->optionKey;
    }

    /**
     * Gets the target.
     *
     * @return object|string
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * Gets the target FQCN
     *
     * This will return the FQCN even if the target was provided as an concrete instance.
     *
     * @return string
     */
    public function getTargetFQCN()
    {
        return is_object($this->target) ? get_class($this->target) : (string) $this->target;
    }
}


<?php
/**
*
 */

namespace Core\Form\View\Helper;

use Zend\Form\View\Helper\FormCheckbox;
use Zend\Form\ElementInterface;
use Zend\Form\Exception;

class ToggleButton extends FormCheckbox
{
    /**
     * Render a form <input> element from the provided $element
     *
     * @param  ElementInterface $element
     * @param String $buttonContent
     * @throws Exception\DomainException
     * @return string
     */
    public function render(ElementInterface $element, $buttonContent = null)
    {
        if (null === $buttonContent) {
            $buttonContent = $element->getLabel();
            if (null === $buttonContent) {
                throw new Exception\DomainException(
                    sprintf(
                        '%s expects either button content as the second argument, ' .
                        'or that the element provided has a label value; neither found',
                        __METHOD__
                    )
                );
            }

            if (null !== ($translator = $this->getTranslator())) {
                $buttonContent = $translator->translate(
                    $buttonContent,
                    $this->getTranslatorTextDomain()
                );
            }
            $element->setLabel('');
        }

        $value          = $element->getValue();
        $checkedBoole   = ($value == 1 || $value == 'on');

        $checkedClass   = $checkedBoole?'active"':'';

        $hiddenElement = '';
        if ($element->useHiddenElement()) {
            $hiddenAttributes = [
                'name'     => $element->getName(),
                'value'    => $element->getUncheckedValue(),
            ];

            $hiddenElement = sprintf(
                        '<input type="hidden" %s%s',
                        $this->createAttributesString($hiddenAttributes),
                        '>'
                    );
            $element->setUseHiddenElement(false);
        }

        $buttonContent = $hiddenElement . PHP_EOL
                . '
' . PHP_EOL
                . '' . PHP_EOL
                . parent::render($element) . $buttonContent . PHP_EOL . '' . PHP_EOL
                . '
' . PHP_EOL;

        return $buttonContent;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** FileUploadStrategy.php */
namespace Core\Entity\Hydrator\Strategy;

use Zend\Hydrator\Strategy\StrategyInterface;
use Core\Entity\FileInterface;
use Core\Entity\FileEntity;

class FileUploadStrategy implements StrategyInterface
{
    /**
     * @var FileEntity
     */
    protected $fileEntity;

    /**
     * @param FileInterface $file
     */
    public function __construct(FileInterface $file)
    {
        $this->setFileEntity($file);
    }

    /**
     * @param FileInterface $file
     *
     * @return $this
     */
    public function setFileEntity(FileInterface $file)
    {
        $this->fileEntity = $file;
        return $this;
    }

    /**
     * @return FileEntity
     */
    public function getFileEntity()
    {
        if (!$this->fileEntity) {
            $file = new FileEntity();
            $this->setFileEntity($file);
        }
        return clone $this->fileEntity;
    }

    /**
     * @param mixed $value
     *
     * @return mixed|null
     */
    public function extract($value)
    {
        if (!$value instanceof FileInterface) {
            return null;
        }

        return $value->getId();
    }

    /**
     * @param mixed $value
     *
     * @return FileEntity|null
     */
    public function hydrate($value)
    {
        if (!UPLOAD_ERR_OK == $value['error']) {
            return null;
        }

        $file = $this->getFileEntity();

        $file->setName($value['name'])
             ->setType($value['type'])
             ->setFile($value['tmp_name']);

        return $file;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
 * @license   MIT
 */

/** AbstractRatingEntity.php */
namespace Core\Entity;

/**
 * Base rating entity.
 *
 * Implements common and helper methods for Rating Entities.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 */
abstract class AbstractRatingEntity extends AbstractEntity implements RatingInterface
{
    /**
     * Average rating.
     * This is not mapped by doctrine!
     * @var int;
     */
    protected $_average;

    /**
     * {@inheritDoc}
     * @see \Core\Entity\RatingInterface::getAverage()
     */
    public function getAverage($includeNoneRating = false, $recalculate = false)
    {
        if ($this->_average && !$recalculate) {
            return $this->_average;
        }

        $sum   = 0;
        $count = 0;
        foreach (get_class_methods($this) as $method) {
            if ('getAverage' == $method || 0 !== strpos($method, 'get')) {
                continue;
            }

            $rating = $this->$method();
            if (!$this->checkRatingValue($rating, /*throwException*/ false)
                || (!$includeNoneRating && $rating == static::RATING_NONE)) {
                continue;
            }

            $sum   += $rating;
            $count += 1;
        }

        $average = 0 == $count ? 0 : round($sum / $count);
        $this->_average = $average;

        return $average;
    }

    /**
     * Checks if rating is a valid value.
     *
     * @param int $rating
     * @param bool $throwException
     * @throws \InvalidArgumentException
     * @return bool
     */
    protected function checkRatingValue($rating, $throwException = true)
    {
        if (!is_int($rating) || static::RATING_EXCELLENT < $rating || static::RATING_NONE > $rating) {
            if ($throwException) {
                throw new \InvalidArgumentException(sprintf('%s is not a valid rating value.', $rating));
            }
            return false;
        }
        return true;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2017 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\View\Helper\Proxy;

use Zend\View\Helper\AbstractHelper;

/**
 * Proxies to a view helper which may or may not exists.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.29
 */
class HelperProxy
{

    /**#@+
     * Special expected result types.
     * @var string
     */
    const EXPECT_SELF = '__SELF__';
    const EXPECT_ARRAY = '__ARRAY__';
    const EXPECT_ITERATOR = '__ITERATOR__';
    /**#@-*/

    /**
     * The helper instance.
     *
     * @var false|AbstractHelper
     */
    private $helper;

    /**
     * @param false|AbstractHelper $helper
     */
    public function __construct($helper)
    {
        $this->helper = $helper;
    }

    /**
     * Proxy invokation to the helper.
     *
     * Note:
     *      This will always return NULL.
     *
     * @return mixed
     */
    public function __invoke()
    {
        $args = func_get_args();

        return $this->call('__invoke', $args, null);
    }

    /**
     * Proxy all methods calls to the helper.
     *
     * Note:
     *      The expected value is NULL.
     *
     *      You may specify an alternate expect value when prefixing
     *      the method name with "call" and provide the expected value
     *      as the last argument. (This is popped of the args array before
     *      proxy to call(),)
     *
     *      e.g. callOriginalMethod(arg1, arg2, expectedValue)
     *
     * @param string $method
     * @param array  $args
     *
     * @return null|mixed
     */
    public function __call($method, $args)
    {
        if (0 === strpos($method, 'call')) {
            $method = substr($method, 4);
            $expect = array_pop($args);
        } else {
            $expect = null;
        }

        return $this->call($method, $args, $expect);
    }

    /**
     * Get the helper instance.
     *
     * @return false|AbstractHelper
     */
    public function helper()
    {
        return $this->helper;
    }

    /**
     * Call a method on the proxied plugin.
     *
     * If a helper instance exists, the returned value from the method call is returned,
     * otherwise the $expect value is returned,
     *
     * If the $args is no array, it is used as $expect.
     *
     * If you need to expect an array, you must pass in an empty array for $args and
     * specify your expected array in the $expect variable, however if you expect an
     * empty array, you can pass {@link EXPECT_ARRAY}.
     *
     * @param string $method
     * @param array|mixed  $args
     * @param mixed $expect
     *
     * @return mixed
     */
    public function call($method, $args = [], $expect = self::EXPECT_SELF)
    {
        if (!is_array($args)) {
            $expect = $args;
            $args   = [];
        }

        if (!$this->helper) {
            return $this->expected($expect);
        }

        return call_user_func_array([$this->helper, $method], $args);
    }

    /**
     * Call consecutive methods on this proxied helper.
     *
     * This means, every specified method is called on this
     * proxy instance (which proxies further to the helper)
     * and this proxy is returned.
     *
     * If no helper instance exists, this is a noop and this proxy instance is returned.
     *
     *

     * $methods = [
     *      'methodName',                   // <=> 'methodName' => []
     *      'methodName' => [arguments,...],
     *      ['invokeArg',...]               // omitting method name, but provide arguments will call "__invoke"
     * ];
     *

     *
     * @param array $methods
     *
     * @return self
     */
    public function consecutive($methods)
    {
        return $this->doStackedCalls($methods, self::EXPECT_SELF, 'consecutive');
    }

    /**
     * Chain method calls on this proxied helper.
     *
     * This means, every method in the stack is called on
     * the return value ofthe previous method and the returned value
     * from the last method is returned.
     *
     * If no helper instance exists, the $expect value is returned.
     *
     *

     * $stack = [
     *      'methodName',                   // <=> 'methodName' => []
     *      'methodName' => [arguments,...]
     *      ['invokeArg',...]               // omitting method name but provide arguments, will call "__invoke"
     * ];
     *

     *
     * @param array $methods
     * @param mixed $expect
     *
     * @return mixed
     */
    public function chain($methods, $expect = self::EXPECT_SELF)
    {
        return $this->doStackedCalls($methods, $expect, 'chain');
    }

    private function doStackedCalls($methods, $expect, $mode)
    {
        if (!$this->helper) {
            return $this->expected($expect);
        }

        $plugin = $result = $this->helper;
        foreach ($methods as $method => $args) {
            if (is_numeric($method)) {
                if (is_array($args)) {
                    $method = '__invoke';
                } else {
                    $method = $args;
                    $args   = [];
                }
            }

            if (!is_array($args)) {
                $args = [$args];
            }

            $result = call_user_func_array([$plugin, $method], $args);

            if ('chain' == $mode) {
                $plugin = $result;
            }
        }

        return 'consecutive' == $mode ? $this : $result;
    }

    private function expected($expect)
    {
        if (self::EXPECT_SELF == $expect) {
            return $this;
        }

        if (self::EXPECT_ARRAY == $expect) {
            return [];
        }

        if (self::EXPECT_ITERATOR == $expect) {
            return new \ArrayIterator();
        }

        return $expect;
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @license MIT
 * @copyright  2013 - 2016 Cross Solution <http://cross-solution.de>
 */

/** */
namespace Core\Form\Service;

use Core\Form\HeadscriptProviderInterface;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Initializer\InitializerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * This initializer inject the scripts provided by form elements
 * which implements HeadscriptProviderInterface in the Headscript view helper.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.19
 */
class InjectHeadscriptInitializer implements InitializerInterface
{
    public function __invoke(ContainerInterface $container, $instance)
    {
        /* @var $serviceLocator \Zend\Form\FormElementManager\FormElementManagerV3Polyfill */

        if (!$instance instanceof HeadscriptProviderInterface) {
            return;
        }

        $scripts = $instance->getHeadscripts();

        if (!is_array($scripts) || empty($scripts)) {
            return;
        }

        /* @var $basepath \Zend\View\Helper\BasePath
         * @var $headscript \Zend\View\Helper\HeadScript */
        $helpers  = $container->get('ViewHelperManager');
        $basepath = $helpers->get('basepath');
        $headscript = $helpers->get('headscript');

        foreach ($scripts as $script) {
            $headscript->appendFile($basepath($script));
        }
    }

    /**
     * Injects scripts to the headscript view helper.
     *
     * If the created instance implements {@link HeadscriptProviderInterface},
     * the provided scripts will be injected in the Headscript view helper, prepended
     * with the base path.
     *
     * @param \Zend\Form\ElementInterface | HeadscriptProviderInterface $instance
     * @param ServiceLocatorInterface $serviceLocator
     *
     * @return void
     */
    public function initialize($instance, ServiceLocatorInterface $serviceLocator)
    {
        /* @var $serviceLocator \Zend\Form\FormElementManager\FormElementManagerV3Polyfill */

        if (!$instance instanceof HeadscriptProviderInterface) {
            return;
        }

        $scripts = $instance->getHeadscripts();

        if (!is_array($scripts) || empty($scripts)) {
            return;
        }

        /* @var $basepath \Zend\View\Helper\BasePath
         * @var $headscript \Zend\View\Helper\HeadScript */
        $services = $serviceLocator;
        $helpers  = $services->get('ViewHelperManager');
        $basepath = $helpers->get('basepath');
        $headscript = $helpers->get('headscript');

        foreach ($scripts as $script) {
            $headscript->appendFile($basepath($script));
        }
    }
}