<?php
/**
 * Project: zf2-twb-bundle, File: TwbBundleFontAwesome.php
 * @author Michael Schakulat <michael@fetchit.de>
 * @package zf2-twb-bundle
 */

namespace TwbBundle\View\Helper;

use InvalidArgumentException;
use Zend\Form\View\Helper\AbstractHelper;

/**
 * Class TwbBundleFontAwesome
 * @package TwbBundle\View\Helper
 */
class TwbBundleFontAwesome extends AbstractHelper
{
    /**
     * @var string
     */
    protected static $faFormat = '<span %s></span>';

    /**
     * Invoke helper as functor, proxies to {@link render()}.
     * @param string $sFontAwesome
     * @param array $aFontAwesomeAttributes : [optionnal]
     * @return string|TwbBundleFontAwesome
     */
    public function __invoke($sFontAwesome = null, array $aFontAwesomeAttributes = null)
    {
        if (!$sFontAwesome) {
            return $this;
        }
        return $this->render($sFontAwesome, $aFontAwesomeAttributes);
    }

    /**
     * Retrieve fontAwesome markup
     * @param string $sFontAwesome
     * @param  array $aFontAwesomeAttributes : [optionnal]
     * @throws InvalidArgumentException
     * @return string
     */
    public function render($sFontAwesome, array $aFontAwesomeAttributes = null)
    {
        if (!is_scalar($sFontAwesome)) {
            throw new \InvalidArgumentException(
                sprintf(
                    'FontAwesome expects a scalar value, "%s" given',
                    gettype($sFontAwesome)
                )
            );
        }

        if (empty($aFontAwesomeAttributes)) {
            $aFontAwesomeAttributes = array('class' => 'fa');
        } else {

            if (empty($aFontAwesomeAttributes['class'])) {
                $aFontAwesomeAttributes['class'] = 'fa';
            } elseif (!preg_match('/(\s|^)fa(\s|$)/', $aFontAwesomeAttributes['class'])) {
                $aFontAwesomeAttributes['class'] .= ' fa';
            }
        }

        if (strpos('fa-', $sFontAwesome) !== 0) {
            $sFontAwesome = 'fa-' . $sFontAwesome;
        }

        if (!preg_match('/(\s|^)' . preg_quote($sFontAwesome, '/') . '(\s|$)/', $aFontAwesomeAttributes['class'])) {
            $aFontAwesomeAttributes['class'] .= ' ' . $sFontAwesome;
        }

        return sprintf(
            static::$faFormat,
            $this->createAttributesString($aFontAwesomeAttributes)
        );
    }
}


<?php

namespace TwbBundle\View\Helper;

use InvalidArgumentException;
use Zend\Form\View\Helper\AbstractHelper;

class TwbBundleGlyphicon extends AbstractHelper
{
    /**
     * @var string
     */
    protected static $glyphiconFormat = '<span %s></span>';

    /**
     * Invoke helper as functor, proxies to {@link render()}.
     * @param string $sGlyphicon
     * @param array $aGlyphiconAttributes : [optionnal]
     * @return string|TwbBundleGlyphicon
     */
    public function __invoke($sGlyphicon = null, array $aGlyphiconAttributes = null)
    {
        if (!$sGlyphicon) {
            return $this;
        }
        return $this->render($sGlyphicon, $aGlyphiconAttributes);
    }

    /**
     * Retrieve glyphicon markup
     * @param string $sGlyphicon
     * @param  array $aGlyphiconAttributes : [optionnal]
     * @throws InvalidArgumentException
     * @return string
     */
    public function render($sGlyphicon, array $aGlyphiconAttributes = null)
    {
        if (!is_scalar($sGlyphicon)) {
            throw new InvalidArgumentException('Glyphicon expects a scalar value, "' . gettype($sGlyphicon) . '" given');
        }

        if (empty($aGlyphiconAttributes)) {
            $aGlyphiconAttributes = array('class' => 'glyphicon');
        } else {

            if (empty($aGlyphiconAttributes['class'])) {
                $aGlyphiconAttributes['class'] = 'glyphicon';
            } elseif (!preg_match('/(\s|^)glyphicon(\s|$)/', $aGlyphiconAttributes['class'])) {
                $aGlyphiconAttributes['class'] .= ' glyphicon';
            }
        }

        if (strpos('glyphicon-', $sGlyphicon) !== 0) {
            $sGlyphicon = 'glyphicon-' . $sGlyphicon;
        }

        if (!preg_match('/(\s|^)' . preg_quote($sGlyphicon, '/') . '(\s|$)/', $aGlyphiconAttributes['class'])) {
            $aGlyphiconAttributes['class'] .= ' ' . $sGlyphicon;
        }

        return sprintf(
            static::$glyphiconFormat,
            $this->createAttributesString($aGlyphiconAttributes)
        );
    }
}


<?php

namespace TwbBundle\View\Helper;

use InvalidArgumentException;
use Zend\Form\View\Helper\AbstractHelper;

class TwbBundleAlert extends AbstractHelper
{
    /**
     * @var string
     */
    protected static $alertFormat = '<div %s>%s</div>';

    /**
     * @var string
     */
    protected static $dismissButtonFormat = '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>';

    /**
     * Invoke helper as functor, proxies to {@link render()}.
     * @param string $sAlertMessage
     * @param string|array $aAlertAttributes : [optionnal] if string, alert class
     * @param boolean $bDismissable
     * @return string|TwbBundleAlert
     */
    public function __invoke($sAlertMessage = null, $aAlertAttributes = null, $bDismissable = false)
    {
        if ($sAlertMessage === null) {
            return $this;
        }
        if ($sAlertMessage === '' || $sAlertMessage === false) {
            return '';
        }
        return $this->render($sAlertMessage, $aAlertAttributes, $bDismissable);
    }

    /**
     * Retrieve alert markup
     * @param string $sAlertMessage
     * @param  string|array $aAlertAttributes : [optionnal] if string, alert class
     * @param boolean $bDismissable
     * @throws InvalidArgumentException
     * @return string
     */
    public function render($sAlertMessage, $aAlertAttributes = null, $bDismissable = false)
    {
        if (!is_scalar($sAlertMessage)) {
            throw new InvalidArgumentException('Alert message expects a scalar value, "' . gettype($sAlertMessage) . '" given');
        }

        if (empty($aAlertAttributes)) {
            $aAlertAttributes = array('class' => 'alert');
        } elseif (is_string($aAlertAttributes)) {
            $aAlertAttributes = array('class' => $aAlertAttributes);
        } elseif (!is_array($aAlertAttributes)) {
            throw new InvalidArgumentException('Alert attributes expects a string or an array, "' . gettype($aAlertAttributes) . '" given');
        } elseif (empty($aAlertAttributes['class'])) {
            throw new InvalidArgumentException('Alert "class" attribute is empty');
        } elseif (!is_string($aAlertAttributes['class'])) {
            throw new InvalidArgumentException('Alert "class" attribute expects string, "' . gettype($aAlertAttributes) . '" given');
        }

        if (!preg_match('/(\s|^)alert(\s|$)/', $aAlertAttributes['class'])) {
            $aAlertAttributes['class'] .= ' alert';
        }

        if (null !== ($oTranslator = $this->getTranslator())) {
            $sAlertMessage = $oTranslator->translate($sAlertMessage, $this->getTranslatorTextDomain());
        }

        if ($bDismissable) {
            $sAlertMessage = static::$dismissButtonFormat . $sAlertMessage;
            if (!preg_match('/(\s|^)alert-dismissable(\s|$)/', $aAlertAttributes['class'])) {
                $aAlertAttributes['class'] .= ' alert-dismissable';
            }
        }

        return sprintf(
            static::$alertFormat,
            $this->createAttributesString($aAlertAttributes),
            $sAlertMessage
        );
    }
}


<?php

namespace TwbBundle\View\Helper;

use Traversable;
use LogicException;
use Zend\Form\View\Helper\AbstractHelper;
use Zend\Form\ElementInterface;
use Zend\Form\Factory;

class TwbBundleButtonGroup extends AbstractHelper
{
    /**
     * @var string
     */
    protected static $buttonGroupContainerFormat = '
%s
';

    /**
     * @var string
     */
    protected static $buttonGroupJustifiedFormat = '
%s
';

    /**
     * @var TwbBundleFormElement
     */
    protected $formElementHelper;

    /**
     * @param array $aButtons
     * @param array $aButtonGroupOptions
     * @return TwbBundleButtonGroup|string
     */
    public function __invoke(array $aButtons = null, array $aButtonGroupOptions = null)
    {
        return $aButtons ? $this->render($aButtons, $aButtonGroupOptions) : $this;
    }

    /**
     * Render button groups markup
     * @param array $aButtons
     * @param array $aButtonGroupOptions
     * @throws LogicException
     * @return string
     */
    public function render(array $aButtons, array $aButtonGroupOptions = null)
    {
        /*
         * Button group container attributes
         */
        if (empty($aButtonGroupOptions['attributes'])) {
            $aButtonGroupOptions['attributes'] = array('class' => 'btn-group');
        } else {
            if (!is_array($aButtonGroupOptions['attributes'])) {
                throw new LogicException('"attributes" option expects an array, "' . gettype($aButtonGroupOptions['attributes']) . '" given');
            }
            if (empty($aButtonGroupOptions['attributes']['class'])) {
                $aButtonGroupOptions['attributes']['class'] = 'btn-group';
            } elseif (!preg_match('/(\s|^)(?:btn-group|btn-group-vertical)(\s|$)/', $aButtonGroupOptions['attributes']['class'])) {
                $aButtonGroupOptions['attributes']['class'] .= ' btn-group';
            }
        }

        /*
         * Render button group
         */
        return sprintf(
            static::$buttonGroupContainerFormat,
            //Container attributes
            $this->createAttributesString($aButtonGroupOptions['attributes']),
            //Buttons
            $this->renderButtons(
                $aButtons,
                strpos($aButtonGroupOptions['attributes']['class'], 'btn-group-justified') !== false
            )
        );
    }

    /**
     * Render buttons markup
     * @param array $aButtons
     * @return string
     */
    protected function renderButtons(array $aButtons, $bJustified = false)
    {
        $sMarkup = '';
        foreach ($aButtons as $oButton) {
            if (is_array($oButton) ||
                ($oButton instanceof Traversable &&
                !($oButton instanceof ElementInterface))
            ) {
                $oFactory = new Factory();
                $oButton = $oFactory->create($oButton);
            } elseif (!($oButton instanceof ElementInterface)) {
                throw new LogicException(sprintf(
                    'Button expects an instanceof Zend\Form\ElementInterface or an array / Traversable, "%s" given', is_object($oButton) ? get_class($oButton) : gettype($oButton)
                ));
            }

            $sButtonMarkup = $this->getFormElementHelper()->__invoke($oButton);

            $sMarkup .= $bJustified ? sprintf(static::$buttonGroupJustifiedFormat, $sButtonMarkup) : $sButtonMarkup;
        }
        return $sMarkup;
    }

    /**
     * @return TwbBundleFormElement
     */
    public function getFormElementHelper()
    {
        if ($this->formElementHelper instanceof TwbBundleFormElement) {
            return $this->formElementHelper;
        }
        if (method_exists($this->view, 'plugin')) {
            return $this->formElementHelper = $this->view->plugin('form_element');
        }

        return $this->formElementHelper = new TwbBundleFormElement();
    }
}


<?php

namespace TwbBundle\Form\View\Helper;

use Zend\Form\View\Helper\AbstractHelper;
use Zend\Form\FormInterface;

class TwbBundleFormErrors extends AbstractHelper
{

    protected $defaultErrorText = 'There were errors in the form submission';
    protected $messageOpenFormat = '
%s

';
    protected $messageCloseString = '

';
    protected $messageSeparatorString = '

';

    /**
     * Invoke as function
     * @param \Zend\Form\FormInterface $oForm
     * @param string $sMessage
     * @param string $bDismissable
     * @return string|null
     */
    public function __invoke(FormInterface $oForm = null, $sMessage = null, $bDismissable = false)
    {
        if (!$oForm) {
            return $this;
        }

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

        if ($oForm->hasValidated() && !$oForm->isValid()) {

            return $this->render($oForm, $sMessage, $bDismissable);
        }

        return null;
    }

    /**
     * Renders the error messages.
     * @param \Zend\Form\FormInterface $oForm
     * @return string
     */
    public function render(FormInterface $oForm, $sMessage, $bDismissable = false)
    {
        $errorHtml = sprintf($this->messageOpenFormat, $sMessage);

        $sMessagesArray = array();

        foreach ($oForm->getMessages() as $fieldName => $sMessages) {
            foreach ($sMessages as $sMessage) {
                if ($oForm->get($fieldName)->getAttribute('id')) {
                    $sMessagesArray[] = sprintf(
                        '%s',
                        $oForm->get($fieldName)->getAttribute('id'),
                        $oForm->get($fieldName)->getLabel() . ': ' . $sMessage
                    );
                } else {
                    $sMessagesArray[] = $oForm->get($fieldName)->getLabel() . ': ' . $sMessage;
                }
            }
        }

        return $this->dangerAlert(
            $errorHtml .
            implode($this->messageSeparatorString, $sMessagesArray) .
            $this->messageCloseString,
            $bDismissable
        );
    }

    /**
     * Creates and returns a "danger" alert.
     * @param string  $content
     * @param boolean $bDismissable
     * @return string
     */
    public function dangerAlert($content, $bDismissable = false)
    {
        return $this->getView()->alert($content, array('class' => 'alert-danger'), $bDismissable);
    }
}


<?php

namespace TwbBundle\Form\View\Helper;

use Traversable;
use InvalidArgumentException;
use LogicException;
use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormElement;
use Zend\Form\Element\Collection;
use Zend\Form\Factory;
use Zend\I18n\Translator\TranslatorAwareInterface;
use Zend\I18n\Translator\TranslatorInterface;
use Zend\I18n\Translator\Translator;
use TwbBundle\Options\ModuleOptions;
use Zend\Form\Element\Button;

class TwbBundleFormElement extends FormElement implements TranslatorAwareInterface
{
    /**
     * @var string
     */
    protected static $addonFormat = '<%s class="%s">%s</%s>';

    /**
     * @var string
     */
    protected static $inputGroupFormat = '
%s
';

    /**
     * Translator (optional)
     * @var Translator
     */
    protected $translator;

    /**
     * Translator text domain (optional)
     * @var string
     */
    protected $translatorTextDomain = 'default';

    /**
     * Whether translator should be used
     * @var boolean
     */
    protected $translatorEnabled = true;

    /**
     * Hold configurable options
     * @var ModuleOptions
     */
    protected $options;

    /**
     * Instance map to view helper
     *
     * @var array
     */
    protected $classMap = array(
        'Zend\Form\Element\Button' => 'formbutton',
        'Zend\Form\Element\Captcha' => 'formcaptcha',
        'Zend\Form\Element\Csrf' => 'formhidden',
        'Zend\Form\Element\Collection' => 'formcollection',
        'Zend\Form\Element\DateTimeSelect' => 'formdatetimeselect',
        'Zend\Form\Element\DateSelect' => 'formdateselect',
        'Zend\Form\Element\MonthSelect' => 'formmonthselect',
        'TwbBundle\Form\Element\StaticElement' => 'formStatic',
    );

    public function __construct(ModuleOptions $options)
    {
        if (is_array($options->getTypeMap())) {
            $this->typeMap = array_merge($this->typeMap, $options->getTypeMap());
        }
        if (is_array($options->getClassMap())) {
            $this->classMap = array_merge($this->classMap, $options->getClassMap());
        }
        $this->options = $options;
    }

    /**
     * Render an element
     * @param ElementInterface $oElement
     * @return string
     */
    public function render(ElementInterface $oElement)
    {
        // Add form-controll class
        $sElementType = $oElement->getAttribute('type');
        if (!in_array($sElementType, $this->options->getIgnoredViewHelpers()) &&
            !($oElement instanceof Collection)
        ) {
            if ($sElementClass = $oElement->getAttribute('class')) {
                if (!preg_match('/(\s|^)form-control(\s|$)/', $sElementClass)) {
                    $oElement->setAttribute('class', trim($sElementClass . ' form-control'));
                }
            } else {
                $oElement->setAttribute('class', 'form-control');
            }
        }

        $sMarkup = parent::render($oElement);

        // Addon prepend
        if ($aAddOnPrepend = $oElement->getOption('add-on-prepend')) {
            $sMarkup = $this->renderAddOn($aAddOnPrepend) . $sMarkup;
        }

        // Addon append
        if ($aAddOnAppend = $oElement->getOption('add-on-append')) {
            $sMarkup .= $this->renderAddOn($aAddOnAppend);
        }

        if ($aAddOnAppend || $aAddOnPrepend) {
            $sSpecialClass = '';
            // Input size
            if ($sElementClass = $oElement->getAttribute('class')) {
                if (preg_match('/(\s|^)input-lg(\s|$)/', $sElementClass)) {
                    $sSpecialClass .= ' input-group-lg';
                } elseif (preg_match('/(\s|^)input-sm(\s|$)/', $sElementClass)) {
                    $sSpecialClass .= ' input-group-sm';
                }
            }
            return sprintf(
                static::$inputGroupFormat,
                trim($sSpecialClass),
                $sMarkup
            );
        }
        return $sMarkup;
    }

    /**
     * Render addo-on markup
     * @param string $aAddOnOptions
     * @throws InvalidArgumentException
     * @throws LogicException
     * @return string
     */
    protected function renderAddOn($aAddOnOptions)
    {
        if (empty($aAddOnOptions)) {
            throw new InvalidArgumentException('Addon options are empty');
        }
        if ($aAddOnOptions instanceof ElementInterface) {
            $aAddOnOptions = array('element' => $aAddOnOptions);
        } elseif (is_scalar($aAddOnOptions)) {
            $aAddOnOptions = array('text' => $aAddOnOptions);
        } elseif (!is_array($aAddOnOptions)) {
            throw new InvalidArgumentException(sprintf(
                'Addon options expects an array or a scalar value, "%s" given',
                is_object($aAddOnOptions) ? get_class($aAddOnOptions) : gettype($aAddOnOptions)
            ));
        }

        $sMarkup = '';
        $sAddonTagName = 'span';
        $sAddonClass = '';
        if (!empty($aAddOnOptions['text'])) {
            if (!is_scalar($aAddOnOptions['text'])) {
                throw new InvalidArgumentException(sprintf(
                    '"text" option expects a scalar value, "%s" given',
                    is_object($aAddOnOptions['text']) ? get_class($aAddOnOptions['text']) : gettype($aAddOnOptions['text'])
                ));
            } elseif (($oTranslator = $this->getTranslator())) {
                $sMarkup .= $oTranslator->translate($aAddOnOptions['text'], $this->getTranslatorTextDomain());
            } else {
                $sMarkup .= $aAddOnOptions['text'];
            }
            $sAddonClass .= ' input-group-addon';
        } elseif (!empty($aAddOnOptions['element'])) {
            if (is_array($aAddOnOptions['element']) ||
                ($aAddOnOptions['element'] instanceof Traversable &&
                !($aAddOnOptions['element'] instanceof ElementInterface))
            ) {
                $oFactory = new Factory();
                $aAddOnOptions['element'] = $oFactory->create($aAddOnOptions['element']);
            } elseif (!($aAddOnOptions['element'] instanceof ElementInterface)) {
                throw new LogicException(sprintf(
                    '"element" option expects an instanceof Zend\Form\ElementInterface, "%s" given',
                    is_object($aAddOnOptions['element']) ? get_class($aAddOnOptions['element']) : gettype($aAddOnOptions['element'])
                ));
            }

            $aAddOnOptions['element']->setOptions(array_merge(
                $aAddOnOptions['element']->getOptions(),
                array('disable-twb' => true)
            ));

            $sMarkup .= $this->render($aAddOnOptions['element']);

            //Element is a button, so add-on container must be a "div"
            if ($aAddOnOptions['element'] instanceof Button) {
                $sAddonClass .= ' input-group-btn';
                $sAddonTagName = 'div';
            } else {
                $sAddonClass .= ' input-group-addon';
            }
        }

        return sprintf(static::$addonFormat, $sAddonTagName, trim($sAddonClass), $sMarkup, $sAddonTagName);
    }

    /**
     * Sets translator to use in helper
     * @see TranslatorAwareInterface::setTranslator()
     * @param TranslatorInterface $oTranslator : [optional] translator. Default is null, which sets no translator.
     * @param string $sTextDomain : [optional] text domain Default is null, which skips setTranslatorTextDomain
     * @return TwbBundleFormElement
     */
    public function setTranslator(TranslatorInterface $oTranslator = null, $sTextDomain = null)
    {
        $this->translator = $oTranslator;
        if (null !== $sTextDomain) {
            $this->setTranslatorTextDomain($sTextDomain);
        }
        return $this;
    }

    /**
     * Returns translator used in helper
     * @see TranslatorAwareInterface::getTranslator()
     * @return null|TranslatorInterface
     */
    public function getTranslator()
    {
        return $this->isTranslatorEnabled() ? $this->translator : null;
    }

    /**
     * Checks if the helper has a translator
     * @see TranslatorAwareInterface::hasTranslator()
     * @return boolean
     */
    public function hasTranslator()
    {
        return !!$this->getTranslator();
    }

    /**
     * Sets whether translator is enabled and should be used
     * @see TranslatorAwareInterface::setTranslatorEnabled()
     * @param boolean $bEnabled
     * @return TwbBundleFormElement
     */
    public function setTranslatorEnabled($bEnabled = true)
    {
        $this->translatorEnabled = !!$bEnabled;
        return $this;
    }

    /**
     * Returns whether translator is enabled and should be used
     * @see TranslatorAwareInterface::isTranslatorEnabled()
     * @return boolean
     */
    public function isTranslatorEnabled()
    {
        return $this->translatorEnabled;
    }

    /**
     * Set translation text domain
     * @see TranslatorAwareInterface::setTranslatorTextDomain()
     * @param string $sTextDomain
     * @return TwbBundleFormElement
     */
    public function setTranslatorTextDomain($sTextDomain = 'default')
    {
        $this->translatorTextDomain = $sTextDomain;
        return $this;
    }

    /**
     * Return the translation text domain
     * @see TranslatorAwareInterface::getTranslatorTextDomain()
     * @return string
     */
    public function getTranslatorTextDomain()
    {
        return $this->translatorTextDomain;
    }
}


<?php

namespace TwbBundle\View\Helper;

use Zend\Form\View\Helper\AbstractHelper;
use LogicException;
use InvalidArgumentException;

class TwbBundleDropDown extends AbstractHelper
{

    /**
     * @var string
     */
    const TYPE_ITEM_HEADER = 'header';

    /**
     * @var string
     */
    const TYPE_ITEM_DIVIDER = '---';

    /**
     * @var string
     */
    const TYPE_ITEM_LINK = 'link';

    /**
     * @var string
     */
    protected static $dropdownContainerFormat = '
%s
';

    /**
     * @var string
     */
    protected static $dropdownToggleFormat = '%s ';

    /**
     * @var string
     */
    protected static $dropdownListFormat = '
%s
';

    /**
     * @var string
     */
    protected static $dropdownItemContainerFormat = '
%s
';

    /**
     * @var string
     */
    protected static $dropdownItemFormats = array(
        self::TYPE_ITEM_LINK => '%s',
    );

    /**
     * @param array $aDropdownOptions
     * @return TwbBundleDropDown|string
     */
    public function __invoke(array $aDropdownOptions = null)
    {
        return $aDropdownOptions ? $this->render($aDropdownOptions) : $this;
    }

    /**
     * Render dropdown markup
     * @param array $aDropdownOptions
     * @throws LogicException
     * @return string
     */
    public function render(array $aDropdownOptions)
    {
        // Dropdown container attributes
        if (empty($aDropdownOptions['attributes'])) {

            $aDropdownOptions['attributes'] = array('class' => 'dropdown');
        } else {
            if (!is_array($aDropdownOptions['attributes'])) {
                throw new LogicException('"attributes" option expects an array, "' . gettype($aDropdownOptions['attributes']) . '" given');
            }

            if (empty($aDropdownOptions['attributes']['class'])) {

                $aDropdownOptions['attributes']['class'] = 'dropdown';
            } elseif (!preg_match('/(\s|^)dropdown(\s|$)/', $aDropdownOptions['attributes']['class'])) {

                $aDropdownOptions['attributes']['class'] .= ' dropdown';
            }
        }

        // Render dropdown
        return sprintf(
                static::$dropdownContainerFormat, $this->createAttributesString($aDropdownOptions['attributes']), //Container attributes
                $this->renderToggle($aDropdownOptions) . //Toggle
                $this->renderListItems($aDropdownOptions) //List items
        );
    }

    /**
     * Render dropdown toggle markup
     * @param array $aDropdownOptions
     * @throws LogicException
     * @return string
     */
    public function renderToggle(array $aDropdownOptions)
    {
        // Dropdown toggle
        if (empty($aDropdownOptions['label'])) {
            $aDropdownOptions['label'] = '';
        } elseif (!is_scalar($aDropdownOptions['label'])) {
            throw new InvalidArgumentException('"label" option expects a scalar value, "' . gettype($aDropdownOptions['label']) . '" given');
        } elseif (($oTranslator = $this->getTranslator())) {
            $aDropdownOptions['label'] = $oTranslator->translate($aDropdownOptions['label'], $this->getTranslatorTextDomain());
        }

        // Dropdown toggle attributes (class)
        if (empty($aDropdownOptions['toggle_attributes'])) {
            $aDropdownOptions['toggle_attributes'] = array('class' => 'sr-only dropdown-toggle');
        } else {
            if (!is_array($aDropdownOptions['toggle_attributes'])) {
                throw new InvalidArgumentException('"toggle_attributes" option expects an array, "' . gettype($aDropdownOptions['toggle_attributes']) . '" given');
            }

            if (empty($aDropdownOptions['toggle_attributes']['class'])) {
                $aDropdownOptions['toggle_attributes']['class'] = 'sr-only dropdown-toggle';
            } else {
                if (!preg_match('/(\s|^)sr-only(\s|$)/', $aDropdownOptions['toggle_attributes']['class'])) {
                    $aDropdownOptions['toggle_attributes']['class'] .= ' sr-only';
                }
                if (!preg_match('/(\s|^)dropdown-toggle(\s|$)/', $aDropdownOptions['toggle_attributes']['class'])) {
                    $aDropdownOptions['toggle_attributes']['class'] .= ' dropdown-toggle';
                }
            }
        }

        // Dropdown toggle attributes (data-toggle)
        if (empty($aDropdownOptions['toggle_attributes']['data-toggle'])) {
            $aDropdownOptions['toggle_attributes']['data-toggle'] = 'dropdown';
        }

        // Dropdown toggle attributes (role)
        if (empty($aDropdownOptions['toggle_attributes']['role'])) {
            $aDropdownOptions['toggle_attributes']['role'] = 'button';
        }

        // Dropdown toggle attributes (href)
        if (empty($aDropdownOptions['toggle_attributes']['href'])) {
            $aDropdownOptions['toggle_attributes']['href'] = '#';
        }

        // Dropdown toggle attributes (id)
        if (!empty($aDropdownOptions['name'])) {
            $aDropdownOptions['toggle_attributes']['id'] = $aDropdownOptions['name'];
        }

        $aValidTagAttributes = $this->validTagAttributes;
        $this->validTagAttributes = array('href' => true);
        $sAttributeString = $this->createAttributesString($aDropdownOptions['toggle_attributes']);
        $this->validTagAttributes = $aValidTagAttributes;

        return sprintf(
                static::$dropdownToggleFormat, $sAttributeString, // Toggle attributes
                $this->getEscapeHtmlHelper()->__invoke($aDropdownOptions['label']) // Toggle label
        );
    }

    /**
     * Render dropdown list items markup
     * @param array $aDropdownOptions
     * @throws LogicException
     * @return string
     */
    public function renderListItems(array $aDropdownOptions)
    {
        if (!isset($aDropdownOptions['items'])) {
            throw new LogicException(__METHOD__ . ' expects "items" option');
        }

        if (!is_array($aDropdownOptions['items'])) {
            throw new LogicException('"items" option expects an array, "' . gettype($aDropdownOptions['items']) . '" given');
        }

        // Dropdown list attributes (class)
        if (empty($aDropdownOptions['list_attributes'])) {
            $aDropdownOptions['list_attributes'] = array('class' => 'dropdown-menu');
        } else {
            if (!is_array($aDropdownOptions['list_attributes'])) {
                throw new \LogicException('"list_attributes" option expects an array, "' . gettype($aDropdownOptions['list_attributes']) . '" given');
            }

            if (empty($aDropdownOptions['list_attributes']['class'])) {
                $aDropdownOptions['list_attributes']['class'] = 'dropdown-menu';
            } elseif (!preg_match('/(\s|^)dropdown-menu(\s|$)/', $aDropdownOptions['list_attributes']['class'])) {
                $aDropdownOptions['list_attributes']['class'] .= ' dropdown-menu';
            }
        }

        // Dropdown list attributes (role)
        if (empty($aDropdownOptions['list_attributes']['role'])) {
            $aDropdownOptions['list_attributes']['role'] = 'menu';
        }

        // Dropdown list attributes (name)
        if (!empty($aDropdownOptions['name'])) {
            $aDropdownOptions['list_attributes']['aria-labelledby'] = $aDropdownOptions['name'];
        }

        // Dropdown list attributes (items)
        $sItems = '';
        foreach ($aDropdownOptions['items'] as $sKey => $aItemOptions) {
            if (!is_array($aItemOptions)) {
                if (!is_scalar($aItemOptions)) {
                    throw new \LogicException('item option expects an array or a scalar value, "' . gettype($aItemOptions) . '" given');
                }
                $aItemOptions = $aItemOptions === self::TYPE_ITEM_DIVIDER
                        // Divider
                        ? array('type' => self::TYPE_ITEM_DIVIDER)
                        // Link
                        : array(
                    'label' => $aItemOptions,
                    'type' => self::TYPE_ITEM_LINK,
                    'item_attributes' => array('href' => is_string($sKey) ? $sKey : null)
                );
            } else {
                if (!isset($aItemOptions['label'])) {
                    $aItemOptions['label'] = is_string($sKey) ? $sKey : null;
                }

                if (!isset($aItemOptions['type'])) {
                    $aItemOptions['type'] = self::TYPE_ITEM_LINK;
                }
            }
            $sItems .= $this->renderItem($aItemOptions) . "\n";
        }

        return sprintf(
                static::$dropdownListFormat, $this->createAttributesString($aDropdownOptions['list_attributes']), // List attributes
                $sItems // Items
        );
    }

    /**
     * Render dropdown list item markup
     * @param array $aItemOptions
     * @throws LogicException
     * @return string
     */
    protected function renderItem($aItemOptions)
    {
        if (empty($aItemOptions['type'])) {
            throw new \LogicException(__METHOD__ . ' expects "type" option');
        }

        // Item container attributes
        if (empty($aItemOptions['attributes'])) {
            $aItemOptions['attributes'] = array();
        } elseif (!is_array($aItemOptions['attributes'])) {
            throw new \LogicException('"attributes" option expects an array, "' . gettype($aItemOptions['attributes']) . '" given');
        }

        // Item container attributes (role)
        if (empty($aItemOptions['attributes']['role'])) {
            $aItemOptions['attributes']['role'] = 'presentation';
        }

        $sItemContent = '';
        switch ($aItemOptions['type']) {
            case self::TYPE_ITEM_HEADER:
                // Define item container "header" class
                if (empty($aItemOptions['attributes']['class'])) {
                    $aItemOptions['attributes']['class'] = 'dropdown-header';
                } elseif (!preg_match('/(\s|^)dropdown-header(\s|$)/', $aItemOptions['attributes']['class'])) {
                    $aItemOptions['attributes']['class'] .= ' dropdown-header';
                }

                // Header label
                if (empty($aItemOptions['label'])) {
                    throw new \LogicException('"' . $aItemOptions['type'] . '" item expects "label" option');
                }

                if (!is_scalar($aItemOptions['label'])) {
                    throw new \LogicException('"label" option expect scalar value, "' . gettype($aItemOptions['label']) . '" given');
                } elseif (($oTranslator = $this->getTranslator())) {
                    $aItemOptions['label'] = $oTranslator->translate($aItemOptions['label'], $this->getTranslatorTextDomain());
                }

                $sItemContent = $this->getEscapeHtmlHelper()->__invoke($aItemOptions['label']);
                break;
            case self::TYPE_ITEM_DIVIDER:
                // Define item container "divider" class
                if (empty($aItemOptions['attributes']['class'])) {
                    $aItemOptions['attributes']['class'] = 'divider';
                } elseif (!preg_match('/(\s|^)divider(\s|$)/', $aItemOptions['attributes']['class'])) {
                    $aItemOptions['attributes']['class'] .= ' divider';
                }

                $sItemContent = '';
                break;

            case self::TYPE_ITEM_LINK:
                if (empty($aItemOptions['label'])) {
                    throw new \LogicException('"' . $aItemOptions['type'] . '" item expects "label" option');
                }
                if (!is_scalar($aItemOptions['label'])) {
                    throw new \LogicException('"label" option expect scalar value, "' . gettype($aItemOptions['label']) . '" given');
                } elseif (($oTranslator = $this->getTranslator())) {
                    $aItemOptions['label'] = $oTranslator->translate($aItemOptions['label'], $this->getTranslatorTextDomain());
                }

                // Item attributes (Role)
                if (empty($aItemOptions['item_attributes']['role'])) {
                    $aItemOptions['item_attributes']['role'] = 'menuitem';
                }

                // Item attributes (Tab index)
                if (!isset($aItemOptions['item_attributes']['tabindex'])) {
                    $aItemOptions['item_attributes']['tabindex'] = '-1';
                }

                // Item attributes (Href)
                if (!isset($aItemOptions['item_attributes']['href'])) {
                    $aItemOptions['item_attributes']['href'] = '#';
                }

                $aValidTagAttributes = $this->validTagAttributes;
                $this->validTagAttributes = array('href' => true);
                $sAttributeString = $this->createAttributesString($aItemOptions['item_attributes']);
                $this->validTagAttributes = $aValidTagAttributes;

                $sItemContent = sprintf(static::$dropdownItemFormats[self::TYPE_ITEM_LINK], $sAttributeString, $this->getEscapeHtmlHelper()->__invoke($aItemOptions['label']));
                break;
        }

        return sprintf(static::$dropdownItemContainerFormat, $this->createAttributesString($aItemOptions['attributes']), $sItemContent);
    }
}


<?php
declare(strict_types = 1);

namespace Middlewares;

use Middlewares\Utils\CallableHandler;
use Middlewares\Utils\RequestHandlerContainer;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;

class RequestHandler implements MiddlewareInterface
{
    /**
     * @var ContainerInterface Used to resolve the handlers
     */
    private $container;

    /**
     * @var bool
     */
    private $continueOnEmpty = false;

    /**
     * @var string Attribute name for handler reference
     */
    private $handlerAttribute = 'request-handler';

    /**
     * Set the resolver instance.
     */
    public function __construct(ContainerInterface $container = null)
    {
        $this->container = $container ?: new RequestHandlerContainer();
    }

    /**
     * Set the attribute name to store handler reference.
     */
    public function handlerAttribute(string $handlerAttribute): self
    {
        $this->handlerAttribute = $handlerAttribute;

        return $this;
    }

    /**
     * Configure whether continue with the next handler if custom requestHandler is empty.
     */
    public function continueOnEmpty(bool $continueOnEmpty = true): self
    {
        $this->continueOnEmpty = $continueOnEmpty;

        return $this;
    }

    /**
     * Process a server request and return a response.
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $requestHandler = $request->getAttribute($this->handlerAttribute);

        if (empty($requestHandler)) {
            if ($this->continueOnEmpty) {
                return $handler->handle($request);
            }

            throw new RuntimeException('Empty request handler');
        }

        if (is_string($requestHandler)) {
            $requestHandler = $this->container->get($requestHandler);
        }

        if (is_array($requestHandler) && count($requestHandler) === 2 && is_string($requestHandler[0])) {
            $requestHandler[0] = $this->container->get($requestHandler[0]);
        }

        if ($requestHandler instanceof MiddlewareInterface) {
            return $requestHandler->process($request, $handler);
        }

        if ($requestHandler instanceof RequestHandlerInterface) {
            return $requestHandler->handle($request);
        }

        if (is_callable($requestHandler)) {
            return (new CallableHandler($requestHandler))->process($request, $handler);
        }

        throw new RuntimeException(sprintf('Invalid request handler: %s', gettype($requestHandler)));
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: robregonm
 * Date: 18/05/15
 * Time: 07:51 PM
 */

namespace auth\models;

use yii\base\Model;
use Yii;

class SignupForm extends Model
{
    public $username;
    public $email;
    public $password;

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            ['username', 'filter', 'filter' => 'trim'],
            ['username', 'required'],
            [
                'username',
                'unique',
                'targetClass' => '\auth\models\User',
                'message' => Yii::t('auth.user', 'This username has already been taken.')
            ],
            ['username', 'string', 'min' => 2, 'max' => 255],
            ['email', 'filter', 'filter' => 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            [
                'email',
                'unique',
                'targetClass' => '\auth\models\User',
                'message' => Yii::t('auth.user', 'This email address has already been taken.')
            ],
            ['password', 'required'],
            ['password', 'string', 'min' => 6],
        ];
    }

    public function beforeValidate()
    {
        if (parent::beforeValidate()) {
            if (Yii::$app->getModule('auth')->signupWithEmailOnly) {
                $this->username = $this->email;
            }

            return true;
        }

        return false;
    }

    /**
     * Signs user up.
     *
     * @return User|null the saved model or null if saving fails
     */
    public function signup()
    {
        if ($this->validate()) {
            $user = new User();
            $user->username = $this->username;
            $user->email = $this->email;
            $user->setPassword($this->password);
            $user->generateAuthKey();
            if ($user->save()) {
                return $user;
            }
        }

        return null;
    }
}


<?php
/**
 * Auth AccessControl class file.
 * Behavior that automatically checks if the user has access to the current controller action.
 *
 * @author Ricardo Obregón <ricardo@obregon.co>
 * @copyright Copyright © Ricardo Obregón 2012-
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @package auth.components
 */

namespace auth\components;

use Yii;
use yii\base\Action;
use yii\base\ActionFilter;

/**
 * Auth AccessControl provides RBAC access control.
 *
 * Auth AccessControl is an action filter. It will check the item names to find
 * the first match that will dictate whether to allow or deny the access to the requested controller
 * action. If no matches, the access will be denied.
 *
 * To use Auth AccessControl, declare it in the `behaviors()` method of your controller class.
 * For example, the following declaration will enable rbac filtering in your controller.
 *
 * ~~~
 * public function behaviors()
 * {
 *     return [
 *         'access' => [
 *             'class' => \auth\AccessControl::className(),
 *         ],
 *     ];
 * }
 * ~~~
 *
 * @author Ricardo Obregón <robregonm@gmail.com>
 * @since 2.0
 */
class AccessControl extends ActionFilter
{
	/**
	 * @var array name-value pairs that would be passed to business rules associated
	 * with the tasks and roles assigned to the user.
	 */
	public $params = [];

	/**
	 * @var callback a callback that will be called if the access should be denied
	 * to the current user. If not set, [[denyAccess()]] will be called.
	 *
	 * The signature of the callback should be as follows:
	 *
	 * ~~~
	 * function ($item, $action)
	 * ~~~
	 *
	 * where `$item` is this item name, and `$action` is the current [[Action|action]] object.
	 */
	public $denyCallback;

	private $separator = '.';

	private function getItemName($component)
	{
		return strtr($component->getUniqueId(), '/', $this->separator);
	}

	/**
	 * This method is invoked right before an action is to be executed (after all possible filters.)
	 * You may override this method to do last-minute preparation for the action.
	 *
	 * @param Action $action the action to be executed.
	 * @return boolean whether the action should continue to be executed.
	 */
	public function beforeAction($action)
	{
		$user = Yii::$app->getUser();

		$controller = $action->controller;

		if ($controller->module !== null) {
			if ($user->checkAccess($this->getItemName($controller->module) . $this->separator . '*', $this->params)) {
				return true;
			}
		}

		if ($user->checkAccess($this->getItemName($controller) . $this->separator . '*', $this->params)) {
			return true;
		}

		if ($user->checkAccess($itemName = $this->getItemName($action), $this->params)) {
			return true;
		}

		if (isset($this->denyCallback)) {
			call_user_func($this->denyCallback, $itemName, $action);
		} else {
			$this->denyAccess($user);
		}
		return false;
	}

	/**
	 * Denies the access of the user.
	 * The default implementation will redirect the user to the login page if he is a guest;
	 * if the user is already logged, a 403 HTTP exception will be thrown.
	 *
	 * @param User $user the current user
	 * @throws HttpException if the user is already logged in.
	 */
	protected function denyAccess($user)
	{
		if ($user->getIsGuest()) {
			$user->loginRequired();
		} else {
			throw new \yii\web\ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
		}
	}

}


<?php

namespace auth\controllers;

use auth\models\User;
use auth\models\UserSearch;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * UserController implements the CRUD actions for User model.
 */
class UserController extends Controller
{
	public function behaviors()
	{
		return [
			'verbs' => [
				'class' => VerbFilter::className(),
				'actions' => [
					'delete' => ['post'],
				],
			],
			'access' => [
				'class' => AccessControl::className(),
				'rules' => [
					[
						'allow' => true,
						'matchCallback' => function () {
								return \Yii::$app->user->getIsSuperAdmin();
							},
					],
				],
			],
		];
	}

	public function init()
	{
		$layout = $this->module->layoutLogged;
		if (!empty($layout)) {
			$this->layout = $layout;
		}
	}

	/**
	 * Lists all User models.
	 *
	 * @return mixed
	 */
	public function actionIndex()
	{
		$searchModel = new UserSearch;
		$dataProvider = $searchModel->search($_GET);

		return $this->render('index', [
			'dataProvider' => $dataProvider,
			'searchModel' => $searchModel,
		]);
	}

	/**
	 * Displays a single User model.
	 *
	 * @param integer $id
	 * @return mixed
	 */
	public function actionView($id)
	{
		return $this->render('view', [
			'model' => $this->findModel($id),
		]);
	}

	/**
	 * Creates a new User model.
	 * If creation is successful, the browser will be redirected to the 'view' page.
	 *
	 * @return mixed
	 */
	public function actionCreate()
	{
		$model = new User;

		if ($model->load($_POST) && $model->save()) {
			return $this->redirect(['view', 'id' => $model->id]);
		} else {
			return $this->render('create', [
				'model' => $model,
			]);
		}
	}

	/**
	 * Updates an existing User model.
	 * If update is successful, the browser will be redirected to the 'view' page.
	 *
	 * @param integer $id
	 * @return mixed
	 */
	public function actionUpdate($id)
	{
		$model = $this->findModel($id);
		$model->setScenario('profile');

        if (isset($_POST['User']['password'])) {
            $model->setPassword($_POST['User']['password']);
        }

		if ($model->load($_POST) && $model->save()) {
			return $this->redirect(['view', 'id' => $model->id]);
		} else {
			return $this->render('update', [
				'model' => $model,
			]);
		}
	}

	/**
	 * Deletes an existing User model.
	 * If deletion is successful, the browser will be redirected to the 'index' page.
	 *
	 * @param integer $id
	 * @return mixed
	 */
	public function actionDelete($id)
	{
		$this->findModel($id)->delete();
		return $this->redirect(['index']);
	}

	/**
	 * Finds the User model based on its primary key value.
	 * If the model is not found, a 404 HTTP exception will be thrown.
	 *
	 * @param integer $id
	 * @return User the loaded model
	 * @throws HttpException if the model cannot be found
	 */
	protected function findModel($id)
	{
		if (($model = User::findOne($id)) !== null) {
			return $model;
		} else {
			throw new NotFoundHttpException('The requested page does not exist.');
		}
	}
}


<?php

namespace auth\models;

use Yii;
use yii\base\Model;
use auth\models\User;

/**
 * LoginForm is the model behind the login form.
 */
class LoginForm extends Model
{
	public $username;
	public $password;
	public $rememberMe = true;
	public $verifyCode;

	private $_user = false;

	/**
	 * @return array the validation rules.
	 */
	public function rules()
	{
		return [
			// username and password are both required
			[['username', 'password'], 'required'],
			// password is validated by validatePassword()
			['password', 'validatePassword'],
			// rememberMe must be a boolean value
			['rememberMe', 'boolean'],
			['verifyCode', 'captcha', 'captchaAction' => 'auth/default/captcha', 'on' => 'withCaptcha'],
		];
	}

	/**
	 * @inheritdoc
	 */
	public function attributeLabels()
	{
		return [
			'username' => Yii::t('auth.user', 'Username or Email'),
			'password' => Yii::t('auth.user', 'Password'),
			'rememberMe' => Yii::t('auth.user', 'Remember Me'),
			'verifyCode' => Yii::t('auth.user', 'Verify Code'),
		];
	}

	/**
	 * Validates the password.
	 * This method serves as the inline validation for password.
	 */
	public function validatePassword()
	{
		$user = $this->getUser();
		if (!$user || !$user->validatePassword($this->password)) {
			$this->addError('password', Yii::t('auth.user', 'Incorrect username or password.'));
		}
	}

	/**
	 * Logs in a user using the provided username and password.
	 *
	 * @return boolean whether the user is logged in successfully
	 */
	public function login()
	{
		if ($this->validate()) {
			return $this->getUser()->login($this->rememberMe ? Yii::$app->getModule('auth')->rememberMeTime : 0);
		} else {
			return false;
		}
	}

	/**
	 * Finds user by [[username]]
	 *
	 * @return User|null
	 */
	private function getUser()
	{
		if ($this->_user === false) {
			$this->_user = User::findByUsername($this->username);
		}
		return $this->_user;
	}
}


<?php

namespace auth\models;

use Yii;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\db\Expression;
use yii\web\IdentityInterface;

/**
 * This is the model class for table "User".
 *
 * @property integer $id
 * @property string $username
 * @property string $email
 * @property string $password_hash
 * @property string $password_reset_token
 * @property string $auth_key
 * @property integer $status
 * @property string $last_visit_time
 * @property string $create_time
 * @property string $update_time
 * @property string $delete_time
 *
 * @property ProfileFieldValue $profileFieldValue
 */
class User extends ActiveRecord implements IdentityInterface
{
	const STATUS_DELETED = 0;
	const STATUS_INACTIVE = 1;
	const STATUS_ACTIVE = 2;
	const STATUS_SUSPENDED = 3;

	private $_isSuperAdmin = null;

	private $statuses = [
		self::STATUS_DELETED => 'Deleted',
		self::STATUS_INACTIVE => 'Inactive',
		self::STATUS_ACTIVE => 'Active',
		self::STATUS_SUSPENDED => 'Suspended',
	];

	public function behaviors()
	{
		return [
			'timestamp' => [
				'class' => 'yii\behaviors\TimestampBehavior',
				'attributes' => [
					self::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
					self::EVENT_BEFORE_DELETE => 'delete_time',
				],
				'value' => function () {
					return new Expression('CURRENT_TIMESTAMP');
				}
			],
		];
	}

	public function getStatus($status = null)
	{
		if ($status === null) {
			return Yii::t('auth.user', $this->statuses[$this->status]);
		}
		return Yii::t('auth.user', $this->statuses[$status]);
	}

	/**
	 * Finds an identity by the given ID.
	 *
	 * @param string|integer $id the ID to be looked for
	 * @return IdentityInterface|null the identity object that matches the given ID.
	 */
	public static function findIdentity($id)
	{
		return static::findOne($id);
	}

	/**
	 * Finds user by username
	 *
	 * @param string $username
	 * @return null|User
	 */
	public static function findByUsername($username)
	{
		return static::find()
					 ->andWhere(['and', ['or', ['username' => $username], ['email' => $username]], ['status' => static::STATUS_ACTIVE]])
					 ->one();
	}

	/**
	 * @inheritdoc
	 */
	public static function findIdentityByAccessToken($token, $type = null)
	{
		throw new \yii\base\NotSupportedException('"findIdentityByAccessToken" is not implemented.');
	}

	/**
	 * Finds user by password reset token
	 *
	 * @param string $token password reset token
	 * @return static|null
	 */
	public static function findByPasswordResetToken($token)
	{
		$expire = Yii::$app->getModule('auth')->passwordResetTokenExpire;
		$parts = explode('_', $token);
		$timestamp = (int)end($parts);
		if ($timestamp + $expire < time()) {
			// token expired
			return null;
		}

		return static::findOne([
			'password_reset_token' => $token,
			'status' => self::STATUS_ACTIVE,
		]);
	}

	/**
	 * @return int|string current user ID
	 */
	public function getId()
	{
		return $this->id;
	}

	/**
	 * @return string current user auth key
	 */
	public function getAuthKey()
	{
		return $this->auth_key;
	}

	/**
	 * @param string $authKey
	 * @return boolean if auth key is valid for current user
	 */
	public function validateAuthKey($authKey)
	{
		return $this->auth_key === $authKey;
	}

    /**
     * Validates password
     *
     * @param string $password password to validate
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    public function getPassword()
    {
        return $this->password_hash;
    }
    /**
     * Generates password hash from password and sets it to the model
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        if (!empty($password) and $password != $this->password_hash) {
            $this->password_hash = Yii::$app->security->generatePasswordHash($password);
        }
    }

    /**
     * Generates "remember me" authentication key
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

	/**
	 * @inheritdoc
	 */
	public static function tableName()
	{
		return Yii::$app->getModule('auth')->tableMap['User'];
	}

	/**
	 * @inheritdoc
	 */
	public function rules()
	{
		return [
			['status', 'default', 'value' => self::STATUS_ACTIVE],
            [
                'status',
                'in',
                'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED, self::STATUS_INACTIVE, self::STATUS_SUSPENDED]
            ],
			['username', 'filter', 'filter' => 'trim'],
			['username', 'required'],
			['username', 'unique', 'message' => Yii::t('auth.user', 'This username has already been taken.')],
			['username', 'string', 'min' => 2, 'max' => 255],

			['email', 'filter', 'filter' => 'trim'],
			['email', 'required'],
			['email', 'email'],
			['email', 'unique', 'message' => Yii::t('auth.user', 'This email address has already been taken.')],
			['email', 'exist', 'message' => Yii::t('auth.user', 'There is no user with such email.'), 'on' => 'requestPasswordResetToken'],
		];
	}

	public function scenarios()
	{
		return [
			'profile' => ['username', 'email', 'password_hash', 'password'],
			'resetPassword' => ['password_hash'],
			'requestPasswordResetToken' => ['email'],
			'login' => ['last_visit_time'],
		] + parent::scenarios();
	}

	/**
	 * @inheritdoc
	 */
	public function attributeLabels()
	{
		return [
			'id' => 'ID',
			'username' => Yii::t('auth.user', 'Username'),
			'email' => Yii::t('auth.user', 'Email'),
			'password_hash' => Yii::t('auth.user', 'Password Hash'),
			'password_reset_token' => Yii::t('auth.user', 'Password Reset Token'),
			'auth_key' => Yii::t('auth.user', 'Auth Key'),
			'status' => Yii::t('auth.user', 'Status'),
			'last_visit_time' => Yii::t('auth.user', 'Last Visit Time'),
			'create_time' => Yii::t('auth.user', 'Create Time'),
			'update_time' => Yii::t('auth.user', 'Update Time'),
			'delete_time' => Yii::t('auth.user', 'Delete Time'),
		];
	}

	/**
	 * @return \yii\db\ActiveRelation
	 */
	public function getProfileFieldValue()
	{
		return $this->hasOne(ProfileFieldValue::className(), ['id' => 'user_id']);
	}

	public function beforeValidate()
	{
		if (parent::beforeValidate()) {

			return true;
		}

		return false;
	}

	public function beforeSave($insert)
	{
		if (parent::beforeSave($insert)) {
			if ($this->isNewRecord) {
				$this->auth_key = Yii::$app->getSecurity()->generateRandomString();
			}
			if ($this->getScenario() !== \yii\web\User::EVENT_AFTER_LOGIN) {
				$this->setAttribute('update_time', new Expression('CURRENT_TIMESTAMP'));
			}

			return true;
		}
		return false;
	}

	public function delete()
	{
		$db = static::getDb();
		$transaction = $this->isTransactional(self::OP_DELETE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
		try {
			$result = false;
			if ($this->beforeDelete()) {
				$this->setAttribute('status', static::STATUS_DELETED);
				$this->save(false);
			}
			if ($transaction !== null) {
				if ($result === false) {
					$transaction->rollback();
				} else {
					$transaction->commit();
				}
			}
		} catch (\Exception $e) {
			if ($transaction !== null) {
				$transaction->rollback();
			}
			throw $e;
		}
		return $result;
	}

	/**
	 * Returns whether the logged in user is an administrator.
	 *
	 * @return boolean the result.
	 */
	public function getIsSuperAdmin()
	{
		if ($this->_isSuperAdmin !== null) {
			return $this->_isSuperAdmin;
		}

		$this->_isSuperAdmin = in_array($this->username, Yii::$app->getModule('auth')->superAdmins);
		return $this->_isSuperAdmin;
	}

	public function login($duration = 0)
	{
		return Yii::$app->user->login($this, $duration);
	}

	/**
	 * Generates new password reset token
	 */
	public function generatePasswordResetToken()
	{
		$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
	}

	/**
	 * Removes password reset token
	 */
	public function removePasswordResetToken()
	{
		$this->password_reset_token = null;
	}
}


<?php
namespace auth\models;

use Yii;
use yii\base\Model;

/**
 * Password reset request form
 */
class PasswordResetRequestForm extends Model
{
	public $email;

	/**
	 * @inheritdoc
	 */
	public function rules()
	{
		return [
			['email', 'filter', 'filter' => 'trim'],
			['email', 'required'],
			['email', 'email'],
			['email', 'exist',
				'targetClass' => '\auth\models\User',
				'filter' => ['status' => User::STATUS_ACTIVE],
				'message' => Yii::t('auth.reset-password', 'There is no user with such email.')
			],
		];
	}

	/**
	 * Sends an email with a link, for resetting the password.
	 *
	 * @return boolean whether the email was send
	 */
	public function sendEmail()
	{
		/* @var $user User */
		$user = User::findOne([
			'status' => User::STATUS_ACTIVE,
			'email' => $this->email,
		]);

		if ($user) {
			$user->generatePasswordResetToken();
			if ($user->save()) {
				return \Yii::$app->mailer->compose('@auth/views/mail/passwordResetToken', ['user' => $user])
										 ->setFrom([\Yii::$app->getModule('auth')->supportEmail => \Yii::$app->name])
										 ->setTo($this->email)
										 ->setSubject(Yii::t('auth.reset-password', 'Password reset for {name}', ['name' => \Yii::$app->name]))
										 ->send();
			}
		}

		return false;
	}

	/**
	 * @inheritdoc
	 */
	public function attributeLabels()
	{
		return [
			'email' => Yii::t('auth.user', 'Email')
		];
	}
}


<?php
/**
 *
 * @author Ricardo Obregón <ricardo@obregon.co>
 * @created 28/11/13 11:27 AM
 */

namespace auth\controllers;

use Yii;
use yii\web\Controller;
use yii\helpers\Security;
use auth\models\User;
use yii\web\NotFoundHttpException;

class ProfileController extends Controller
{
	/**
	 * @var string the ID of the action that is used when the action ID is not specified
	 * in the request. Defaults to 'index'.
	 */
	public $defaultAction = 'view';

	public function init()
	{
		$layout = $this->module->layoutLogged;
		if (!empty($layout)) {
			$this->layout = $layout;
		}
	}

	/**
	 * @var \auth\Module
	 */
	public $module;

	public function behaviors()
	{
		return [
			'access' => [
				'class' => \yii\filters\AccessControl::className(),
				'rules' => [
					[
						'allow' => true,
						'roles' => ['@'],
					],
				],
			],
		];
	}

	/**
	 * Displays current User model.
	 *
	 * @return mixed
	 */
	public function actionView()
	{
		return $this->render('view', [
			'model' => $this->findModel(),
		]);
	}

	/**
	 * Updates the current User model.
	 * If update is successful, the browser will be redirected to the 'view' page.
	 *
	 * @param integer $id
	 * @return mixed
	 */
	public function actionUpdate()
	{
		$model = $this->findModel();
		$model->setScenario('profile');

		if ($model->load($_POST) && $model->save()) {
			return $this->redirect(['view', 'id' => $model->id]);
		} else {
			return $this->render('update', [
				'model' => $model,
			]);
		}
	}

	/**
	 * Finds the logged in User model based on its primary key value.
	 * If the model is not found, a 404 HTTP exception will be thrown.
	 *
	 * @return User the loaded model
	 * @throws HttpException if the model cannot be found
	 */
	protected function findModel()
	{
		if (($model = Yii::$app->user->getIdentity()) !== null) {
			return $model;
		} else {
			throw new NotFoundHttpException('The requested page does not exist.');
		}
	}

}


<?php

namespace richardfan\sortable;

use Closure;

use yii\base\InvalidConfigException;
use yii\grid\GridView;
use yii\helpers\Json;
use yii\grid\GridViewAsset;
use yii\helpers\Html;

class SortableGridView extends GridView {
    /**
     * (required) The URL of related SortableAction
     *
     * @see \richardfan1126\sortable\SortableAction
     * @var string
     */
    public $sortUrl;

    /**
     * (optional) The text shown in the model while the server is reordering model
     * You can use HTML tag in this attribute.
     *
     * @var string
     */
    public $sortingPromptText = 'Loading...';

    /**
     * (optional) The text shown in alert box when sorting failed.
     *
     * @var string
     */
    public $failText = 'Fail to sort';

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

        if(!isset($this->sortUrl)){
            throw new InvalidConfigException("You must specify the sortUrl");
        }

        GridViewAsset::register($this->view);
        SortableGridViewAsset::register($this->view);

        $this->tableOptions['class'] .= ' sortable-grid-view';
    }

    /**
     * {@inheritDoc}
     * @see \yii\grid\GridView::renderTableRow()
     */
    public function renderTableRow($model, $key, $index)
    {
        $cells = [];
        /* @var $column Column */
        foreach ($this->columns as $column) {
            $cells[] = $column->renderDataCell($model, $key, $index);
        }

        if ($this->rowOptions instanceof Closure) {
            $options = call_user_func($this->rowOptions, $model, $key, $index, $this);
        } else {
            $options = $this->rowOptions;
        }

        // $options['id'] = "items[]_{$model->primaryKey}";
        $options['data-key'] = is_array($key) ? json_encode($key) : (string) $key;

        return Html::tag('tr', implode('', $cells), $options);
    }

    public function run(){
        foreach($this->columns as $column){
			if(property_exists($column, 'enableSorting'))
				$column->enableSorting = false;
        }
        parent::run();

        $options = [
            'id' => $this->id,
            'action' => $this->sortUrl,
            'sortingPromptText' => $this->sortingPromptText,
            'sortingFailText' => $this->failText,
            'csrfTokenName' => \Yii::$app->request->csrfParam,
            'csrfToken' => \Yii::$app->request->csrfToken,
        ];
        $options = Json::encode($options);
        $this->view->registerJs("jQuery.SortableGridView($options);");
    }
}


<?php

namespace Freebird\Services\freebird;

/**
 * A request handler for Tumblr authentication
 * and requests
 */
class RequestHandler
{

    private $bearer;
    private $client;

    /**
     * Instantiate a new RequestHandler
     */
    public function __construct ()
    {
        $this->client = new \Guzzle\Http\Client('https://api.twitter.com/1.1');
    }

    public function set_bearer_token ($bearer_token)
    {
        $this->bearer = $bearer_token;
    }

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

    /**
     * Encodes the Bearer according to twitter's standards
     * @param  [string] $consumer_key    Your Twitter Application Consumer Key
     * @param  [string] $consumer_secret Your Twitter Application Consumer Secret Key
     * @return [string]                  Your encoded Twitter Bearer token credentials
     */
    private function encodeBearer ($consumer_key, $consumer_secret){

        // Create Bearer Token as per Twitter recomends at
        // https://dev.twitter.com/docs/auth/application-only-auth
        $consumer_key = rawurlencode($consumer_key);
        $consumer_secret = rawurlencode($consumer_secret);
        return base64_encode($consumer_key . ':' . $consumer_secret);

    }

    /**
     * Calling this method will establish the hand shake with Twitter OAuth as an application
     * and return this sessions Bearer Token to be used for the call being made
     * @param  [string] $consumer_key    Your Twitter Application Consumer Key
     * @param  [string] $consumer_secret Your Twitter Application Consumer Secret Key
     * @return [string]                  Twitter OAuth Bearer Access Token
     */
    public function authenticateApp ($consumer_key, $consumer_secret) {

        $bearer_token = $this->encodeBearer($consumer_key, $consumer_secret);

        // Twitter Required Headers
        $headers = array(
            'Authorization' => 'Basic ' . $bearer_token,
            'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'
        );

        // Twitter Required Body
        $body = 'grant_type=client_credentials';

        $response = $this->client->post('/oauth2/token', $headers, $body)->send();
        $data = $response->json();
        $this->bearer = $data['access_token'];
        return $this->bearer;
    }

    /**
     * Make a request with this request handler
     *
     * @param string $path    Twitter resource path
     * @param array  $options the array of params to pass to the resource
     *
     * @return \stdClass response object
     */
    public function request($path, $options)
    {
        // Ensure we have options
        $options ?: array();

        $headers = array(
            'Authorization' => 'Bearer ' . $this->bearer
        );

        // GET requests get the params in the query string
        $request = $this->client->get($path, $headers);
        $request->getQuery()->merge($options);

        // Guzzle throws errors, but we collapse them and just grab the
        // response, since we deal with this at the \Tumblr\Client level
        try {
            $response = $request->send();
        } catch (\Guzzle\Http\Exception\BadResponseException $e) {
            $response = $request->getResponse();
        }

        // Construct the object that the Client expects to see, and return it
        $obj = new \stdClass;
        $obj->status = $response->getStatusCode();
        $obj->body = $response->getBody();
        $obj->headers = $response->getHeaders();
        $obj->json = $response->json();

        return $obj;
    }

}


<?php

namespace Freebird\Services\freebird;

class Client
{

	/**
	 * Create a new Freebird Client
	 * @param [string] $consumer_key    [Twitter Application Consumer Key]
	 * @param [string] $consumer_secret [Twitter Application Consumer Secret Key]
	 */
	public function __construct ()
	{
		$this->requestHandler = new RequestHandler();
	}

	public function init_bearer_token ($consumer_key, $consumer_secret)
	{
		// Establishes Twitter Applications Authentication token for this session.
		return $this->requestHandler->authenticateApp($consumer_key, $consumer_secret);
	}

	public function set_bearer_token ($bearer_token)
	{
		$this->requestHandler->set_bearer_token($bearer_token);
	}

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

	/**
	 * Simple method to make requests to the Twitter API
	 * @param  [string] $path    [description]
	 * @param  [array] $options [description]
	 * @return [json]          [description]
	 */
	public function api_request ($path, $options)
	{
		$data = $this->requestHandler->request($path, $options);
		return json_encode($data->json);
	}

}


<?php
/**
 * WindowsAzure BlobStorage Client
 *
 * Copyright (c) 2012, Benjamin Eberlei
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to kontakt@beberlei.de so I can send you a copy immediately.
 */

namespace Beberlei\AzureBlobStorage;

/**
 * Storage Registry for registering Blob Storage Accounts
 */
class StorageRegistry
{
    /**
     * @var array
     */
    private $accounts;

    /**
     * Register Account
     *
     * @param string $name
     * @param string $accountName
     * @param string $accountKey
     * @param string $streamName
     * @return void
     */
    public function registerAccount($name, $accountName, $accountKey, $streamName = false)
    {
        if ( isset($this->accounts[$name])) {
            throw new \RuntimeException("An account with name $name is already registered.");
        }

        $this->accounts[$name] = new BlobClient(
            sprintf('https://%s.blob.core.windows.net', $accountName),
            $accountName,
            $accountKey
        );

        if (! $streamName) {
            return;
        }

        $this->accounts[$name]->registerStreamWrapper($streamName);
    }

    /**
     * Get the BlobClient associated with the account name.
     *
     * @param string $name
     * @return BlobClient
     */
    public function get($name)
    {
        if ( ! isset($this->accounts[$name])) {
            throw new \RuntimeException("No account found with " . $name);
        }

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


<?php
/**
 * WindowsAzure BlobStorage Client
 *
 * Copyright (c) 2009 - 2012, RealDolmen
 * Copyright (c) 2012, Benjamin Eberlei
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to kontakt@beberlei.de so I can send you a copy immediately.
 */

namespace Beberlei\AzureBlobStorage;

/**
 * @copyright  Copyright (c) 2009 - 2012, RealDolmen (http://www.realdolmen.com)
 * @license    http://phpazure.codeplex.com/license
 */
class SharedKey extends CredentialsAbstract
{
    /**
	 * Sign request URL with credentials
	 *
	 * @param string $requestUrl Request URL
	 * @param string $resourceType Resource type
	 * @param string $requiredPermission Required permission
	 * @return string Signed request URL
	 */
	public function signRequestUrl(
		$requestUrl = '',
		$resourceType = Storage::RESOURCE_UNKNOWN,
		$requiredPermission = CredentialsAbstract::PERMISSION_READ
	) {
	    return $requestUrl;
	}

	/**
	 * Sign request headers with credentials
	 *
	 * @param string $httpVerb HTTP verb the request will use
	 * @param string $path Path for the request
	 * @param array $query Query arguments for the request (key/value pairs)
	 * @param array $headers x-ms headers to add
	 * @param boolean $forTableStorage Is the request for table storage?
	 * @param string $resourceType Resource type
	 * @param string $requiredPermission Required permission
	 * @param mixed  $rawData Raw post data
	 * @return array Array of headers
	 */
	public function signRequestHeaders(
		$httpVerb = 'GET',
		$path = '/',
		$query = array(),
		$headers = null,
		$forTableStorage = false,
		$resourceType = Storage::RESOURCE_UNKNOWN,
		$requiredPermission = CredentialsAbstract::PERMISSION_READ,
		$rawData = null
	) {
		// http://github.com/sriramk/winazurestorage/blob/214010a2f8931bac9c96dfeb337d56fe084ca63b/winazurestorage.py

		// Determine path
		if ($this->usePathStyleUri) {
			$path = substr($path, strpos($path, '/'));
		}

		// Canonicalized headers
		$canonicalizedHeaders = array();

		// Request date
		$requestDate = '';
		if (isset($headers[CredentialsAbstract::PREFIX_STORAGE_HEADER . 'date'])) {
		    $requestDate = $headers[CredentialsAbstract::PREFIX_STORAGE_HEADER . 'date'];
		} else {
            $time = time();
		    $requestDate = gmdate('D, d M Y H:i:s', $time) . ' GMT'; // RFC 1123
		    $canonicalizedHeaders[] = CredentialsAbstract::PREFIX_STORAGE_HEADER . 'date:' . $requestDate;
		}

		// Build canonicalized headers
		if (!is_null($headers)) {
			foreach ($headers as $header => $value) {
				if (is_bool($value)) {
					$value = $value === true ? 'True' : 'False';
				}

				$headers[$header] = $value;
				if (substr($header, 0, strlen(CredentialsAbstract::PREFIX_STORAGE_HEADER)) == CredentialsAbstract::PREFIX_STORAGE_HEADER) {
				    $canonicalizedHeaders[] = strtolower($header) . ':' . $value;
				}
			}
		}
		sort($canonicalizedHeaders);

		// Build canonicalized resource string
		$canonicalizedResource  = '/' . $this->accountName;
		if ($this->usePathStyleUri) {
			$canonicalizedResource .= '/' . $this->accountName;
		}
		$canonicalizedResource .= $path;
		if (count($query) > 0) {
			ksort($query);

		    foreach ($query as $key => $value) {
		    	$canonicalizedResource .= "\n" . strtolower($key) . ':' . rawurldecode($value);
		    }
		}

		// Content-Length header
		$contentLength = '';
		if (strtoupper($httpVerb) != 'GET'
			 && strtoupper($httpVerb) != 'DELETE'
			 && strtoupper($httpVerb) != 'HEAD') {
			$contentLength = 0;

			if (!is_null($rawData)) {
				$contentLength = strlen($rawData);
			}
		}

		// Create string to sign
		$stringToSign   = array();
		$stringToSign[] = strtoupper($httpVerb); 									// VERB
    	$stringToSign[] = $this->issetOr($headers, 'Content-Encoding', '');		// Content-Encoding
    	$stringToSign[] = $this->issetOr($headers, 'Content-Language', '');		// Content-Language
    	$stringToSign[] = $contentLength; 											// Content-Length
    	$stringToSign[] = $this->issetOr($headers, 'Content-MD5', '');				// Content-MD5
    	$stringToSign[] = $this->issetOr($headers, 'Content-Type', '');			// Content-Type
    	$stringToSign[] = "";														// Date
    	$stringToSign[] = $this->issetOr($headers, 'If-Modified-Since', '');		// If-Modified-Since
    	$stringToSign[] = $this->issetOr($headers, 'If-Match', '');				// If-Match
    	$stringToSign[] = $this->issetOr($headers, 'If-None-Match', '');			// If-None-Match
    	$stringToSign[] = $this->issetOr($headers, 'If-Unmodified-Since', '');		// If-Unmodified-Since
    	$stringToSign[] = $this->issetOr($headers, 'Range', '');					// Range

    	if (!$forTableStorage && count($canonicalizedHeaders) > 0) {
    		$stringToSign[] = implode("\n", $canonicalizedHeaders); // Canonicalized headers
    	}

    	$stringToSign[] = $canonicalizedResource;		 			// Canonicalized resource
    	$stringToSign   = implode("\n", $stringToSign);
    	$signString     = base64_encode(hash_hmac('sha256', $stringToSign, $this->accountKey, true));

    	// Sign request
    	$headers[CredentialsAbstract::PREFIX_STORAGE_HEADER . 'date'] = $requestDate;
    	$headers['Authorization'] = 'SharedKey ' . $this->accountName . ':' . $signString;

    	// Return headers
    	return $headers;
	}
}


<?php
/**
 * WindowsAzure BlobStorage Client
 *
 * Copyright (c) 2009 - 2012, RealDolmen
 * Copyright (c) 2012, Benjamin Eberlei
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to kontakt@beberlei.de so I can send you a copy immediately.
 */

namespace Beberlei\AzureBlobStorage;

/**
 * @copyright  Copyright (c) 2009 - 2012, RealDolmen (http://www.realdolmen.com)
 * @license    http://phpazure.codeplex.com/license
 */
class SharedAccessSignature extends CredentialsAbstract
{
    /**
     * Permission set
     *
     * @var array
     */
    protected $permissionSet = array();

	/**
	 * Creates a new Credentials_SharedAccessSignature instance
	 *
	 * @param string $accountName Account name for Windows Azure
	 * @param string $accountKey Account key for Windows Azure
	 * @param boolean $usePathStyleUri Use path-style URI's
	 * @param array $permissionSet Permission set
	 */
	public function __construct(
		$accountName = CredentialsAbstract::DEVSTORE_ACCOUNT,
		$accountKey  = CredentialsAbstract::DEVSTORE_KEY,
		$usePathStyleUri = false, $permissionSet = array()
	) {
	    parent::__construct($accountName, $accountKey, $usePathStyleUri);
	    $this->permissionSet = $permissionSet;
	}

	/**
	 * Get permission set
	 *
	 * @return array
	 */
    public function getPermissionSet()
	{
	    return $this->permissionSet;
	}

	/**
	 * Set permisison set
	 *
	 * Warning: fine-grained permissions should be added prior to coarse-grained permissions.
	 * For example: first add blob permissions, end with container-wide permissions.
	 *
	 * Warning: the signed access signature URL must match the account name of the
	 * Credentials_Credentials_SharedAccessSignature instance
	 *
	 * @param  array $value Permission set
	 * @return void
	 */
    public function setPermissionSet($value = array())
	{
		foreach ($value as $url) {
			if (strpos($url, $this->accountName) === false) {
				throw new Exception('The permission set can only contain URLs for the account name specified in the Credentials_SharedAccessSignature instance.');
			}
		}
	    $this->permissionSet = $value;
	}

    /**
     * Create signature
     *
     * @param string $path 		   Path for the request
     * @param string $resource     Signed resource - container (c) - blob (b)
     * @param string $permissions  Signed permissions - read (r), write (w), delete (d) and list (l)
     * @param string $start        The time at which the Shared Access Signature becomes valid.
     * @param string $expiry       The time at which the Shared Access Signature becomes invalid.
     * @param string $identifier   Signed identifier
     * @return string
     */
    public function createSignature(
    	$path = '/',
    	$resource = 'b',
    	$permissions = 'r',
    	$start = '',
    	$expiry = '',
    	$identifier = ''
    ) {
		// Determine path
		if ($this->usePathStyleUri) {
			$path = substr($path, strpos($path, '/'));
		}

		// Add trailing slash to $path
		if (substr($path, 0, 1) !== '/') {
		    $path = '/' . $path;
		}

		// Build canonicalized resource string
		$canonicalizedResource  = '/' . $this->accountName;
		/*if ($this->usePathStyleUri) {
			$canonicalizedResource .= '/' . $this->accountName;
		}*/
		$canonicalizedResource .= $path;

		// Create string to sign
		$stringToSign   = array();
		$stringToSign[] = $permissions;
    	$stringToSign[] = $start;
    	$stringToSign[] = $expiry;
    	$stringToSign[] = $canonicalizedResource;
    	$stringToSign[] = $identifier;

    	$stringToSign = implode("\n", $stringToSign);
    	$signature    = base64_encode(hash_hmac('sha256', $stringToSign, $this->accountKey, true));

    	return $signature;
    }

    /**
     * Create signed query string
     *
     * @param string $path 		   Path for the request
     * @param string $queryString  Query string for the request
     * @param string $resource     Signed resource - container (c) - blob (b)
     * @param string $permissions  Signed permissions - read (r), write (w), delete (d) and list (l)
     * @param string $start        The time at which the Shared Access Signature becomes valid.
     * @param string $expiry       The time at which the Shared Access Signature becomes invalid.
     * @param string $identifier   Signed identifier
     * @return string
     */
    public function createSignedQueryString(
    	$path = '/',
    	$queryString = '',
    	$resource = 'b',
    	$permissions = 'r',
    	$start = '',
    	$expiry = '',
    	$identifier = ''
    ) {
        // Parts
        $parts = array();
        if ($start !== '') {
            $parts[] = 'st=' . urlencode($start);
        }
        $parts[] = 'se=' . urlencode($expiry);
        $parts[] = 'sr=' . $resource;
        $parts[] = 'sp=' . $permissions;
        if ($identifier !== '') {
            $parts[] = 'si=' . urlencode($identifier);
        }
        $parts[] = 'sig=' . urlencode($this->createSignature($path, $resource, $permissions, $start, $expiry, $identifier));

        // Assemble parts and query string
        if ($queryString != '') {
            $queryString .= '&';
	    }
        $queryString .= implode('&', $parts);

        return $queryString;
    }

    /**
	 * Permission matches request?
	 *
	 * @param string $permissionUrl Permission URL
	 * @param string $requestUrl Request URL
	 * @param string $resourceType Resource type
	 * @param string $requiredPermission Required permission
	 * @return string Signed request URL
	 */
    public function permissionMatchesRequest(
    	$permissionUrl = '',
    	$requestUrl = '',
    	$resourceType = Storage::RESOURCE_UNKNOWN,
    	$requiredPermission = CredentialsAbstract::PERMISSION_READ
    ) {
        // Build requirements
        $requiredResourceType = $resourceType;
        if ($requiredResourceType == Storage::RESOURCE_BLOB) {
            $requiredResourceType .= Storage::RESOURCE_CONTAINER;
        }

        // Parse permission url
	    $parsedPermissionUrl = parse_url($permissionUrl);

	    // Parse permission properties
	    $permissionParts = explode('&', $parsedPermissionUrl['query']);

	    // Parse request url
	    $parsedRequestUrl = parse_url($requestUrl);

	    // Check if permission matches request
	    $matches = true;
	    foreach ($permissionParts as $part) {
	        list($property, $value) = explode('=', $part, 2);

	        if ($property == 'sr') {
	            $matches = $matches && (strpbrk($value, $requiredResourceType) !== false);
	        }

	    	if ($property == 'sp') {
	            $matches = $matches && (strpbrk($value, $requiredPermission) !== false);
	        }
	    }

	    // Ok, but... does the resource match?
	    $matches = $matches && (strpos($parsedRequestUrl['path'], $parsedPermissionUrl['path']) !== false);

        // Return
	    return $matches;
    }

    /**
	 * Sign request URL with credentials
	 *
	 * @param string $requestUrl Request URL
	 * @param string $resourceType Resource type
	 * @param string $requiredPermission Required permission
	 * @return string Signed request URL
	 */
	public function signRequestUrl(
		$requestUrl = '',
		$resourceType = Storage::RESOURCE_UNKNOWN,
		$requiredPermission = CredentialsAbstract::PERMISSION_READ
	) {
	    // Look for a matching permission
	    foreach ($this->getPermissionSet() as $permittedUrl) {
	        if ($this->permissionMatchesRequest($permittedUrl, $requestUrl, $resourceType, $requiredPermission)) {
	            // This matches, append signature data
	            $parsedPermittedUrl = parse_url($permittedUrl);

	            if (strpos($requestUrl, '?') === false) {
	                $requestUrl .= '?';
	            } else {
	                $requestUrl .= '&';
	            }

	            $requestUrl .= $parsedPermittedUrl['query'];

	            // Return url
	            return $requestUrl;
	        }
	    }

	    // Return url, will be unsigned...
	    return $requestUrl;
	}

	/**
	 * Sign request with credentials
	 *
	 * @param string $httpVerb HTTP verb the request will use
	 * @param string $path Path for the request
	 * @param array $query Query arguments for the request (key/value pairs)
	 * @param array $headers x-ms headers to add
	 * @param boolean $forTableStorage Is the request for table storage?
	 * @param string $resourceType Resource type
	 * @param string $requiredPermission Required permission
	 * @param mixed  $rawData Raw post data
	 * @return array Array of headers
	 */
	public function signRequestHeaders(
		$httpVerb = 'GET',
		$path = '/',
		$query = array(),
		$headers = null,
		$forTableStorage = false,
		$resourceType = Storage::RESOURCE_UNKNOWN,
		$requiredPermission = CredentialsAbstract::PERMISSION_READ,
		$rawData = null
	) {
	    return $headers;
	}
}