Merge pull request #284 from roadster31/basemodulecontroller

Various improvements, and URL calculation fix
This commit is contained in:
Manuel Raynaud
2014-04-14 16:32:29 +02:00
11 changed files with 393 additions and 32 deletions

View File

@@ -206,7 +206,7 @@ class Order extends BaseAction implements EventSubscriberInterface
$placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
$placedOrder->setStatusId(
OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_NOT_PAID)->getId()
OrderStatusQuery::getNotPaidStatus()->getId()
);
/* memorize discount */

View File

@@ -266,9 +266,9 @@ class BaseAdminController extends BaseController
/**
* Redirect to à route ID related URL
*
* @param unknown $routeId the route ID, as found in Config/Resources/routing/admin.xml
* @param array|\Thelia\Controller\Admin\unknown $urlParameters the URL parametrs, as a var/value pair array
* @param array $routeParameters
* @param string $routeId the route ID, as found in Config/Resources/routing/admin.xml
* @param array $urlParameters the URL parameters, as a var/value pair array
* @param array $routeParameters
*/
public function redirectToRoute($routeId, array $urlParameters = array(), array $routeParameters = array())
{
@@ -405,9 +405,9 @@ class BaseAdminController extends BaseController
/**
* Render the given template, and returns the result as an Http Response.
*
* @param $templateName the complete template name, with extension
* @param array $args the template arguments
* @param int $status http code status
* @param string $templateName the complete template name, with extension
* @param array $args the template arguments
* @param int $status http code status
* @return \Thelia\Core\HttpFoundation\Response
*/
protected function render($templateName, $args = array(), $status = 200)

View File

@@ -138,7 +138,7 @@ class OrderEvent extends ActionEvent
}
/**
* @param $status
* @param int $status
*/
public function setStatus($status)
{

View File

@@ -23,6 +23,11 @@
namespace Thelia\Install;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Connection\ConnectionWrapper;
use Propel\Runtime\Propel;
use Propel\Runtime\ServiceContainer\ServiceContainerInterface;
/**
* Class Database
* @package Thelia\Install
@@ -30,10 +35,34 @@ namespace Thelia\Install;
*/
class Database
{
public $connection;
/**
* @var \PDO
*/
protected $connection;
public function __construct(\PDO $connection)
/**
* Create a new instance, using the provided connection information, either none for
* automatically a connection, a ConnectionWrapper instance (through ConnectionInterface) or a PDO connection.
*
* @param ConnectionInterface|\PDO|null $connection the connection object
* @throws \InvalidArgumentException if $connection is not of the suitable type.
*/
public function __construct($connection = null)
{
// Get a connection from Propel if we don't have one
if (null == $connection) {
$connection = Propel::getConnection(ServiceContainerInterface::CONNECTION_WRITE);
}
// Get the PDO connection from an
if ($connection instanceof ConnectionWrapper) {
$connection = $connection->getWrappedConnection();
}
if (!$connection instanceof \PDO) {
throw new \InvalidArgumentException("A PDO connection shoud be provided");
}
$this->connection = $connection;
}
@@ -41,8 +70,8 @@ class Database
* Insert all sql needed in database
* Default insert /install/thelia.sql and /install/insert.sql
*
* @param string $dbName Database name
* @param array $extraSqlFiles SQL Files uri to insert
* @param string $dbName Database name
* @param array $extraSqlFiles SQL Files uri to insert
*/
public function insertSql($dbName = null, array $extraSqlFiles = null)
{
@@ -67,13 +96,35 @@ class Database
}
}
$size = count($sql);
for ($i = 0; $i < $size; $i ++) {
for ($i = 0; $i < $size; $i++) {
if (!empty($sql[$i])) {
$this->connection->query($sql[$i]);
$this->execute($sql[$i]);
}
}
}
/**
* A simple wrapper around PDO::exec
*
* @param string $sql SQL query
* @param array $args SQL request parameters (PDO style)
* @throws \RuntimeException|\PDOException if something goes wrong.
*/
public function execute($sql, $args = array())
{
$stmt = $this->connection->prepare($sql);
if ($stmt === false) {
throw new \RuntimeException("Failed to prepare statement for $sql: " . print_r($this->connection->errorInfo(), 1));
}
$success = $stmt->execute($args);
if ($success === false || $stmt->errorCode() != 0) {
throw new \RuntimeException("Failed to execute SQL '$sql', arguments:" . print_r($args,1).", error:".print_r($stmt->errorInfo(), 1));
}
}
/**
* Separate each sql instruction in an array
*
@@ -88,7 +139,7 @@ class Database
$tab = explode(";\n", $sql);
$size = count($tab);
for ($i=0; $i<$size; $i++) {
for ($i = 0; $i < $size; $i++) {
$queryTemp = str_replace("-CODE-", ";',", $tab[$i]);
$queryTemp = str_replace("|", ";", $queryTemp);
$query[] = $queryTemp;
@@ -104,7 +155,7 @@ class Database
*/
public function createDatabase($dbName)
{
$this->connection->exec(
$this->execute(
sprintf(
"CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8",
$dbName

View File

@@ -16,4 +16,26 @@ use Thelia\Model\Base\OrderStatusQuery as BaseOrderStatusQuery;
*/
class OrderStatusQuery extends BaseOrderStatusQuery
{
public static function getNotPaidStatus() {
return OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_NOT_PAID);
}
public static function getPaidStatus() {
return OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_PAID);
}
public static function getProcessingStatus() {
return OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_PROCESSING);
}
public static function getSentStatus() {
return OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_SENT);
}
public static function getCancelledStatus() {
return OrderStatusQuery::create()->findOneByCode(OrderStatus::CODE_CANCELED);
}
} // OrderStatusQuery

View File

@@ -0,0 +1,198 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
namespace Thelia\Module;
use Symfony\Component\Routing\Router;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Log\Tlog;
use Thelia\Model\OrderQuery;
use Thelia\Model\OrderStatus;
use Thelia\Model\OrderStatusQuery;
/**
* This class implement the minimum
*
* @package Paypal\Controller
* @author Thelia <info@thelia.net>
*/
abstract class BasePaymentModuleController extends BaseFrontController
{
protected $log = null;
/**
* Return a module identifier used to calculate the name of the log file,
* and in the log messages.
*
* @return string the module code
*/
protected abstract function getModuleCode();
/**
* Initialize a module-specific logger.
*
* @return Tlog a Tlog instance
*/
protected function getLog() {
if ($this->log == null) {
$this->log = Tlog::getNewInstance();
$logFilePath = sprintf(THELIA_ROOT."log".DS."%s.log", strtolower($this->getModuleCode()));
$this->log->setPrefix("#LEVEL: #DATE #HOUR: ");
$this->log->setDestinations("\\Thelia\\Log\\Destination\\TlogDestinationFile");
$this->log->setConfig("\\Thelia\\Log\\Destination\\TlogDestinationFile", 0, $logFilePath);
}
return $this->log;
}
/**
* Process the confirmation of an order. This method should be called
* once the module has performed the required checks to confirm a valid payment.
*
* @param int $order_id the order ID
*/
public function confirmPayment($order_id)
{
try {
$order_id = intval($order_id);
if (null !== $order = $this->getOrder($order_id)) {
$this->getLog()->addInfo(
$this->getTranslator()->trans("Processing confirmation of order ref. %ref, ID %id",
array('%ref' => $order->getRef(), '%id' => $order->getId()))
);
$event = new OrderEvent($order);
$event->setStatus(OrderStatusQuery::getPaidStatus()->getId());
$this->dispatch(TheliaEvents::ORDER_UPDATE_STATUS, $event);
$this->getLog()->addInfo(
$this->getTranslator()->trans("Order ref. %ref, ID %id has been successfully paid.",
array('%ref' => $order->getRef(), '%id' => $order->getId()))
);
}
}
catch (\Exception $ex) {
$this->getLog()->addError(
$this->getTranslator()->trans("Error occured while processing order ref. %ref, ID %id: %err",
array(
'%err' => $ex->getMessage(),
'%ref' => ! isset($order) ? "?" : $order->getRef(),
'%id' => ! isset($order) ? "?" : $order->getId()
)
)
);
throw $ex;
}
}
/**
* Process the cancelation of a payment on the payment gateway. The order will go back to the
* "not paid" status.
*
* @param int $order_id the order ID
*/
public function cancelPayment($order_id)
{
$order_id = intval($order_id);
if (null !== $order = $this->getOrder($order_id)) {
$this->getLog()->addInfo(
$this->getTranslator()->trans("Processing cancelation of payment for order ref. %ref",
array('%ref' => $order->getRef()))
);
$event = new OrderEvent($order);
$event->setStatus(OrderStatus::CODE_NOT_PAID);
$this->getLog()->addInfo(
$this->getTranslator()->trans("Order ref. %ref is now unpaid.",
array('%ref' => $order->getRef()))
);
$this->dispatch(TheliaEvents::ORDER_UPDATE_STATUS, $event);
}
}
/**
* Get an order and issue a log message if not found.
* @param $order_id
* @return null|\Thelia\Model\Order
*/
protected function getOrder($order_id)
{
if (null == $order = OrderQuery::create()->findPk($order_id)) {
$this->getLog()->addError($this->getTranslator()->trans("Unknown order ID: %id", array('%id' => $order_id)));
}
return $order;
}
/**
* Redirect the customer to the successful payment page.
*
* @param int $order_id the order ID
*/
public function redirectToSuccessPage($order_id) {
$this->getLog()->addInfo("Redirecting customer to payment success page");
$this->redirectToRoute(
'order.placed',
array(
'order_id' => $order_id
),
Router::ABSOLUTE_PATH
);
}
/**
* Redirect the customer to the failure payment page. if $message is null, a generic message is displayed.
*
* @param int $order_id the order ID
* @param string|null $message an error message.
*/
public function redirectToFailurePage($order_id, $message = null) {
$this->getLog()->addInfo("Redirecting customer to payment failure page");
$this->redirectToRoute(
'order.failed',
array(
'order_id' => $order_id,
'message' => $message
),
Router::ABSOLUTE_PATH
);
}
}

View File

@@ -23,6 +23,7 @@
namespace Thelia\Tools;
use Symfony\Component\Routing\RequestContext;
use Thelia\Model\ConfigQuery;
use Thelia\Rewriting\RewritingResolver;
use Thelia\Rewriting\RewritingRetriever;
@@ -32,9 +33,13 @@ 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;
@@ -42,10 +47,13 @@ class URL
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 intanciated.
// For this to work, the URL service has to be instanciated very early. This is done manually
// 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;
@@ -72,26 +80,34 @@ class URL
* 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()
public function getBaseUrl($scheme_only = false)
{
if ($host = $this->requestContext->getHost()) {
if (null === $this->baseUrlScheme) {
$scheme = $this->requestContext->getScheme();
$scheme = "http";
$port = 80;
$port = '';
if ($host = $this->requestContext->getHost()) {
if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) {
$port = ':'.$this->requestContext->getHttpPort();
} elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) {
$port = ':'.$this->requestContext->getHttpsPort();
$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();
}
}
$schemeAuthority = "$scheme://$host"."$port";
$this->baseUrlScheme = "$scheme://$host"."$port";
}
return $schemeAuthority.$this->requestContext->getBaseUrl();
return $scheme_only ? $this->baseUrlScheme : $this->baseUrlScheme . $this->requestContext->getBaseUrl();
}
/**
@@ -119,17 +135,25 @@ class URL
// Already absolute ?
if (substr($path, 0, 4) != 'http') {
$base_url = $this->getBaseUrl();
// 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.
$hasSubdirectory = 0 === strpos($path, $this->requestContext->getBaseUrl());
$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) {
// As the base_url always ends with '/', if we don't find / at the end, we have a script.
if (substr($base_url, -3) == 'php') $base_url = dirname($base_url);
}

View File

@@ -36,6 +36,7 @@ return array(
'Choose your delivery method' => 'Choose your delivery method',
'Choose your payment method' => 'Choose your payment method',
'Code :' => 'Code :',
'Connecting to the secure payment server, please wait a few seconds...' => 'Connection au serveur depaiement sécurisé, merci de patienter.',
'Contact Us' => 'Contact Us',
'Continue Shopping' => 'Continue Shopping',
'Copyright' => 'Copyright',
@@ -68,6 +69,7 @@ return array(
'Grid' => 'Grid',
'Home' => 'Home',
'I\'ve read and agreed on <a href=\'#\'>Terms &amp; Conditions</a>' => 'I\'ve read and agreed on <a href=\'#\'>Terms &amp; Conditions</a>',
'If nothing happens within 10 seconds, <a id="force-submit-payment-form" href="#">please click here</a>.' => 'Si rien ne se passe dans les 10 prochaines secondes, <a id="force-submit-payment-form" href="#">merci de cliquer ici</a>. ',
'In Stock' => 'In Stock',
'Instagram' => 'Instagram',
'Language' => 'Language',
@@ -113,6 +115,7 @@ return array(
'Pagination' => 'Pagination',
'Password' => 'Password',
'Password Forgotten' => 'Password Forgotten',
'Pay with %module_title' => 'Payer avec %module_title',
'Personal Information' => 'Personal Information',
'Placeholder address label' => 'Home, Work office, other',
'Placeholder address1' => '76 Ninth Avenue',
@@ -177,6 +180,7 @@ return array(
'Sign In' => 'Sign In',
'Sign up to receive our latest news.' => 'Sign up to receive our latest news.',
'Skip to content' => 'Skip to content',
'Sorry, your cart is empty. There\'s nothing to pay.' => 'Désolé, mais votre panier est vide. Il n\'y a rien à payer.',
'Sort By' => 'Sort By',
'Special Price:' => 'Special Price:',
'Status' => 'Status',
@@ -189,6 +193,7 @@ return array(
'Thelia V2' => 'Thelia V2',
'Toggle navigation' => 'Toggle navigation',
'Total' => 'Total',
'Try again' => 'Merci de ré-essayer.',
'Twitter' => 'Twitter',
'Unit Price' => 'Unit Price',
'Update' => 'Update',
@@ -204,6 +209,7 @@ return array(
'View order %ref as pdf document' => 'View order %ref as pdf document',
'View product' => 'View product',
'Warning' => 'Warning',
'We\'re sorry, a problem occured during the payement process, and your payment has not been accepted.' => 'Nous sommes désolés, votre paiement n\'a opas été accepté.',
'Welcome to Thelia. This is a demo site built with Thelia V2 an E-Commerce solution based on Symfony 2.' => 'Welcome to Thelia. This is a demo site built with Thelia V2 an E-Commerce solution based on Symfony 2.',
'You are here:' => 'You are here:',
'You choose to pay by' => 'You choose to pay by',

View File

@@ -36,6 +36,7 @@ return array(
'Choose your delivery method' => 'Choisissez votre moyen de livraison',
'Choose your payment method' => 'Choisissez voter moyen de paiement',
'Code :' => 'Code : ',
'Connecting to the secure payment server, please wait a few seconds...' => 'Connexion au serveur sécurisé, merci de patienter quelques secondes.',
'Contact Us' => 'Contactez-nous',
'Continue Shopping' => 'Continuer mes achats',
'Copyright' => 'Copyright',
@@ -68,6 +69,7 @@ return array(
'Grid' => 'Grille',
'Home' => 'Accueil',
'I\'ve read and agreed on <a href=\'#\'>Terms &amp; Conditions</a>' => 'J\'ai lu et j\'accepte les <a href=\'#\'>CGV</a>',
'If nothing happens within 10 seconds, <a id="force-submit-payment-form" href="#">please click here</a>.' => 'Si rien ne se passe dans les 10 secondes, <a id="force-submit-payment-form" href="#">meri de cliquer ici</a>. ',
'In Stock' => 'Disponible',
'Instagram' => 'Instagram',
'Language' => 'Langue',
@@ -113,6 +115,7 @@ return array(
'Pagination' => 'Pagination',
'Password' => 'Mot de passe',
'Password Forgotten' => 'Mot de passe oublié',
'Pay with %module_title' => 'Payer avec %module_title ',
'Personal Information' => 'Informations personnelles',
'Placeholder address label' => 'Maison, Domicile, Travail...',
'Placeholder address1' => 'Adresse',
@@ -172,6 +175,7 @@ return array(
'Sign In' => 'Se connecter',
'Sign up to receive our latest news.' => 'Inscrivez-vous pour recevoir les dernières nouveautés.',
'Skip to content' => 'Aller au contenu',
'Sorry, your cart is empty. There\'s nothing to pay.' => 'Désolé, votre panier est vide. Il n\'y a rien à payer.',
'Sort By' => 'Trier par',
'Special Price:' => 'Prix promo',
'Status' => 'Etat',
@@ -184,6 +188,7 @@ return array(
'Thelia V2' => 'Thelia v2',
'Toggle navigation' => 'Basculer la navigation',
'Total' => 'Total',
'Try again' => 'Ré-essayer le paiement',
'Twitter' => 'Twitter',
'Unit Price' => 'Prix unitaire',
'Update' => 'Mettre à jour',
@@ -199,6 +204,7 @@ return array(
'View order %ref as pdf document' => 'Ouvrir la commande %ref dans un pdf',
'View product' => 'Voir le produit',
'Warning' => 'Attention',
'We\'re sorry, a problem occured and your payment was not successful.' => 'Nous sommes désolés, un problème est survenu lors du paiement.',
'Welcome to Thelia. This is a demo site built with Thelia V2 an E-Commerce solution based on Symfony 2.' => 'Bienvenue sur ce site Thelia. ce site est un site de démonstration motorisé par la solution e-commerce libre Thelia v2 basée sur Symfony 2.',
'You are here:' => 'Vous êtes ici :',
'You choose to pay by' => 'Vous avez choisi de payer par',

View File

@@ -0,0 +1,50 @@
{extends file="layout.tpl"}
{* Security *}
{block name="no-return-functions" prepend}
{check_auth role="CUSTOMER" login_tpl="login"}
{/block}
{* Body Class *}
{block name="body-class"}page-order-payment{/block}
{* Breadcrumb *}
{block name='no-return-functions' append}
{$breadcrumbs = [
['title' => {intl l="Cart"}, 'url'=>{url path="/cart"}],
['title' => {intl l="Secure Payment"}, 'url'=>{url path="/order/pay"}]
]}
{/block}
{block name="main-content"}
<div class="main">
<article class="col-main clearfix" role="main" aria-labelledby="main-label">
<h1 id="main-label" class="page-header">{intl l="Your Cart"}</h1>
{include file="misc/checkout-progress.tpl" step="last"}
{loop type="order" name="failed-order" id=$failed_order_id}
<div id="payment-failure" class="panel">
<div class="panel-heading">
<h3 class="panel-title">{intl l="You choose to pay by"} : <span class="payment-method">{loop name="payment-module" type="module" id=$PAYMENT_MODULE}{$TITLE}{/loop}</span></h3>
</div>
<div class="panel-body">
<h3>{intl l="We're sorry, a problem occured and your payment was not successful."}</h3>
{if null !== $failed_order_message}
<p>{$failed_order_message}</p>
{/if}
<a href="{url path="/order/invoice"}" role="button" class="btn btn-checkout"><span>{intl l="Try again"}</span></a>
</div>
</div>
{/loop}
<a href="{navigate to="index"}" role="button" class="btn btn-checkout-home"><span>{intl l="Go home"}</span></a>
</article>
</div>
{/block}

View File

@@ -1,10 +1,14 @@
Options +FollowSymlinks -Indexes
#Options +FollowSymlinks -Indexes
AddDefaultCharset UTF-8
<IfModule mod_rewrite.c>
RewriteEngine On
# If thelia is installed in a subdirectory (e.g., thelia2)
# define the RewriteBase below to get a proper URL rewriting
# RewriteBase /thelia2
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d