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

namespace Organizations\Repository;

use Auth\Entity\UserInterface;
use Core\Repository\AbstractRepository;
use Organizations\Entity\EmployeeInterface;
use Organizations\Entity\OrganizationInterface;

/**
 * This is the repository for Organizations entities.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author Miroslav Fedeleš <miroslav.fedeles@gmail.com>
 * @todo   write test
 */
class Organization extends AbstractRepository
{
    /**
     * Gets a cursor to a set of organizations
     *
     * @param array $params
     */
    public function getPaginatorCursor($params)
    {
        return $this->getPaginationQueryBuilder($params)
                    ->getQuery()
                    ->execute();
    }

    /**
     * Gets a query builder to search for organizations
     *
     * @param array $params
     * @return mixed
     */
    protected function getPaginationQueryBuilder($params)
    {
        $filter = $this->getService('filterManager')->get('Organizations/PaginationQuery');
        $qb = $filter->filter($params, $this->createQueryBuilder());

        return $qb;
    }

    /**
     * Gets a cursor for all hiring organizations.
     *
     * @param OrganizationInterface $organization parent organization
     *
     * @return \Doctrine\ODM\MongoDB\Cursor
     * @usedBy \Organizations\Entity\Organization::getHiringOrganizations()
     * @since 0.18
     */
    public function getHiringOrganizationsCursor(OrganizationInterface $organization)
    {
        $qb = $this->createQueryBuilder();
        $qb->field('parent')->equals($organization->getId());
        $qb->field('isDraft')->equals(false);
        $q  = $qb->getQuery();
        $cursor = $q->execute();

        return $cursor;
    }

    /**
     * Find a organizations by an name
     *
     * @param String $name
     * @param boolean $create
     * @return array
     */
    public function findByName($name, $create = true)
    {
        $query = $this->dm->createQueryBuilder('Organizations\Entity\OrganizationName')->hydrate(false)->field('name')->equals($name)->select("_id");
        $result = $query->getQuery()->execute()->toArray(false);
        if (empty($result) && $create) {
            $repositoryNames = $this->dm->getRepository('Organizations\Entity\OrganizationName');
            $entityName = $repositoryNames->create();
            $entityName->setName($name);
            $entity = $this->create();
            $entity->setOrganizationName($entityName);
            $this->store($entity);
            $organizations = array($entity);
        } else {
            $idName = $result[0]['_id'];
            $organizations = $this->findBy(array('organizationName' => $idName));
        }
        return $organizations;
    }

    public function findbyRef($ref, $create = true)
    {
        $entity = $this->findOneBy(array('externalId' => $ref));
        if (empty($entity)) {
            $entity = $this->create();
            $entity->setExternalId($ref);
        }
        return $entity;
    }

    /**
     * Finds the main organization of an user.
     *
     * @param string|UserInterface $userOrId
     *
     * @return null|OrganizationInterface
     */
    public function findByUser($userOrId)
    {
        $userId = $userOrId instanceof \Auth\Entity\UserInterface ? $userOrId->getId() : $userOrId;
        $qb     = $this->createQueryBuilder();

        /*
         * HiringOrganizations also could be associated to the user, but we
         * do not want them to be queried here, so the query needs to check the
         * "parent" field, too.
         */
//        $qb->addAnd(
//           $qb->expr()->field('user')->equals($userId)
//                      ->addOr(
//                            $qb->expr()->addOr($qb->expr()->field('parent')->exists(false))
//                                       ->addOr($qb->expr()->field('parent')->equals(null))
//           )
//        );
        $qb->addAnd($qb->expr()->field('user')->equals($userId))
           ->addAnd(
               $qb->expr()->addOr($qb->expr()->field('parent')->exists(false))
                               ->addOr($qb->expr()->field('parent')->equals(null))
           );

        $q      = $qb->getQuery();
        $entity = $q->getSingleResult();

        return $entity;
    }

    /**
     * Finds the organization, an user is employed by.
     *
     * @param string|UserInterface $userOrId
     *
     * @return null|OrganizationInterface
     */
    public function findByEmployee($userOrId)
    {
        $userId = $userOrId instanceof \Auth\Entity\UserInterface ? $userOrId->getId() : $userOrId;

        /*
         * Employees collection is only set on main organization,
         * so here, we do not have to query the "parent" field.
         *
         * Only search for "assigned" employees.
         */
        $entity = $this->findOneBy(
            array(
            'employees.user' => new \MongoId($userId),
            'employees.status' => EmployeeInterface::STATUS_ASSIGNED
            )
        );

        return $entity;
    }

    public function findPendingOrganizationsByEmployee($userOrId)
    {
        $userId = $userOrId instanceof \Auth\Entity\UserInterface ? $userOrId->getId() : $userOrId;

        $collection = $this->findBy(
            array(
            'employees.user' => new \MongoId($userId),
            'employees.status' => EmployeeInterface::STATUS_PENDING
            )
        );

        return $collection;
    }

    public function getEmployersCursor(UserInterface $user)
    {
        $qb = $this->createQueryBuilder();
        $qb->field('refs.employees')->equals($user->getId());

        $q  = $qb->getQuery();
        $c  = $q->execute();

        return $c;
    }

    /**
     * @param array|null    $data
     * @param bool          $persist
     * @return \Organizations\Entity\Organization
     */
    public function create(array $data = null, $persist=false)
    {
        $entity = parent::create($data);
        $entity->isDraft(true);
        return $entity;
    }

    /**
     * creates a new Organization, no matter if a organization with this name already exists,
     * also creates a new Name, but link this Name to another OrganizationName-Link, if this Name already exists
     * @param $name
     */
    public function createWithName($name)
    {
        $entity = parent::create();
        $repositoryNames = $this->dm->getRepository('Organizations\Entity\OrganizationName');
        $entityName = $repositoryNames->create();
        $entityName->setName($name);
        $entity->setOrganizationName($entityName);
        return $entity;
    }

    /**
     * Look for an drafted Document of a given user
     *
     * @param $user
     * @return \Organizations\Entity\Organization|null
     */
    public function findDraft($user)
    {
        if ($user instanceof UserInterface) {
            $user = $user->getId();
        }

        $document = $this->findOneBy(
            array(
            'isDraft' => true,
            'user' => $user
            )
        );

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

        return null;
    }

    /**
     * Get organizations for given user ID
     *
     * @param string $userId
     * @param int $limit
     * @return Cursor
     * @since 0.27
     */
    public function getUserOrganizations($userId, $limit = null)
    {
        $qb = $this->createQueryBuilder(null)
            ->field('user')->equals($userId)
            ->sort(['DateCreated.date' => -1]);

        if (isset($limit)) {
            $qb->limit($limit);
        }

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


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

namespace Organizations\Form;

use Zend\Form\Fieldset;
use Core\Entity\Hydrator\EntityHydrator;
use Organizations\Entity\OrganizationContact;

/**
 * Class OrganizationsContactFieldset
 *
 * @package Organizations\Form
 */
class OrganizationsContactFieldset extends Fieldset
{
    /**
     * Gets the Hydrator
     *
     * @return \Zend\Hydrator\HydratorInterface
     */
    public function getHydrator()
    {
        if (!$this->hydrator) {
            $hydrator           = new EntityHydrator();
            $this->setHydrator($hydrator);
        }
        return $this->hydrator;
    }

    /**
     * Set the elements for the contact form
     */
    public function init()
    {
        $this->setName('contact');

        $this->add(
            array(
                'name' => 'street',
                'options' => array(
                        'label' => /* @translate */ 'Street'
                )
            )
        );

        $this->add(
            array(
                'name' => 'houseNumber',
                'options' => array(
                        'label' => /* @translate */ 'House Number'
                )
            )
        );

        $this->add(
            array(
                'name' => 'postalcode',
                'options' => array(
                        'label' => /* @translate */ 'Postal Code'
                )
            )
        );

        $this->add(
            array(
                'name' => 'city',
                'options' => array(
                        'label' => /* @translate */ 'City'
                )
            )
        );
        $this->add(
            [
                'name' => 'country',
                'options' => [
                    'label' => /* @translate */ 'Country'
                ]
            ]
        );
        $this->add(
            array(
                'name' => 'phone',
                'options' => array(
                    'label' => /* @translate */ 'Phone'
                )
            )
        );
        $this->add(
            array(
                'name' => 'fax',
                'options' => array(
                    'label' => /* @translate */ 'Fax'
                )
            )
        );
    }

    /**
     * for later use - all the mandatory fields
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return array();
    }

    /**
     * a required method to overwrite the generic method to make the binding of the entity work
     * @param object $object
     * @return bool
     */
    public function allowObjectBinding($object)
    {
        return $object instanceof OrganizationContact;
    }
}


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

namespace Organizations\Entity;

use Auth\Entity\UserInterface;
use Core\Entity\AbstractIdentifiableModificationDateAwareEntity as BaseEntity;
use Core\Entity\Collection\ArrayCollection;
use Core\Entity\DraftableEntityInterface;
use Core\Entity\EntityInterface;
use Core\Entity\Hydrator\EntityHydrator;
use Core\Entity\ImageSet;
use Core\Entity\Permissions;
use Core\Entity\PermissionsInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Zend\Hydrator\HydratorInterface;
use Zend\Permissions\Acl\Resource\ResourceInterface;

/**
 * The organization.
 *
 * @ODM\Document(collection="organizations", repositoryClass="Organizations\Repository\Organization")
 * @ODM\HasLifecycleCallbacks
 * @ODM\Indexes({
 *      @ODM\Index(keys={
 *          "_organizationName"="text"
 *      }, name="fulltext")
 * })
 *
 * @todo   write test
 * @author Mathias Weitz <weitz@cross-solution.de>
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author Anthonius Munthi <me@itstoni.com>
 */
class Organization extends BaseEntity implements
    OrganizationInterface,
    DraftableEntityInterface,
    ResourceInterface
{

    /**
     * Always enabled even if there are no active jobs
     */
    const PROFILE_ALWAYS_ENABLE     = 'always';

    /**
     * Hide if there are no jobs available
     */
    const PROFILE_ACTIVE_JOBS       = 'active-jobs';

    /**
     * Always disabled profile
     */
    const PROFILE_DISABLED          = 'disabled';

    /**
     * Event name of post construct event.
     *
     * @var string
     */
    const postConstruct = 'postRepositoryConstruct';

    /**
     * externalId. Allows external applications to reference their primary key.
     *
     * @var string
     * @ODM\Field(type="string")
     * @ODM\Index
     */
    protected $externalId;

    /**
     * The actual name of the organization.
     *
     * @var \Organizations\Entity\OrganizationName
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\OrganizationName", storeAs="id", cascade="persist")
     */
    protected $organizationName;

    /**
     * Only for sorting/searching purposes
     *
     * @var string
     * @ODM\Field(type="string")
     */
    protected $_organizationName;

    /**
     * Assigned permissions.
     *
     * @var PermissionsInterface
     * @ODM\EmbedOne(targetDocument="\Core\Entity\Permissions")
     */
    protected $permissions;

    /**
     * primary logo of an organization
     *
     * @var \Organizations\Entity\OrganizationImage
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\OrganizationImage", inversedBy="organization", storeAs="id", nullable="true", cascade={"all"})
     */
    protected $image;

    /**
     *
     *
     * @ODM\EmbedOne(targetDocument="\Core\Entity\ImageSet")
     * @var ImageSet
     */
    protected $images;

    /**
     * Flag indicating draft state of this job.
     *
     * @var bool
     * @ODM\Field(type="boolean")
     */
    protected $isDraft = false;

    /**
     * Organization contact data.
     *
     * @ODM\EmbedOne(targetDocument="\Organizations\Entity\OrganizationContact")
     */
    protected $contact;

    /**
     * The organizations' description.
     *
     * @var string
     * @ODM\Field(type="string")
     */
    protected $description;

    /**
     * The parent of this organization.
     *
     * @see   setParent()
     * @var OrganizationInterface | null
     * @ODM\ReferenceOne(targetDocument="\Organizations\Entity\Organization", storeAs="id", nullable=true)
     * @since 0.18
     */
    protected $parent;

    /**
     * The hiring organizations of this organization.
     *
     * @var Collection
     * @ODM\ReferenceMany(
     *      targetDocument="Organizations\Entity\Organization",
     *      repositoryMethod="getHiringOrganizationsCursor"
     * )
     * @since 0.18
     */
    protected $hiringOrganizations;

    /**
     * The associated employees (users)
     *
     * @ODM\EmbedMany(targetDocument="\Organizations\Entity\Employee")
     * @var Collection
     */
    protected $employees;

    /**
     * Jobs of this organization.
     *
     * @var Collection
     * @ODM\ReferenceMany(targetDocument="\Jobs\Entity\Job", storeAs="id", mappedBy="organization")
     * @since 0.18
     */
    protected $jobs;

    /**
     * the owner of a Organization
     *
     * @var UserInterface $user
     * @ODM\ReferenceOne(targetDocument="\Auth\Entity\User", storeAs="id")
     * @ODM\Index
     */
    protected $user;

    /**
     * Default values of an organizations job template
     *
     * @var TemplateInterface;
     * @ODM\EmbedOne(targetDocument="\Organizations\Entity\Template")
     */
    protected $template;

    /**
     * Default values Workflow
     *
     * @var WorkflowSettingsInterface $workflowSettings ;
     * @ODM\EmbedOne(targetDocument="\Organizations\Entity\WorkflowSettings")
     */
    protected $workflowSettings;

    /**
     * Profile Setting
     * @var string
     * @ODM\Field(type="string", nullable=true)
     */
    protected $profileSetting;

    /**
     * @return string
     */
    public function getProfileSetting()
    {
        return $this->profileSetting;
    }

    /**
     * @param string $profileSetting
     *
     * @return $this
     */
    public function setProfileSetting($profileSetting)
    {
        $this->profileSetting = $profileSetting;

        return $this;
    }

    /**
     * Returns the string identifier of the Resource
     *
     * @return string
     */
    public function getResourceId()
    {
        return 'Entity/Organization';
    }

    /**
     * Gets the organization name
     *
     * @return string
     */
    public function getName()
    {
        if (empty($this->organizationName)) {
            return '';
        }

        return $this->organizationName->getName();
    }

    /**
     * Sets the parent of an organization
     *
     * @param OrganizationInterface $parent
     *
     * @return $this
     */
    public function setParent(OrganizationInterface $parent)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * @deprecated
     * @return array
     */
    public function getSearchableProperties()
    {
        return array();
    }

    /**
     * Gets the parent of an organization
     *
     * @param bool $returnSelf returns itself, if this organization does not have a parent?
     *
     * @return null|OrganizationInterface
     */
    public function getParent($returnSelf = false)
    {
        return $this->parent ? : ($returnSelf ? $this : null);
    }

    /**
     * Gets the Draft flag
     *
     * @return bool
     */
    public function isDraft()
    {
        return $this->isDraft;
    }

    /**
     * Gets linked organizations
     *
     * @return Collection
     */
    public function getHiringOrganizations()
    {
        return $this->hiringOrganizations;
    }

    /**
     * Sets the draft flag
     *
     * @param bool $flag
     *
     * @return $this
     */
    public function setIsDraft($flag)
    {
        $this->isDraft = (bool) $flag;

        return $this;
    }

    /**
     * @return bool
     */
    public function isHiringOrganization()
    {
        return null !== $this->parent;
    }

    /**
     * Returns true, if the user belongs to the organization.
     *
     * @param UserInterface $user
     *
     * @return bool
     */
    public function isAssociated(UserInterface $user)
    {
        return $this->isOwner($user) || $this->isEmployee($user);
    }

    /**
     * Sets the external id.
     *
     * @param $externalId
     *
     * @return self
     */
    public function setExternalId($externalId)
    {
        $this->externalId = $externalId;

        return $this;
    }

    /**
     * Checks, if a User is the owner of an organization
     *
     * @param UserInterface $user
     *
     * @return bool
     */
    public function isOwner(UserInterface $user)
    {
        return $this->getUser()->getId() == $user->getId();
    }

    /**
     * Gets the internal id.
     *
     * @return string
     */
    public function getExternalId()
    {
        return $this->externalId;
    }

    /**
     * Returns true, if a User is an employee of the organization
     *
     * @param UserInterface $user
     *
     * @return bool
     */
    public function isEmployee(UserInterface $user)
    {
        return $this->refs && in_array($user->getId(), $this->refs->getEmployeeIds());
    }

    /**
     * @todo review this
     *
     * @param HydratorInterface $hydrator
     *
     * @return $this
     */
    public function setHydrator(HydratorInterface $hydrator)
    {
        return $this;
    }

    /**
     * Updates the organizationsPermissions to allow all employees to view this organization.
     *
     * In case of a HiringOrganization Permissions are granted to all employees of the parent
     * organization.
     *
     * @ODM\PreUpdate
     * @ODM\PrePersist
     * @since 0.18
     */
    public function updatePermissions()
    {
        if ($this->isHiringOrganization()) {
            $organization = $this->getParent();
            $owner        = $organization->getUser();

            $this->setUser($owner);
        } else {
            $organization = $this;
        }

        /* @var $employees null | ArrayCollection | \Doctrine\ODM\MongoDB\PersistentCollection */
        $employees = $organization->getEmployees();

        $perms = $this->getPermissions();

        foreach ($employees as $emp) {
            /* @var $emp \Organizations\Entity\Employee */
            $perms->grant($emp->getUser(), PermissionsInterface::PERMISSION_CHANGE, false);
        }
        $perms->build();
    }

    /**
     * * @todo review this
     * @return EntityHydrator
     */
    public function getHydrator()
    {
        return new EntityHydrator();
    }

    /**
     * Sets the name of an organization
     *
     * @param OrganizationName $organizationName
     *
     * @return $this
     */
    public function setOrganizationName(OrganizationName $organizationName)
    {
        if (isset($this->organizationName)) {
            $this->organizationName->refCounterDec()->refCompanyCounterDec();
        }
        $this->organizationName = $organizationName;
        $this->organizationName->refCounterInc()->refCompanyCounterInc();
        $this->_organizationName = $organizationName->getName();

        return $this;
    }

    /**
     * Gets the organization name entity of an organisation
     *
     * @return OrganizationName
     */
    public function getOrganizationName()
    {
        return $this->organizationName;
    }

    /**
     * Gets the Permissions of an organization
     *
     * @return PermissionsInterface
     */
    public function getPermissions()
    {
        if (!$this->permissions) {
            $permissions = new Permissions();
            if ($this->user) {
                $permissions->grant($this->user, Permissions::PERMISSION_ALL);
            }
            $this->setPermissions($permissions);
        }

        return $this->permissions;
    }

    /**
     * Sets the Permissions of an Organization
     *
     * @param PermissionsInterface $permissions
     *
     * @return $this
     */
    public function setPermissions(PermissionsInterface $permissions)
    {
        // Assure the user has always all rights.
        if ($this->user) {
            $permissions->grant($this->user, Permissions::PERMISSION_ALL);
        }
        $this->permissions = $permissions;

        return $this;
    }

    /**
     * Gets the Permissions Resource ID
     *
     * @return string
     */
    public function getPermissionsResourceId()
    {
        return 'organization:' . $this->getId();
    }

    /**
     * @param null $type
     *
     * @return array
     */
    public function getPermissionsUserIds($type = null)
    {
        // if we have a user, grant him full access to all associated permissions.
        $user = $this->getUser();
        $spec = $user
            ? $spec = array(PermissionsInterface::PERMISSION_ALL => array($this->getUser()->getId()))
            : array();

        if (null === $type || ('Job/Permissions' != $type && 'Application' != $type)) {
            return $spec;
        }

        if ('Job/Permissions' == $type) {
            $change = EmployeePermissionsInterface::JOBS_CHANGE;
            $view   = EmployeePermissionsInterface::JOBS_VIEW;
        } else {
            $change = EmployeePermissionsInterface::APPLICATIONS_CHANGE;
            $view   = EmployeePermissionsInterface::APPLICATIONS_VIEW;
        }

        $employees = $this->isHiringOrganization()
            ? $this->getParent()->getEmployees()
            : $this->getEmployees();

        foreach ($employees as $emp) {
            /* @var $emp EmployeeInterface */
            if ($emp->isUnassigned()) {
                continue;
            }

            $perm = $emp->getPermissions();
            if ($perm->isAllowed($change)) {
                $spec[PermissionsInterface::PERMISSION_CHANGE][] = $emp->getUser()->getId();
            } elseif ($perm->isAllowed($view)) {
                $spec[PermissionsInterface::PERMISSION_VIEW][] = $emp->getUser()->getId();
            }
        }

        return $spec;
    }

    /**
     * Sets the logo of an organization
     *
     * @param OrganizationImage $image
     *
     * @return self
     * @deprecated since 0.29; use $this->getImages()->set()
     */
    public function setImage(OrganizationImage $image = null)
    {
        $this->image = $image;

        return $this;
    }

    /**
     * Gets the Logo of an organization
     *
     * @param string|bool $key Key of the image to get.
     *                         If true: get Thumbnail
     *                         If false: get Original
     *
     * @return OrganizationImage
     * @deprecated since 0.29; use $this->getImages()->get()
     * @since 0.29 modified to return images from the image set for compatibility reasons
     */
    public function getImage($key = ImageSet::ORIGINAL)
    {
        if (is_bool($key)) {
            $key = $key ? ImageSet::THUMBNAIL : ImageSet::ORIGINAL;
        }

        return $this->getImages()->get($key, false) ?: $this->image;
    }

    /**
     * @param ImageSet $images
     *
     * @return self
     */
    public function setImages(ImageSet $images)
    {
        $this->images = $images;

        return $this;
    }

    /**
     * @return ImageSet
     */
    public function getImages()
    {
        if (!$this->images) {
            $this->images = new ImageSet();
        }

        return $this->images;
    }

    /**
     *
     *
     * @return self
     */
    public function removeImages()
    {
        $this->images = null;

        return $this;
    }

    /**
     * Sets the Contact Data of an organization
     *
     * @param EntityInterface $contact
     *
     * @return $this
     */
    public function setContact(EntityInterface $contact = null)
    {
        if (!$contact instanceof OrganizationContact) {
            $contact = new OrganizationContact($contact);
        }
        $this->contact = $contact;

        return $this;
    }

    /**
     * Gets the contact Data of an organization
     *
     * @return OrganizationContact
     */
    public function getContact()
    {
        if (!$this->contact instanceof OrganizationContact) {
            $this->contact = new OrganizationContact();
        }

        return $this->contact;
    }

    /**
     * Gets the default description of an organization.
     *
     * This description is used as the default of the company_description
     * used in a job template
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the default description af an organization
     *
     * @param string $description
     *
     * @return $this
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Sets the the list of employees
     *
     * @param Collection $employees
     *
     * @return $this
     */
    public function setEmployees(Collection $employees)
    {
        /* todo: Throw exception or at least log incidents, where employees are added to "hiring orgs" */
        if (!$this->isHiringOrganization()) {
            $this->employees = $employees;
        }

        return $this;
    }

    /**
     * Gets the list of employees
     *
     * @return ArrayCollection|Collection
     */
    public function getEmployees()
    {
        if ($this->isHiringOrganization()) {
            // Always return empty list, as we never have employees in this case.
            return new ArrayCollection();
        }

        if (!$this->employees) {
            $this->setEmployees(new ArrayCollection());
        }

        return $this->employees;
    }

    /**
     * Gets an employee by User or ID.
     *
     * @param UserInterface|string $userOrId
     *
     * @return mixed|null
     */
    public function getEmployee($userOrId)
    {
        $employees = $this->getEmployees();
        $userId    = $userOrId instanceof \Auth\Entity\UserInterface ? $userOrId->getId() : $userOrId;

        foreach ($employees as $employee) {
            if ($employee->getUser()->getId() == $userId) {
                return $employee;
            }
        }

        return null;
    }

    /**
     * Gets a list of Employees by a user role
     *
     * @param string $role
     *
     * @return ArrayCollection
     */
    public function getEmployeesByRole($role)
    {
        $employees = new ArrayCollection();

        /* @var \Organizations\Entity\Employee $employee */
        foreach ($this->getEmployees() as $employee) {
            if ($role === $employee->getRole()) {
                $employees->add($employee);
            }
        }

        return $employees;
    }

    public function setUser(UserInterface $user)
    {
        if ($this->user) {
            $this->getPermissions()->revoke($this->user, Permissions::PERMISSION_ALL, false);
        }
        $this->user = $user;
        $this->getPermissions()->grant($user, Permissions::PERMISSION_ALL);

        return $this;
    }

    /**
     * Gets the owner of the organization
     *
     * @return UserInterface
     */
    public function getUser()
    {
        return $this->user;
    }

    /**
     * Gets the Jobs of an organization
     *
     * @return Collection
     */
    public function getJobs()
    {
        return $this->jobs;
    }

    /**
     * Gets default values of an organizations job template
     *
     * @return TemplateInterface
     */
    public function getTemplate()
    {
        if (null === $this->template) {
            $this->template = new Template();
        }

        return $this->template;
    }

    /**
     * Sets default values of an organizations job template
     *
     * @return self
     */
    public function setTemplate(TemplateInterface $template)
    {
        $this->template = $template;

        return $this;
    }

    /**
     * Gets Workflow Settings
     *
     * @return WorkflowSettings|WorkflowSettingsInterface
     */
    public function getWorkflowSettings()
    {
        if (null == $this->workflowSettings) {
            $this->workflowSettings = new WorkflowSettings();
        }

        return $this->workflowSettings;
    }

    /**
     * Sets Workflow Settings
     *
     * @param $workflowSettings
     *
     * @return self
     */
    public function setWorkflowSettings($workflowSettings)
    {
        $this->workflowSettings = $workflowSettings;

        return $this;
    }
}


<?php

namespace HnhDigital\LaravelFrontendAssetLoader;

use Blade;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;

class ServiceProvider extends BaseServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->mergeConfigFrom(__DIR__.'/../config/config.default.php', 'frontend-assets');

        $this->app->bind('FrontendAsset', function () {
            return new FrontendAsset();
        });
    }

    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        $this->publishes([
            __DIR__.'/../config/config.php' => config_path('frontend-assets.php'),
        ]);

        blade::directive('captureScript', function ($name) {
            $name = empty($name) ? 'inline' : substr(str_replace('$', '', $name), 1, -1);

            return "<?php FrontendAsset::addScript(ob_get_clean(), '".$name."'); ?>";
        });

        blade::directive('captureStyle', function ($name) {
            $name = empty($name) ? 'header' : substr(str_replace('$', '', $name), 1, -1);

            return "<?php FrontendAsset::addStyle(ob_get_clean(), '".$name."'); ?>";
        });

        blade::directive('resources', function ($name) {
            $name = trim($name, "'\"");
            $name = "'$name'";

            return "<?php FrontendAsset::controller(['js', 'css'], $name); ?>";
        });

        blade::directive('asset', function ($name) {
            if (strlen(trim($name, "'\"[]")) == strlen($name)) {
                $name = "'$name'";
            }

            return "<?php FrontendAsset::container($name); ?>";
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['FrontendAsset'];
    }
}


<?php
/**
 * YAWIK
 *
 * @filesource
 * @copyright (c) 2013-2016 Cross Solution (http://cross-solution.de)
 * @author Carsten Bleek <bleek@cross-solution.de>
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author Miroslav Fedeleš <miroslav.fedeles@gmail.com>
 * @license   MIT
 */

/** */
namespace Organizations\Controller\Plugin;

use Organizations\Exception\MissingParentOrganizationException;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Core\Repository\RepositoryService;
use Auth\AuthenticationService;
use Auth\Exception\UnauthorizedAccessException;
use Zend\Mvc\Controller\Plugin\Params;
use Acl\Controller\Plugin\Acl;
use Core\Entity\Exception\NotFoundException;

/**
 * Class GetOrganizationHandler
 *
 * @package Organization\Controller\Plugin
 */
class GetOrganizationHandler extends AbstractPlugin
{

    /**
     * @var RepositoryService
     */
    protected $repositoryService;

    /**
     * @var AuthenticationService
     */
    protected $auth;

    /**
     * @var \Acl\Controller\Plugin\Acl
     */
    protected $acl;

    public function __construct(RepositoryService $repositoryService, AuthenticationService $auth, Acl $acl)
    {
        $this->repositoryService=$repositoryService;
        $this->auth=$auth;
        $this->acl=$acl;
    }

    public function __invoke()
    {
        return $this;
    }

    /**
     * @param Params $params
     * @param bool   $allowDraft
     *
     * @return object|\Organizations\Entity\Organization
     * @throws UnauthorizedAccessException
     * @throws \Doctrine\ODM\MongoDB\LockException
     * @throws NotFoundException
     */
    public function process(Params $params, $allowDraft = true)
    {
        $repositories   = $this->repositoryService;
        /* @var \Organizations\Repository\Organization $organizationRepository */
        $organizationRepository = $this->repositoryService->get('Organizations/Organization');

        $idFromRoute = $params('id', 0);
        $idFromSubForm = $params()->fromPost('id', 0);
        $user = $this->auth->getUser(); /* @var $user \Auth\Entity\UserInterface */

        /* @var $organizationId string */
        $organizationId = empty($idFromRoute)?$idFromSubForm:$idFromRoute;

        $editOwnOrganization = '__my__' === $organizationId;

        if ($editOwnOrganization) {
            /* @var $userOrg \Organizations\Entity\OrganizationReference */
            $userOrg = $user->getOrganization();
            if ($userOrg->hasAssociation() && !$userOrg->isOwner()) {
                throw new UnauthorizedAccessException('You may not edit this organization as you are only employer.');
            }
            $organizationId = $userOrg->hasAssociation() ? $userOrg->getId() : 0;
        }

        if (empty($organizationId) && $allowDraft) {
            /* @var $organization \Organizations\Entity\Organization */
            $organization = $organizationRepository->findDraft($user);
            if (empty($organization)) {
                $organization = $organizationRepository->create();
                $organization->setIsDraft(true);
                $organization->setUser($user);
                if (!$editOwnOrganization) {
                    /* @var $parent \Organizations\Entity\OrganizationReference */
                    $parent = $user->getOrganization();
                    if (!$parent->hasAssociation()) {
                        throw new MissingParentOrganizationException('You cannot create organizations, because you do not belong to a parent organization. Use "User menu -> create my organization" first.');
                    }
                    $organization->setParent($parent->getOrganization());
                }

                $repositories->store($organization);
            }
            return $organization;
        }

        $organization      = $organizationRepository->find($organizationId);
        if (!$organization) {
            throw new NotFoundException($organizationId);
        }

        $this->acl->check($organization, 'edit');

        return $organization;
    }
}


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

/** */
namespace Organizations\Mail;

use Auth\Entity\UserInterface;
use Interop\Container\ContainerInterface;
use Organizations\ImageFileCache\ODMListener;
use Zend\ServiceManager\Factory\FactoryInterface;

/**
 * This Factory creates and configures the HTMLTemplateMail send to an invited person.
 *
 * @author  Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author  Anthonius Munthi <me@itstoni.com>
 *
 * @TODO    [ZF3] Check if removing MutableCreationsOptionsInterface is not affecting application
 * @since   0.19
 */
class EmployeeInvitationFactory implements FactoryInterface
{
    /**
     * Dynamic options for each invocation.
     *
     * @var array
     */
    protected $options;

    /**
     * Create a ODMListener
     *
     * @param  ContainerInterface $container
     * @param  string             $requestedName
     * @param  null|array         $options
     * @TODO   fix method description, this method is not used to create an ODMListener but it will configure HTMLTemplateMail
     *
     * @return ODMListener
     */
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        /* @var $serviceLocator \Core\Mail\MailService */
        /* @var $owner \Auth\Entity\UserInterface */
        /* @var $user \Auth\Entity\UserInterface */
        $this->setCreationOptions($options);
        $auth     = $container->get('AuthenticationService');
        $router   = $container->get('Router');

        // we assume here, that the logged in user is the inviter.
        $owner   = $auth->getUser();
        $org     = $owner->getOrganization()->getOrganization();
        $orgName = $org->getOrganizationName()->getName();
        $user    = $this->options['user'];

        $url = $router->assemble(
            array('action' => 'accept'),
            array(
                'name'  => 'lang/organizations/invite',
                'query' => array(
                    'token'        => $this->options['token'],
                    'organization' => $org->getId()
                )
            )
        );

        $variables = array(
            'inviter'        => $owner->getInfo()->getDisplayName(),
            'organization'   => $orgName,
            'token'          => $this->options['token'],
            'user'           => $user->getInfo()->getDisplayName(/*emailifEmpty*/ false),
            'hasAssociation' => false,
            'url'            => $url,
        );

        if ($user->getOrganization()->hasAssociation()) {
            $variables['hasAssociation']      = true;
            $variables['isOwner']             = $user->getOrganization()->isOwner();
            $variables['currentOrganization'] =
                $user->getOrganization()->getOrganization()->getOrganizationName()->getName();
        }

        $mail = $container->get('Core/MailService')->get('htmltemplate');
        $mail->setTemplate($this->options['template'])
                ->setVariables($variables)
                ->setSubject(
                    sprintf(
                    /* @translate */ 'Invitation to join the team of %s',
                                    $orgName
                    )
                )
                ->addTo($user->getEmail());

        return $mail;
    }

    /**
     * Sets creation options.
     *
     * @param array $options
     *
     * @return void
     * @throws \InvalidArgumentException
     */
    public function setCreationOptions(array $options=null)
    {
        if (!isset($options['user']) || !$options['user'] instanceof UserInterface) {
            throw new \InvalidArgumentException('An user interface is required!');
        }

        if (!isset($options['token']) || !is_string($options['token'])) {
            $options['token'] = false;
        }

        if (!isset($options['template']) || !is_string($options['template'])) {
            $options['template'] = 'organizations/mail/invite-employee';
        }

        $this->options = $options;
    }
}


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

/** */
namespace Organizations\Form;

use Core\Entity\Hydrator\EntityHydrator;
use Core\Form\Hydrator\Strategy\CollectionStrategy;
use Core\Form\ViewPartialProviderInterface;
use Zend\Form\Fieldset;

/**
 * The employees fieldset used for managing employees.
 *
 * Currently has two elements:
 * A user search bar to add new employees and a Collection of EmployeeFieldsets (one for each employee).
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.18
 */
class EmployeesFieldset extends Fieldset implements ViewPartialProviderInterface
{
    protected $partial = 'organizations/form/employees-fieldset';

    public function setViewPartial($partial)
    {
        $this->partial = (string) $partial;

        return $this;
    }

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

    /**
     * Initializes the fieldset.
     */
    public function init()
    {
        $this->setName('employees');

        $this->add(
            array(
            'name' => 'inviteemployee',
            'type' => 'Organizations/InviteEmployeeBar',
            'options' => [
                'description' => /*@translate*/ 'Invite an employee via email address.',
            ]
            )
        );

        $this->add(
            array(

            'type' => 'Collection',
            'name' => 'employees',
            'options' => array(
                'count' => 0,
                'should_create_template' => true,
                'use_labeled_items' => false,
                'allow_add' => true,
                'allow_remove' => true,
                'renderFieldset' => true,
                'target_element' => array(
                    'type' => 'Organizations/EmployeeFieldset'
                )
            ),
            )
        );
    }
}


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

/** HttploadStrategy.php */
namespace Organizations\Entity\Hydrator\Strategy;

use Zend\Hydrator\Strategy\StrategyInterface;
use Organizations\Repository\OrganizationName as OrganizationNameRepository;

class OrganizationNameStrategy implements StrategyInterface
{
    /**
     * @var $repository \Organizations\Repository\OrganizationName
     */
    protected $repository;

    public function __construct(OrganizationNameRepository $repository)
    {
        $this->repository = $repository;
        return $this;
    }

    /**
     * @param mixed $value
     *
     * @return string
     */
    public function extract($value)
    {
        $name = '';
        if (method_exists($value, "getName")) {
            $name = $value->getName();
        }
        return $name;
    }

    /**
     * @param mixed $value
     *
     * @return mixed|object
     */
    public function hydrate($value)
    {
        $organizationNameEntity = $value;
        if (is_string($value)) {
            if (!isset($this->repository)) {
                throw new \InvalidArgumentException('OrganizationNameStrategy needs to access to the Repository');
            }
            if (!$this->repository instanceof OrganizationNameRepository) {
                throw new \InvalidArgumentException('OrganizationNameStrategy repository needs to be of the class Organizations\Repository\OrganizationName');
            }
            $organizationNameEntity = $this->repository->findbyName($value);
        }
        return $organizationNameEntity;
    }
}


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

/** ActionController of Organizations */
namespace Organizations\Controller;

use Core\Entity\Collection\ArrayCollection;
use Core\Form\SummaryForm;
use Organizations\Exception\MissingParentOrganizationException;
use Zend\Form\FormElementManager\FormElementManagerV3Polyfill;
use Zend\I18n\Translator\TranslatorInterface;
use Zend\Mvc\Controller\AbstractActionController;
use Organizations\Repository;
use Organizations\Form;
use Zend\Mvc\I18n\Translator;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
use Zend\Http\PhpEnvironment\Response;
use Core\Entity\Exception\NotFoundException;

/**
 * Main Action Controller for the Organization.
 * Responsible for handling the organization form.
 *
 * @author Mathias Weitz <weitz@cross-solution.de>
 * @author Carsten Bleek <bleek@cross-solution.de>
 * @author Rafal Ksiazek
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @author Miroslav Fedeleš <miroslav.fedeles@gmail.com>
 * @author Anthonius Munthi <me@itstoni.com>
 *
 * @method \Acl\Controller\Plugin\Acl acl()
 * @method \Core\Controller\Plugin\PaginationParams paginationParams()
 * @method \Core\Controller\Plugin\CreatePaginator paginator(string $repositoryName, array $defaultParams = array(), bool $usePostParams = false)
 * @method \Auth\Controller\Plugin\Auth auth()
 */
class IndexController extends AbstractActionController
{
    /**
     * The organization form.
     *
     * @var Form\Organizations
     */
    private $form;

    /**
     * The organization repository.
     *
     * @var Repository\Organization
     */
    private $repository;

    /**
     * @var FormElementManagerV3Polyfill
     */
    private $formManager;

    private $viewHelper;

    /**
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * Create new controller instance
     *
     * @param Form\Organizations $form
     * @param Repository\Organization $repository
     * @param TranslatorInterface $translator
     * @param $formManager
     * @param $viewHelper
     */
    public function __construct(
        Form\Organizations $form,
        Repository\Organization $repository,
        TranslatorInterface $translator,
        $formManager,
        $viewHelper
    ) {
        $this->repository = $repository;
        $this->form = $form;
        $this->formManager = $formManager;
        $this->viewHelper = $viewHelper;
        $this->translator = $translator;
    }

    /**
     * Generates a list of organizations
     *
     * @return array
     */
    public function indexAction()
    {
        return $this->pagination([
            'paginator' => [
                'Organizations/Organization',
                'as' => 'organizations'
            ],
            'form' => [
                'Core/Search',
                [
                    'text_name' => 'text',
                    'text_placeholder' => /*@translate*/ 'Search for organizations',
                    'button_element' => 'text'
                ],
                'as' => 'form'
            ]
        ]);
    }

    /**
     * Change (Upsert) organizations
     *
     * @return JsonModel|array
     * @throws \RuntimeException
     */
    public function editAction()
    {
        /* @var $request \Zend\Http\Request */
        $translator      = $this->translator;
        $return          = null;
        $request         = $this->getRequest();
        $params          = $this->params();
        $formIdentifier  = $params->fromQuery('form');

        try {
            /* @var $handler \Organizations\Controller\Plugin\GetOrganizationHandler */
            $handler = $this->plugin('Organizations/GetOrganizationHandler');
            $org  = $handler->process($this->params(), true);
        } catch (MissingParentOrganizationException $e) {
            return $this->getErrorViewModel('no-parent');
        } catch (NotFoundException $e) {
            $this->getResponse()->setStatusCode(Response::STATUS_CODE_404);
            return [
                'message' => sprintf($translator->translate('Organization with id "%s" not found'), $e->getId()),
                'exception' => $e
            ];
        }

        $container       = $this->getFormular($org);

        if (isset($formIdentifier) && $request->isPost()) {
            /* @var $form \Zend\Form\FormInterface */
            $postData = $this->params()->fromPost();
            $filesData = $this->params()->fromFiles();
            /* due to issues in ZF2 we need to clear the employees collection in the entity,
             * prior to binding. Otherwise it is not possible to REMOVE an employee, as the
             * MultiCheckbox Validation will FAIL on empty values!
             */
            if ("employeesManagement" == $formIdentifier) {
                $markedEmps = array();
                // Check if no permissions are set, and set one, mark this employee and restore it afterwards.
                foreach ($postData['employees']['employees'] as &$empData) {
                    if (!isset($empData['permissions'])) {
                        $empData['permissions'][] = 16;
                        $markedEmps[] = $empData['user'];
                    }
                }
                $org->setEmployees(new ArrayCollection());
            }

            $form = $container->get($formIdentifier);
            $form->setData(array_merge($postData, $filesData));
            if (!isset($form)) {
                throw new \RuntimeException('No form found for "' . $formIdentifier . '"');
            }
            $isValid = $form->isValid();

            if ("employeesManagement" == $formIdentifier) {
                // remove permissions from marked employees
                foreach ($org->getEmployees() as $emp) {
                    $empId = $emp->getUser()->getId();
                    if (in_array($empId, $markedEmps)) {
                        $emp->getPermissions()->revokeAll();
                    }
                }
            }
            if ($isValid) {
                $orgName = $org->getOrganizationName();
                if ($orgName && '' !== (string) $orgName->getName()) {
                    $org->setIsDraft(false);
                }
                $this->repository->store($org);
            }

            $organization = $container->getEntity();
            $this->repository->store($organization);

            if ('file-uri' === $this->params()->fromPost('return')) {
                /* @var $hydrator \Core\Entity\Hydrator\FileCollectionUploadHydrator
                 * @var $file     \Organizations\Entity\OrganizationImage */
                $basepath = $this->viewHelper->get('basepath');
                $hydrator = $form->getHydrator();
                $file     = $hydrator->getLastUploadedFile();
                $content = $basepath($file->getUri());
            } else {
                if ($form instanceof SummaryForm) {
                    /* @var $form \Core\Form\SummaryForm */
                    $form->setRenderMode(SummaryForm::RENDER_SUMMARY);
                    $viewHelper = 'summaryForm';
                } else {
                    $viewHelper = 'form';
                }

                $content = $this->viewHelper->get($viewHelper)->__invoke($form);
            }

            return new JsonModel(
                array(
                'valid' => $isValid,
                'content' => $content,
                )
            );
        }

        if (!isset($return)) {
            $return = array(
                'form' => $container
            );
        }
        return $return;
    }

    /**
     * Gets the organization form container.
     *
     * @param \Organizations\Entity\OrganizationInterface $organization
     *
     * @return \Organizations\Form\Organizations
     */
    protected function getFormular($organization)
    {
        /* @var $container \Organizations\Form\Organizations */
        //$services  = $this->serviceLocator;
        $forms     = $this->formManager;
        $container = $forms->get(
            'Organizations/Form',
            array(
            'mode' => $organization->getId() ? 'edit' : 'new'
            )
        );
        $container->setEntity($organization);
        $container->setParam('id', $organization->getId());
//        $container->setParam('applyId',$job->applyId);

        if ('__my__' != $this->params('id', '')) {
            $container->disableForm('employeesManagement')
                        ->disableForm('workflowSettings');
        } else {
            $container ->disableForm('organizationLogo')
                        ->disableForm('descriptionForm');
        }
        return $container;
    }

    protected function getErrorViewModel($script)
    {
        $this->getResponse()->setStatusCode(Response::STATUS_CODE_500);

        $model = new ViewModel();
        $model->setTemplate("organizations/error/$script");

        return $model;
    }
}


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

use Organizations\Entity\OrganizationImage;
use Organizations\Options\ImageFileCacheOptions as Options;
use Zend\Stdlib\ErrorHandler;
use InvalidArgumentException;
use RuntimeException;

/**
 * Image file cache manager
 *
 * @author Miroslav Fedeleš <miroslav.fedeles@gmail.com>
 * @since 0.28
 */
class Manager
{

    /**
     * @var Options
     */
    protected $options;

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

    /**
     * Returns image URI
     *
     * @param OrganizationImage $image
     * @return string
     */
    public function getUri(OrganizationImage $image)
    {
        if ($this->options->getEnabled()) {
            return sprintf('%s/%s', $this->options->getUriPath(), $this->getImageSubPath($image));
        }

        return $image->getUri();
    }

    /**
     * @return bool
     */
    public function isEnabled()
    {
        return $this->options->getEnabled();
    }

    /**
     * Store an image as a file in a file system
     *
     * @param OrganizationImage $image
     */
    public function store(OrganizationImage $image)
    {
        $resource = $image->getResource();
        $path = $this->getImagePath($image);
        $this->createDirectoryRecursively(dirname($path));
        file_put_contents($path, $resource);
    }

    /**
     * Delete an image file from file system
     *
     * @param OrganizationImage $image
     */
    public function delete(OrganizationImage $image)
    {
        @unlink($this->getImagePath($image));
    }

    /**
     * Match the passed $uri and return an image ID on success
     *
     * @param string $uri
     * @return null|string Image ID
     */
    public function matchUri($uri)
    {
        $pattern = '#^' . preg_quote($this->options->getUriPath(), '#') . '/[0-9a-z]/[0-9a-z]/([0-9a-z]+)\.[a-zA-Z]{3,4}$#';
        $matches = [];
        preg_match($pattern, $uri, $matches);

        return isset($matches[1]) ? $matches[1] : null;
    }

    /**
     * @param OrganizationImage $image
     * @return string
     */
    protected function getImagePath(OrganizationImage $image)
    {
        return sprintf('%s/%s', $this->options->getFilePath(), $this->getImageSubPath($image));
    }

    /**
     * @param OrganizationImage $image
     * @return string
     */
    protected function getImageSubPath(OrganizationImage $image)
    {
        $id = $image->getId();

        if (!$id) {
            throw new InvalidArgumentException('image must have ID');
        }

        $extension = null;
        $filename = $image->getName();

        if ($filename) {
            // get an extension from the filename
            $extension = pathinfo($filename, PATHINFO_EXTENSION);
        } else {
            // try get an extension from MIME if any
            $extension = str_replace('image/', '', $image->getType());
        }

        if (!$extension) {
            throw new InvalidArgumentException('unable to get an image file extension');
        }

        $firstLevel = substr($id, -1) ?: '0';
        $secondLevel = substr($id, -2, 1) ?: '0';

        return sprintf('%s/%s/%s.%s', $firstLevel, $secondLevel, $id, $extension);
    }

    /**
     * @param string $dir
     */
    protected function createDirectoryRecursively($dir)
    {
        $dir = rtrim($dir, '/\\');

        if (! is_dir($dir)) {
            $this->createDirectoryRecursively(dirname($dir));

            $oldUmask = umask(0);

            ErrorHandler::start();
            $created = mkdir($dir, 0777);
            $error = ErrorHandler::stop();

            if (!$created) {
                throw new RuntimeException(sprintf('unable to create directory "%s"', $dir), 0, $error);
            }

            umask($oldUmask);
        }
    }
}


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

/** OrganizationEntityHydrator.php */
namespace Organizations\Entity\Hydrator;

use Zend\Hydrator\Reflection;

/**
 * Class OrganizationHydrator
 * @package Organizations\Entity\Hydrator
 */
class OrganizationHydrator extends Reflection
{
    /**
     * @var $repOrganization \Organizations\Repository\Organization
     */
    protected $repOrganization;

    /**
     * @var $repOrganizationName \Organizations\Repository\OrganizationName
     */
    protected $repOrganizationName;

    /**
     * @var $repOrganizationImage \Organizations\Repository\OrganizationImage
     */
    protected $repOrganizationImage;

    protected $data;

    protected $object;

    public function __construct($repOrganization, $repOrganizationName, $repOrganizationImage)
    {
        parent::__construct();
        $this->repOrganization = $repOrganization;
        $this->repOrganizationName = $repOrganizationName;
        $this->repOrganizationImage = $repOrganizationImage;
    }

    /**
     * Extract values from an object
     *
     * @param  object $object
     * @return array
     */
    public function extract($object)
    {
        $result = array();
        foreach (self::getReflProperties($object) as $property) {
            $propertyName = $property->getName();
            if (!$this->filterComposite->filter($propertyName)) {
                continue;
            }
            $getter = 'get' . ucfirst($propertyName);
            $value = method_exists($object, $getter)
                   ? $object->$getter()
                   : $property->getValue($object);

            $result[$propertyName] = $this->extractValue($propertyName, $value);
        }

        return $result;
    }

    /**
     * Hydrate $object with the provided $data.
     *
     * @param  array $data
     * @param  object $object
     * @return object
     */
    public function hydrate(array $data, $object)
    {
        $this->data = $data;
        $this->object = $object;
        // download Image
        $this->hydrateSkimData();
        $reflProperties = self::getReflProperties($object);
        foreach ($this->data as $key => $value) {
            if (isset($reflProperties[$key])) {
                $value  = $this->hydrateValue($key, $value);
                $setter = 'set' . ucfirst($key);
                if (method_exists($object, $setter)) {
                    $object->$setter($value);
                } else {
                    // the values of the entity have to be set explicitly
                    $reflProperties[$key]->setValue($object, $this->hydrateValue($key, $value));
                }
            }
        }
        return $object;
    }

    /**
     * Converts a value for hydration. If no strategy exists the plain value is returned.
     *
     * @param string $name The name of the strategy to use.
     * @param mixed $value The value that should be converted.
     * @param array $data The whole data is optionally provided as context.
     * @return mixed
     */
    public function hydrateValue($name, $value, $data = null)
    {
        if ($this->hasStrategy($name)) {
            $strategy = $this->getStrategy($name);
            $value = $strategy->hydrate($value, $this->data, $this->object);
        }
        return $value;
    }

    protected function hydrateSkimData()
    {
        if (!empty($this->data['image']) && is_string($this->data['image'])) {
            // image uri is given, decide if image should be downloaded
            $image = $this->object->getImage();
            if (isset($image)) {
                $uri = $image->getUri();
                if (!empty($uri) && $uri == $this->data['image']) {
                    unset($this->data['image']);
                }
            }
        }
    }
}


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

/** */
namespace Organizations\Acl\Listener;

use Acl\Assertion\AssertionEvent;
use Auth\Entity\UserInterface;
use Organizations\Entity\EmployeePermissionsInterface;
use Zend\EventManager\SharedEventManagerInterface;

/**
 * Checks if an user may create jobs according to the organization permissions.
 *
 * @author Mathias Gelhausen <gelhausen@cross-solution.de>
 * @since 0.18
 */
class CheckJobCreatePermissionListener
{

    /**
     * Attach one or more listeners
     * Implementors may add an optional $priority argument; the SharedEventManager
     * implementation will pass this to the aggregate.
     *
     * @param SharedEventManagerInterface $events
     */
    public function attachShared(SharedEventManagerInterface $events)
    {
        $events->attach('Jobs/Acl/Assertion/Create', AssertionEvent::EVENT_ASSERT, array($this, 'checkCreatePermission'));
    }

    /**
     * Detach all previously attached listeners
     *
     * @param SharedEventManagerInterface $events
     */
    public function detachShared(SharedEventManagerInterface $events)
    {
        // not used.
    }

    /**
     * Checks if the user may create jobs according to the organization permissions.
     *
     * @param AssertionEvent $e
     *
     * @return bool
     */
    public function checkCreatePermission(AssertionEvent $e)
    {
        /* @var $role \Auth\Entity\User
         * @var $organization \Organizations\Entity\OrganizationReference
         */
        $role = $e->getRole();

        if (!$role instanceof UserInterface) {
            return false;
        }

        $organization = $role->getOrganization();

        if (!$organization->hasAssociation()
            || $organization->isOwner()
        ) {
            return true;
        }

        $employees = $organization->getEmployees();

        foreach ($employees as $emp) {
            /* @var $emp \Organizations\Entity\EmployeeInterface */
            if ($emp->getUser()->getId() == $role->getId()
                && $emp->getPermissions()->isAllowed(EmployeePermissionsInterface::JOBS_CREATE)
            ) {
                return true;
            }
        }

        return false;
    }
}


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

namespace Organizations\Form;

use Core\Form\FileUploadFactory;
use Organizations\Form\Hydrator\ImageHydrator;
use Zend\Stdlib\AbstractOptions;

/**
 * Class LogoImageFactory
 *
 * @package Organizations\Form
 */
class LogoImageFactory extends FileUploadFactory
{
    protected $fileName = 'original';
    protected $fileEntityClass = '\Organizations\Entity\OrganizationImage';
    protected $configKey = 'organization_logo_image';

    /**
     * abstract options defined in "Applications/Options"
     *
     * @var string
     */
    protected $options="Jobs/Options";

    /**
     * Configure the Form width Options
     *
     * @param \Core\Form\Form $form
     * @param AbstractOptions $options
     */
    protected function configureForm($form, AbstractOptions $options)
    {
        $size = $options->getCompanyLogoMaxSize();
        $type = $options->getCompanyLogoMimeType();

        $form->get($this->fileName)->setViewHelper('formImageUpload')
            ->setMaxSize($size)
            ->setAllowedTypes($type)
            ->setForm($form);

        $form->setIsDescriptionsEnabled(true);
        $form->setDescription(
            /*@translate*/ 'Choose a Logo. This logo will be shown in the job opening and the application form.'
        );

        //$form->setHydrator(new ImageHydrator());
    }
}


<?php
namespace Crevillo\Payum\Redsys\Action;

use Crevillo\Payum\Redsys\Api;
use Payum\Core\Action\ActionInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\GetStatusInterface;

class StatusAction implements ActionInterface
{
    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request GetStatusInterface */
        RequestNotSupportedException::assertSupports($this, $request);

        $model = ArrayObject::ensureArrayObject($request->getModel());

        if (null == $model['Ds_Response']) {
            $request->markNew();

            return;
        }

        if ($model['Ds_AuthorisationCode'] && null === $model['Ds_Response']) {
            $request->markPending();

            return;
        }

        if (in_array($model['Ds_Response'],
            array(Api::DS_RESPONSE_CANCELED, Api::DS_RESPONSE_USER_CANCELED))) {
            $request->markCanceled();

            return;
        }

        if (0 <= $model['Ds_Response'] && 99 >= $model['Ds_Response']) {
            $request->markCaptured();

            return;
        }

        $request->markUnknown();
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof GetStatusInterface &&
            $request->getModel() instanceof \ArrayAccess;
    }
}


<?php
namespace Crevillo\Payum\Redsys\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\ApiAwareInterface;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Exception\UnsupportedApiException;
use Payum\Core\Model\PaymentInterface;
use Payum\Core\Request\Convert;
use Payum\Core\Bridge\Spl\ArrayObject;
use Crevillo\Payum\Redsys\Api;

class ConvertPaymentAction implements ActionInterface, ApiAwareInterface
{
    /**
     * @var Api
     */
    protected $api;

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        if (false == $api instanceof Api) {
            throw new UnsupportedApiException('Not supported.');
        }

        $this->api = $api;
    }

    /**
     * {@inheritDoc}
     *
     * @param Convert $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var PaymentInterface $payment */
        $payment = $request->getSource();

        $details = ArrayObject::ensureArrayObject($payment->getDetails());

        $details->defaults(array(
            'Ds_Merchant_Amount' => $payment->getTotalAmount(),
            'Ds_Merchant_Order' => $this->api->ensureCorrectOrderNumber($payment->getNumber()),
            'Ds_Merchant_MerchantCode' => $this->api->getMerchantCode(),
            'Ds_Merchant_Currency' => $this->api->getISO4127($payment->getCurrencyCode()),
            'Ds_Merchant_TransactionType' => Api::TRANSACTIONTYPE_AUTHORIZATION,
            'Ds_Merchant_Terminal' => $this->api->getMerchantTerminalCode(),
        ));

        $request->setResult((array)$details);
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Convert &&
            'array' == $request->getTo() &&
            $request->getSource() instanceof PaymentInterface;
    }
}


<?php
namespace Crevillo\Payum\Redsys;

use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\InvalidArgumentException;
use Payum\Core\Exception\LogicException;

class Api
{
    const TRANSACTIONTYPE_AUTHORIZATION = 0;

    const CONSUMERLANGUAGE_SPANISH = '001';

    const DS_RESPONSE_CANCELED = '0184';

    const DS_RESPONSE_USER_CANCELED = '9915';

    const ORDER_NUMBER_MINIMUM_LENGTH = 4;

    const ORDER_NUMBER_MAXIMUM_LENGHT = 12;

    const SIGNATURE_VERSION = 'HMAC_SHA256_V1';

    protected $options = array(
        'merchant_code' => null,
        'terminal' => null,
        'secret_key' => null,
        'sandbox' => true,
    );

    /**
     * Currency codes to the values the bank
     * understand. Remember you can only work
     * with one of them per commerce
     */
    protected $currencies = array(
        'EUR' => '978',
        'USD' => '840',
        'GBP' => '826',
        'JPY' => '392',
        'ARA' => '32',
        'CAD' => '124',
        'CLP' => '152',
        'COP' => '170',
        'INR' => '356',
        'MXN' => '484',
        'PEN' => '604',
        'CHF' => '756',
        'BRL' => '986',
        'VEF' => '937',
        'TRL' => '949',
    );

    private $payment_vars = array();

    public function setParameter($key, $value)
    {
        $this->payment_vars[$key] = $value;
    }

    public function __construct(array $options)
    {
        $this->options = array_replace($this->options, $options);

        if (true == empty($this->options['merchant_code'])) {
            throw new InvalidArgumentException('The merchant_code option must be set.');
        }
        if (true == empty($this->options['terminal'])) {
            throw new InvalidArgumentException('The terminal option must be set.');
        }
        if (true == empty($this->options['secret_key'])) {
            throw new InvalidArgumentException('The secret_key option must be set.');
        }
        if (false == is_bool($this->options['sandbox'])) {
            throw new InvalidArgumentException('The boolean sandbox option must be set.');
        }
    }

    /**
     * @return string
     */
    public function getRedsysUrl()
    {
        return $this->options['sandbox'] ?
            'https://sis-t.redsys.es:25443/sis/realizarPago' :
            'https://sis.redsys.es/sis/realizarPago';
    }

    /**
     * @param $currency
     *
     * @return mixed
     */
    public function getISO4127($currency)
    {
        if (!isset($this->currencies[$currency])) {
            throw new LogicException('Currency not allowed by the gateway.');
        }

        return $this->currencies[$currency];
    }

    /**
     * @return string
     */
    public function getMerchantCode()
    {
        return $this->options['merchant_code'];
    }

    /**
     * @return string
     */
    public function getMerchantTerminalCode()
    {
        return $this->options['terminal'];
    }

    /**
     * Validate the order number passed to the bank. it needs to pass the
     * following test
     *
     * - Must be between 4 and 12 characters
     *     - We complete with 0 to the left in case length or the number is lower
     *       than 4 in order to make the integration easier
     * - Four first characters must be digits
     * - Following eight can be digits or characters which ASCII numbers are:
     *    - between 65 and 90 ( A - Z)
     *    - between 97 and 122 ( a - z )
     *
     * If the test pass, orderNumber will be returned. if not, a Exception will be thrown
     *
     * @param string $orderNumber
     *
     * @return string
     */
    public function ensureCorrectOrderNumber($orderNumber)
    {
        if (strlen($orderNumber) > self::ORDER_NUMBER_MAXIMUM_LENGHT) {
            throw new LogicException('Payment number can\'t have more than 12 characters');
        }

        // add 0 to the left in case length of the order number is less than 4
        $orderNumber = str_pad($orderNumber, self::ORDER_NUMBER_MINIMUM_LENGTH,
            '0', STR_PAD_LEFT);

        if (!preg_match('/^[0-9]{4}[a-z0-9]{0,12}$/i', $orderNumber)) {
            throw new LogicException('The payment gateway doesn\'t allow order numbers with this format.');
        }

        return $orderNumber;
    }

    /**
     * 3DES Function provided by Redsys
     *
     * @param string $merchantOrder
     * @param string $key
     *
     * @return string
     */
    private function encrypt_3DES($merchantOrder, $key)
    {

        $ciphertext = null;
        if ( function_exists( 'mcrypt_encrypt' ) && version_compare(phpversion(), '7.1', '<')  ) {
            // default IV
            $bytes = array(0,0,0,0,0,0,0,0); //byte [] IV = {0, 0, 0, 0, 0, 0, 0, 0}
            $iv = implode(array_map("chr", $bytes));
            $ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key, $merchantOrder, MCRYPT_MODE_CBC, $iv);

        } elseif(function_exists( 'openssl_encrypt' ) && version_compare(phpversion(), '7.1', '>=') ) {
            $l = ceil(strlen($merchantOrder) / 8) * 8;
            $ciphertext = substr(openssl_encrypt($merchantOrder . str_repeat("\0", $l - strlen($merchantOrder)), 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "\0\0\0\0\0\0\0\0"), 0, $l);
        }

        return $ciphertext;
    }

    /**
     * base64_url_encode function provided by Redsys
     *
     * @param $input
     * @return string
     */
    function base64_url_encode($input)
    {
        return strtr(base64_encode($input), '+/', '-_');
    }

    /**
     * Decode function provided by Redsys
     *
     * @param $input
     * @return string
     */
    private function base64_url_decode($input)
    {
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * Mac256 function provided by Redsys
     *
     * @param $ent
     * @param $key
     * @return string
     */
    private function mac256($ent, $key)
    {
        $res = hash_hmac('sha256', $ent, $key, true);

        return $res;
    }

    /**
     * encodeBase64 function provided by Redsys
     *
     * @param $data
     * @return string
     */
    private function encodeBase64($data)
    {
        $data = base64_encode($data);

        return $data;
    }

    /**
     * decodeBase64 function provided by Redsys
     *
     * @param $data
     * @return string
     */
    private function decodeBase64($data)
    {
        $data = base64_decode($data);

        return $data;
    }

    /**
     * builds signature from the data sent by Redsys in the reply
     *
     * @param $key
     * @param $data
     * @return string
     */
    function createMerchantSignatureNotif($key, $data)
    {
        $key = $this->decodeBase64($key);
        $decodec = $this->base64_url_decode($data);
        $orderData = json_decode($decodec, true);
        $key = $this->encrypt_3DES($orderData['Ds_Order'], $key);
        $res = $this->mac256($data, $key);

        return $this->base64_url_encode($res);
    }

    /**
     * Validates notification sent by Redsys when it receives the payment
     *
     * @param array $notification
     *
     * @return bool
     */
    public function validateNotificationSignature(array $notification)
    {
        $notification = ArrayObject::ensureArrayObject($notification);
        $notification->validateNotEmpty('Ds_Signature');
        $notification->validateNotEmpty('Ds_MerchantParameters');
        $data = $notification["Ds_MerchantParameters"];

        $key = $this->options['secret_key'];
        $signedResponse = $this->createMerchantSignatureNotif($key, $data);

        return $signedResponse == $notification['Ds_Signature'];
    }

    /**
     * Builds Merchant Parameters encoded string.
     * Bank will take care of decode this
     *
     * @param array $params
     * @return string
     */
    function createMerchantParameters(array $params)
    {
        $json = json_encode($params);

        return $this->encodeBase64($json);
    }

    /**
     * Sing request sent to Gateway
     *
     * @param array $params
     *
     * @return string
     */
    public function sign(array $params)
    {
        $base64DecodedKey = base64_decode($this->options['secret_key']);
        $key = $this->encrypt_3DES($params['Ds_Merchant_Order'],
            $base64DecodedKey);

        $res = $this->mac256(
            $this->createMerchantParameters($params),
            $key
        );

        return base64_encode($res);
    }
}


<?php
namespace Crevillo\Payum\Redsys\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\ApiAwareInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Exception\UnsupportedApiException;
use Payum\Core\Reply\HttpPostRedirect;
use Payum\Core\Request\Capture;
use Crevillo\Payum\Redsys\Api;
use Payum\Core\Security\GenericTokenFactoryAwareInterface;
use Payum\Core\Security\GenericTokenFactoryInterface;

class CaptureAction implements ActionInterface, ApiAwareInterface, GenericTokenFactoryAwareInterface
{
    /**
     * @var Api
     */
    protected $api;

    /**
     * @var GenericTokenFactoryInterface
     */
    protected $tokenFactory;

    /**
     * @param GenericTokenFactoryInterface $genericTokenFactory
     *
     * @return void
     */
    public function setGenericTokenFactory(
        GenericTokenFactoryInterface $genericTokenFactory = null
    ) {
        $this->tokenFactory = $genericTokenFactory;
    }

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        if (false === $api instanceof Api) {
            throw new UnsupportedApiException('Not supported.');
        }

        $this->api = $api;
    }

    /**
     * {@inheritDoc}
     */
    public function execute($request)
    {
        /** @var $request Capture */
        RequestNotSupportedException::assertSupports($this, $request);

        $postData = ArrayObject::ensureArrayObject($request->getModel());

        if (empty($postData['Ds_Merchant_MerchantURL']) && $request->getToken() && $this->tokenFactory) {
            $notifyToken = $this->tokenFactory->createNotifyToken(
                $request->getToken()->getGatewayName(),
                $request->getToken()->getDetails()
            );

            $postData['Ds_Merchant_MerchantURL'] = $notifyToken->getTargetUrl();
        }

        $postData->validatedKeysSet(array(
            'Ds_Merchant_Amount',
            'Ds_Merchant_Order',
            'Ds_Merchant_Currency',
            'Ds_Merchant_TransactionType',
            'Ds_Merchant_MerchantURL',
        ));

        $details['Ds_Merchant_MerchantCode'] = $this->api->getMerchantCode();
        $details['Ds_Merchant_Terminal'] = $this->api->getMerchantTerminalCode();

        if (false == $postData['Ds_Merchant_UrlOK'] && $request->getToken()) {
            $postData['Ds_Merchant_UrlOK'] = $request->getToken()
                ->getTargetUrl();
        }
        if (false == $postData['Ds_Merchant_UrlKO'] && $request->getToken()) {
            $postData['Ds_Merchant_UrlKO'] = $request->getToken()
                ->getTargetUrl();
        }

        $details['Ds_SignatureVersion'] = Api::SIGNATURE_VERSION;

        if (false == $postData['Ds_MerchantParameters'] && $request->getToken()) {
            $details['Ds_MerchantParameters'] = $this->api->createMerchantParameters($postData->toUnsafeArray());
        }

        if (false == $postData['Ds_Signature']) {
            $details['Ds_Signature'] = $this->api->sign($postData->toUnsafeArray());

            throw new HttpPostRedirect($this->api->getRedsysUrl(), $details);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Capture &&
            $request->getModel() instanceof \ArrayAccess;
    }
}


<?php
namespace Crevillo\Payum\Redsys\Action;

use Crevillo\Payum\Redsys\Api;
use Payum\Core\Action\GatewayAwareAction;
use Payum\Core\ApiAwareInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Exception\UnsupportedApiException;
use Payum\Core\Reply\HttpResponse;
use Payum\Core\Request\GetHttpRequest;
use Payum\Core\Request\Notify;

class NotifyAction extends GatewayAwareAction implements ApiAwareInterface
{
    /**
     * @var Api
     */
    protected $api;

    /**
     * {@inheritDoc}
     */
    public function setApi($api)
    {
        if (false === $api instanceof Api) {
            throw new UnsupportedApiException('Not supported.');
        }

        $this->api = $api;
    }

    /**
     * {@inheritDoc}
     *
     * @param Notify $request
     */
    public function execute($request)
    {
        RequestNotSupportedException::assertSupports($this, $request);

        $details = ArrayObject::ensureArrayObject($request->getModel());

        $this->gateway->execute($httpRequest = new GetHttpRequest());

        if (null === $httpRequest->request['Ds_Signature']) {
            throw new HttpResponse('The notification is invalid', 400);
        }

        if (null === $httpRequest->request['Ds_MerchantParameters']) {
            throw new HttpResponse('The notification is invalid', 400);
        }

        if (false == $this->api->validateNotificationSignature($httpRequest->request)) {
            throw new HttpResponse('The notification is invalid', 400);
        }

        // After migrating to sha256, DS_Response param is not present in the
        // post request sent by the bank. Instead, bank sends an encoded string
        //  our gateway needs to decode.
        // Once this is decoded we need to add this info to the details among
        // with the $httpRequest->request part
        $details->replace(
            ArrayObject::ensureArrayObject(
                json_decode(base64_decode(strtr($httpRequest->request['Ds_MerchantParameters'], '-_', '+/')))
            )->toUnsafeArray() +
            $httpRequest->request
        );

        throw new HttpResponse('', 200);
    }

    /**
     * {@inheritDoc}
     */
    public function supports($request)
    {
        return
            $request instanceof Notify &&
            $request->getModel() instanceof \ArrayAccess
        ;
    }
}


<?php
namespace Crevillo\Payum\Redsys;

use Crevillo\Payum\Redsys\Action\NotifyAction;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\GatewayFactory as CoreGatewayFactory;
use Crevillo\Payum\Redsys\Action\CaptureAction;
use Crevillo\Payum\Redsys\Action\ConvertPaymentAction;
use Crevillo\Payum\Redsys\Action\StatusAction;
use Payum\Core\GatewayFactoryInterface;

class RedsysGatewayFactory implements GatewayFactoryInterface
{
    /**
     * @var GatewayFactoryInterface
     */
    protected $coreGatewayFactory;

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

    /**
     * @param array $defaultConfig
     * @param GatewayFactoryInterface $coreGatewayFactory
     */
    public function __construct(array $defaultConfig = array(), GatewayFactoryInterface $coreGatewayFactory = null)
    {
        $this->coreGatewayFactory = $coreGatewayFactory ?: new CoreGatewayFactory();
        $this->defaultConfig = $defaultConfig;
    }

    /**
     * {@inheritDoc}
     */
    public function create(array $config = array())
    {
        return $this->coreGatewayFactory->create($this->createConfig($config));
    }

    /**
     * {@inheritDoc}
     */
    public function createConfig(array $config = array())
    {
        $config = ArrayObject::ensureArrayObject($config);
        $config->defaults($this->defaultConfig);
        $config->defaults($this->coreGatewayFactory->createConfig());

        $config->defaults(array(
            'payum.factory_name' => 'redsys',
            'payum.factory_title' => 'Redsys',

            'payum.action.capture' => new CaptureAction(),
            'payum.action.notify' => new NotifyAction(),
            'payum.action.convert_payment' => new ConvertPaymentAction(),
            'payum.action.status' => new StatusAction(),
        ));

        if (false == $config['payum.api']) {
            $config['payum.default_options'] = array(
                'merchant_code' => '',
                'terminal' => '',
                'secret_key' => '',
                'sandbox' => true,
            );

            $config->defaults($config['payum.default_options']);
            $config['payum.required_options'] = array('merchant_code', 'terminal', 'secret_key');

            $config['payum.api'] = function (ArrayObject $config) {
                $config->validateNotEmpty($config['payum.required_options']);

                $redsysConfig = array(
                    'merchant_code' => $config['merchant_code'],
                    'terminal' => $config['terminal'],
                    'secret_key' => $config['secret_key'],
                    'sandbox' => $config['sandbox'],
                );

                return new Api($redsysConfig);
            };
        }

        return (array) $config;
    }
}


<?php

/**
 * @package Zammad API Client
 * @author  Jens Pfeifer <jens.pfeifer@znuny.com>
 */

namespace ZammadAPIClient;

class HTTPClient extends \GuzzleHttp\Client
{
    private $base_url;
    private $authentication_options;

    /**
     * Creates an HTTPClient object.
     *
     * @param Array $options                    Options to use for client:
     *                                          $options = [
     *                                              // URL of Zammad
     *                                              'url' => 'https://my.zammad.com:3000',
     *
     *                                              // authentication via username and password
     *                                              'username' => 'my-username',
     *                                              'password' => 'my-password',
     *                                              // OR: authentication via HTTP token
     *                                              'http_token' => 'my-token',
     *                                              // OR: authentication via OAuth2 token
     *                                              'oauth2_token' => 'my-token',
     *
     *                                              // Optional: timeout (in seconds) for requests, defaults to 5
     *                                              // 0: no timeout
     *                                              timeout => 10,
     *
     *                                              // Optional: Enable debug output
     *                                              debug => true,
     *                                          ];
     *
     * @return Object                           HTTPClient object
     */
    public function __construct( array $options = [] )
    {
        //
        // Check options
        //

        // URL
        if ( empty( $options['url'] ) ) {
            throw new \RuntimeException('Missing option "url"');
        }
        $url_components = parse_url( $options['url'] );
        if (
            $url_components === false
            || empty( $url_components['scheme'] )
            || empty( $url_components['host'] )
        ) {
            throw new \RuntimeException('Invalid URL');
        }

        // Authentication
        $number_of_authentication_types_given = 0;
        if ( !empty( $options['username'] ) || !empty( $options['password'] ) ) {
            if ( empty( $options['username'] ) ) {
                throw new \RuntimeException('Missing option "username"');
            }
            else if ( empty( $options['password'] ) ) {
                throw new \RuntimeException('Missing option "password"');
            }

            $number_of_authentication_types_given++;

            $this->authentication_options = [
                'username' => $options['username'],
                'password' => $options['password'],
            ];
        }

        if ( !empty( $options['http_token'] ) ) {
            $number_of_authentication_types_given++;

            $this->authentication_options = [
                'http_token' => $options['http_token'],
            ];
        }

        if ( !empty( $options['oauth2_token'] ) ) {
            $number_of_authentication_types_given++;

            $this->authentication_options = [
                'oauth2_token' => $options['oauth2_token'],
            ];
        }

        if ( $number_of_authentication_types_given != 1 ) {
            throw new \RuntimeException('Missing authentication options: Either give username/password, http_token or oauth2_token');
        }

        // Assemble base URL
        $this->base_url = $options['url'] . '/api/' . Client::API_VERSION . '/';

        // Optional: override timeout
        $timeout = 5;
        if ( array_key_exists( 'timeout', $options ) ) {
            $timeout = intval($options['timeout']);
            if ($timeout < 0) {
                $timeout = 0;
            }
        }

        // Debug flag
        $debug = false;
        if ( array_key_exists( 'debug', $options ) ) {
            $debug = $options['debug'] ? true : false;
        }

        // Execute constructor of base class
        parent::__construct([
            'base_uri' => $this->base_url,
            'timeout'  => $timeout,
            'debug'    => $debug,
        ]);
    }

    /**
     * Overrides base class request method to automatically add authentication options to request.
     */
    public function request( $method, $uri = '', array $options = [] )
    {
        //
        // Add authentication options
        //

        // Username and password
        if (
            !empty( $this->authentication_options['username'] )
            && !empty( $this->authentication_options['password'] )
        ) {
            $options['auth'] = [
                $this->authentication_options['username'],
                $this->authentication_options['password'],
            ];
        }
        // HTTP token
        else if ( !empty( $this->authentication_options['http_token'] ) ) {
            $options['headers']['Authorization']
                = 'Token token=' . $this->authentication_options['http_token'];
        }
        // OAuth2 token
        else if ( !empty( $this->authentication_options['oauth2_token'] ) ) {
            $options['headers']['Authorization']
                = 'Bearer ' . $this->authentication_options['oauth2_token'];
        }
        else {
            throw new \RuntimeException('No authentication options available');
        }

        try {
            $response = parent::request( $method, $uri, $options );
        }
        catch ( \GuzzleHttp\Exception\TransferException $e ) {
            $response = $e->getResponse();
        }

        return $response;
    }
}


<?php

/**
 * @package Zammad API Client
 * @author  Jens Pfeifer <jens.pfeifer@znuny.com>
 */

namespace ZammadAPIClient\Resource;

use ZammadAPIClient\ResourceType;

class Ticket extends AbstractResource
{
    const URLS = [
        'get'    => 'tickets/{object_id}',
        'all'    => 'tickets',
        'create' => 'tickets',
        'update' => 'tickets/{object_id}',
        'delete' => 'tickets/{object_id}',
        'search' => 'tickets/search',
    ];

    /**
     * Fetches TicketArticle objects of this Ticket object.
     *
     * @return Array of TicketArticle objects   Returns array of ZammadAPIClient\Resource\TicketArticle objects or an empty array.
     */
    public function getTicketArticles()
    {
        $this->clearError();

        if ( empty( $this->getID() ) ) {
            return [];
        }

        $ticket_articles = $this->getClient()->resource( ResourceType::TICKET_ARTICLE )->getForTicket( $this->getID() );
        if ( !is_array($ticket_articles) ) {
            $this->setError( $ticket_articles->getError() );
            return [];
        }

        return $ticket_articles;
    }
}


<?php

/**
 * @package Zammad API Client
 * @author  Jens Pfeifer <jens.pfeifer@znuny.com>
 */

namespace ZammadAPIClient;

use ZammadAPIClient\Client\Response;

class Client
{
    const API_VERSION = 'v1';

    private $http_client;
    private $last_response;
    private $options;
    private $on_behalf_of_user;

    /**
     * Creates a Client object.
     *
     * @param Array $options                    Options to use for client:
     *                                          $options = [
     *                                              // URL of Zammad
     *                                              'url' => 'https://my.zammad.com:3000',
     *
     *                                              // authentication via username and password
     *                                              'username' => 'my-username',
     *                                              'password' => 'my-password',
     *                                              // OR: authentication via HTTP token
     *                                              'http_token' => 'my-token',
     *                                              // OR: authentication via OAuth2 token
     *                                              'oauth2_token' => 'my-token',
     *
     *                                              // Optional: timeout (in seconds) for requests, defaults to 5
     *                                              // 0: no timeout
     *                                              timeout => 10,
     *
     *                                              // Optional: Enable debug output
     *                                              debug => true,
     *                                          ];
     *
     * @return Object                           Client object
     */
    public function __construct( array $options = [] )
    {
        $this->options     = $options;
        $this->http_client = new HTTPClient($options);
    }

    /**
     * Executes a request.
     *
     * @param String $method         GET, PUT, POST, DELETE
     * @param String $url            E. g. tickets/1
     * @param Array  $url_parameters E. g. [ 'expand' => true, ]
     *
     * @return Response object
     */
    private function request ( $method, $url, array $url_parameters = [], array $options = [] )
    {
        $method = mb_strtoupper($method);

        if (!empty($url_parameters)) {
          $options['query'] = $url_parameters;
        }

        // Set JSON headers
        $options['headers']['Accept']       = 'application/json';
        $options['headers']['Content-Type'] = 'application/json; charset=utf-8';

        // Set "on behalf of user" header
        if ( mb_strlen($this->on_behalf_of_user) ) {
            $options['headers']['X-On-Behalf-Of'] = $this->on_behalf_of_user;
        }

        $http_client_response = $this->http_client->request( $method, $url, $options );
        if ( !is_object($http_client_response) ) {
            throw new \RuntimeException('Unable to create HTTP client request.');
        }

        // Turn HTTP client's response into our own.
        $response = new Response(
            $http_client_response->getStatusCode(),
            $http_client_response->getReasonPhrase(),
            $http_client_response->getBody(),
            $http_client_response->getHeaders()
        );

        $this->setLastResponse($response);

        return $response;
    }

    /**
     * Executes GET request.
     *
     * @param String $url            E. g. tickets/1
     * @param Array  $url_parameters E. g. [ 'expand' => true, ]
     *
     * @return Response object
     */
    public function get( $url, array $url_parameters = [] )
    {
        $response = $this->request(
            'GET',
            $url,
            $url_parameters
        );

        return $response;
    }

    /**
     * Executes POST request.
     *
     * @param String $url            E. g. tickets/1
     * @param Array $data            Array with data to send as JSON.
     * @param Array  $url_parameters E. g. [ 'expand' => true, ]
     *
     * @return Response object
     */
    public function post( $url, array $data = [], array $url_parameters = [] )
    {
        $response = $this->request(
            'POST',
            $url,
            $url_parameters,
            [ 'json' => $data, ]
        );

        return $response;
    }

    /**
     * Executes PUT request.
     *
     * @param String $url            E. g. tickets/1
     * @param Array $data            Array with data to send as JSON.
     * @param Array  $url_parameters E. g. [ 'expand' => true, ]
     *
     * @return Response object
     */
    public function put( $url, array $data = [], array $url_parameters = [] )
    {
        $response = $this->request(
            'PUT',
            $url,
            $url_parameters,
            [ 'json' => $data, ]
        );

        return $response;
    }

    /**
     * Executes DELETE request.
     *
     * @param String $url            E. g. tickets/1
     * @param Array  $url_parameters E. g. [ 'expand' => true, ]
     *
     * @return Response object
     */
    public function delete( $url, array $url_parameters = [] )
    {
        $response = $this->request(
            'DELETE',
            $url,
            $url_parameters
        );

        return $response;
    }

    /**
     * Creates a resource object.
     *
     * @param String $resource_type         ResourceType::TICKET
     *
     * @return Object                       Resource object
     */
    public function resource($resource_type)
    {
        $resource_object = new $resource_type($this);
        return $resource_object;
    }

    /**
     * Sets user on behalf of which API calls will be executed.
     *
     * @param String $user         User ID, login or email address
     */
    public function setOnBehalfOfUser($user)
    {
        $this->on_behalf_of_user = $user;
    }

    /**
     * Unsets user on behalf of which API calls will be executed.
     * API calls will then be called again by the user who is being used
     * for authentication.
     */
    public function unsetOnBehalfOfUser()
    {
        $this->on_behalf_of_user = null;
    }

    /**
     * Stores Response object as last Response object.
     *
     * @param Object $response              Response object to store.
     */
    private function setLastResponse( Response $response )
    {
        $this->last_response = $response;
    }

    /**
     * Returns last Response object.
     *
     * @return objects                      Last Response object.
     */
    public function getLastResponse()
    {
        return $this->last_response;
    }
}