Initial commit

This commit is contained in:
2021-03-23 13:54:38 +01:00
commit 82b142ff95
16941 changed files with 2617212 additions and 0 deletions

View File

@@ -0,0 +1,231 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use CommerceGuys\Addressing\Formatter\DefaultFormatter;
use CommerceGuys\Addressing\Formatter\PostalLabelFormatter;
use CommerceGuys\Addressing\Model\Address;
use CommerceGuys\Addressing\Model\AddressInterface;
use CommerceGuys\Addressing\Repository\AddressFormatRepository;
use CommerceGuys\Addressing\Repository\CountryRepository;
use CommerceGuys\Addressing\Repository\SubdivisionRepository;
use Thelia\Model\Country;
use Thelia\Model\CountryQuery;
use Thelia\Model\Lang;
use Thelia\Model\OrderAddress;
/**
* Class AddressFormat
* @package Thelia\Tools
* @author Julien Chanséaume <julien@thelia.net>
*/
class AddressFormat
{
private static $instance;
private function __construct()
{
}
/**
* @return $this
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new AddressFormat();
}
return self::$instance;
}
/**
* Format an address
*
* @param AddressInterface $address
* @param null $locale
* @param bool $html
* @param string $htmlTag
* @param array $htmlAttributes
* @return string
*/
public function format(
AddressInterface $address,
$locale = null,
$html = true,
$htmlTag = "p",
$htmlAttributes = []
) {
$locale = $this->normalizeLocale($locale);
$addressFormatRepository = new AddressFormatRepository();
$countryRepository = new CountryRepository();
$subdivisionRepository = new SubdivisionRepository();
$formatter = new DefaultFormatter(
$addressFormatRepository,
$countryRepository,
$subdivisionRepository,
$locale
);
$formatter->setOption('html', $html);
$formatter->setOption('html_tag', $htmlTag);
$formatter->setOption('html_attributes', $htmlAttributes);
$addressFormatted = $formatter->format($address);
return $addressFormatted;
}
/**
* Format a Thelia address (Address or OrderAddress)
*
* @param \Thelia\Model\OrderAddress|OrderAddress $address
* @param null $locale
* @param bool $html
* @param string $htmlTag
* @param array $htmlAttributes
* @return string
*/
public function formatTheliaAddress($address, $locale = null, $html = true, $htmlTag = "p", $htmlAttributes = [])
{
$address = $this->mapTheliaAddress($address, $locale);
return $this->format($address, $locale, $html, $htmlTag, $htmlAttributes);
}
/**
* Format an address to a postal label
*
* @param AddressInterface $address
* @param null $locale
* @param null $originCountry
* @param array $options
* @return string
*/
public function postalLabelFormat(AddressInterface $address, $locale = null, $originCountry = null, $options = [])
{
$locale = $this->normalizeLocale($locale);
$addressFormatRepository = new AddressFormatRepository();
$countryRepository = new CountryRepository();
$subdivisionRepository = new SubdivisionRepository();
if (null === $originCountry) {
$countryId = Country::getShopLocation();
if (null === $country = CountryQuery::create()->findPk($countryId)) {
$country = Country::getDefaultCountry();
}
$originCountry = $country->getIsoalpha2();
}
$formatter = new PostalLabelFormatter(
$addressFormatRepository,
$countryRepository,
$subdivisionRepository,
$originCountry,
$locale,
$options
);
$addressFormatted = $formatter->format($address);
return $addressFormatted;
}
/**
* Format a Thelia address (Address or OrderAddress) to a postal label
*
* @param \Thelia\Model\OrderAddress|OrderAddress $address
* @param null $locale
* @param null $originCountry
* @param array $options
* @return string
*/
public function postalLabelFormatTheliaAddress($address, $locale = null, $originCountry = null, $options = [])
{
$address = $this->mapTheliaAddress($address, $locale);
return $this->postalLabelFormat($address, $locale, $originCountry, $options);
}
/**
* Convert a Thelia address (Address or OrderAddress) to ImmutableAddressInterface
*
* @param \Thelia\Model\OrderAddress|OrderAddress $address
* @return Address|\CommerceGuys\Addressing\Model\ImmutableAddressInterface
*/
protected function mapTheliaAddress($address, $locale = null)
{
$country = $address->getCountry();
if (null === $locale) {
$locale = Lang::getDefaultLanguage()->getLocale();
}
$customerTitle = $address->getCustomerTitle()
->setLocale($this->denormalizeLocale($locale))
->getShort()
;
$addressModel = new Address();
$addressModel = $addressModel
->withCountryCode($country->getIsoalpha2())
->withAddressLine1($address->getAddress1())
->withAddressLine2($address->getAddress2())
->withPostalCode($address->getZipcode())
->withLocality($address->getCity())
->withOrganization($address->getCompany())
->withRecipient(
sprintf(
'%s %s %s',
$customerTitle,
$address->getLastname(),
$address->getFirstname()
)
)
;
if ($country->getHasStates() && intval($address->getStateId()) !== 0) {
$addressModel = $addressModel->withAdministrativeArea(
sprintf(
'%s-%s',
$country->getIsoalpha2(),
$address->getState()->getIsocode()
)
);
}
return $addressModel;
}
private function normalizeLocale($locale)
{
if (null !== $locale) {
$locale = str_replace('_', '-', $locale);
}
return $locale;
}
private function denormalizeLocale($locale)
{
if (null !== $locale) {
$locale = str_replace('-', '_', $locale);
}
return $locale;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Symfony\Component\HttpFoundation\Request;
class DateTimeFormat
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public static function getInstance(Request $request)
{
return new DateTimeFormat($request);
}
public function getFormat($output = null)
{
$lang = $this->request->getSession()->getLang();
$format = null;
if ($lang) {
switch ($output) {
case "date":
$format = $lang->getDateFormat();
break;
case "time":
$format = $lang->getTimeFormat();
break;
default:
case "datetime":
$format = $lang->getDateTimeFormat();
break;
}
}
return $format;
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\FileDownload;
use Psr\Log\LoggerInterface;
use Symfony\Component\Translation\Translator;
use Thelia\Core\Translation\Translator as TheliaTranslator;
use Thelia\Exception\FileNotFoundException;
use Thelia\Exception\HttpUrlException;
use Thelia\Log\Tlog;
use Thelia\Tools\URL;
/**
* Class FileDownloader
* @package Thelia\Tools\FileDownload
* @author Benjamin Perche <bperche@openstudio.fr>
*/
class FileDownloader implements FileDownloaderInterface
{
/** @var LoggerInterface */
protected $logger;
/** @var Translator */
protected $translator;
public function __construct(LoggerInterface $logger, Translator $translator)
{
$this->logger = $logger;
$this->translator = $translator;
}
public static function getInstance()
{
return new static(Tlog::getInstance(), TheliaTranslator::getInstance());
}
/**
* @param string $url
* @param string $pathToStore
* @throws \Thelia\Exception\FileNotFoundException
* @throws \ErrorException
* @throws \HttpUrlException
*
* Downloads the file $url in $pathToStore
*/
public function download($url, $pathToStore)
{
if (!URL::checkUrl($url)) {
/**
* The URL is not valid
*/
throw new HttpUrlException(
$this->translator->trans(
"Tried to download a file, but the URL was not valid: %url",
[
"%url" => $url
]
)
);
}
/**
* Try to get the file if it is online
*/
$con = curl_init($url);
curl_setopt($con, CURLOPT_RETURNTRANSFER, true);
curl_setopt($con, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($con);
$errno = curl_errno($con);
$curlErrorMessage = curl_error($con);
$httpCode = curl_getinfo($con, CURLINFO_HTTP_CODE);
curl_close($con);
if (false === $response || $errno !== 0 ||
($httpCode != "200" && $httpCode != "204")
) {
/**
* The server is down ? The file doesn't exist ? Anything else ?
*/
$errorMessage = $this->translator->trans(
"cURL errno %errno, http code %http_code on link \"%path\": %error",
[
"%errno" => $errno,
"%path" => $url,
"%error" => $curlErrorMessage,
"%http_code" => $httpCode,
]
);
$this->logger
->error($errorMessage)
;
throw new FileNotFoundException($errorMessage);
}
/**
* Inform that you've downloaded a file
*/
$this->logger
->info(
$this->translator->trans(
"The file %path has been successfully downloaded",
[
"%path" => $url
]
)
)
;
/**
* Then try to write it on the disk
*/
$file = @fopen($pathToStore, "w");
if ($file === false) {
$translatedErrorMessage = $this->translator->trans(
"Failed to open a writing stream on the file: %file",
[
"%file" => $pathToStore
]
);
$this->logger->error($translatedErrorMessage);
throw new \ErrorException($translatedErrorMessage);
}
fputs($file, $response);
fclose($file);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\FileDownload;
/**
* Trait FileDownloaderAwareTrait
* @package Thelia\Tools\FileDownload
* @author Benjamin Perche <bperche@openstudio.fr>
*/
trait FileDownloaderAwareTrait
{
/** @var FileDownloaderInterface */
protected $fileDownloader;
/**
* @return FileDownloaderInterface
*/
public function getFileDownloader()
{
if (!$this->fileDownloader instanceof FileDownloaderInterface) {
$this->fileDownloader = FileDownloader::getInstance();
}
return $this->fileDownloader;
}
/**
* @param FileDownloaderInterface $fileDownloader
* @return $this
*/
public function setFileDownloader(FileDownloaderInterface $fileDownloader)
{
$this->fileDownloader = $fileDownloader;
return $this;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\FileDownload;
use Psr\Log\LoggerInterface;
use Symfony\Component\Translation\Translator;
/**
* Class FileDownloader
* @package Thelia\Tools\FileDownload
* @author Benjamin Perche <bperche@openstudio.fr>
*/
interface FileDownloaderInterface
{
/**
* @param string $url
* @param string $pathToStore
* @throws \Thelia\Exception\FileNotFoundException
* @throws \ErrorException
* @throws \HttpUrlException
*
* Downloads the file $url in $pathToStore
*/
public function download($url, $pathToStore);
public function __construct(LoggerInterface $logger, Translator $translator);
/**
* @return $this
*
* Returns an hydrated instance
*/
public static function getInstance();
}

View File

@@ -0,0 +1,142 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Thelia\Model\Lang;
/**
* Created by JetBrains PhpStorm.
* Date: 8/19/13
* Time: 3:24 PM
*
* Helper for translations
*
* @package I18n
* @author Guillaume MOREL <gmorel@openstudio.fr>
*
*/
class I18n
{
protected static $defaultLocale;
/**
* Create a \DateTime from a date picker form input
* The date format is the same as the one from the current User Session
* Ex : $lang = $session->getLang()
*
* @param Lang $lang Object containing date format
* @param string $date String to convert
*
* @return \DateTime
*/
public function getDateTimeFromForm(Lang $lang, $date)
{
$currentDateFormat = $lang->getDateFormat();
return \DateTime::createFromFormat($currentDateFormat, $date);
}
public static function forceI18nRetrieving($askedLocale, $modelName, $id, $needed = array('Title'))
{
$i18nQueryClass = sprintf("\\Thelia\\Model\\%sI18nQuery", $modelName);
$i18nClass = sprintf("\\Thelia\\Model\\%sI18n", $modelName);
/* get customer language translation */
$i18n = $i18nQueryClass::create()
->filterById($id)
->filterByLocale(
$askedLocale
)->findOne();
/* or default translation */
if (null === $i18n) {
$i18n = $i18nQueryClass::create()
->filterById($id)
->filterByLocale(
Lang::getDefaultLanguage()->getLocale()
)->findOne();
}
if (null === $i18n) {
// @todo something else ?
$i18n = new $i18nClass();
;
$i18n->setId($id);
foreach ($needed as $need) {
$method = sprintf('set%s', $need);
if (method_exists($i18n, $method)) {
$i18n->$method('DEFAULT ' . strtoupper($need));
} else {
// @todo throw sg ?
}
}
}
return $i18n;
}
public static function addI18nCondition(
ModelCriteria $query,
$i18nTableName,
$tableIdColumn,
$i18nIdColumn,
$localeColumn,
$locale
) {
if (null === static::$defaultLocale) {
static::$defaultLocale = Lang::getDefaultLanguage()->getLocale();
}
$locale = static::realEscape($locale);
$defaultLocale = static::realEscape(static::$defaultLocale);
$query
->_and()
->where(
"CASE WHEN ".$tableIdColumn." IN".
"(SELECT DISTINCT ".$i18nIdColumn." ".
"FROM `".$i18nTableName."` ".
"WHERE locale=$locale) ".
"THEN ".$localeColumn." = $locale ".
"ELSE ".$localeColumn." = $defaultLocale ".
"END"
)
;
}
/**
* @param $str
* @return string
*
* Really escapes a string for SQL query.
*/
public static function realEscape($str)
{
$str = trim($str, "\"'");
$return = "CONCAT(";
$len = strlen($str);
for ($i = 0; $i < $len; ++$i) {
$return .= "CHAR(".ord($str[$i])."),";
}
if ($i > 0) {
$return = substr($return, 0, -1);
} else {
$return = "\"\"";
}
$return .= ")";
return $return;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
class Image
{
public static function isImage($filePath, $allowedImageTypes = null)
{
$imageFile = getimagesize($filePath);
$imageType = $imageFile[2];
if (!is_array($allowedImageTypes) && $imageType != IMAGETYPE_UNKNOWN) {
return true;
}
if (in_array($imageType, $allowedImageTypes)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Symfony\Component\HttpFoundation\Request;
use Thelia\Model\CurrencyQuery;
class MoneyFormat extends NumberFormat
{
public static function getInstance(Request $request)
{
return new MoneyFormat($request);
}
/**
* Get a standard number, with '.' as decimal point no thousands separator, and no currency symbol
* so that this number can be used to perform calculations.
*
* @param float $number the number
* @param string $decimals number of decimal figures
* @return string
*/
public function formatStandardMoney($number, $decimals = null)
{
return parent::formatStandardNumber($number, $decimals);
}
public function format(
$number,
$decimals = null,
$decPoint = null,
$thousandsSep = null,
$symbol = null
) {
$number = parent::format($number, $decimals, $decPoint, $thousandsSep);
if ($symbol !== null) {
return $number . ' ' . $symbol;
}
return $number;
}
/**
* @since 2.3
* @param float $number
* @param int $decimals
* @param string $decPoint
* @param string $thousandsSep
* @param int|null $currencyId
* @return string
*/
public function formatByCurrency(
$number,
$decimals = null,
$decPoint = null,
$thousandsSep = null,
$currencyId = null
) {
$number = parent::format($number, $decimals, $decPoint, $thousandsSep);
$currency = $currencyId !== null ? CurrencyQuery::create()->findPk($currencyId) : $this->request->getSession()->getCurrency();
if ($currency !== null && strpos($currency->getFormat(), '%n') !== false) {
return str_replace(
['%n', '%s', '%c'],
[$number, $currency->getSymbol(), $currency->getCode()],
$currency->getFormat()
);
}
return $number;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Symfony\Component\HttpFoundation\Request;
class NumberFormat
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public static function getInstance(Request $request)
{
return new NumberFormat($request);
}
/**
* Get a standard number, with '.' as decimal point and no thousands separator
* so that this number can be used to perform calculations.
*
* @param float $number the number
* @param string $decimals number of decimal figures
* @return string
*/
public function formatStandardNumber($number, $decimals = null)
{
$lang = $this->request->getSession()->getLang();
if ($decimals === null) {
$decimals = $lang->getDecimals();
}
return number_format($number, $decimals, '.', '');
}
public function format($number, $decimals = null, $decPoint = null, $thousandsSep = null)
{
$lang = $this->request->getSession()->getLang();
if ($decimals === null) {
$decimals = $lang->getDecimals();
}
if ($decPoint === null) {
$decPoint = $lang->getDecimalSeparator();
}
if ($thousandsSep === null) {
$thousandsSep = $lang->getThousandsSeparator();
}
return number_format($number, $decimals, $decPoint, $thousandsSep);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
/**
* Class Password
* @package Thelia\Tools
* @author Manuel Raynaud <manu@raynaud.io>
*/
class Password
{
private static function randgen($letter, $length)
{
$string = "";
do {
$string .= substr(str_shuffle($letter), 0, 1);
} while (strlen($string) < $length);
return $string;
}
/**
* generate a Random password with defined length
*
* @param int $length
* @return mixed
*/
public static function generateRandom($length = 8)
{
$letter = "abcdefghijklmnopqrstuvwxyz";
$letter .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$letter .= "0123456789";
return self::randgen($letter, $length);
}
public static function generateHexaRandom($length = 8)
{
$letter = "ABCDEF";
$letter .= "0123456789";
return self::randgen($letter, $length);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Security\Token\CookieTokenProvider;
use Thelia\Core\Security\User\UserInterface;
/**
* Trait RememberMeTrait
* @package Thelia\Tools
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
trait RememberMeTrait
{
/**
* Get the remember me key from the cookie.
*
* @return string hte key found, or null if no key was found.
*/
protected function getRememberMeKeyFromCookie(Request $request, $cookieName)
{
$ctp = new CookieTokenProvider();
return $ctp->getKeyFromCookie($request, $cookieName);
}
/**
* Create the remember me cookie for the given user.
*/
protected function createRememberMeCookie(UserInterface $user, $cookieName, $cookieExpiration)
{
$ctp = new CookieTokenProvider();
$ctp->createCookie(
$user,
$cookieName,
$cookieExpiration
);
}
/**
* Clear the remember me cookie.
*/
protected function clearRememberMeCookie($cookieName)
{
$ctp = new CookieTokenProvider();
$ctp->clearCookie($cookieName);
}
}

View File

@@ -0,0 +1,97 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Rest;
use Thelia\Core\HttpFoundation\Response;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
/**
* Class ResponseRest Create a serialized Response
*
* @package Thelia\Tools\Rest
*/
class ResponseRest extends Response
{
/** @var Response Response Object */
protected $response;
/** @var string Response format */
protected $format;
/**
* Constructor.
*
* @param array $data Array to be serialized
* @param string $format serialization format, text, xml or json available
* @param integer $status The response status code
* @param array $headers An array of response headers
*
* @throws \InvalidArgumentException When the HTTP status code is not valid
*
* @api
*/
public function __construct($data = null, $format = 'json', $status = 200, $headers = array())
{
parent::__construct('', $status, $headers);
if ($format == 'text') {
if (isset($data)) {
$this->setContent($data);
}
$this->headers->set('Content-Type', 'text/plain');
} else {
$this->format = $format;
$serializer = $this->getSerializer();
if (isset($data)) {
$this->setContent($serializer->serialize($data, $this->format));
}
$this->headers->set('Content-Type', 'application/' . $this->format);
}
}
/**
* Set Content to be serialized in the response, array or object
*
* @param array $data array or object to be serialized
*
* @return $this
*/
public function setRestContent($data)
{
$serializer = $this->getSerializer();
if (isset($data)) {
$this->setContent($serializer->serialize($data, $this->format));
}
return $this;
}
/**
* Get Serializer
*
* @return Serializer
*/
protected function getSerializer()
{
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
return new Serializer($normalizers, $encoders);
}
}

View File

@@ -0,0 +1,156 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Thelia\Core\Security\Exception\TokenAuthenticationException;
/**
* Class TokenProvider
* @package Thelia\Tools
* @author Benjamin Perche <bperche@openstudio.fr>
*/
class TokenProvider
{
/**
* @var string The stored token for this page
*/
protected $token;
/**
* @var SessionInterface The session where the token is stored
*/
protected $session;
/**
* @var RequestStack
*/
protected $requestStack;
/**
* @var TranslatorInterface The translator
*/
protected $translator;
/**
* @var string the current name of the token
*/
protected $tokenName;
/**
* @param RequestStack $requestStack
* @param TranslatorInterface $translator
* @param $tokenName
*/
public function __construct(RequestStack $requestStack, TranslatorInterface $translator, $tokenName)
{
/**
* Store the services
*/
$this->requestStack = $requestStack;
$this->session = $this->requestStack->getCurrentRequest()->getSession();
$this->translator = $translator;
$this->tokenName = $tokenName;
$this->token = $this->session->get($this->tokenName);
}
/**
* @return string
*/
public function assignToken()
{
if (null === $this->token) {
$this->token = $this->getToken();
$this->session->set($this->tokenName, $this->token);
}
return $this->token;
}
/**
* @param string $entryValue
* @return bool
* @throws \Thelia\Core\Security\Exception\TokenAuthenticationException
*/
public function checkToken($entryValue)
{
if (null === $this->token) {
throw new TokenAuthenticationException(
"Tried to check a token without assigning it before"
);
} elseif ($this->token !== $entryValue) {
throw new TokenAuthenticationException(
"Tried to validate an invalid token"
);
}
return true;
}
protected function refreshToken()
{
$this->token = null;
$this->assignToken();
}
/**
* @return string
*/
public function getToken()
{
return self::generateToken();
}
/**
* Same method as getToken but can be called statically
*
* @alias getToken
* @return string
*/
public static function generateToken()
{
$raw = self::getOpenSSLRandom();
if (false === $raw) {
$raw = self::getComplexRandom();
}
return md5($raw);
}
/**
* @param int $length
* @return string
*/
protected static function getOpenSSLRandom($length = 40)
{
if (!function_exists("openssl_random_pseudo_bytes")) {
return false;
}
return openssl_random_pseudo_bytes($length);
}
/**
* @return string
*/
protected static function getComplexRandom()
{
$firstValue = (float) (mt_rand(1, 0xFFFF) * rand(1, 0x10001));
$secondValues = (float) (rand(1, 0xFFFF) * mt_rand(1, 0x10001));
return microtime() . ceil($firstValue / $secondValues) . uniqid();
}
}

View File

@@ -0,0 +1,355 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Validator\Constraints\UrlValidator;
use Thelia\Model\ConfigQuery;
use Thelia\Model\LangQuery;
use Thelia\Rewriting\RewritingResolver;
use Thelia\Rewriting\RewritingRetriever;
use Thelia\Core\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
class URL
{
/** @var RewritingResolver $resolver */
protected $resolver = null;
/** @var RewritingRetriever $retriever */
protected $retriever = null;
/** @var RequestContext $requestContext */
protected $requestContext;
const PATH_TO_FILE = true;
const WITH_INDEX_PAGE = false;
protected static $instance = null;
/** @var string $baseUrlScheme a cache for the base URL scheme */
private $baseUrlScheme = null;
public function __construct(ContainerInterface $container = null)
{
// Allow singleton style calls once instantiated.
// For this to work, the URL service has to be instantiated very early. This is done manually
// in TheliaHttpKernel, by calling $this->container->get('thelia.url.manager');
self::$instance = $this;
if ($container !== null) {
$this->requestContext = $container->get('router.admin')->getContext();
}
$this->retriever = new RewritingRetriever();
$this->resolver = new RewritingResolver();
}
/**
* @param RequestContext $requestContext
* @since Version 2.2
*/
public function setRequestContext(RequestContext $requestContext)
{
$this->requestContext = $requestContext;
}
/**
* Return this class instance, only once instanciated.
*
* @throws \RuntimeException if the class has not been instanciated.
* @return \Thelia\Tools\URL the instance.
*/
public static function getInstance()
{
if (self::$instance == null) {
throw new \RuntimeException("URL instance is not initialized.");
}
return self::$instance;
}
/**
* Return the base URL, either the base_url defined in Config, or the URL
* of the current language, if 'one_domain_foreach_lang' is enabled.
*
* @param bool $scheme_only if true, only the scheme will be returned. If false, the complete base URL, including path, is returned.
*
* @return string the base URL, with a trailing '/'
*/
public function getBaseUrl($scheme_only = false)
{
if (null === $this->baseUrlScheme) {
$scheme = "http";
$port = 80;
if ($host = $this->requestContext->getHost()) {
$scheme = $this->requestContext->getScheme();
$port = '';
if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) {
$port = ':'.$this->requestContext->getHttpPort();
} elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) {
$port = ':'.$this->requestContext->getHttpsPort();
}
}
$this->baseUrlScheme = "$scheme://$host"."$port";
}
return $scheme_only ? $this->baseUrlScheme : $this->baseUrlScheme . $this->requestContext->getBaseUrl();
}
/**
* @return string the index page, which is in fact the base URL.
*/
public function getIndexPage()
{
// The index page is the base URL :)
return $this->getBaseUrl();
}
/**
* Returns the Absolute URL for a given path relative to web root. By default,
* the script name (index_dev.php) is added to the URL in dev_environment, use
* $path_only = true to get a path without the index script.
*
* @param string $path the relative path
* @param array $parameters An array of parameters
* @param boolean $path_only if true (PATH_TO_FILE), getIndexPage() will not be added
*
* @return string The generated URL
*/
public function absoluteUrl($path, array $parameters = null, $path_only = self::WITH_INDEX_PAGE)
{
// Already absolute ?
if (substr($path, 0, 4) != 'http') {
// Prevent duplication of the subdirectory name when Thelia is installed in a subdirectory.
// This happens when $path was calculated with Router::generate(), which returns an absolute URL,
// starting at web server root. For example, if Thelia is installed in /thelia2, we got something like /thelia2/my/path
// As base URL also contains /thelia2 (e.g. http://some.server.com/thelia2), we end up with
// http://some.server.com/thelia2/thelia2/my/path, instead of http://some.server.com/thelia2/my/path
// We have to compensate for this.
$rcbu = $this->requestContext->getBaseUrl();
$hasSubdirectory = ! empty($rcbu) && (0 === strpos($path, $rcbu));
$base_url = $this->getBaseUrl($hasSubdirectory);
/* Seems no longer required
// TODO fix this ugly patch
if (strpos($path, "index_dev.php")) {
$path = str_replace('index_dev.php', '', $path);
}
*/
// If only a path is requested, be sure to remove the script name (index.php or index_dev.php), if any.
if ($path_only == self::PATH_TO_FILE) {
if (substr($base_url, -3) == 'php') {
$base_url = dirname($base_url);
}
}
// Normalize the given path
$base = rtrim($base_url, '/') . '/' . ltrim($path, '/');
} else {
$base = $path;
}
$base = str_replace('&amp;', '&', $base);
$queryString = '';
$anchor = '';
if (! is_null($parameters)) {
foreach ($parameters as $name => $value) {
// Remove this parameter from base URL to prevent duplicate parameters
$base = preg_replace('`([?&])'.preg_quote($name, '`').'=(?:[^&]*)(?:&|$)`', '$1', $base);
$queryString .= sprintf("%s=%s&", urlencode($name), urlencode($value));
}
}
if ('' !== $queryString = rtrim($queryString, "&")) {
// url could contain anchor
$pos = strrpos($base, '#');
if ($pos !== false) {
$anchor = substr($base, $pos);
$base = substr($base, 0, $pos);
}
$base = rtrim($base, "?&");
$sepChar = strstr($base, '?') === false ? '?' : '&';
$queryString = $sepChar . $queryString;
}
return $base . $queryString . $anchor;
}
/**
* Returns the Absolute URL to a administration view
*
* @param string $viewName the view name (e.g. login for login.html)
* @param mixed $parameters An array of parameters
*
* @return string The generated URL
*/
public function adminViewUrl($viewName, array $parameters = array())
{
$path = sprintf("%s/admin/%s", $this->getIndexPage(), $viewName);
return $this->absoluteUrl($path, $parameters);
}
/**
* Returns the Absolute URL to a view
*
* @param string $viewName the view name (e.g. login for login.html)
* @param mixed $parameters An array of parameters
*
* @return string The generated URL
*/
public function viewUrl($viewName, array $parameters = array())
{
$path = sprintf("?view=%s", $viewName);
return $this->absoluteUrl($path, $parameters);
}
/**
* Retrieve a rewritten URL from a view, a view id and a locale
*
* @param $view
* @param $viewId
* @param $viewLocale
*
* @return RewritingRetriever You can access $url and $rewrittenUrl properties
*/
public function retrieve($view, $viewId, $viewLocale)
{
if (ConfigQuery::isRewritingEnable()) {
$this->retriever->loadViewUrl($view, $viewLocale, $viewId);
} else {
$allParametersWithoutView = array();
$allParametersWithoutView['lang'] = $viewLocale;
if (null !== $viewId) {
$allParametersWithoutView[$view . '_id'] = $viewId;
}
$this->retriever->rewrittenUrl = null;
$this->retriever->url = URL::getInstance()->viewUrl($view, $allParametersWithoutView);
}
return $this->retriever;
}
/**
* Retrieve a rewritten URL from the current GET parameters
*
* @param Request $request
*
* @return RewritingRetriever You can access $url and $rewrittenUrl properties or use toString method
*/
public function retrieveCurrent(Request $request)
{
if (ConfigQuery::isRewritingEnable()) {
$view = $request->attributes->get('_view', null);
$viewLocale = $this->getViewLocale($request);
$viewId = $view === null ? null : $request->query->get($view . '_id', null);
$allOtherParameters = $request->query->all();
if ($view !== null) {
unset($allOtherParameters['view']);
if ($viewId !== null) {
unset($allOtherParameters[$view . '_id']);
}
}
if ($viewLocale !== null) {
unset($allOtherParameters['lang']);
unset($allOtherParameters['locale']);
}
$this->retriever->loadSpecificUrl($view, $viewLocale, $viewId, $allOtherParameters);
} else {
$allParametersWithoutView = $request->query->all();
$view = $request->attributes->get('_view');
if (isset($allOtherParameters['view'])) {
unset($allOtherParameters['view']);
}
$this->retriever->rewrittenUrl = null;
$this->retriever->url = URL::getInstance()->viewUrl($view, $allParametersWithoutView);
}
return $this->retriever;
}
/**
* Retrieve a rewritten URL from the current GET parameters or use toString method
*
* @param $url
*
* @return RewritingResolver
*/
public function resolve($url)
{
$this->resolver->load($url);
return $this->resolver;
}
protected function sanitize($string, $force_lowercase = true, $alphabetic_only = false)
{
static $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
"}", "\\", "|", ";", ":", "\"", "'", "&#8216;", "&#8217;", "&#8220;", "&#8221;", "&#8211;", "&#8212;",
"—", "–", ",", "<", ".", ">", "/", "?");
$clean = trim(str_replace($strip, "", strip_tags($string)));
$clean = preg_replace('/\s+/', "-", $clean);
$clean = ($alphabetic_only) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ;
return ($force_lowercase) ?
(function_exists('mb_strtolower')) ?
mb_strtolower($clean, 'UTF-8') :
strtolower($clean) :
$clean;
}
public static function checkUrl($url, array $protocols = ["http", "https"])
{
$pattern = sprintf(UrlValidator::PATTERN, implode('|', $protocols));
return (bool) preg_match($pattern, $url);
}
/**
* Get the locale code from the lang attribute in URL.
*
* @param Request $request
* @return null|string
*/
private function getViewLocale(Request $request)
{
$viewLocale = $request->query->get('lang', null);
if (null === $viewLocale) {
// fallback for old parameter
$viewLocale = $request->query->get('locale', null);
}
return $viewLocale;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class BaseConstraint
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
abstract class BaseConstraint implements ConstraintInterface
{
protected $operator = "=";
protected $expression = null;
public function __construct($expression)
{
$this->expression = $expression;
}
public function test($version, $strict = false)
{
$version = $this->normalize($version, $strict);
return version_compare($version, $this->expression, $this->operator);
}
public function normalize($version, $strict = false)
{
return $strict ? $version : $this->normalizePrecision($version);
}
protected function normalizePrecision($version, $changeExpression = true)
{
$expressionElements = preg_split('/[\.\-]/', $this->expression);
// cleaning alpha RC beta
$version = preg_replace('/\-.*$/', '', $version);
$versionElements = preg_split('/\./', $version);
if (count($expressionElements) < count($versionElements)) {
if (true === $changeExpression) {
$this->expression = implode(
'.',
array_merge(
$expressionElements,
array_fill(
count($expressionElements) - 1,
count($versionElements) - count($expressionElements),
'0'
)
)
);
} else {
$version = implode(
'.',
array_slice($versionElements, 0, count($expressionElements))
);
}
}
return $version;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class ConstraintEqual
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ConstraintEqual extends BaseConstraint
{
public function __construct($expression)
{
$this->expression = str_replace('=', '', $expression);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class ConstraintGreater
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ConstraintGreater extends BaseConstraint
{
public function __construct($expression, $strict = false)
{
$this->operator = $strict ? ">" : ">=";
$this->expression = substr(
$expression,
strlen($this->operator)
);
}
}

View File

@@ -0,0 +1,38 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class ContraintInterface
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
interface ConstraintInterface
{
/**
* Normalize a version number in a version that will be used in `version_compare`
*
* @param string $version the version expression
* @return string the normalized expression
*/
public function normalize($version, $strict = false);
/**
* Test if the version number is valid
*
* @param string $version the version number
* @param bool $strict if false precision will be normalized. eg: 2.1.0 > 2.1 will become 2.1.0 > 2.1.0 (default false)
* @return bool true if the version is equal, otherwise false
*/
public function test($version, $strict = false);
}

View File

@@ -0,0 +1,30 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class ConstraintLower
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ConstraintLower extends BaseConstraint
{
public function __construct($expression, $strict = false)
{
$this->operator = $strict ? "<" : "<=";
$this->expression = substr(
$expression,
strlen($this->operator)
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version\Constraints;
/**
* Class ConstraintNearlyEqual
* @package Thelia\Tools\Version\Constraints
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ConstraintNearlyEqual extends BaseConstraint
{
public function __construct($expression)
{
$this->expression = str_replace('~', '', $expression);
}
public function normalize($version, $strict = false)
{
if (!$strict) {
$version = $this->normalizePrecision($version, false);
}
return $version;
}
}

View File

@@ -0,0 +1,134 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Thelia\Tools\Version;
use Thelia\Tools\Version\Constraints\ConstraintEqual;
use Thelia\Tools\Version\Constraints\ConstraintGreater;
use Thelia\Tools\Version\Constraints\ConstraintInterface;
use Thelia\Tools\Version\Constraints\ConstraintLower;
use Thelia\Tools\Version\Constraints\ConstraintNearlyEqual;
/**
* Class Version
* @package Thelia\Tools
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class Version
{
/**
* Test if a version matched the version contraints.
*
* constraints can be simple or complex (multiple constraints separated by space) :
*
* - "~2.1" : version 2.1.*
* - "~2.1 <=2.1.4" : version 2.1.* but lower or equal to 2.1.4
* - ">=2.1" : version 2.1.*, 2.2, ...
* - ">2.1.1 <=2.1.5" : version greater than 2.1.1 but lower or equal than 2.1.5
* - ...
*
* @param string $version the version to test
* @param string $constraints the versions constraints
* @param bool $strict if true 2.1 is different of 2.1.0, if false version are normalized so 2.1
* will be expended to 2.1.0
* @param string $defaultComparison the default comparison if nothing provided
* @return bool true if version matches the constraints
*/
public static function test($version, $constraints, $strict = false, $defaultComparison = "=")
{
$constraints = self::parseConstraints($constraints, $defaultComparison);
/** @var ConstraintInterface $constraint */
foreach ($constraints as $constraint) {
if (!$constraint->test($version, $strict)) {
return false;
}
}
return true;
}
private static function parseConstraints($constraints, $defaultComparison = "=")
{
$constraintsList = [];
foreach (explode(" ", $constraints) as $expression) {
if (1 === preg_match('/^[0-9]/', $expression)) {
$expression = $defaultComparison . $expression;
}
if (strpos($expression, '>=') !== false) {
$constraint = new ConstraintGreater($expression);
} elseif (strpos($expression, '>') !== false) {
$constraint = new ConstraintGreater($expression, true);
} elseif (strpos($expression, '<=') !== false) {
$constraint = new ConstraintLower($expression);
} elseif (strpos($expression, '<') !== false) {
$constraint = new ConstraintLower($expression, true);
} elseif (strpos($expression, '~') !== false) {
$constraint = new ConstraintNearlyEqual($expression);
} else {
$constraint = new ConstraintEqual($expression);
}
$constraintsList[] = $constraint;
}
return $constraintsList;
}
/**
* Split a version into an associative array
* [version, major, minus, release, extra]
*
* @param string $version the version to split
* @return array associative array
* [
* 'version' => 'digit',
* 'major' => 'digit',
* 'minus' => 'digit',
* 'release' => 'digit',
* 'extra' => 'alphanumeric'
* ]
*/
public static function parse($version = null)
{
if (is_null($version)) {
$version = \Thelia\Core\Thelia::THELIA_VERSION;
}
$pattern = "`^(?<version>
(?<major>[0-9]+)\.
(?<minus>[0-9]+)\.
(?<release>[0-9]+)
-?(?<extra>[a-zA-Z0-9]*) # extra_version will also match empty string
)$`x";
if (!preg_match($pattern, $version, $match)) {
throw new \InvalidArgumentException(
sprintf(
"Invalid version number provided : %s".PHP_EOL,
$version
)
);
}
return [
'version' => $match['version'],
'major' => $match['major'],
'minus' => $match['minus'],
'release' => $match['release'],
'extra' => $match['extra'],
];
}
}