<?php

/**
* The class to login to a web site with Facebook account
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/
namespace Gelembjuk\Auth\SocialLogin;

class Facebook extends Base {
	/**
	 * Facebook API object
	 *
	 * @var Facebook\Facebook
	 */
	protected $fb;

	/**
	 * Facebook API token
	 *
	 * @var string
	 */
	protected $accesstoken;
	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
    public function isConfigured() {
        if (empty($this->options['api_key']) || empty($this->options['secret_key'])) {
            return false;
        }
        return true;
    }

	/**
	 * Returns Faceboo API object inited with API settings
	 *
	 * @return Facebook\Facebook
	 */
	protected function getFacebookObject() {
		// keep only one instance during a session
		if (is_object($this->fb)) {
			return $this->fb;
		}

		$fb = new \Facebook\Facebook([
			'app_id' => $this->options['api_key'],
			'app_secret' => $this->options['secret_key'],
			'default_graph_version' => 'v3.0',
			]);

		$this->fb = $fb;

		return $fb;
	}
	/**
	 * Returns list of properties to serialise.
	 *
	 * @param array $skip Defines what properties to skip
	 */
	protected function getSerializeVars($skip = array()) {
		// skip fb property. Noo need to serialize it
		$skip[] = 'fb';
		return parent::getSerializeVars($skip);
	}
	/**
	 * Get array of GET/POST arguments posted fromfacebook back to complete login URL
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		return array('code','state','error_code','error','error_reason','error_description');
	}
	/**
	 * Returns facebook login start auth process
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	public function getLoginStartUrl($redirecturl) {
		$facebook = $this->getFacebookObject();

		$helper = $facebook->getRedirectLoginHelper();

		$permissions = ['email']; // Optional permissions
		$loginUrl = $helper->getLoginUrl($redirecturl, $permissions);

		return $loginUrl;
	}
	/**
	 * Completes social login. Is caled after redirect from facebook auth page
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	public function completeLogin($extrainputs = array()) {
		$facebook = $this->getFacebookObject();

		// we are not sure about $_GET contains all correct data
		// as in this model data are posted with $extrainputs
		// ideally it would be good if facebook lib accept data not only from _GET but also from any array
		$old_GET = $_GET;
		$_GET = $extrainputs;

		$helper = $facebook->getRedirectLoginHelper();

		// don't catch exceptions. it will be done in the model or controller
		$accessToken = $helper->getAccessToken();

		$_GET = $old_GET;

		if (! isset($accessToken)) {
			if ($helper->getError()) {
				throw new \Exception($helper->getError().' '.
					$helper->getErrorCode().' '.
					$helper->getErrorReason().' '.
					$helper->getErrorDescription());
    			} else {
    				throw new \Exception('Unknown error from Facebook');
    			}
  		}

  		$this->accesstoken = $accessToken;

  		return $this->getUserProfile();
	}
	/**
	 * Returns short facebook profile after succes login
	 *
	 * @return array User Profile
	 */
	public function getUserProfile() {
		$facebook = $this->getFacebookObject();

		$response = $facebook->get('/me', $this->accesstoken);

		$me = $response->getGraphUser();

		return array(
			'userid'=>$me->getId(),
			'name'=>$me->getName(),
			'email'=>$me->getField('email'),
			'imageurl'=>'https://graph.facebook.com/'.$me->getId().'/picture?type=large');
	}
}


<?php

namespace Iadvize\ApiRestClient;

/**
 * Request
 *
 * @package Iadvize\ApiRestClient
 */
class Request
{
    /** @const int Default pagination limit */
    const DEFAULT_PAGINATION_LIMIT = 20;

    /** @const string Mode read */
    const MODE_READ = 0;

    /** @const string Mode create */
    const MODE_CREATE = 1;

    /** @const string Mode update */
    const MODE_UPDATE = 2;

    /** @const string Mode delete */
    const MODE_DELETE = 3;

    /** @var string Resource name */
    protected $resourceName = '';

    /** @var array Filters list */
    protected $filters = [];

    /** @var bool Display full results */
    protected $displayFullResults = false;

    /** @var array Display fields list */
    protected $displayFields = [];

    /** @var array Fields (create or update) */
    protected $fields = [];

    /** @var string Mode */
    protected $mode = self::MODE_READ;

    /** @var int Pagination limit */
    protected $paginationLimit = self::DEFAULT_PAGINATION_LIMIT;

    /** @var int Current page */
    protected $currentPage = 1;

    /** @var int Identifier */
    protected $identifier;

    /**
     * @param string $resourceName Resource name
     * @param bool $displayFullResult Display full result
     * @param int $currentPage Current page
     */
    public function __construct($resourceName = '', $displayFullResult = false, $currentPage = 1)
    {
        $this->resourceName       = $resourceName;
        $this->displayFullResults = $displayFullResult;
        $this->currentPage        = $currentPage;
    }

    /**
     * Set resource name
     *
     * @param string $resourceName
     */
    public function setResourceName($resourceName)
    {
        $this->resourceName = $resourceName;
    }

    /**
     * Get resource name
     *
     * @return string
     */
    public function getResourceName()
    {
        return $this->resourceName;
    }

    /**
     * Set filters
     *
     * @param array $filters
     */
    public function setFilters(array $filters)
    {
        $this->filters = $filters;
    }

    /**
     * Add filter
     *
     * @param string $filter
     * @param mixed $value
     */
    public function addFilter($filter, $value)
    {
        if (!in_array($filter, $this->filters)) {
            $this->filters[$filter] = $value;
        }
    }

    /**
     * Remove filter
     *
     * @param string $filter Filter
     */
    public function removeFilter($filter)
    {
        if (array_key_exists($filter, $this->filters)) {
            unset($this->filters[$filter]);
        }
    }

    /**
     * Get filters
     *
     * @return array
     */
    public function getFilters()
    {
        return $this->filters;
    }

    /**
     * Enable full results
     */
    public function enableFullResults()
    {
        $this->displayFullResults = true;
    }

    /**
     * Disable full results
     */
    public function disableFullResults()
    {
        $this->displayFullResults = false;
    }

    /**
     * Get full results
     *
     * @return bool
     */
    public function getFullResults()
    {
        return $this->displayFullResults;
    }

    /**
     * Set display fields
     *
     * @param array $displayFields
     */
    public function setDisplayFields(array $displayFields)
    {
        $this->displayFields = $displayFields;
    }

    /**
     * Add display field
     *
     * @param string $displayField
     */
    public function addDisplayField($displayField)
    {
        if (!in_array($displayField, $this->displayFields)) {
            $this->displayFields[] = $displayField;
        }
    }

    /**
     * Remove display field
     *
     * @param string $displayField
     */
    public function removeDisplayField($displayField)
    {
        $key = array_search($displayField, $this->displayFields);
        if ($key) {
            unset($this->displayFields[$key]);
            $this->displayFields = array_values($this->displayFields);
        }
    }

    /**
     * Get display fields
     *
     * @return array
     */
    public function getDisplayFields()
    {
        return $this->displayFields;
    }

    /**
     * Set fields
     *
     * @param array $fields
     */
    public function setFields(array $fields)
    {
        $this->fields = $fields;
    }

    /**
     * Add field
     *
     * @param string $field Field
     * @param mixed $value Value
     */
    public function addField($field, $value)
    {
        if (!in_array($field, $this->fields)) {
            $this->fields[$field] = $value;
        }
    }

    /**
     * Remove field
     *
     * @param string $field Field
     */
    public function removeField($field)
    {
        if (array_key_exists($field, $this->fields)) {
            unset($this->fields[$field]);
        }
    }

    /**
     * Get fields
     *
     * @return array
     */
    public function getFields()
    {
        return $this->fields;
    }

    /**
     * Set mode
     *
     * @param string $mode
     */
    public function setMode($mode)
    {
        $this->mode = $mode;
    }

    /**
     * Get mode
     *
     * @return string
     */
    public function getMode()
    {
        return $this->mode;
    }

    /**
     * Set pagination limit
     *
     * @param int $paginationLimit
     */
    public function setPaginationLimit($paginationLimit)
    {
        $this->paginationLimit = $paginationLimit;
    }

    /**
     * Get pagination limit
     *
     * @return int
     */
    public function getPaginationLimit()
    {
        return $this->paginationLimit;
    }

    /**
     * Set current page
     *
     * @param int $currentPage
     */
    public function setCurrentPage($currentPage)
    {
        $this->currentPage = $currentPage;
    }

    /**
     * Get current page
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->currentPage;
    }

    /**
     * Set identifier
     *
     * @param int $identifier
     */
    public function setIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * Get identifier
     *
     * @return int
     */
    public function getIdentifier()
    {
        return $this->identifier;
    }
}


<?php

/**
* This class the base class for social login integrations
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/

namespace Gelembjuk\Auth\SocialLogin;

abstract class Base {
	// include logger functionality to be able to log if needed
	use \Gelembjuk\Logger\ApplicationLogger;

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

	/**
	 * Constructor.
	 *
	 * Only saves optiosn to a property
	 *
	 * @param int $options
	 */
	public function __construct($options) {
		$this->options = $options;
	}

	/**
	 * Returns list of properties to serialise.
	 *
	 * @param array $skip Defines what properties to skip
	 */
	protected function getSerializeVars($skip = array()) {
		$vars = get_object_vars($this);

		$servars = array();

		foreach ($vars as $k=>$v) {
			// skip what is in the array
			if (in_array($k,$skip)) {
				continue;
			}

			// skip 2 standars properties as no sence to serialize them
			if ($k == 'options' || $k == 'logger') {
				continue;
			}
			$servars[] = $k;
		}
		return $servars;
	}
	/**
	 * Returns serialized string
	 * We presume there will not be some complex objects
	 * only integers/strings etc
	 *
	 * @return string JSON string with values of selected properties
	 */
	public function serialize() {
		$properties = $this->getSerializeVars();

		$data = array();

		foreach ($properties as $p) {
			$data[$p] = $this->$p;
		}

		return json_encode($data);
	}
	/**
	 * Unserialize/restore an object state to continue login
	 *
	 * @param string $json_string JSON string with values of selected properties
	 */
	public function unSerialize($json_string) {
		$data = json_decode($json_string,true);

		foreach ($data as $k => $v) {
			$this->$k = $v;
		}

		return true;
	}

	/**
	 * Abstract function to get social login start auth url
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	abstract public function getLoginStartUrl($redirecturl);

	/**
	 * Get array of GET/POST arguments posted from social network
	 * as part of final redirect after login back to the site
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		// by default no extra inputs
		return array();
	}

	/**
	 * Abstract function to complete social login. Is caled after redirect from a social network
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	abstract public function completeLogin($extrainputs = array());

	/**
	 * Get social network user ID after success auth
	 *
	 * @return string User ID
	 */
	public function getUserID() {
		$profile = $this->getUserProfile();
		return $profile['userid'];
	}
	/**
	 * Get social network user profile after success auth
	 * A profile is array and contains keys: id, name, email, imageurl
	 *
	 * @return array User Profile
	 */
	abstract public function getUserProfile();

	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
	public function isConfigured() {
        return true;
	}
	/**
     * Checks if an integration is active and works.
     *
     * @return bool
     */
    public function isActive() {
        if (!$this->isConfigured()) {
            return false;
        }
        return true;
    }
}


<?php

/**
* The class to login to a web site with Xing account
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/

namespace Gelembjuk\Auth\SocialLogin;

use \League\OAuth1\Client\Server\Xing;

class Xingapi extends Base {
	/**
	 * Xing API access token object
	 *
	 * @var \League\OAuth1\Client\Credentials\TokenCredentials
	 */
	protected $access_token;
	/**
	 * Xing API oAuth1 process temp credentials
	 *
	 * @var \League\OAuth1\Client\Credentials\TokenCredentials
	 */
	protected $temp_credentials;

	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
    public function isConfigured() {
        if (empty($this->options['consumer_key']) ||
            empty($this->options['consumer_secret'])) {
            return false;
        }
        return true;
    }
	/**
	 * Returns Xing login start auth process
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	public function getLoginStartUrl($redirecturl) {
		$credentials = array(
			'identifier' => $this->options['consumer_key'],
			'secret' => $this->options['consumer_secret'],
			'callback_uri' => $redirecturl
		);

		$server = new \League\OAuth1\Client\Server\Xing($credentials);

		$this->temp_credentials = $server->getTemporaryCredentials();

		return $server->getAuthorizationUrl($this->temp_credentials);
	}
	/**
	 * Get array of GET/POST arguments posted from Xing back to complete login URL
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		return array('oauth_token','oauth_verifier'); // by default no extra inputs
	}

	/**
	 * Completes social login. Is caled after redirect from Xing auth page
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	public function completeLogin($extrainputs = array()) {
		if (!isset($extrainputs['oauth_token']) || $extrainputs['oauth_token'] == '') {
			throw new \Exception('Xing oauth. Somethign went wrong. No token in the session');
		}

		$credentials = array(
			'identifier' => $this->options['consumer_key'],
			'secret' => $this->options['consumer_secret']
		);

		$server = new \League\OAuth1\Client\Server\Xing($credentials);

		$this->access_token = $server->getTokenCredentials($this->temp_credentials,
			$extrainputs['oauth_token'], $extrainputs['oauth_verifier']);

		return $this->getUserProfile();
	}
	/**
	 * Returns short Xing profile after succes login
	 *
	 * @return array User Profile
	 */
	public function getUserProfile() {
		$credentials = array(
			'identifier' => $this->options['consumer_key'],
			'secret' => $this->options['consumer_secret']
		);

		$server = new \League\OAuth1\Client\Server\Xing($credentials);

		$user = $server->getUserDetails($this->access_token);

		return array(
			'userid'=>$user->uid,
			'name'=>$user->display_name,
			'imageurl'=>$user->imageUrl);
	}

	/**
	 * Returns serialized string
	 * We presume there will not be some complex objects
	 * only integers/strings etc
	 *
	 * @return string $skip JSON string with values of selected properties
	 */
	public function serialize() {
		return serialize($this->temp_credentials);
	}
	/**
	 * Unserialize/restore an object state to continue login
	 *
	 * @param string $json_string JSON string with values of selected properties
	 */
	public function unSerialize($json_string) {
		$this->temp_credentials = unserialize($json_string);

		return true;
	}
}


<?php

/**
* The class to login to a web site with Twitter account
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/

namespace Gelembjuk\Auth\SocialLogin;

use \Abraham\TwitterOAuth\TwitterOAuth;

class Twitter extends Base {
	/**
	 * Twitter API access token
	 *
	 * @var string
	 */
	protected $access_token;
	/**
	 * Twitter API request token
	 *
	 * @var string
	 */
	protected $request_token;

	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
    public function isConfigured() {
        if (empty($this->options['consumer_key']) ||
            empty($this->options['consumer_secret'])) {
            return false;
        }
        return true;
    }

	/**
	 * Returns twitter login start auth process
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	public function getLoginStartUrl($redirecturl) {
		$connection = new TwitterOAuth($this->options['consumer_key'],$this->options['consumer_secret']);
		$connection->setTimeouts(10, 15);
		$request_token = $connection->oauth('oauth/request_token', array('oauth_callback' => $redirecturl));

		$this->request_token = array();
		$this->request_token['oauth_token'] = $request_token['oauth_token'];
		$this->request_token['oauth_token_secret'] = $request_token['oauth_token_secret'];

		return $connection->url('oauth/authorize', array('oauth_token' => $request_token['oauth_token']));
	}
	/**
	 * Get array of GET/POST arguments posted from twitter back to complete login URL
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		return array('oauth_token','oauth_verifier'); // by default no extra inputs
	}

	/**
	 * Completes social login. Is caled after redirect from twitter auth page
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	public function completeLogin($extrainputs = array()) {
		$request_token = [];
		$request_token['oauth_token'] = $this->request_token['oauth_token'];
		$request_token['oauth_token_secret'] = $this->request_token['oauth_token_secret'];

		$this->logQ('session token '.print_r($request_token,true),'twitter');
		$this->logQ('extra options '.print_r($extrainputs,true),'twitter');

		if (isset($extrainputs['oauth_token']) && $request_token['oauth_token'] !== $extrainputs['oauth_token']) {
			throw new \Exception('Twitter oauth. Somethign went wrong. No token in the session');
		}

		$connection = new TwitterOAuth($this->options['consumer_key'],$this->options['consumer_secret'],
			$request_token['oauth_token'], $request_token['oauth_token_secret']);
		$connection->setTimeouts(10, 15);

		$access_token = $connection->oauth("oauth/access_token", array("oauth_verifier" => $extrainputs['oauth_verifier']));

		$this->access_token = $access_token;

		return $this->getUserProfile();
	}
	/**
	 * Returns short twitter profile after succes login
	 *
	 * @return array User Profile
	 */
	public function getUserProfile() {
		$connection = new TwitterOAuth($this->options['consumer_key'],$this->options['consumer_secret'],
			$this->access_token['oauth_token'], $this->access_token['oauth_token_secret']);
		$connection->setTimeouts(10, 15);
		$user = $connection->get("account/verify_credentials");

		return array(
			'userid'=>$user->id,
			'name'=>$user->screen_name,
			'imageurl'=>$user->profile_image_url);
	}

}


<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2014-2018
 * @package MW
 * @subpackage View
 */

namespace Aimeos\MW\View\Helper\Url;

/**
 * View helper class for building URLs using Symfony2 Router.
 *
 * @package MW
 * @subpackage View
 */
class Symfony2
	extends \Aimeos\MW\View\Helper\Url\Base
	implements \Aimeos\MW\View\Helper\Url\Iface
{
	private $router;
	private $fixed;

	/**
	 * Initializes the URL view helper.
	 *
	 * @param \Aimeos\MW\View\Iface $view View instance with registered view helpers
	 * @param Symfony\Component\Routing\RouterInterface $router Symfony2 Router implementation
	 * @param array $fixed Fixed parameters that should be added to each URL
	 */
	public function __construct( $view, \Symfony\Component\Routing\RouterInterface $router, array $fixed )
	{
		parent::__construct( $view );

		$this->router = $router;
		$this->fixed = $fixed;
	}

	/**
	 * Returns the URL assembled from the given arguments.
	 *
	 * @param string|null $target Route or page which should be the target of the link (if any)
	 * @param string|null $controller Name of the controller which should be part of the link (if any)
	 * @param string|null $action Name of the action which should be part of the link (if any)
	 * @param array $params Associative list of parameters that should be part of the URL
	 * @param array $trailing Trailing URL parts that are not relevant to identify the resource (for pretty URLs)
	 * @param array $config Additional configuration parameter per URL
	 * @return string Complete URL that can be used in the template
	 */
	public function transform( $target = null, $controller = null, $action = null, array $params = [], array $trailing = [], array $config = [] )
	{
		if( !empty( $trailing ) ) {
			$params['trailing'] = join( '_', $trailing );
		}

		$params = $this->sanitize( $params );
		$refType = \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_PATH;

		if( isset( $config['absoluteUri'] ) ) {
			$refType = \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL;
		}

		return $this->router->generate( $target, $params + $this->fixed, $refType );
	}
}


<?php

/**
* This class is a factory for auth systems objects . It is used statically to create social login object
*
* Usage:
*
* $socialnetwork = 'facebook';
*
* $network = Gelembjuk\Auth\AuthFactory::getSocialLoginObject($socialnetwork,$integrations[$socialnetwork]);
*
* $redirecturl = '.....';
*
* $socialauthurl = $network->getLoginStartUrl($redirecturl);
*
* header("Location: $socialauthurl",true,301);
* exit;
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/

namespace Gelembjuk\Auth;

class AuthFactory {
	/**
	 * returns social login object with a name
	 *
	 * @param string $network One of: facebook, twitter, google, linkedin
	 * @param array $options This are AI settings for a social network named in a first argument. Settings are different for each network integration
	 * @logger Psr\Log $logger This is logger instance
	 *
	 * @return Gelembjuk\Auth\SocialLogin\Base Instance of social login class
	 */
	public static function getSocialLoginObject($network,$options = array(), $logger = null) {
		// do some filters and checks for name
		$network = preg_replace('![^a-z0-9]!i','',$network);

		if ($network == '') {
			throw new \Exception('Social Login Network can not be empty');
		}

		$class = '\\Gelembjuk\\Auth\\SocialLogin\\'.ucfirst($network);

		if (!class_exists($class)) {
			throw new \Exception(sprintf('Integration with a class name %s not found',$class));
		}

		// create an object
		$object = new $class($options);

		// set logger (even if it is null then no problem)
		$object->setLogger($logger);

		return $object;
	}

}


<?php

namespace Iadvize\ApiRestClient;

use Buzz\Browser;
use Buzz\Client\Curl;
use Zend\Stdlib\ArrayUtils;

/**
 * Client
 *
 * @package Iadvize\ApiRestClient
 */
class Client
{
    /** @const string Default base URI */
    const DEFAULT_BASE_URI = 'https://www.iadvize.com/api/2/';

    /** @const string High  */
    const HA_BASE_URI = 'https://ha.iadvize.com/api/2/';

    /** @var string Base URI */
    protected $baseUri = self::DEFAULT_BASE_URI;

    /** @var string Authentication API Key */
    protected $authenticationKey = '';

    /** @var Request $lastRequest Last request */
    protected $lastRequest;

    /** @var Response $lastResponse Last response */
    protected $lastResponse;

    /** @var Browser $browser Browser */
    protected $browser;

    /**
     * Constructor
     * Set default timeout to 24h
     *
     * @param string       $authenticationKey Authentication API Key
     * @param null|Browser $browser
     */
    public function __construct($authenticationKey = '', $browser = null)
    {
        $this->setAuthenticationKey($authenticationKey);
        if (null == $browser) {
            $client = new Curl();
            $client->setTimeout(60 * 60 * 24);
            $this->setBrowser(new Browser($client));
        }
    }

    /**
     * Set authentication API key
     *
     * @param $authenticationKey
     */
    public function setAuthenticationKey($authenticationKey)
    {
        $this->authenticationKey = $authenticationKey;
    }

    /**
     * Get authentication API key
     *
     * @return string
     */
    public function getAuthenticationKey()
    {
        return $this->authenticationKey;
    }

    /**
     * Set request
     *
     * @param Request $request
     */
    public function setLastRequest(Request $request)
    {
        $this->lastRequest = $request;
    }

    /**
     * Get request
     *
     * @return Request
     */
    public function getLastRequest()
    {
        return $this->lastRequest;
    }

    /**
     * Set response
     *
     * @param Response $response
     */
    public function setLastResponse(Response $response)
    {
        $this->lastResponse = $response;
    }

    /**
     * Get response
     *
     * @return Response
     */
    public function getLastResponse()
    {
        return $this->lastResponse;
    }

    /**
     * Set base URI
     *
     * @param string $baseUri
     */
    public function setBaseUri($baseUri)
    {
        $this->baseUri = $baseUri;
    }

    /**
     * Get base URI
     *
     * @return string
     */
    public function getBaseUri()
    {
        return $this->baseUri;
    }

    /**
     * Set browser
     *
     * @param Browser $browser
     */
    public function setBrowser($browser)
    {
        $this->browser = $browser;
    }

    /**
     * Get browser
     *
     * @return Browser
     */
    public function getBrowser()
    {
        return $this->browser;
    }

    /**
     * Get resource
     *
     * @param string $name       Name
     * @param int    $identifier Identifier
     *
     * @return Response\Data
     */
    public function getResource($name, $identifier)
    {
        $this->lastRequest = new Request;
        $this->lastRequest->setMode(Request::MODE_READ);
        $this->lastRequest->setResourceName($name);
        $this->lastRequest->setIdentifier($identifier);

        $response = $this->proceed();

        return $response->getData()->getData();
    }

    /**
     * Get resources
     *
     * @param string     $name    Name
     * @param bool       $full    Display full data
     * @param array      $filters Use filter (E.g. ['website_id' => 123]
     * @param array      $fields  Fields selected to display
     * @param int | null $page    The page to request
     * @param int | null $limit   The pagination limit
     *
     * @return Response\Data
     */
    public function getResources(
        $name,
        $full = false,
        array $filters = [],
        array $fields = [],
        $page = null,
        $limit = null
    ) {
        $this->lastRequest = new Request;
        $this->lastRequest->setMode(Request::MODE_READ);
        $this->lastRequest->setResourceName($name);

        if ($full) {
            $this->lastRequest->enableFullResults();
        }
        $this->lastRequest->setFilters($filters);
        $this->lastRequest->setFields($fields);

        if (!is_null($page)) {
            $this->lastRequest->setCurrentPage($page);
        }
        if (!is_null($limit)) {
            $this->lastRequest->setPaginationLimit($limit);
        }

        $response = $this->proceed();

        return $response->getData()->getData();
    }

    /**
     * Get all resources
     *
     * @param string     $name    Name
     * @param bool       $full    Display full data
     * @param array      $filters Use filter (E.g. ['website_id' => 123]
     * @param array      $fields  Fields selected to display
     *
     * @return array
     */
    public function getAllResources($name, $full = false, array $filters = [], array $fields = []) {
        $this->lastRequest = new Request;
        $this->lastRequest->setMode(Request::MODE_READ);
        $this->lastRequest->setResourceName($name);

        if ($full) {
            $this->lastRequest->enableFullResults();
        }
        $this->lastRequest->setFilters($filters);
        $this->lastRequest->setFields($fields);

        $data = [];

        $page = 1;
        do {
            $this->lastRequest->setCurrentPage($page);
            $response = $this->proceed();

            $data = ArrayUtils::merge($data, $response->getData()->getData());

            $page++;
        } while ($response->getPagination()->getPage() != $response->getPagination()->getPages());

        return $data;
    }

    /**
     * Create resource
     *
     * @param string $name   Name
     * @param array  $fields Fields
     *
     * @return Response\Data
     */
    public function createResource($name, array $fields = [])
    {
        $this->lastRequest = new Request();
        $this->lastRequest->setMode(Request::MODE_CREATE);
        $this->lastRequest->setResourceName($name);
        $this->lastRequest->setFields($fields);

        $response = $this->proceed();

        return $response;
    }

    /**
     * Update resource
     *
     * @param string $name       Name
     * @param int    $identifier Identifier
     * @param array  $fields     Fields
     *
     * @return Response\Data
     */
    public function updateResource($name, $identifier, array $fields = [])
    {
        $this->lastRequest = new Request();
        $this->lastRequest->setMode(Request::MODE_UPDATE);
        $this->lastRequest->setResourceName($name);
        $this->lastRequest->setIdentifier($identifier);
        $this->lastRequest->setFields($fields);

        $response = $this->proceed();

        return $response;
    }

    /**
     * Delete resource
     *
     * @param string $name       Name
     * @param int    $identifier Identifier
     *
     * @return bool
     */
    public function deleteResource($name, $identifier)
    {
        $this->lastRequest = new Request();
        $this->lastRequest->setMode(Request::MODE_DELETE);
        $this->lastRequest->setResourceName($name);
        $this->lastRequest->setIdentifier($identifier);

        $response = $this->proceed();

        return $response->getMeta()->getStatus() == Response\Meta::STATUS_SUCCESS;
    }

    /**
     * Send request
     *
     * @return Response
     */
    public function proceed()
    {
        // Get request
        $request = $this->getLastRequest();

        // Build parameters
        $parameters = [];
        if ($request->getFullResults()) {
            $parameters['full'] = true;
        }
        if ($request->getFilters()) {
            $parameters['filters'] = $request->getFilters();
        }
        if ($request->getFields()) {
            $parameters['fields'] = $request->getFields();
        }
        if ($request->getCurrentPage()) {
            $parameters['page'] = $request->getCurrentPage();
        }
        if ($request->getPaginationLimit()) {
            $parameters['limit'] = $request->getPaginationLimit();
        }

        // Create browser
        $browser = $this->getBrowser();
        $headers = ['X-API-Key' => $this->getAuthenticationKey()];
        $url     = $this->getBaseUri() . $request->getResourceName();
        if ($request->getIdentifier()) {
            $url .= '/' . $request->getIdentifier();
        }
        if ($parameters) {
            $url .= '?' . http_build_query($parameters);
        }

        $content = http_build_query($request->getFields());

        switch ($request->getMode()) {
            default:
            case Request::MODE_READ:
                $response = $browser->get($url, $headers);
                break;
            case Request::MODE_CREATE:
                $response = $browser->post($url, $headers, $content);
                break;
            case Request::MODE_UPDATE:
                $response = $browser->put($url, $headers, $content);
                break;
            case Request::MODE_DELETE:
                $response = $browser->delete($url, $headers);
                break;
        }

        // Content
        $content = $response->getContent();
        $content = json_decode($content, true);

        // Create meta
        $meta = new Response\Meta;
        if (!empty($content['meta'])) {
            if (isset($content['meta']['status'])) {
                $meta->setStatus($content['meta']['status']);
            }
            if (isset($content['meta']['message'])) {
                $meta->setMessage($content['meta']['message']);
            }
            if (isset($content['meta']['filters'])) {
                $meta->setFilters($content['meta']['filters']);
            }
        }

        // Create data
        $data = new Response\Data;
        if (!empty($content['data'])) {
            $data->setData($content['data']);
        }

        // Create pagination
        $pagination = new Response\Pagination;
        if (!empty($content['pagination'])) {

            if (!empty($content['pagination']['page'])) {
                $pagination->setPage($content['pagination']['page']);
            }
            if (!empty($content['pagination']['pages'])) {
                $pagination->setPages($content['pagination']['pages']);
            }
            if (!empty($content['pagination']['limit'])) {
                $pagination->setLimit($content['pagination']['limit']);
            }
            if (!empty($content['pagination']['count'])) {
                $pagination->setCount($content['pagination']['count']);
            }
        }

        // Create response
        $response = new Response;
        $response->setMeta($meta);
        $response->setData($data);
        $response->setPagination($pagination);

        // Set to current response
        $this->setLastResponse($response);

        return $response;
    }
}


<?php
/**
 * @copyright  Copyright (C) 2013 - 2015 Alex Prut (Alexandru Pruteanu)
 * @license    Licensed under the MIT License; see LICENSE
 */

include_once 'configuration.php';

/**
 * A class to handle and make HTTP requests using the cURL library
 */
class Http
{
	/**
	 * Makes an HTTP request and return the page HTML response
	 *
	 * @param   string  $url  The url to make HTTP request and retrieve the HTML
	 *
	 * @return	string
	 */
	public function httpRequest($url)
	{
		if (DEBUG)
			echo "HTTP request: $url\n";

		// Initialize
		$curl = curl_init();

		// Setup the target
		curl_setopt($curl, CURLOPT_URL, $url);
		curl_setopt($curl, CURLOPT_HEADER, 0);

		// Return in string
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

		// Timeout
		curl_setopt($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);

		// Webbot name
		curl_setopt($curl, CURLOPT_USERAGENT, SPIDER_NAME);

		// Minimize logs
		curl_setopt($curl, CURLOPT_VERBOSE, false);

		// Limit redirections to 4
		curl_setopt($curl, CURLOPT_MAXREDIRS, 4);

		// Follow redirects
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

		// Create return array
		$response['file']	= curl_exec($curl);
		$response['status']	= curl_getinfo($curl);
		$response['error']	= curl_error($curl);

		// Execute the request
		curl_exec($curl);

		// Close the handler
		curl_close($curl);

		if (DEBUG === 'verbose')
		{
			echo "Retrieved HTTP:\n";
			var_dump($response['status']);
			var_dump($response['error']);
		}

		if ($response['file'] == '')
			die("Error while making the HTTP request: no HTML retrieved.");

		return $response;
	}
}


<?php
/**
* The class to login to a web site with LinkedIn account
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/

namespace Gelembjuk\Auth\SocialLogin;

class Linkedin extends Base {
	/**
	 * LinkedIn API object
	 *
	 * @var LinkedIn\LinkedIn
	 */
	protected $linkedin;
	/**
	 * To remember redirect url during auth session
	 *
	 * @var string
	 */
	protected $redirecturl;
	/**
	 * LinkedIn API token
	 *
	 * @var string
	 */
	protected $token;

	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
    public function isConfigured() {
        if (empty($this->options['api_key']) ||
            empty($this->options['api_secret'])) {
            return false;
        }
        return true;
    }
	/**
	 * Returns list of properties to serialise.
	 *
	 * @param array $skip Defines what properties to skip
	 */
	protected function getSerializeVars($skip = array()) {
		$skip[] = 'linkedin';
		return parent::getSerializeVars($skip);
	}
	/**
	 * Returns LinkedIn API object inited with API settings
	 *
	 * @return LinkedIn\LinkedIn
	 */
	protected function getClient($redirecturl = '') {
		// keep only one instance during a session
		if (is_object($this->linkedin)) {
			return $this->linkedin;
		}

		if ($redirecturl == '') {
			$redirecturl = $this->redirecturl;
		} else {
			$this->redirecturl = $redirecturl;
		}
		$this->logQ('redirect '.$redirecturl,'linkedin');

		$API_CONFIG = array(
			'api_key' => $this->options['api_key'],
			'api_secret' => $this->options['api_secret'],
			'callback_url' => $redirecturl
			);

		$this->linkedin = $linkedin = new \LinkedIn\LinkedIn($API_CONFIG);

		return $this->linkedin;
	}
	/**
	 * Returns linkedin login start auth process
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	public function getLoginStartUrl($redirecturl) {
		$linkedin = $this->getClient($redirecturl);

		$url = $linkedin->getLoginUrl(
			array(
				\LinkedIn\LinkedIn::SCOPE_BASIC_PROFILE,
				\LinkedIn\LinkedIn::SCOPE_EMAIL_ADDRESS
				)
			);
		return $url;
	}
	/**
	 * Get array of GET/POST arguments posted from linkedin back to complete login URL
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		return array('code');
	}
	/**
	 * Completes social login. Is caled after redirect from linkedin auth page
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	public function completeLogin($extrainputs = array()) {
		$linkedin = $this->getClient();
		$this->token = $linkedin->getAccessToken($extrainputs['code']);

		return $this->getUserProfile();
	}
	/**
	 * Returns short linkedin profile after succes login
	 *
	 * @return array User Profile
	 */
	public function getUserProfile() {
		$linkedin = $this->getClient();

		$response = $linkedin->get('/people/~:(id,first-name,last-name,picture-url,public-profile-url,email-address)');

		if (isset($response['emailAddress'])) {
			return array(
				'userid'=>$response['id'],
				'name'=>$response['firstName'].' '.$response['lastName'],
				'email'=>$response['emailAddress'],
				'imageurl'=>$response['pictureUrl']
			);
		}
	}
}


<?php

namespace Iadvize\ApiRestClient\Response;

/**
 * Response data
 *
 * @package Iadvize\ApiRestClient\Response
 */
class Data
{
    /** @var Mixed Data */
    protected $data = [];

    /**
     * Set data
     * (fix JSON Decode)
     *
     * @param mixed $data
     */
    public function setData($data)
    {
        $this->data = null;

        if (is_array($data)) {
            foreach ($data as $index => $fields) {
                if (is_array($fields)) {
                    foreach ($fields as $key => $value) {
                        if ('id' == $key && is_array($value)) {
                            foreach ($value as $subKey => $subValue) {
                                $this->data[$index][$subKey] = $subValue;
                            }
                        } else {
                            $this->data[$index][$key] = $value;
                        }
                    }
                } else {
                    $this->data[$index] = $fields;
                }
            }
        } else {
            $this->data = $data;
        }
    }

    /**
     * Get data
     *
     * @return mixed
     */
    public function getData()
    {
        return $this->data;
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context\Email;

// Contexts.
use Drupal\TqExtension\Context\RawTqContext;
// Utils.
use Behat\DebugExtension\Message;
use Drupal\TqExtension\Utils\Imap;
use Drupal\TqExtension\Utils\Database\FetchField;

class RawEmailContext extends RawTqContext
{
    use Imap;

    private $messages = [];
    private $email = '';

    /**
     * @param string $to
     *
     * @throws \RuntimeException
     *
     * @return array
     */
    public function getEmailMessages($to = '')
    {
        // Update address for checking.
        if (!empty($to) && $this->email !== $to) {
            $this->email = $to;
        }

        if (empty($this->messages[$this->email])) {
            $messages = self::hasTag('imap')
              ? $this->getMessagesViaImap($this->email)
              : $this->getMessagesFromDb();

            if (empty($messages)) {
                throw new \RuntimeException(sprintf('The message for "%s" was not sent.', $this->email));
            }

            foreach ($messages as &$message) {
                if ($message['to'] === $this->email) {
                    $message['links'] = $this->parseLinksText($message['body']);
                }
            }

            $this->messages[$this->email] = $messages;
        }

        // The debug messages may differ due to testing testing mode:
        // Drupal mail system collector or IMAP protocol.
        self::debug(['%s'], [var_export($this->messages[$this->email], true)]);

        return $this->messages[$this->email];
    }

    /**
     * @param string $email
     *
     * @throws \InvalidArgumentException
     *
     * @return array
     */
    public function getAccount($email)
    {
        $accounts = $this->getTqParameter('email_accounts');

        if (empty($accounts[$email])) {
            throw new \InvalidArgumentException(sprintf(
                'An account for "%s" email address is not defined. Available addresses: "%s".',
                $email,
                implode(', ', array_keys($accounts))
            ));
        }

        return $accounts[$email];
    }

    public function parseLinksText($string)
    {
        $links = [];

        /** @var \DOMElement $link */
        foreach ((new \DOMXPath(self::parseHTML($string)))->query('//a[@href]') as $link) {
            $links[$link->textContent] = $link->getAttribute('href');
        }

        if (empty($links)) {
            preg_match_all('/((?:http(?:s)?|www)[^\s]+)/i', $string, $matches);

            if (!empty($matches[1])) {
                $links = $matches[1];
            }
        }

        return $links;
    }

    private function getMessagesViaImap($email)
    {
        $account = $this->getAccount($email);
        $timeout = $this->getTqParameter('wait_for_email');

        $this->setConnection($email, $account['imap'], $account['username'], $account['password']);

        if ($timeout > 0) {
            new Message('comment', 4, ['Waiting %s seconds for letter...'], [$timeout]);
            sleep($timeout);
        }

        return $this->getMessages($email);
    }

    private function getMessagesFromDb()
    {
        // We can't use variable_get() because Behat has another bootstrapped
        // variable $conf that is not updated from cURL bootstrapped Drupal instance.
        $result = (new FetchField('variable', 'value'))
            ->condition('name', 'drupal_test_email_collector')
            ->execute();

        $result = empty($result) ? [] : unserialize($result);

        self::debug(['Emails from the database: %s'], [var_export($result, true)]);

        return $result;
    }

    private static function parseHTML($string)
    {
        $document = new \DOMDocument();

        // Handle errors/warnings and don't mess up output of your script.
        // @see http://stackoverflow.com/a/17559716
        $libxml_state = libxml_use_internal_errors(true);
        $document->loadHTML($string);

        libxml_clear_errors();
        libxml_use_internal_errors($libxml_state);

        return $document;
    }
}


<?php

/**
 * Aphiria
 *
 * @link      https://www.aphiria.com
 * @copyright Copyright (C) 2019 David Young
 * @license   https://github.com/aphiria/net/blob/master/LICENSE.md
 */

declare(strict_types=1);

namespace Aphiria\Net\Http\ContentNegotiation;

use Aphiria\Net\Http\Headers\AcceptLanguageHeaderValue;
use Aphiria\Net\Http\Headers\IHeaderValueWithQualityScore;

/**
 * Defines the language matcher
 */
final class LanguageMatcher
{
    /**
     * Gets the best language match between a list of supported languages and Accept-Language headers
     * This uses "lookup" matching per RFC-4647 section 3.4
     *
     * @param array $supportedLanguages The list of supported languages
     * @param AcceptLanguageHeaderValue[] $languageHeaders The list of language headers to rank
     * @return string|null The best language match if one existed, otherwise null
     * @link https://tools.ietf.org/html/rfc4647#section-3.4
     */
    public function getBestLanguageMatch(array $supportedLanguages, array $languageHeaders): ?string
    {
        usort($languageHeaders, [$this, 'compareAcceptLanguageHeaders']);
        $rankedLanguageHeaders = array_filter($languageHeaders, [$this, 'filterZeroScores']);
        $rankedLanguageHeaderValues = $this->getLanguageValuesFromHeaders($rankedLanguageHeaders);

        foreach ($rankedLanguageHeaderValues as $language) {
            $languageParts = explode('-', $language);

            // Progressively truncate this language tag and try to match a supported language
            do {
                foreach ($supportedLanguages as $supportedLanguage) {
                    if ($language === '*' || implode('-', $languageParts) === $supportedLanguage) {
                        return $supportedLanguage;
                    }
                }

                array_pop($languageParts);
            } while (count($languageParts) > 0);
        }

        return null;
    }

    /**
     * Compares two languages and returns which of them is "lower" than the other
     *
     * @param AcceptLanguageHeaderValue $a The first language header to compare
     * @param AcceptLanguageHeaderValue $b The second language header to compare
     * @return int -1 if $a is lower than $b, 0 if they're even, or 1 if $a is higher than $b
     */
    private function compareAcceptLanguageHeaders(AcceptLanguageHeaderValue $a, AcceptLanguageHeaderValue $b): int
    {
        $aQuality = $a->getQuality();
        $bQuality = $b->getQuality();

        if ($aQuality < $bQuality) {
            return 1;
        }

        if ($aQuality > $bQuality) {
            return -1;
        }

        $aValue = $a->getLanguage();
        $bValue = $b->getLanguage();

        if ($aValue === '*') {
            if ($bValue === '*') {
                return 0;
            }

            return 1;
        }

        if ($bValue === '*') {
            return -1;
        }

        return 0;
    }

    /**
     * Filters out any header values with a zero quality score
     *
     * @param IHeaderValueWithQualityScore $header The value to check
     * @return bool True if we should keep the value, otherwise false
     */
    private function filterZeroScores(IHeaderValueWithQualityScore $header): bool
    {
        return $header->getQuality() > 0;
    }

    /**
     * Gets the language values from a list of headers
     *
     * @param AcceptLanguageHeaderValue[] $headers The list of language headers
     * @return array The list of language values from the headers
     */
    private function getLanguageValuesFromHeaders(array $headers): array
    {
        $languages = [];

        foreach ($headers as $header) {
            $languages[] = $header->getLanguage();
        }

        return $languages;
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context\User;

// Contexts.
use Drupal\TqExtension\Context\RawTqContext;
// Utils.
use Drupal\TqExtension\Utils\BaseEntity;
use Drupal\TqExtension\Utils\Database\FetchField;
use Drupal\TqExtension\Utils\EntityDrupalWrapper;

class RawUserContext extends RawTqContext
{
    use BaseEntity;

    /**
     * {@inheritdoc}
     */
    protected function entityType()
    {
        return 'user';
    }

    /**
     * {@inheritdoc}
     */
    public function getCurrentId()
    {
        return empty($this->user->uid) ? 0 : $this->user->uid;
    }

    /**
     * @param string $column
     *   Column of a "users" table.
     * @param string $value
     *   Expected value in column.
     *
     * @return int
     */
    public function getIdByArguments($column, $value)
    {
        return (new FetchField('users', 'uid'))
            ->condition($column, $value)
            ->execute();
    }

    /**
     * @param string $roles
     *   Necessary user roles separated by comma.
     * @param array $fields
     *
     * @return \stdClass
     */
    public function createUserWithRoles($roles, array $fields = [])
    {
        $user = $this->createTestUser($fields);
        $driver = $this->getDriver();

        foreach (array_map('trim', explode(',', $roles)) as $role) {
            $driver->userAddRole($user, $role);
        }

        return $user;
    }

    /**
     * @throws \Exception
     */
    public function loginUser()
    {
        $this->logoutUser();

        if (empty($this->user)) {
            throw new \Exception('Tried to login without a user.');
        }

        $this->fillLoginForm([
            'username' => $this->user->name,
            'password' => $this->user->pass,
        ]);
    }

    /**
     * @param array $props
     *   An array with two keys: "username" and "password". Both of them are required.
     * @param string $message
     *   An error message, that will be thrown when user cannot be authenticated.
     *
     * @throws \Behat\Mink\Exception\ElementNotFoundException
     *   When one of a fields cannot be not found.
     * @throws \Exception
     *   When login process failed.
     * @throws \WebDriver\Exception\NoSuchElement
     *   When log in button cannot be found.
     */
    public function fillLoginForm(array $props, $message = '')
    {
        $this->visitPath('/user/login');
        $formContext = $this->getFormContext();

        foreach (['username', 'password'] as $prop) {
            $formContext->fillField($this->getDrupalText($prop . '_field'), $props[$prop]);
        }

        $this->getWorkingElement()->pressButton($this->getDrupalText('log_in'));

        if (!$this->isLoggedIn()) {
            if (empty($message)) {
                $message = sprintf(
                    'Failed to login as a user "%s" with password "%s".',
                    $props['username'],
                    $props['password']
                );
            }

            throw new \Exception($message);
        }

        $GLOBALS['user'] = $this->user = user_load_by_name($props['username']);
    }

    /**
     * Cookies are set when at least one page of the site has been visited. This
     * action done in "beforeScenario" hook of TqContext.
     *
     * @see TqContext::beforeScenario()
     *
     * @return bool
     */
    public function isLoggedIn()
    {
        $cookieName = session_name();
        $cookie = $this->getSession()->getCookie($cookieName);

        if (null !== $cookie) {
            $this->getSession('goutte')->setCookie($cookieName, $cookie);

            return true;
        }

        return false;
    }

    public function logoutUser()
    {
        if ($this->isLoggedIn()) {
            $this->logout();
        }
    }

    /**
     * @param array $fields
     *   Additional data for user account.
     *
     * @throws \Exception
     *
     * @return \stdClass
     */
    public function createTestUser(array $fields = [])
    {
        $random = $this->getRandom();
        $username = $random->name(8);
        $user = [
            'uid' => 0,
            'name' => $username,
            'pass' => $random->name(16),
            'mail' => "$username@example.com",
            'roles' => [
                DRUPAL_AUTHENTICATED_RID => 'authenticated user',
            ],
        ];

        $user = (object) $user;

        if (!empty($fields)) {
            $entity = new EntityDrupalWrapper('user');
            $required = $entity->getRequiredFields();

            // Fill fields. Field can be found by name or label.
            foreach ($fields as $field_name => $value) {
                $field_info = $entity->getFieldInfo($field_name);

                if (!empty($field_info)) {
                    $field_name = $field_info['field_name'];
                }

                $user->$field_name = $value;

                // Remove field from $required if it was there and filled.
                unset($required[$field_name]);
            }

            // Throw an exception when one of required fields was not filled.
            if (!empty($required)) {
                throw new \Exception(sprintf(
                    'The following fields "%s" are required and has not filled.',
                    implode('", "', $required)
                ));
            }
        }

        if (isset($user->name)) {
            $existing_user = user_load_by_name($user->name);

            if (!empty($existing_user)) {
                user_delete($existing_user->uid);
            }
        }

        // $this->user always exist but when no user created it has "false" as a value.
        // Variable stored to another because RawDrupalContext::userCreate() will modify
        // it and this will affect for future actions.
        if (!empty($this->user)) {
            $tmp = $this->user;
        }

        $this->userCreate($user);

        if (isset($tmp)) {
            $this->user = $tmp;
        }

        return $user;
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context;

// Contexts.
use Drupal\DrupalExtension\Context\RawDrupalContext;
// Exceptions.
use WebDriver\Exception\NoSuchElement;
// Helpers.
use Behat\Mink\Element\NodeElement;
// Utils.
use Drupal\TqExtension\Utils\XPath;

class RawPageContext extends RawDrupalContext
{
    /**
     * @var NodeElement
     */
    private static $workingElement;

    /**
     * @return NodeElement
     */
    public function getWorkingElement()
    {
        if (null === self::$workingElement) {
            $this->setWorkingElement($this->getBodyElement());
        }

        return self::$workingElement;
    }

    /**
     * @param NodeElement $element
     */
    public function setWorkingElement(NodeElement $element)
    {
        self::$workingElement = $element;
    }

    public function unsetWorkingElement()
    {
        self::$workingElement = null;
    }

    /**
     * @param string $selector
     *
     * @return NodeElement
     */
    public function findByCss($selector)
    {
        return $this->getWorkingElement()
            ->find(empty($this->getDrupalParameter('region_map')[$selector]) ? 'css' : 'region', $selector);
    }

    /**
     * @param string $selector
     *
     * @return NodeElement|null
     */
    public function findField($selector)
    {
        $selector = ltrim($selector, '#');
        $element = $this->getWorkingElement();

        foreach ($this->findLabels($selector) as $forAttribute => $label) {
            // We trying to find an ID with "-upload" suffix, because some
            // image inputs in Drupal are suffixed by it.
            foreach ([$forAttribute, "$forAttribute-upload"] as $elementID) {
                $field = $element->findById($elementID);

                if (null !== $field) {
                    return $field;
                }
            }
        }

        return $element->findField($selector);
    }

    /**
     * @param string $selector
     *
     * @return NodeElement
     */
    public function findButton($selector)
    {
        $element = $this->getWorkingElement();

        // Search inside of: "id", "name", "title", "alt" and "value" attributes.
        return $element->findButton($selector)
            ?: (new XPath\InaccurateText('//button', $element))->text($selector)->find();
    }

    /**
     * @param string $text
     *
     * @return NodeElement|null
     */
    public function findByText($text)
    {
        return (new XPath\InaccurateText('//*', $this->getWorkingElement()))->text($text)->find();
    }

    /**
     * @param string $locator
     *   Element locator. Can be inaccurate text, inaccurate field label, CSS selector or region name.
     *
     * @throws NoSuchElement
     *
     * @return NodeElement
     */
    public function findElement($locator)
    {
        return $this->findByCss($locator)
            ?: $this->findField($locator)
                ?: $this->findButton($locator)
                    ?: $this->findByText($locator);
    }

    /**
     * Find all field labels by text.
     *
     * @param string $text
     *   Label text.
     *
     * @return NodeElement[]
     */
    public function findLabels($text)
    {
        $xpath = new XPath\InaccurateText('//label[@for]', $this->getWorkingElement());
        $labels = [];

        foreach ($xpath->text($text)->findAll() as $label) {
            $labels[$label->getAttribute('for')] = $label;
        }

        return $labels;
    }

    /**
     * @return NodeElement
     */
    public function getBodyElement()
    {
        return $this->getSession()->getPage()->find('css', 'body');
    }

    /**
     * @param string $selector
     *   Element selector.
     * @param mixed $element
     *   Existing element or null.
     *
     * @throws NoSuchElement
     */
    public function throwNoSuchElementException($selector, $element)
    {
        if (null === $element) {
            throw new NoSuchElement(sprintf('Cannot find an element by "%s" selector.', $selector));
        }
    }

    /**
     * @param string $locator
     * @param string $selector
     *
     * @throws \RuntimeException
     * @throws NoSuchElement
     *
     * @return NodeElement
     */
    public function element($locator, $selector)
    {
        $map = [
            'button' => 'Button',
            'field' => 'Field',
            'text' => 'ByText',
            'css' => 'ByCss',
            '*' => 'Element',
        ];

        if (!isset($map[$locator])) {
            throw new \RuntimeException(sprintf('Locator "%s" was not specified.'));
        }

        $selector = t($selector);
        $element = $this->{'find' . $map[$locator]}($selector);
        $this->throwNoSuchElementException($selector, $element);

        return $element;
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Utils\Database;

// Utils.
use Behat\DebugExtension\Debugger;

class FetchField
{
    use Debugger;

    /**
     * @var \SelectQuery
     */
    private $query;

    /**
     * FetchField constructor.
     *
     * @param string $table
     * @param string $field
     */
    public function __construct($table, $field)
    {
        $this->query = db_select($table);
        $this->query->fields($table, [$field]);
    }

    /**
     * @param string $field
     * @param string|array $value
     * @param string $operator
     *
     * @return $this
     */
    public function condition($field, $value = null, $operator = null)
    {
        $this->query->condition($field, $value, $operator);

        return $this;
    }

    /**
     * @return string
     */
    public function execute()
    {
        self::debug(
            ['SQL query is: %s'],
            [trim(str_replace("\n", ' ', $this->query))]
        );

        return $this->query->execute()->fetchField();
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Utils;

// Contexts.
use Drupal\TqExtension\Context\RawTqContext;
// Helpers.
use Behat\DebugExtension\Debugger;
use Behat\Mink\Element\NodeElement;

class DatePicker
{
    use Debugger;

    /**
     * @var NodeElement
     */
    private $element;
    /**
     * @var string
     */
    private $date = '';
    /**
     * @var RawTqContext
     */
    private $context;

    /**
     * @param RawTqContext $context
     *   Behat context.
     * @param string $selector
     *   Element selector.
     * @param string $date
     *   Human-readable date.
     */
    public function __construct(RawTqContext $context, $selector, $date)
    {
        $this->context = $context;

        if (null === $this->execute('$.fn.datepicker')) {
            throw new \RuntimeException('jQuery DatePicker is not available on the page.');
        }

        $this->element = $this->context->element('*', $selector);
        $this->date = self::jsDate($date);
    }

    /**
     * @return self
     */
    public function setDate()
    {
        // @todo setDate will not work if browser window is not active.
        $this->datePicker([__FUNCTION__, '<date>']);

        return $this;
    }

    /**
     * @throws \Exception
     *
     * @return self
     */
    public function isDateSelected()
    {
        $value = $this->datePicker(['getDate']);
        $initial = $this->execute($this->date);

        // By some reasons DatePicker could not return a date using "getDate" method
        // and we'll try to use it from input value directly. An issue could occur
        // after saving the form and/or reloading the page.
        if (empty($value)) {
            $value = $this->execute(self::jsDate($this->element->getValue()));
        }

        self::debug(['Comparing "%s" with "%s".'], [$value, $initial]);

        if ($value !== $initial) {
            throw new \Exception(sprintf('DatePicker contains the "%s" but should "%s".', $value, $initial));
        }

        return $this;
    }

    /**
     * @throws \Exception
     *
     * @return self
     */
    public function isDateAvailable()
    {
        // @todo For now, any out of scope variable inside of "beforeShowDay" method will be undefined.
        // @link https://github.com/refactoror/SelBlocks/issues/5#issuecomment-68511965
        $beforeShowDay = $this->datePicker(['option', 'beforeShowDay']);

        if (!empty($beforeShowDay) && !empty($this->execute("$beforeShowDay($this->date)")[0])) {
            throw new \Exception(sprintf('The "%s" is not available for choosing.', $this->date));
        }

        return $this;
    }

    /**
     * @param string $javascript
     *   JS code for execution.
     *
     * @return mixed
     *   Result of JS execution.
     */
    private function execute($javascript)
    {
        return $this->context->executeJs("return $javascript;");
    }

    /**
     * @param array $arguments
     *   jQuery.fn.datepicker arguments.
     *
     * @return mixed
     *   Result of JS execution.
     */
    private function datePicker(array $arguments)
    {
        return $this->context->executeJsOnElement($this->element, sprintf(
            "return jQuery({{ELEMENT}}).datepicker(%s);",
            implode(', ', array_map(function ($value) {
                return in_array($value, ['<date>']) ? $this->date : "'$value'";
            }, $arguments))
        ));
    }

    /**
     * @param string $date
     *   The string to parse.
     *
     * @return string
     */
    private static function jsDate($date)
    {
        return sprintf("new Date('%s')", date('c', strtotime($date)));
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context;

use Behat\Behat\Context as Behat;

class TqContextInitializer implements Behat\Initializer\ContextInitializer
{
    /**
     * Parameters of TqExtension.
     *
     * @var array
     */
    private $parameters = [];

    /**
     * @param array $parameters
     * @param string $namespace
     * @param string $path
     */
    public function __construct(array $parameters, $namespace, $path)
    {
        $parameters['namespace'] = $namespace;

        $this->parameters = $parameters;
    }

    /**
     * {@inheritdoc}
     */
    public function initializeContext(Behat\Context $context)
    {
        if ($context instanceof TqContextInterface) {
            $context->setTqParameters($this->parameters);
        }
    }
}


<?php

/**
 * Aphiria
 *
 * @link      https://www.aphiria.com
 * @copyright Copyright (C) 2019 David Young
 * @license   https://github.com/aphiria/net/blob/master/LICENSE.md
 */

declare(strict_types=1);

namespace Aphiria\Net\Http\ContentNegotiation;

use Aphiria\Net\Http\HttpException;
use Aphiria\Net\Http\HttpHeaders;
use Aphiria\Net\Http\HttpStatusCodes;
use Aphiria\Net\Http\IHttpBody;
use Aphiria\Net\Http\IHttpRequestMessage;
use Aphiria\Net\Http\IHttpResponseMessage;
use Aphiria\Net\Http\Response;
use Aphiria\Net\Http\StreamBody;
use Aphiria\Net\Http\StringBody;
use Aphiria\Serialization\SerializationException;
use Aphiria\Serialization\TypeResolver;
use InvalidArgumentException;
use Opulence\IO\Streams\IStream;
use Opulence\IO\Streams\Stream;

/**
 * Defines the factory that generates HTTP responses from negotiated content
 */
final class NegotiatedResponseFactory implements INegotiatedResponseFactory
{
    /** @var IContentNegotiator The content negotiator to use */
    private $contentNegotiator;

    /**
     * @param IContentNegotiator $contentNegotiator The content negotiator to use
     */
    public function __construct(IContentNegotiator $contentNegotiator)
    {
        $this->contentNegotiator = $contentNegotiator;
    }

    /**
     * @inheritdoc
     */
    public function createResponse(
        IHttpRequestMessage $request,
        int $statusCode,
        ?HttpHeaders $headers,
        $rawBody
    ): IHttpResponseMessage {
        $headers = $headers ?? new HttpHeaders;

        try {
            /** @var ContentNegotiationResult|null $contentNegotiationResult */
            $contentNegotiationResult = null;
            $body = $this->createBody($request, $rawBody, $contentNegotiationResult);
        } catch (InvalidArgumentException $ex) {
            throw new HttpException(
                HttpStatusCodes::HTTP_INTERNAL_SERVER_ERROR,
                'Failed to create response body',
                0,
                $ex
            );
        }

        if (
            $contentNegotiationResult !== null
            && ($mediaType = $contentNegotiationResult->getMediaType()) !== null
        ) {
            $headers->add('Content-Type', $mediaType);
        }

        if (
            $body !== null
            && !$headers->containsKey('Content-Length')
            && ($contentLength = $body->getLength()) !== null
        ) {
            $headers->add('Content-Length', $contentLength);
        }

        return new Response($statusCode, $headers, $body);
    }

    /**
     * Creates a negotiated response body from a request
     *
     * @param IHttpRequestMessage $request The current request
     * @param object|string|int|float|array|null $rawBody The raw body to use in the response
     * @param ContentNegotiationResult|null $contentNegotiationResult The response content negotiation result
     * @return IHttpBody|null The body if one was created, otherwise null
     * @throws InvalidArgumentException Thrown if the body is not a supported type
     * @throws HttpException Thrown if the response content could not be negotiated
     */
    private function createBody(
        IHttpRequestMessage $request,
        $rawBody,
        ContentNegotiationResult &$contentNegotiationResult = null
    ): ?IHttpBody {
        if ($rawBody === null || $rawBody instanceof IHttpBody) {
            return $rawBody;
        }

        if ($rawBody instanceof IStream) {
            return new StreamBody($rawBody);
        }

        if (is_scalar($rawBody)) {
            return new StringBody((string)$rawBody);
        }

        if ((!is_object($rawBody) && !is_array($rawBody)) || is_callable($rawBody)) {
            throw new InvalidArgumentException('Unsupported body type ' . gettype($rawBody));
        }

        $type = TypeResolver::resolveType($rawBody);
        $contentNegotiationResult = $this->contentNegotiator->negotiateResponseContent($type, $request);
        $mediaTypeFormatter = $contentNegotiationResult->getFormatter();

        if ($mediaTypeFormatter === null) {
            throw $this->createNotAcceptableException($type);
        }

        $bodyStream = new Stream(fopen('php://temp', 'r+b'));

        try {
            $mediaTypeFormatter->writeToStream(
                $rawBody,
                $bodyStream,
                $contentNegotiationResult->getEncoding()
            );
        } catch (SerializationException $ex) {
            throw new HttpException(
                HttpStatusCodes::HTTP_INTERNAL_SERVER_ERROR,
                'Failed to serialize response body',
                0,
                $ex
            );
        }

        return new StreamBody($bodyStream);
    }

    /**
     * Creates a 406 Not Acceptable exception
     *
     * @param string $type The type that was attempted to be written
     * @return HttpException The exception with the response set
     */
    private function createNotAcceptableException(string $type): HttpException
    {
        $headers = new HttpHeaders();
        $headers->add('Content-Type', 'application/json');
        $body = new StringBody(json_encode($this->contentNegotiator->getAcceptableResponseMediaTypes($type)));
        $response = new Response(HttpStatusCodes::HTTP_NOT_ACCEPTABLE, $headers, $body);

        return new HttpException($response);
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context\User;

// Helpers.
use Behat\Gherkin\Node\TableNode;

class UserContext extends RawUserContext
{
    /**
     * @param string $operation
     *   Allowable values: "edit", "view", "visit".
     * @param string $value
     *   The name or email of a user.
     *
     * @When /^(?:|I )(visit|view|edit) (?:the "([^"]+)"|current) user$/
     */
    public function visitPage($operation, $value = '')
    {
        $column = $value;

        // If we have an empty value then try to use current user.
        if ('' !== $value) {
            $column = strpos($value, '@') === false ? 'name' : 'mail';
        }

        // This helps us restrict an access for editing for users without correct permissions.
        // Will check for 403 HTTP status code.
        $this->getRedirectContext()
            // Check user by email if value contains a "dog" symbol.
            ->visitPage($this->entityUrl($operation, $column, $value));
    }

    /**
     * @example
     * Then I am logged in as a user with "CRM Client" role and filled fields
     *   | Full name                | Sergii Bondarenko |
     *   | Position                 | Developer         |
     *   | field_crm_user_company   | FFW               |
     *
     * @param string $roles
     *   User roles, separated by comma.
     * @param TableNode $fields
     *   | Field machine name or label | Value |
     *
     * @throws \EntityMetadataWrapperException
     *   When user object cannot be saved.
     * @throws \Exception
     *   When required fields are not filled.
     *
     * @Given /^(?:|I am )logged in as a user with "([^"]*)" role(?:|s)(?:| and filled fields:)$/
     */
    public function loginCreatedUser($roles, TableNode $fields = null)
    {
        $this->createDrupalUser($roles, $fields);
        $this->loginUser();
    }

    /**
     * @see loginCreatedUser()
     *
     * @Then /^(?:|I )create a user with "([^"]*)" role(?:|s)(?:| and filled fields:)$/
     */
    public function createDrupalUser($roles, TableNode $fields = null)
    {
        $this->createUserWithRoles($roles, null !== $fields ? $fields->getRowsHash() : []);
    }

    /**
     * @param TableNode $credentials
     *   | username | BR0kEN |
     *   | password | p4sswd |
     *
     * @throws \Exception
     *   When user cannot be authorized.
     *
     * @Given /^(?:|I )am logged in with credentials:/
     */
    public function loginWithCredentials(TableNode $credentials)
    {
        $this->fillLoginForm($credentials->getRowsHash());
    }

    /**
     * This step must be used instead of "I am an anonymous user" because it
     * has more strict checking for authenticated user.
     *
     * @Given /^I am unauthorized user$/
     * @Given /^I am log out$/
     */
    public function logoutDrupalUser()
    {
        $this->logoutUser();
    }

    /**
     * @AfterScenario
     */
    public function afterUserScenario()
    {
        // Logout, when scenario finished an execution, is required for "Scenario Outline" because an
        // object will not be instantiated for every iteration and user data, from previous one, will
        // be kept.
        $this->logoutUser();
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Utils;

// Contexts.
use Drupal\TqExtension\Context\RawTqContext;
// Helpers.
use Behat\DebugExtension\Debugger;
use Behat\Mink\Element\NodeElement;

class FormValueAssertion
{
    use Debugger;

    /**
     * @var RawTqContext
     */
    private $context;
    /**
     * @var string
     *   Field selector.
     */
    private $selector = '';
    /**
     * Found element.
     *
     * @var NodeElement
     */
    private $element;
    /**
     * Expected value.
     *
     * @var string
     */
    private $expected = '';
    /**
     * Field element value.
     *
     * @var string
     */
    private $value = '';
    /**
     * Tag name of found element.
     *
     * @var string
     */
    private $tag = '';
    /**
     * Negate the condition.
     *
     * @var bool
     */
    private $not = false;

    /**
     * @param RawTqContext $context
     *   Behat context.
     * @param string $selector
     *   Field selector.
     * @param bool $not
     *   Negate the condition.
     * @param string $expected
     *   Expected value.
     */
    public function __construct(RawTqContext $context, $selector, $not, $expected = '')
    {
        $this->not = (bool) $not;
        $this->context = $context;
        $this->selector = $selector;
        $this->expected = $expected;

        $this->element = $this->context->element('field', $selector);
        $this->value = $this->element->getValue();
        $this->tag = $this->element->getTagName();
    }

    /**
     * Check value in inputs and text areas.
     */
    public function textual()
    {
        $this->restrictElements([
            'textarea' => [],
            'input' => [],
        ]);

        self::debug([
            'Expected: %s',
            'Value: %s',
            'Tag: %s',
        ], [
            $this->expected,
            $this->value,
            $this->tag,
        ]);

        $this->assert(trim($this->expected) === $this->value);
    }

    /**
     * Ensure option is selected.
     */
    public function selectable()
    {
        $this->restrictElements(['select' => []]);
        $data = [$this->value, $this->element->find('xpath', "//option[@value='$this->value']")->getText()];

        self::debug([
            'Expected: %s',
            'Value: %s',
            'Tag: %s',
        ], [
            $this->expected,
            implode(' => ', $data),
            $this->tag,
        ]);

        $this->assert(in_array($this->expected, $data), 'selected');
    }

    /**
     * Ensure that checkbox/radio button is checked.
     */
    public function checkable()
    {
        $this->restrictElements(['input' => ['radio', 'checkbox']]);

        if (!in_array($this->element->getAttribute('type'), ['radio', 'checkbox'])) {
            throw new \RuntimeException('Element cannot be checked.');
        }

        self::debug(['%s'], [$this->element->getOuterHtml()]);

        $this->assert($this->element->isChecked(), 'checked');
    }

    /**
     * @param array[] $allowedElements
     *   Element machine names.
     */
    private function restrictElements(array $allowedElements)
    {
        // Match element tag with allowed.
        if (!isset($allowedElements[$this->tag])) {
            throw new \RuntimeException("Tag is not allowed: $this->tag.");
        }

        $types = $allowedElements[$this->tag];

        // Restrict by types only if they are specified.
        if (!empty($types)) {
            $type = $this->element->getAttribute('type');

            if (!in_array($type, $types)) {
                throw new \RuntimeException(sprintf('Type "%s" is not allowed for "%s" tag', $type, $this->tag));
            }
        }
    }

    /**
     * @param bool $value
     *   Value for checking.
     * @param string $word
     *   A word for default message (e.g. "checked", "selected", etc).
     *
     * @throws \Exception
     */
    private function assert($value, $word = '')
    {
        if ($value) {
            if ($this->not) {
                throw new \Exception(
                    empty($word)
                    ? 'Field contain a value, but should not.'
                    : "Element is $word, but should not be."
                );
            }
        } else {
            if (!$this->not) {
                throw new \Exception(
                    empty($word)
                    ? 'Field does not contain a value.'
                    : "Element is not $word."
                );
            }
        }
    }
}


<?php

/**
 * Aphiria
 *
 * @link      https://www.aphiria.com
 * @copyright Copyright (C) 2019 David Young
 * @license   https://github.com/aphiria/net/blob/master/LICENSE.md
 */

declare(strict_types=1);

namespace Aphiria\Net\Http\Formatting;

use Aphiria\Net\Http\Cookie;
use Aphiria\Net\Http\IHttpResponseMessage;
use Aphiria\Net\Http\StringBody;
use Aphiria\Net\Uri;
use InvalidArgumentException;
use RuntimeException;

/**
 * Defines the HTTP response message formatter
 */
class ResponseFormatter
{
    /** @var ResponseHeaderFormatter The response header formatter to use */
    private $headerFormatter;

    /**
     * @param ResponseHeaderFormatter|null $headerFormatter The response header formatter to use, or null if using the default one
     */
    public function __construct(ResponseHeaderFormatter $headerFormatter = null)
    {
        $this->headerFormatter = $headerFormatter ?? new ResponseHeaderFormatter();
    }

    /**
     * Deletes a cookie from headers
     *
     * @param IHttpResponseMessage $response The response to format
     * @param string $name The name of the cookie to delete
     * @param string|null $path The path to the cookie to delete if set, otherwise null
     * @param string|null $domain The domain of the cookie to delete if set, otherwise null
     * @param bool $isSecure Whether or not the cookie to be deleted was HTTPS
     * @param bool $isHttpOnly Whether or not the cookie to be deleted was HTTP-only
     * @param string|null $sameSite The same-site setting to use if set, otherwise null
     */
    public function deleteCookie(
        IHttpResponseMessage $response,
        string $name,
        ?string $path = null,
        ?string $domain = null,
        bool $isSecure = false,
        bool $isHttpOnly = true,
        ?string $sameSite = null
    ): void {
        $this->headerFormatter->deleteCookie(
            $response->getHeaders(),
            $name,
            $path,
            $domain,
            $isSecure,
            $isHttpOnly,
            $sameSite
        );
    }

    /**
     * Sets a cookie in the headers
     *
     * @param IHttpResponseMessage $response The response to set the cookie in
     * @param Cookie $cookie The cookie to set
     */
    public function setCookie(IHttpResponseMessage $response, Cookie $cookie): void
    {
        $this->headerFormatter->setCookie($response->getHeaders(), $cookie);
    }

    /**
     * Sets cookies in the headers
     *
     * @param IHttpResponseMessage $response The response to set the cookies in
     * @param Cookie[] $cookies The cookies to set
     */
    public function setCookies(IHttpResponseMessage $response, array $cookies): void
    {
        $this->headerFormatter->setCookies($response->getHeaders(), $cookies);
    }

    /**
     * Sets up the response to redirect to a particular URI
     *
     * @param IHttpResponseMessage $response The response to format
     * @param Uri|string $uri The URI to redirect to
     * @param int $statusCode The status code
     * @throws InvalidArgumentException Thrown if the URI is not an instance of URI or a string
     * @throws RuntimeException Thrown if the location header's hash key could not be calculated
     */
    public function redirectToUri(IHttpResponseMessage $response, $uri, int $statusCode = 302): void
    {
        if (is_string($uri)) {
            $uriString = $uri;
        } elseif ($uri instanceof Uri) {
            $uriString = (string)$uri;
        } else {
            throw new InvalidArgumentException('Uri must be instance of ' . Uri::class . ' or string');
        }

        $response->setStatusCode($statusCode);
        $response->getHeaders()->add('Location', $uriString);
    }

    /**
     * Writes JSON to the response
     *
     * @param IHttpResponseMessage $response The response to write to
     * @param array $content The JSON to write
     * @throws InvalidArgumentException Thrown if the input JSON is incorrectly formatted
     * @throws RuntimeException Thrown if the content type header's hash key could not be calculated
     */
    public function writeJson(IHttpResponseMessage $response, array $content): void
    {
        $json = json_encode($content);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new InvalidArgumentException('Failed to JSON encode content: ' . json_last_error_msg());
        }

        $response->getHeaders()->add('Content-Type', 'application/json');
        $response->setBody(new StringBody($json));
    }
}


<?php
/**
 * @author Sergii Bondarenko, <sb@firstvector.org>
 */
namespace Drupal\TqExtension\Context\Form;

// Exceptions.
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Behat\Mink\Exception\ElementNotFoundException;
use WebDriver\Exception\NoSuchElement;
// Helpers.
use Behat\Gherkin\Node\TableNode;
use WebDriver\Service\CurlService;
use Behat\Mink\Element\NodeElement;
// Utils.
use Drupal\TqExtension\Utils\DatePicker;
use Drupal\TqExtension\Utils\FormValueAssertion;
use Drupal\TqExtension\Utils\EntityDrupalWrapper;

class FormContext extends RawFormContext
{
    /**
     * @param string $value
     *   Typed text.
     * @param string $selector
     *   Selector of the field.
     * @param int $option
     *   An option number. Will be selected from loaded variants.
     *
     * @throws \InvalidArgumentException
     *   When $option is less than zero.
     * @throws NoSuchElement
     *   When autocomplete list was not loaded.
     * @throws \RuntimeException
     *   When neither option was not loaded.
     * @throws \OverflowException
     *   When $option is more than variants are available.
     * @throws \Exception
     *   When value was not changed.
     *
     * @Then /^(?:|I )typed "([^"]*)" in the "([^"]*)" field and chose (\d+) option from autocomplete variants$/
     */
    public function choseOptionFromAutocompleteVariants($value, $selector, $option)
    {
        if (!$option) {
            throw new \InvalidArgumentException(sprintf(
                'An option that will be chosen expected as positive number, but was got the: %s',
                $option
            ));
        }

        $field = $this->element('field', $selector);
        // Syn - a Standalone Synthetic Event Library, provided by Selenium.
        $this->executeJsOnElement($field, sprintf("Syn.type({{ELEMENT}}, '%s')", token_replace($value)));
        $this->waitAjaxAndAnimations();

        $autocomplete = $field->getParent()->findById('autocomplete');
        $this->throwNoSuchElementException('#autocomplete', $autocomplete);

        $options = count($autocomplete->findAll('css', 'li'));

        if ($options < 1) {
            throw new \RuntimeException('Neither option was not loaded.');
        }

        if ($option > $options) {
            throw new \OverflowException(sprintf(
                'You can not select an option %s, as there are only %d.',
                $option,
                $options
            ));
        }

        for ($i = 0; $i < $option; $i++) {
            // 40 - down
            $field->keyDown(40);
            $field->keyUp(40);
        }

        // 13 - return
        $field->keyDown(13);
        $field->keyUp(13);

        if ($field->getValue() == $value) {
            throw new \Exception(sprintf('The value of "%s" field was not changed.', $selector));
        }
    }

    /**
     * Use the current user data for filling fields.
     *
     * @example
     * Then I fill "First name" with value of field "First name" of current user
     * And fill "field_last_name[und][0]" with value of field "field_user_last_name" of current user
     *
     * @param string $field
     *   The name of field to fill in. HTML Label, name or ID can be user as selector.
     * @param string $user_field
     *   The name of field from which the data will taken. Drupal label or machine name can be used as selector.
     *
     * @throws \InvalidArgumentException
     * @throws \UnexpectedValueException
     * @throws \Exception
     * @throws NoSuchElement
     *   When field cannot be found.
     *
     * @Then /^(?:I )fill "([^"]*)" with value of field "([^"]*)" of current user$/
     */
    public function fillInWithValueOfFieldOfCurrentUser($field, $user_field)
    {
        if (!empty($this->user) && !$this->user->uid) {
            throw new \Exception('Anonymous user have no fields');
        }

        $entity = new EntityDrupalWrapper('user');
        $wrapper = $entity->wrapper($this->user->uid);
        $user_field = $entity->getFieldNameByLocator($user_field);

        if (empty($wrapper->{$user_field})) {
            throw new \InvalidArgumentException(sprintf('User entity has no "%s" field.', $user_field));
        }

        $value = $wrapper->{$user_field}->value();

        if (empty($value)) {
            throw new \UnexpectedValueException('The value of "%s" field is empty.', $user_field);
        }

        $this->fillField($field, $value);
    }

    /**
     * @param string $action
     *   Can be "check" or "uncheck".
     * @param TableNode $checkboxes
     *   Table with one row of checkboxes selectors.
     *
     * @example
     * I uncheck the boxes:
     *   | Consumer Products  |
     *   | Financial Services |
     *
     * @example
     * I check the boxes:
     *   | Consumer Products  |
     *   | Financial Services |
     *
     * @Given /^(?:|I )(?:|un)check the boxes:/
     */
    public function checkboxAction($action, TableNode $checkboxes)
    {
        $minkContext = $this->getMinkContext();

        foreach ($checkboxes->getRows() as $checkbox) {
            $minkContext->{trim($action) . 'Option'}(reset($checkbox));
        }
    }

    /**
     * This method was defined and used instead of "assertSelectRadioById",
     * because the field label can contain too long value and better to use
     * another selector instead of label.
     *
     * @see MinkContext::assertSelectRadioById()
     *
     * @param string $customized
     *   Can be an empty string or " customized".
     * @param string $selector
     *   Field selector.
     *
     * @throws NoSuchElement
     *   When radio button was not found.
     * @throws \Exception
     *
     * @Given /^(?:|I )check the(| customized) "([^"]*)" radio button$/
     */
    public function radioAction($customized, $selector)
    {
        $field = $this->getWorkingElement()->findField($selector);
        $customized = (bool) $customized;

        if ($field !== null && !$customized) {
            $field->selectOption($field->getAttribute('value'));
            return;
        }

        // Find all labels of a radio button or only first, if it is not custom.
        foreach ($this->findLabels($selector) as $label) {
            // Check a custom label for visibility.
            if ($customized && !$label->isVisible()) {
                continue;
            }

            $label->click();
            return;
        }

        $this->throwNoSuchElementException($selector, $field);
    }

    /**
     * @param string $selector
     * @param string $value
     *
     * @throws NoSuchElement
     *
     * @When /^(?:|I )fill "([^"]*)" with "([^"]*)"$/
     */
    public function fillField($selector, $value)
    {
        $this->element('field', $selector)->setValue(token_replace($value));
    }

    /**
     * @param TableNode $fields
     *   | Field locator | Value |
     *
     * @throws NoSuchElement
     *
     * @When /^(?:|I )fill the following:$/
     */
    public function fillFields(TableNode $fields)
    {
        foreach ($fields->getRowsHash() as $field => $value) {
            $this->fillField($field, $value);
        }
    }

    /**
     * @param string $file
     *   Path to a file. Relative to the directory specified in "files_path" in behat.yml.
     * @param string $selector
     *   Field selector (label|id|name).
     *
     * @throws \Exception
     * @throws NoSuchElement
     *
     * @Given /^(?:|I )attach file "([^"]*)" to "([^"]*)"$/
     */
    public function attachFile($file, $selector)
    {
        $filesPath = $this->getMinkParameter('files_path');

        if (!$filesPath) {
            throw new \Exception('The "files_path" Mink parameter was not configured.');
        }

        $file = rtrim(realpath($filesPath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;

        if (!is_file($file)) {
            throw new \InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
        }

        $this->element('field', $selector)->attachFile($file);
    }

    /**
     * @param string $selector
     * @param TableNode $values
     *
     * @throws ElementNotFoundException
     * @throws \Exception
     * @throws NoSuchElement
     *
     * @Given /^(?:|I )select the following in "([^"]*)" hierarchical select:$/
     */
    public function setValueForHierarchicalSelect($selector, TableNode $values)
    {
        $element = $this->getWorkingElement();
        // Try to selects by wrapper ID.
        $wrapper = $element->findById($selector);

        if (null !== $wrapper) {
            $labels = $wrapper->findAll('xpath', '//label[@for]');
        } else {
            $labels = $this->findLabels($selector);
        }

        if (empty($labels)) {
            throw new \Exception('No one hierarchical select was found.');
        }

        /** @var NodeElement $label */
        $label = reset($labels);
        $parent = $label->getParent();

        foreach (array_keys($values->getRowsHash()) as $i => $value) {
            /** @var NodeElement[] $selects */
            $selects = [];

            /** @var NodeElement $select */
            foreach ($parent->findAll('css', 'select') as $select) {
                if ($select->isVisible()) {
                    $selects[] = $select;
                }
            }

            if (!isset($selects[$i])) {
                throw new \InvalidArgumentException(sprintf(
                    'The value "%s" was specified for select "%s" but it does not exist.',
                    $value,
                    $i
                ));
            }

            $selects[$i]->selectOption($value);
            $this->waitAjaxAndAnimations();
        }
    }

    /**
     * Check that an image was uploaded and can be viewed on the page.
     *
     * @throws \Exception
     * @throws FileNotFoundException
     *
     * @Then /^(?:|I )should see the thumbnail$/
     */
    public function shouldSeeThumbnail()
    {
        $thumb = false;

        foreach (['.upload-preview', '.media-thumbnail img', '.image-preview img'] as $selector) {
            if ($thumb) {
                break;
            }

            $thumb = $this->findByCss($selector);
        }

        if (null === $thumb) {
            throw new \Exception('An expected image tag was not found.');
        }

        $file = explode('?', $thumb->getAttribute('src'));
        $file = reset($file);

        $curl = new CurlService();
        list(, $info) = $curl->execute('GET', $file);

        if (empty($info) || strpos($info['content_type'], 'image/') === false) {
            throw new FileNotFoundException(sprintf('%s did not return an image', $file));
        }
    }

    /**
     * @param string $option
     * @param string $selector
     *
     * @Then /^(?:|I )pick "([^"]*)" from "([^"]*)"$/
     */
    public function selectFrom($option, $selector)
    {
        $this->element('*', $selector)->selectOption($option);
    }

    /**
     * @example
     * And pick the following:
     *   | Entity Reference                     | Type of new field    |
     *   | Inline entity form - Multiple values | Widget for new field |
     *
     * @param TableNode $rows
     *
     * @Then /^(?:|I )pick the following:$/
     */
    public function selectFromFollowing(TableNode $rows)
    {
        foreach ($rows->getRowsHash() as $option => $selector) {
            $this->selectFrom($option, $selector);
        }
    }

    /**
     * @example
     * And check that "Users" field has "admin" value
     * And check that "Users" field has not "customer" value
     *
     * @Then /^(?:|I )check that "([^"]*)" field has(| not) "([^"]*)" value$/
     */
    public function assertTextualField($selector, $not, $expected)
    {
        (new FormValueAssertion($this, $selector, $not, $expected))->textual();
    }

    /**
     * @example
     * And check that "User" is selected in "Apply to" select
     * And check that "Product(s)" is not selected in "Apply to" select
     *
     * @Then /^(?:|I )check that "([^"]*)" is(| not) selected in "([^"]*)" select$/
     */
    public function assertSelectableField($expected, $not, $selector)
    {
        (new FormValueAssertion($this, $selector, $not, $expected))->selectable();
    }

    /**
     * @example
     * And check that "Order discount" is checked
     * And check that "Product discount" is not checked
     *
     * @Then /^(?:|I )check that "([^"]*)" is(| not) checked$/
     */
    public function assertCheckableField($selector, $not)
    {
        (new FormValueAssertion($this, $selector, $not))->checkable();
    }

    /**
     * @param string $date
     * @param string $selector
     *
     * @Then /^(?:|I )choose "([^"]*)" in "([^"]*)" datepicker$/
     * @Then /^(?:|I )set the "([^"]*)" for "([^"]*)" datepicker$/
     */
    public function setDate($date, $selector)
    {
        (new DatePicker($this, $selector, $date))->isDateAvailable()->setDate()->isDateSelected();
    }

    /**
     * @param string $selector
     * @param string $date
     *
     * @Then /^(?:|I )check that "([^"]*)" datepicker contains "([^"]*)" date$/
     */
    public function isDateSelected($selector, $date)
    {
        (new DatePicker($this, $selector, $date))->isDateSelected();
    }

    /**
     * @param string $date
     * @param string $selector
     *
     * @Then /^(?:|I )check that "([^"]*)" is available for "([^"]*)" datepicker$/
     */
    public function isDateAvailable($date, $selector)
    {
        (new DatePicker($this, $selector, $date))->isDateAvailable();
    }
}