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,48 @@
<?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\Model\Tools;
use Propel\Runtime\Connection\ConnectionInterface;
/**
* Trait I18nTimestampableTrait
* @package Thelia\Model\Tools
* @author Benjamin Perche <bperche@openstudio.fr>
*/
trait I18nTimestampableTrait
{
public function postSave(ConnectionInterface $con = null)
{
parent::postSave($con);
$this->getBaseQueryObject()
->filterById($this->getId())
->update([$this->getUpdatedAtColumnName() => new \DateTime()], $con)
;
}
/**
* @return \Propel\Runtime\ActiveQuery\ModelCriteria
*/
protected function getBaseQueryObject()
{
$parentClass = preg_replace("#^([\w\_\\\\]+)I18n$#", "$1Query", __CLASS__);
return (new $parentClass());
}
protected function getUpdatedAtColumnName()
{
return "UpdatedAt";
}
}

View File

@@ -0,0 +1,293 @@
<?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\Model\Tools;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\Join;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Lang;
use Thelia\Model\LangQuery;
/**
* Class ModelCriteriaTools
*
* @package Thelia\Model\Tools
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class ModelCriteriaTools
{
/**
* @param ModelCriteria $search
* @param $requestedLocale
* @param array $columns
* @param null $foreignTable
* @param string $foreignKey
* @param bool $forceReturn
* @param string $forceReturn
*/
public static function getFrontEndI18n(
ModelCriteria &$search,
$requestedLocale,
$columns,
$foreignTable,
$foreignKey,
$forceReturn = false,
$localeAlias = null
) {
if (!empty($columns)) {
if ($foreignTable === null) {
$foreignTable = $search->getTableMap()->getName();
$aliasPrefix = '';
} else {
$aliasPrefix = $foreignTable . '_';
}
if ($localeAlias === null) {
$localeAlias = $search->getTableMap()->getName();
}
$defaultLangWithoutTranslation = ConfigQuery::getDefaultLangWhenNoTranslationAvailable();
$requestedLocaleI18nAlias = $aliasPrefix . 'requested_locale_i18n';
$defaultLocaleI18nAlias = $aliasPrefix . 'default_locale_i18n';
if ($defaultLangWithoutTranslation == Lang::STRICTLY_USE_REQUESTED_LANGUAGE) {
$requestedLocaleJoin = new Join();
$requestedLocaleJoin->addExplicitCondition(
$localeAlias,
$foreignKey,
null,
$foreignTable . '_i18n',
'ID',
$requestedLocaleI18nAlias
);
$requestedLocaleJoin->setJoinType($forceReturn === false ? Criteria::INNER_JOIN : Criteria::LEFT_JOIN);
$defaultLocaleJoin = new Join();
$defaultLocaleJoin->addExplicitCondition(
$localeAlias,
$foreignKey,
null,
$foreignTable . '_i18n',
'ID',
$defaultLocaleI18nAlias
);
$search->addJoinObject($requestedLocaleJoin, $requestedLocaleI18nAlias)
->addJoinCondition(
$requestedLocaleI18nAlias,
'`' . $requestedLocaleI18nAlias . '`.LOCALE = ?',
$requestedLocale,
null,
\PDO::PARAM_STR
)
;
$search->addJoinObject($defaultLocaleJoin, $defaultLocaleI18nAlias)
->addJoinCondition(
$defaultLocaleI18nAlias,
'`' . $defaultLocaleI18nAlias . '`.LOCALE <> ?',
$requestedLocale,
null,
\PDO::PARAM_STR
)
;
$search->withColumn(
'NOT ISNULL(`' . $requestedLocaleI18nAlias . '`.`ID`)',
$aliasPrefix . 'IS_TRANSLATED'
);
foreach ($columns as $column) {
$search->withColumn(
'`' . $requestedLocaleI18nAlias . '`.`' . $column . '`',
$aliasPrefix . 'i18n_' . $column
);
}
} else {
$defaultLocale = Lang::getDefaultLanguage()->getLocale();
$defaultLocaleJoin = new Join();
$defaultLocaleJoin->addExplicitCondition(
$localeAlias,
$foreignKey,
null,
$foreignTable . '_i18n',
'ID',
$defaultLocaleI18nAlias
);
$defaultLocaleJoin->setJoinType(Criteria::LEFT_JOIN);
$search->addJoinObject($defaultLocaleJoin, $defaultLocaleI18nAlias)
->addJoinCondition(
$defaultLocaleI18nAlias,
'`' . $defaultLocaleI18nAlias . '`.LOCALE = ?',
$defaultLocale,
null,
\PDO::PARAM_STR
)
;
$requestedLocaleJoin = new Join();
$requestedLocaleJoin->addExplicitCondition(
$localeAlias,
$foreignKey,
null,
$foreignTable . '_i18n',
'ID',
$requestedLocaleI18nAlias
);
$requestedLocaleJoin->setJoinType(Criteria::LEFT_JOIN);
$search->addJoinObject($requestedLocaleJoin, $requestedLocaleI18nAlias)
->addJoinCondition(
$requestedLocaleI18nAlias,
'`' . $requestedLocaleI18nAlias . '`.LOCALE = ?',
$requestedLocale,
null,
\PDO::PARAM_STR
)
;
$search->withColumn(
'NOT ISNULL(`' . $requestedLocaleI18nAlias . '`.`ID`)',
$aliasPrefix . 'IS_TRANSLATED'
);
if ($forceReturn === false) {
$search->where('NOT ISNULL(`' . $requestedLocaleI18nAlias . '`.ID)')->_or()->where(
'NOT ISNULL(`' . $defaultLocaleI18nAlias . '`.ID)'
)
;
}
foreach ($columns as $column) {
$search->withColumn(
'CASE WHEN NOT ISNULL(`' . $requestedLocaleI18nAlias . '`.`' . $column . '`) THEN `' . $requestedLocaleI18nAlias . '`.`' . $column . '` ELSE `' . $defaultLocaleI18nAlias . '`.`' . $column . '` END',
$aliasPrefix . 'i18n_' . $column
);
}
}
}
}
public static function getBackEndI18n(
ModelCriteria &$search,
$requestedLocale,
$columns = array('TITLE', 'CHAPO', 'DESCRIPTION', 'POSTSCRIPTUM'),
$foreignTable = null,
$foreignKey = 'ID',
$localeAlias = null
) {
if (!empty($columns)) {
if ($foreignTable === null) {
$foreignTable = $search->getTableMap()->getName();
$aliasPrefix = '';
} else {
$aliasPrefix = $foreignTable . '_';
}
if ($localeAlias === null) {
$localeAlias = $search->getTableMap()->getName();
}
$requestedLocaleI18nAlias = $aliasPrefix . 'requested_locale_i18n';
$requestedLocaleJoin = new Join();
$requestedLocaleJoin->addExplicitCondition(
$localeAlias,
$foreignKey,
null,
$foreignTable . '_i18n',
'ID',
$requestedLocaleI18nAlias
);
$requestedLocaleJoin->setJoinType(Criteria::LEFT_JOIN);
$search->addJoinObject($requestedLocaleJoin, $requestedLocaleI18nAlias)
->addJoinCondition(
$requestedLocaleI18nAlias,
'`' . $requestedLocaleI18nAlias . '`.LOCALE = ?',
$requestedLocale,
null,
\PDO::PARAM_STR
)
;
$search->withColumn('NOT ISNULL(`' . $requestedLocaleI18nAlias . '`.`ID`)', $aliasPrefix . 'IS_TRANSLATED');
foreach ($columns as $column) {
$search->withColumn(
'`' . $requestedLocaleI18nAlias . '`.`' . $column . '`',
$aliasPrefix . 'i18n_' . $column
);
}
}
}
/**
* Bild query to retrieve I18n
*
* @param bool $backendContext
* @param int $requestedLangId
* @param ModelCriteria $search
* @param string $currentLocale
* @param array $columns
* @param string $foreignTable
* @param string $foreignKey
* @param bool $forceReturn
* @param string|null $localeAlias le local table if different of the main query table
*
* @return string
*/
public static function getI18n(
$backendContext,
$requestedLangId,
ModelCriteria &$search,
$currentLocale,
$columns,
$foreignTable,
$foreignKey,
$forceReturn = false,
$localeAlias = null
) {
// If a lang has been requested, find the related Lang object, and get the locale
if ($requestedLangId !== null) {
$localeSearch = LangQuery::create()->findByIdOrLocale($requestedLangId);
if ($localeSearch === null) {
throw new \InvalidArgumentException(
sprintf(
'Incorrect lang argument given : lang %s not found',
$requestedLangId
)
);
}
$locale = $localeSearch->getLocale();
} else {
// Use the currently defined locale
$locale = $currentLocale;
}
// Call the proper method depending on the context: front or back
if ($backendContext) {
self::getBackEndI18n($search, $locale, $columns, $foreignTable, $foreignKey, $localeAlias);
} else {
self::getFrontEndI18n($search, $locale, $columns, $foreignTable, $foreignKey, $forceReturn, $localeAlias);
}
return $locale;
}
}

View File

@@ -0,0 +1,68 @@
<?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\Model\Tools;
use Thelia\Core\Event\ActionEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* A trait to provide event dispatching mechanism to Model objects
*/
trait ModelEventDispatcherTrait
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $dispatcher = null;
/**
* @param EventDispatcherInterface $dispatcher
*
* @return $this
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
return $this;
}
public function getDispatcher()
{
return $this->dispatcher;
}
public function clearDispatcher()
{
$this->dispatcher = null;
}
protected function dispatchEvent($eventName, ActionEvent $event)
{
if (!is_null($this->dispatcher)) {
$this->dispatcher->dispatch($eventName, $event);
}
}
public function __sleep()
{
$data = parent::__sleep();
$key = array_search("dispatcher", $data);
if (isset($data[$key])) {
unset($data[$key]);
}
return $data;
}
}

View File

@@ -0,0 +1,216 @@
<?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\Model\Tools;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\ActiveQuery\PropelQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Propel;
trait PositionManagementTrait
{
/**
* Create an instance of this object query
*/
private function createQuery()
{
return PropelQuery::from(__CLASS__);
}
/**
* Implementors may add some search criteria (e.g., parent id) to the queries
* used to change/get position by overloading this method.
*
* @param $query ModelCriteria
*/
protected function addCriteriaToPositionQuery(ModelCriteria $query)
{
// Add required criteria here...
}
/**
* Get the position of the next inserted object
*/
public function getNextPosition()
{
$query = $this->createQuery()
->orderByPosition(Criteria::DESC)
->limit(1);
$this->addCriteriaToPositionQuery($query);
$last = $query->findOne();
return $last != null ? $last->getPosition() + 1 : 1;
}
/**
* Move up a object
*/
public function movePositionUp()
{
$this->movePositionUpOrDown(true);
}
/**
* Move down a object
*/
public function movePositionDown()
{
$this->movePositionUpOrDown(false);
}
/**
* Move up or down a object
*
* @param bool $up the exchange mode: go up (POSITION_UP) or go down (POSITION_DOWN)
*/
protected function movePositionUpOrDown($up = true)
{
// The current position of the object
$myPosition = $this->getPosition();
// Find object to exchange position with
$search = $this->createQuery();
$this->addCriteriaToPositionQuery($search);
// Up or down ?
if ($up === true) {
// Find the object immediately before me
$search->filterByPosition(array('max' => $myPosition-1))->orderByPosition(Criteria::DESC);
} else {
// Find the object immediately after me
$search->filterByPosition(array('min' => $myPosition+1))->orderByPosition(Criteria::ASC);
}
$result = $search->findOne();
// If we found the proper object, exchange their positions
if ($result) {
$cnx = Propel::getWriteConnection($this->getDatabaseName());
$cnx->beginTransaction();
try {
$this
->setPosition($result->getPosition())
->save($cnx)
;
// For BC
if (method_exists($result, 'setDispatcher') && method_exists($this, 'getDispatcher')) {
$result->setDispatcher($this->getDispatcher());
}
$result->setPosition($myPosition)->save($cnx);
$cnx->commit();
} catch (\Exception $e) {
$cnx->rollback();
}
}
}
/**
* Simply return the database name, from the constant in the MAP class.
*/
protected function getDatabaseName()
{
// Find DATABASE_NAME constant
$mapClassName = self::TABLE_MAP;
return $mapClassName::DATABASE_NAME;
}
/**
* Changes object position
*
* @param newPosition
*/
public function changeAbsolutePosition($newPosition)
{
// The current position
$current_position = $this->getPosition();
if ($newPosition != null && $newPosition > 0 && $newPosition != $current_position) {
// Find categories to offset
$search = $this->createQuery();
$this->addCriteriaToPositionQuery($search);
if ($newPosition > $current_position) {
// The new position is after the current position -> we will offset + 1 all categories located between us and the new position
$search->filterByPosition(array('min' => 1+$current_position, 'max' => $newPosition));
$delta = -1;
} else {
// The new position is brefore the current position -> we will offset - 1 all categories located between us and the new position
$search->filterByPosition(array('min' => $newPosition, 'max' => $current_position - 1));
$delta = 1;
}
$results = $search->find();
$cnx = Propel::getWriteConnection($this->getDatabaseName());
$cnx->beginTransaction();
try {
foreach ($results as $result) {
$objNewPosition = $result->getPosition() + $delta;
// For BC
if (method_exists($result, 'setDispatcher') && method_exists($this, 'getDispatcher')) {
$result->setDispatcher($this->getDispatcher());
}
$result->setPosition($objNewPosition)->save($cnx);
}
$this
->setPosition($newPosition)
->save($cnx)
;
$cnx->commit();
} catch (\Exception $e) {
$cnx->rollback();
}
}
}
protected function reorderBeforeDelete($fields = array())
{
// Find DATABASE_NAME constant
$mapClassName = self::TABLE_MAP;
$data = array();
$whereCriteria = array();
foreach ($fields as $field => $value) {
$whereCriteria[] = $field . '=:' . $field;
$data[':' . $field] = $value;
}
$data[':position'] = $this->getPosition();
$sql = sprintf("UPDATE `%s` SET position=(position-1) WHERE " . (count($whereCriteria)>0 ? implode(" AND ", $whereCriteria) : '1') . " AND position>:position", $mapClassName::TABLE_NAME);
$con = Propel::getConnection($mapClassName::DATABASE_NAME);
$statement = $con->prepare($sql);
$statement->execute($data);
}
}

View File

@@ -0,0 +1,59 @@
<?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\Model\Tools;
/**
* Utility class used to store price and promo price for a carte item.
*
* Class ProductPriceTools
* @package Thelia\Model\Tools
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class ProductPriceTools
{
/**
* The value for the price field.
*
* @var double
*/
protected $price;
/**
* The value for the promoPrice field.
*
* @var double
*/
protected $promoPrice;
public function __construct($price, $promoPrice)
{
$this->price = $price;
$this->promoPrice = $promoPrice;
}
/**
* @return float
*/
public function getPrice()
{
return $this->price;
}
/**
* @return float
*/
public function getPromoPrice()
{
return $this->promoPrice;
}
}

View File

@@ -0,0 +1,223 @@
<?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\Model\Tools;
use Thelia\Core\Event\GenerateRewrittenUrlEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Translation\Translator;
use Thelia\Exception\UrlRewritingException;
use Thelia\Model\RewritingArgumentQuery;
use Thelia\Model\RewritingUrlQuery;
use Thelia\Model\RewritingUrl;
use Thelia\Rewriting\RewritingResolver;
use Thelia\Tools\URL;
use Thelia\Model\ConfigQuery;
/**
* A trait for managing Rewritten URLs from model classes
*/
trait UrlRewritingTrait
{
/**
* @returns string the view name of the rewritten object (e.g., 'category', 'product')
*/
abstract public function getRewrittenUrlViewName();
/**
* Get the object URL for the given locale, rewritten if rewriting is enabled.
*
* @param string $locale a valid locale (e.g. en_US)
*/
public function getUrl($locale = null)
{
if (null === $locale) {
$locale = $this->getLocale();
}
return URL::getInstance()->retrieve($this->getRewrittenUrlViewName(), $this->getId(), $locale)->toString();
}
/**
* Generate a rewritten URL from the object title, and store it in the rewriting table
*
* @param string $locale a valid locale (e.g. en_US)
*/
public function generateRewrittenUrl($locale)
{
if ($this->isNew()) {
throw new \RuntimeException(sprintf('Object %s must be saved before generating url', $this->getRewrittenUrlViewName()));
}
// Borrowed from http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe
$this->setLocale($locale);
$generateEvent = new GenerateRewrittenUrlEvent($this, $locale);
$this->dispatchEvent(TheliaEvents::GENERATE_REWRITTENURL, $generateEvent);
if ($generateEvent->isRewritten()) {
return $generateEvent->getUrl();
}
$title = $this->getTitle();
if (null == $title) {
throw new \RuntimeException('Impossible to create an url if title is null');
}
// Replace all weird characters with dashes
$string = preg_replace('/[^\w\-~_\.]+/u', '-', $title);
// Only allow one dash separator at a time (and make string lowercase)
$cleanString = mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8');
$urlFilePart = rtrim($cleanString, '.-~_') . ".html";
try {
$i=0;
while (URL::getInstance()->resolve($urlFilePart)) {
$i++;
$urlFilePart = sprintf("%s-%d.html", $cleanString, $i);
}
} catch (UrlRewritingException $e) {
$rewritingUrl = new RewritingUrl();
$rewritingUrl->setUrl($urlFilePart)
->setView($this->getRewrittenUrlViewName())
->setViewId($this->getId())
->setViewLocale($locale)
->save()
;
}
return $urlFilePart;
}
/**
* return the rewritten URL for the given locale
*
* @param string $locale a valid locale (e.g. en_US)
* @return null
*/
public function getRewrittenUrl($locale)
{
$rewritingUrl = RewritingUrlQuery::create()
->filterByViewLocale($locale)
->filterByView($this->getRewrittenUrlViewName())
->filterByViewId($this->getId())
->filterByRedirected(null)
->findOne()
;
if ($rewritingUrl) {
$url = $rewritingUrl->getUrl();
} else {
$url = null;
}
return $url;
}
/**
* Mark the current URL as obseolete
*/
public function markRewrittenUrlObsolete()
{
RewritingUrlQuery::create()
->filterByView($this->getRewrittenUrlViewName())
->filterByViewId($this->getId())
->update(array(
"View" => ConfigQuery::getObsoleteRewrittenUrlView()
));
}
/**
* Set the rewritten URL for the given locale
*
* @param string $locale a valid locale (e.g. en_US)
* @param $url
* @return $this
* @throws UrlRewritingException
* @throws \Thelia\Exception\UrlRewritingException
*/
public function setRewrittenUrl($locale, $url)
{
$currentUrl = $this->getRewrittenUrl($locale);
if ($currentUrl == $url || null === $url) {
/* no url update */
return $this;
}
try {
$resolver = new RewritingResolver($url);
/* we can reassign old url */
if (null === $resolver->redirectedToUrl) {
/* else ... */
if ($resolver->view == $this->getRewrittenUrlViewName() && $resolver->viewId == $this->getId()) {
/* it's an url related to the current object */
if ($resolver->locale != $locale) {
/* it is an url related to this product for another locale */
throw new UrlRewritingException(Translator::getInstance()->trans('URL_ALREADY_EXISTS'), UrlRewritingException::URL_ALREADY_EXISTS);
}
if (count($resolver->otherParameters) > 0) {
/* it is an url related to this product but with more arguments */
throw new UrlRewritingException(Translator::getInstance()->trans('URL_ALREADY_EXISTS'), UrlRewritingException::URL_ALREADY_EXISTS);
}
/* here it must be a deprecated url */
} else {
/* already related to another object */
throw new UrlRewritingException(Translator::getInstance()->trans('URL_ALREADY_EXISTS'), UrlRewritingException::URL_ALREADY_EXISTS);
}
}
} catch (UrlRewritingException $e) {
/* It's all good if URL is not found */
if ($e->getCode() !== UrlRewritingException::URL_NOT_FOUND) {
throw $e;
}
}
/* set the new URL */
if (isset($resolver)) {
/* erase the old one */
$rewritingUrl = RewritingUrlQuery::create()->findOneByUrl($url);
$rewritingUrl->setView($this->getRewrittenUrlViewName())
->setViewId($this->getId())
->setViewLocale($locale)
->setRedirected(null)
->save()
;
/* erase additional arguments if any : only happens in case it erases a deprecated url */
RewritingArgumentQuery::create()->filterByRewritingUrl($rewritingUrl)->deleteAll();
} else {
/* just create it */
$rewritingUrl = new RewritingUrl();
$rewritingUrl->setUrl($url)
->setView($this->getRewrittenUrlViewName())
->setViewId($this->getId())
->setViewLocale($locale)
->save()
;
}
/* deprecate the old one if needed */
if (null !== $oldRewritingUrl = RewritingUrlQuery::create()->findOneByUrl($currentUrl)) {
$oldRewritingUrl->setRedirected($rewritingUrl->getId())->save();
}
return $this;
}
}