Merge branch 'master' of github.com:thelia/thelia

This commit is contained in:
Etienne Roudeix
2013-09-06 07:49:14 +02:00
23 changed files with 505 additions and 261 deletions

View File

@@ -107,6 +107,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
CurrencyQuery::create()->filterByByDefault(true)->update(array('ByDefault' => false)); CurrencyQuery::create()->filterByByDefault(true)->update(array('ByDefault' => false));
$currency $currency
->setDispatcher($this->getDispatcher())
->setByDefault($event->getIsDefault()) ->setByDefault($event->getIsDefault())
->save() ->save()
; ;
@@ -139,7 +140,7 @@ class Currency extends BaseAction implements EventSubscriberInterface
$rates_url = ConfigQuery::read('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml'); $rates_url = ConfigQuery::read('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml');
$rate_data = file_get_contents($rates_url); $rate_data = @file_get_contents($rates_url);
if ($rate_data && $sxe = new \SimpleXMLElement($rate_data)) { if ($rate_data && $sxe = new \SimpleXMLElement($rate_data)) {
@@ -149,12 +150,16 @@ class Currency extends BaseAction implements EventSubscriberInterface
$rate = floatval($last['rate']); $rate = floatval($last['rate']);
if (null !== $currency = CurrencyQuery::create()->findOneByCode($code)) { if (null !== $currency = CurrencyQuery::create()->findOneByCode($code)) {
$currency->setRate($rate)->save(); $currency
->setDispatcher($this->getDispatcher())
->setRate($rate)
->save()
;
} }
} }
} }
else { else {
throw new \RuntimeException(sprintf("Failed to get currency rates data from URL %s", $url)); throw new \RuntimeException(sprintf("Failed to get currency rates data from URL %s", $rates_url));
} }
} }
@@ -165,12 +170,18 @@ class Currency extends BaseAction implements EventSubscriberInterface
*/ */
public function updatePosition(CurrencyUpdatePositionEvent $event) public function updatePosition(CurrencyUpdatePositionEvent $event)
{ {
if (null !== $category = CurrencyQuery::create()->findOneById($event->getObjectId())) { if (null !== $currency = CurrencyQuery::create()->findOneById($event->getObjectId())) {
if ($event->getMode() == BaseChangePositionEvent::POSITION_ABSOLUTE) $currency->setDispatcher($this->getDispatcher());
return $category->changeAbsolutePosition($event->getPosition());
else $mode = $event->getMode();
return $this->exchangePosition($event->getMode());
if ($mode == CurrencyUpdatePositionEvent::POSITION_ABSOLUTE)
return $currency->changeAbsolutePosition($event->getPosition());
else if ($mode == CurrencyUpdatePositionEvent::POSITION_UP)
return $currency->movePositionUp();
else if ($mode == CurrencyUpdatePositionEvent::POSITION_DOWN)
return $currency->movePositionDown();
} }
} }

View File

@@ -113,7 +113,7 @@
<default key="_controller">Thelia\Controller\Admin\CurrencyController::deleteAction</default> <default key="_controller">Thelia\Controller\Admin\CurrencyController::deleteAction</default>
</route> </route>
<route id="admin.configuration.currencies.delete" path="/admin/configuration/currencies/update-position"> <route id="admin.configuration.currencies.update-position" path="/admin/configuration/currencies/update-position">
<default key="_controller">Thelia\Controller\Admin\CurrencyController::updatePositionAction</default> <default key="_controller">Thelia\Controller\Admin\CurrencyController::updatePositionAction</default>
</route> </route>

View File

@@ -50,7 +50,7 @@
<default key="_view">cart</default> <default key="_view">cart</default>
</route> </route>
<route id="cart.update.process" path="/cart/delete/{cart_item}"> <route id="cart.delete.process" path="/cart/delete/{cart_item}">
<default key="_controller">Thelia\Controller\Front\CartController::deleteItem</default> <default key="_controller">Thelia\Controller\Front\CartController::deleteItem</default>
<default key="_view">cart</default> <default key="_view">cart</default>
</route> </route>

View File

@@ -316,17 +316,22 @@ class CurrencyController extends BaseAdminController
if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response; if (null !== $response = $this->checkAuth("admin.configuration.currencies.update")) return $response;
try { try {
$id = $this->getRequest()->get('currency_id', 0);
$mode = $this->getRequest()->get('mode', null); $mode = $this->getRequest()->get('mode', null);
if ($mode == 'up')
$mode = CurrencyUpdatePositionEvent::POSITION_UP;
else if ($mode == 'down')
$mode = CurrencyUpdatePositionEvent::POSITION_DOWN;
else
$mode = CurrencyUpdatePositionEvent::POSITION_ABSOLUTE;
$position = $this->getRequest()->get('position', null); $position = $this->getRequest()->get('position', null);
$event = new CurrencyUpdatePositionEvent(); $event = new CurrencyUpdatePositionEvent(
$this->getRequest()->get('currency_id', null),
$event $mode,
->setObjectId($this->getRequest()->get('currency_id', 0)) $this->getRequest()->get('position', null)
->setPosition($this->getRequest()->get('position', 0)) );
->setMode($mode)
;
$this->dispatch(TheliaEvents::CURRENCY_UPDATE_POSITION, $event); $this->dispatch(TheliaEvents::CURRENCY_UPDATE_POSITION, $event);
} }

View File

@@ -1,81 +0,0 @@
<?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\Controller\Front;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Exception\UrlRewritingException;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\URL;
class UrlRewritingController extends BaseFrontController
{
public function check(Request $request, $rewritten_url)
{
if(ConfigQuery::isRewritingEnable()) {
try {
$rewrittenUrlData = URL::getInstance()->resolve($rewritten_url);
} catch(UrlRewritingException $e) {
switch($e->getCode()) {
case UrlRewritingException::URL_NOT_FOUND :
return $this->pageNotFound();
break;
default:
throw $e;
}
}
/* is the URL redirected ? */
if(null !== $rewrittenUrlData->redirectedToUrl) {
$this->redirect($rewrittenUrlData->redirectedToUrl, 301);
}
/* define GET arguments in request */
if(null !== $rewrittenUrlData->view) {
$request->query->set('view', $rewrittenUrlData->view);
if(null !== $rewrittenUrlData->viewId) {
$request->query->set($rewrittenUrlData->view . '_id', $rewrittenUrlData->viewId);
}
}
if(null !== $rewrittenUrlData->locale) {
$request->query->set('locale', $rewrittenUrlData->locale);
}
foreach($rewrittenUrlData->otherParameters as $parameter => $value) {
$request->query->set($parameter, $value);
}
}
if (! $view = $request->query->get('view')) {
$view = "index";
if ($request->request->has('view')) {
$view = $request->request->get('view');
}
}
$request->attributes->set('_view', $view);
}
}

View File

@@ -74,14 +74,4 @@ class BaseUpdatePositionEvent extends ActionEvent
$this->object_id = $object_id; $this->object_id = $object_id;
return $this; return $this;
} }
public function getObjectId()
{
return $this->object_id;
}
public function setObjectId($object_id)
{
$this->object_id = $object_id;
}
} }

View File

@@ -23,6 +23,6 @@
namespace Thelia\Core\Event; namespace Thelia\Core\Event;
class CategoryChangePositionEvent extends BaseChangePositionEvent class CurrencyUpdatePositionEvent extends BaseUpdatePositionEvent
{ {
} }

View File

@@ -106,17 +106,50 @@ class AsseticHelper
$factory->setDebug($debug); $factory->setDebug($debug);
$factory->addWorker(new CacheBustingWorker()); $factory->addWorker(new CacheBustingWorker('-'));
// Prepare the assets writer // We do not pass the filter list here, juste to get the asset file name
$writer = new AssetWriter($output_path); $asset = $factory->createAsset($asset_name);
$asset = $factory->createAsset($asset_name, $filter_list); $asset_target_path = $asset->getTargetPath();
$target_file = sprintf("%s/%s", $output_path, $asset_target_path);
$cache = new AssetCache($asset, new FilesystemCache($output_path)); // As it seems that assetic cannot handle a real file cache, let's do the job ourselves.
// It works only if the CacheBustingWorker is used, as a new file name is generated for each version.
//
// the previous version of the file is deleted, by getting the first part of the ouput file name
// (the one before '-'), and delete aby file beginning with the same string. Example:
// old name: 3bc974a-dfacc1f.css
// new name: 3bc974a-ad3ef47.css
//
// before generating 3bc974a-ad3ef47.css, delete 3bc974a-* files.
//
if (! file_exists($target_file)) {
$writer->writeAsset($cache); // Delete previous version of the file
list($commonPart, $dummy) = explode('-', $asset_target_path);
return rtrim($output_url, '/').'/'.$asset->getTargetPath(); foreach (glob("$output_path/$commonPart-*") as $filename) {
@unlink($filename);
}
// Apply filters now
foreach ($filter_list as $filter) {
if ('?' != $filter[0]) {
$asset->ensureFilter($fm->get($filter));
}
elseif (!$debug) {
$asset->ensureFilter($fm->get(substr($filter, 1)));
}
}
//$cache = new AssetCache($asset, new FilesystemCache($output_path));
$writer = new AssetWriter($output_path);
$writer->writeAsset($asset);
}
return rtrim($output_url, '/').'/'.$asset_target_path;
} }
} }

View File

@@ -71,11 +71,11 @@ class AdminUtilities extends AbstractSmartyPlugin
if ($permissions == null || $this->securityContext->isGranted("ADMIN", array($permission))) { if ($permissions == null || $this->securityContext->isGranted("ADMIN", array($permission))) {
return sprintf( return sprintf(
'<a href="%s"><i class="glyphicon glyphicon-arrow-up"></i></a><span class="%s" data-id="%s">%s</span><a href="%s"><i class="glyphicon glyphicon-arrow-down"></i></a>', '<a href="%s"><i class="glyphicon glyphicon-arrow-up"></i></a><span class="%s" data-id="%s">%s</span><a href="%s"><i class="glyphicon glyphicon-arrow-down"></i></a>',
URL::getInstance()->absoluteUrl("$path/positionUp", array($url_parameter => $id)), URL::getInstance()->absoluteUrl($path, array('mode' => 'up', $url_parameter => $id)),
$in_place_edit_class, $in_place_edit_class,
$id, $id,
$position, $position,
URL::getInstance()->absoluteUrl("$path/positionDown", array($url_parameter => $id)) URL::getInstance()->absoluteUrl($path, array('mode' => 'down', $url_parameter => $id))
); );
} }
else { else {

View File

@@ -40,11 +40,47 @@ class CurrencyCreationForm extends BaseForm
} }
$this->formBuilder $this->formBuilder
->add("name" , "text" , array("constraints" => array(new NotBlank()))) ->add("name" , "text" , array(
->add("locale" , "text" , array("constraints" => array(new NotBlank()))) "constraints" => array(
->add("symbol" , "text" , array("constraints" => array(new NotBlank()))) new NotBlank()
->add("rate" , "text" , array("constraints" => array(new NotBlank()))) ),
->add("code" , "text" , array("constraints" => $code_constraints)) "label" => "Name *",
"label_attr" => array(
"for" => "name"
))
)
->add("locale" , "text" , array(
"constraints" => array(
new NotBlank()
))
)
->add("symbol" , "text" , array(
"constraints" => array(
new NotBlank()
),
"label" => "Symbol *",
"label_attr" => array(
"for" => "symbol"
))
)
->add("rate" , "text" , array(
"constraints" => array(
new NotBlank()
),
"label" => "Rate from &euro; *",
"label_attr" => array(
"for" => "rate"
))
)
->add("code" , "text" , array(
"constraints" => array(
new NotBlank()
),
"label" => "ISO 4217 code *",
"label_attr" => array(
"for" => "iso_4217_code"
))
)
; ;
} }

View File

@@ -20,6 +20,9 @@ class Currency extends BaseCurrency {
{ {
$this->dispatchEvent(TheliaEvents::BEFORE_CREATECURRENCY, new CurrencyEvent($this)); $this->dispatchEvent(TheliaEvents::BEFORE_CREATECURRENCY, new CurrencyEvent($this));
// Set the current position for the new object
$this->setPosition($this->getNextPosition());
return true; return true;
} }

View File

@@ -44,6 +44,11 @@ trait ModelEventDispatcherTrait {
return $this; return $this;
} }
public function getDispatcher()
{
return $this->dispatcher;
}
protected function dispatchEvent($eventName, ActionEvent $event) protected function dispatchEvent($eventName, ActionEvent $event)
{ {
if (!is_null($this->dispatcher)) { if (!is_null($this->dispatcher)) {

View File

@@ -25,6 +25,7 @@ namespace Thelia\Model\Tools;
use Propel\Runtime\ActiveQuery\PropelQuery; use Propel\Runtime\ActiveQuery\PropelQuery;
use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Propel;
trait PositionManagementTrait { trait PositionManagementTrait {
@@ -48,13 +49,15 @@ trait PositionManagementTrait {
/** /**
* Get the position of the next inserted object * Get the position of the next inserted object
*/ */
public function getNextPosition($parent) { public function getNextPosition($parent = null) {
$last = $this->createQuery() $query = $this->createQuery()
->filterByParent($parent)
->orderByPosition(Criteria::DESC) ->orderByPosition(Criteria::DESC)
->limit(1) ->limit(1);
->findOne()
if ($parent !== null) $last->filterByParent($parent);
$last = $query->findOne()
; ;
return $last != null ? $last->getPosition() + 1 : 1; return $last != null ? $last->getPosition() + 1 : 1;
@@ -63,14 +66,14 @@ trait PositionManagementTrait {
/** /**
* Move up a object * Move up a object
*/ */
protected function movePositionUp() { public function movePositionUp() {
$this->movePositionUpOrDown(true); $this->movePositionUpOrDown(true);
} }
/** /**
* Move down a object * Move down a object
*/ */
protected function movePositionDown() { public function movePositionDown() {
$this->movePositionUpOrDown(false); $this->movePositionUpOrDown(false);
} }
@@ -85,8 +88,9 @@ trait PositionManagementTrait {
$my_position = $this->getPosition(); $my_position = $this->getPosition();
// Find object to exchange position with // Find object to exchange position with
$search = $this->createQuery() $search = $this->createQuery();
->filterByParent($this->getParent());
if (method_exists($this, 'getParent')) $search->filterByParent($this->getParent());
// Up or down ? // Up or down ?
if ($up === true) { if ($up === true) {
@@ -103,18 +107,17 @@ trait PositionManagementTrait {
// If we found the proper object, exchange their positions // If we found the proper object, exchange their positions
if ($result) { if ($result) {
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); $cnx = Propel::getWriteConnection($this->getDatabaseName());
$cnx->beginTransaction(); $cnx->beginTransaction();
try { try {
$this $this
->setDispatcher($this->getDispatcher())
->setPosition($result->getPosition()) ->setPosition($result->getPosition())
->save() ->save()
; ;
$result->setPosition($my_position)->save(); $result->setDispatcher($this->getDispatcher())->setPosition($my_position)->save();
$cnx->commit(); $cnx->commit();
} catch (Exception $e) { } catch (Exception $e) {
@@ -123,12 +126,22 @@ trait PositionManagementTrait {
} }
} }
/**
* 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 * Changes object position
* *
* @param newPosition * @param newPosition
*/ */
protected function changeAbsolutePosition($newPosition) public function changeAbsolutePosition($newPosition)
{ {
// The current position // The current position
$current_position = $this->getPosition(); $current_position = $this->getPosition();
@@ -136,7 +149,9 @@ trait PositionManagementTrait {
if ($newPosition != null && $newPosition > 0 && $newPosition != $current_position) { if ($newPosition != null && $newPosition > 0 && $newPosition != $current_position) {
// Find categories to offset // Find categories to offset
$search = $this->createQuery()->filterByParent($this->getParent()); $search = $this->createQuery();
if (method_exists($this, 'getParent')) $search->filterByParent($this->getParent());
if ($newPosition > $current_position) { 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 // The new position is after the current position -> we will offset + 1 all categories located between us and the new position
@@ -152,17 +167,16 @@ trait PositionManagementTrait {
$results = $search->find(); $results = $search->find();
$cnx = Propel::getWriteConnection(CategoryTableMap::DATABASE_NAME); $cnx = Propel::getWriteConnection($this->getDatabaseName());
$cnx->beginTransaction(); $cnx->beginTransaction();
try { try {
foreach ($results as $result) { foreach ($results as $result) {
$result->setPosition($result->getPosition() + $delta)->save($cnx); $result->setDispatcher($this->getDispatcher())->setPosition($result->getPosition() + $delta)->save($cnx);
} }
$this $this
->setDispatcher($this->getDispatcher())
->setPosition($newPosition) ->setPosition($newPosition)
->save($cnx) ->save($cnx)
; ;

View File

@@ -37,7 +37,7 @@ use Thelia\Tools\URL;
* *
* @package Thelia\Tests\Action\ImageTest * @package Thelia\Tests\Action\ImageTest
*/ */
class ImageTest extends \PHPUnit_Framework_TestCase class ImageTest extends \Thelia\Tests\TestCaseWithURLToolSetup
{ {
protected $request; protected $request;
@@ -49,10 +49,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase
$dispatcher = $this->getMock("Symfony\Component\EventDispatcher\EventDispatcherInterface"); $dispatcher = $this->getMock("Symfony\Component\EventDispatcher\EventDispatcherInterface");
$url = new URL($container, 'dev');
$container->set("event_dispatcher", $dispatcher); $container->set("event_dispatcher", $dispatcher);
$container->set("thelia.url.manager", $dispatcher);
$request = new Request(); $request = new Request();
$request->setSession($this->session); $request->setSession($this->session);

View File

@@ -34,7 +34,7 @@ use Thelia\Core\HttpFoundation\Session\Session;
* @author Etienne Roudeix <eroudeix@openstudio.fr> * @author Etienne Roudeix <eroudeix@openstudio.fr>
* *
*/ */
abstract class BaseLoopTestor extends \PHPUnit_Framework_TestCase abstract class BaseLoopTestor extends \Thelia\Tests\TestCaseWithURLToolSetup
{ {
protected $request; protected $request;
protected $dispatcher; protected $dispatcher;

View File

@@ -0,0 +1,64 @@
<?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\Tests;
/**
* This class provides URL Tool class initialisation
*
* @package Thelia\Tests\TestCaseWithURLSetup
*/
class TestCaseWithURLToolSetup extends \PHPUnit_Framework_TestCase {
public function __construct() {
$this->setupURLTool();
}
protected function setupURLTool() {
$container = new \Symfony\Component\DependencyInjection\ContainerBuilder();
$context = new \Symfony\Component\Routing\RequestContext(
'/thelia/index.php',
'GET',
'localhost',
'http',
80,
443,
'/path/to/action'
);
$router = $this->getMockBuilder("Symfony\Component\Routing\Router")
->disableOriginalConstructor()
->getMock();
$router->expects($this->any())
->method('getContext')
->will($this->returnValue($context));
$container->set("router.admin", $router);
new \Thelia\Tools\URL($container);
}
}

View File

@@ -28,10 +28,208 @@ use Thelia\Tools\URL;
/** /**
* *
* @author Etienne Roudeix <eroudeix@openstudio.fr> * @author Etienne Roudeix <eroudeix@openstudio.fr>
* @author Franck Allimant <eroudeix@openstudio.fr>
* *
*/ */
class URLTest extends \PHPUnit_Framework_TestCase class URLTest extends \PHPUnit_Framework_TestCase
{ {
protected $context;
public function setUp()
{
$container = new \Symfony\Component\DependencyInjection\ContainerBuilder();
$router = $this->getMockBuilder("Symfony\Component\Routing\Router")
->disableOriginalConstructor()
->getMock();
$this->context = new \Symfony\Component\Routing\RequestContext(
'/thelia/index.php',
'GET',
'localhost',
'http',
80,
443,
'/path/to/action'
);
$router->expects($this->any())
->method('getContext')
->will($this->returnValue($this->context));
$container->set("router.admin", $router);
new \Thelia\Tools\URL($container);
}
public function testGetIndexPage() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->getIndexPage();
$this->assertEquals('http://localhost/thelia/index.php', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->getIndexPage();
$this->assertEquals('http://localhost/thelia/', $url);
$this->context->setBaseUrl('/thelia');
$url = \Thelia\Tools\URL::getInstance()->getIndexPage();
$this->assertEquals('http://localhost/thelia', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->getIndexPage();
$this->assertEquals('http://localhost/', $url);
}
public function testGetBaseUrl() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->getBaseUrl();
$this->assertEquals('http://localhost/thelia/index.php', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->getBaseUrl();
$this->assertEquals('http://localhost/thelia/', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->getBaseUrl();
$this->assertEquals('http://localhost/', $url);
}
public function testAbsoluteUrl() {
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action');
$this->assertEquals('http://localhost/path/to/action', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action');
$this->assertEquals('http://localhost/thelia/path/to/action', $url);
$this->context->setBaseUrl('/thelia');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action');
$this->assertEquals('http://localhost/thelia/path/to/action', $url);
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action');
$this->assertEquals('http://localhost/thelia/index.php/path/to/action', $url);
}
public function testAbsoluteUrlOnAbsolutePath() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action');
$this->assertEquals('http://myhost/path/to/action', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action');
$this->assertEquals('http://myhost/path/to/action', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action');
$this->assertEquals('http://myhost/path/to/action', $url);
}
public function testAbsoluteUrlOnAbsolutePathWithParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p1=v1&p2=v2', $url);
}
public function testAbsoluteUrlOnAbsolutePathWithParametersAddParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('http://myhost/path/to/action?p0=v0', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://myhost/path/to/action?p0=v0&p1=v1&p2=v2', $url);
}
public function testAbsoluteUrlWithParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/index.php/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/path/to/action?p1=v1&p2=v2', $url);
}
public function testAbsoluteUrlPathOnly() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array(), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/thelia/path/to/action', $url);
}
public function testAbsoluteUrlPathOnlyWithParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/thelia/path/to/action?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/path/to/action', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/path/to/action?p1=v1&p2=v2', $url);
}
public function testAbsoluteUrlFromIndexWithParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/index.php/?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"));
$this->assertEquals('http://localhost/?p1=v1&p2=v2', $url);
}
public function testAbsoluteUrlPathOnlyFromIndexWithParameters() {
$this->context->setBaseUrl('/thelia/index.php');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/thelia/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/thelia/?p1=v1&p2=v2', $url);
$this->context->setBaseUrl('/');
$url = \Thelia\Tools\URL::getInstance()->absoluteUrl('/', array("p1" => "v1", "p2" => "v2"), URL::PATH_TO_FILE);
$this->assertEquals('http://localhost/?p1=v1&p2=v2', $url);
}
public function testRetrieve() public function testRetrieve()
{ {

View File

@@ -27,33 +27,30 @@ use Thelia\Model\ConfigQuery;
use Thelia\Rewriting\RewritingResolver; use Thelia\Rewriting\RewritingResolver;
use Thelia\Rewriting\RewritingRetriever; use Thelia\Rewriting\RewritingRetriever;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Thelia\Core\HttpFoundation\Request; use Thelia\Core\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RequestContext;
class URL class URL
{ {
protected $resolver = null; protected $resolver = null;
protected $retriever = null; protected $retriever = null;
protected $container; protected $requestContext;
protected $environment;
const PATH_TO_FILE = true; const PATH_TO_FILE = true;
const WITH_INDEX_PAGE = false; const WITH_INDEX_PAGE = false;
private static $instance = null; protected static $instance = null;
public function __construct(ContainerInterface $container, $environment) public function __construct(ContainerInterface $container)
{ {
// Allow singleton style calls once intanciated. // Allow singleton style calls once intanciated.
// For this to work, the URL service has to be instanciated very early. This is done manually // For this to work, the URL service has to be instanciated very early. This is done manually
// in TheliaHttpKernel, by calling $this->container->get('thelia.url.manager'); // in TheliaHttpKernel, by calling $this->container->get('thelia.url.manager');
self::$instance = $this; self::$instance = $this;
$this->container = $container; $this->requestContext = $container->get('router.admin')->getContext();
$this->environment = $environment;
$this->retriever = new RewritingRetriever(); $this->retriever = new RewritingRetriever();
$this->resolver = new RewritingResolver(); $this->resolver = new RewritingResolver();
@@ -79,49 +76,31 @@ class URL
*/ */
public function getBaseUrl() public function getBaseUrl()
{ {
$request = $this->container->get('request'); if ($host = $this->requestContext->getHost()) {
$lang = $request->getSession()->getLang();
// Check if we have a specific URL for each lang. $scheme = $this->requestContext->getScheme();
$one_domain_foreach_lang = ConfigQuery::read("one_domain_foreach_lang", false);
if ($one_domain_foreach_lang == true) { $port = '';
// If it's the case, get the current lang URL
$base_url = $lang->getUrl();
$err_msg_part = 'base_url'; if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) {
} $port = ':'.$this->requestContext->getHttpPort();
else { } elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) {
// Get the base URL $port = ':'.$this->requestContext->getHttpsPort();
$base_url = ConfigQuery::read('base_url', $request->getSchemeAndHttpHost()); }
$err_msg_part = sprintf('base_url for lang %s', $lang->getCode()); $schemeAuthority = "$scheme://$host"."$port";
} }
// Be sure that base-url starts with http, give up if it's not the case. return $schemeAuthority.$this->requestContext->getBaseUrl();
if (substr($base_url, 0, 4) != 'http') {
throw new \InvalidArgumentException(
sprintf("The %s configuration parameter shoud contains the URL of your shop, starting with http or https.", $err_msg_part));
}
// Normalize the base_url
return rtrim($base_url, '/').'/';
} }
/** /**
* @return string the index page, which is basically the base_url in prod environment. * @return string the index page, which is in fact the base URL.
*/ */
public function getIndexPage() public function getIndexPage()
{ {
// Get the base URL // The index page is the base URL :)
$base_url = $this->getBaseUrl(); return $this->getBaseUrl();
// For dev environment, add the proper page.
if ($this->environment == 'dev') {
$base_url .= "index_dev.php";
}
return $base_url;
} }
/** /**
@@ -140,14 +119,16 @@ class URL
// Already absolute ? // Already absolute ?
if (substr($path, 0, 4) != 'http') { if (substr($path, 0, 4) != 'http') {
/** $base_url = $this->getBaseUrl();
* @etienne : can't be done here for it's already done in ::viewUrl / ::adminViewUrl
* @franck : should be done, as absoluteUrl() is sometimes called directly (see UrlGenerator::generateUrlFunction())
*/
$root = $path_only == self::PATH_TO_FILE ? $this->getBaseUrl() : $this->getIndexPage();
// Normalize root path // If only a path is requested, be sure to remove the script name (index.php or index_dev.php), if any.
$base = rtrim($root, '/') . '/' . ltrim($path, '/'); 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, -1) != '/') $base_url = dirname($base_url);
}
// Normalize the given path
$base = rtrim($base_url, '/') . '/' . ltrim($path, '/');
} else } else
$base = $path; $base = $path;

View File

@@ -210,9 +210,7 @@
{block name="before-javascript-include"}{/block} {block name="before-javascript-include"}{/block}
{javascripts file='assets/js/jquery.min.js'} <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="{$asset_url}"></script>
{/javascripts}
{block name="after-javascript-include"}{/block} {block name="after-javascript-include"}{/block}

View File

@@ -3,4 +3,6 @@
/* Thelia Admin */ /* Thelia Admin */
@import "thelia/thelia.less"; @import "thelia/thelia.less";
// @import "thelia/responsive.less"; // @import "thelia/responsive.less";
//mmm

View File

@@ -4,12 +4,6 @@
{block name="check-permissions"}admin.catalog.view{/block} {block name="check-permissions"}admin.catalog.view{/block}
{block name="after-admin-css"}
{stylesheets file='assets/bootstrap-editable/css/bootstrap-editable.css' filters='cssembed'}
<link rel="stylesheet" href="{$asset_url}">
{/stylesheets}
{/block}
{block name="main-content"} {block name="main-content"}
<div class="catalog"> <div class="catalog">
<div id="wrapper" class="container"> <div id="wrapper" class="container">
@@ -278,13 +272,11 @@
{include file="includes/delete-category-dialog.html"} {include file="includes/delete-category-dialog.html"}
{/block} {/block}
{block name="after-javascript-include"} {block name="javascript-initialization"}
{javascripts file='assets/bootstrap-editable/js/bootstrap-editable.js'} {javascripts file='assets/js/bootstrap-editable/bootstrap-editable.js'}
<script src="{$asset_url}"></script> <script src="{$asset_url}"></script>
{/javascripts} {/javascripts}
{/block}
{block name="javascript-initialization"}
<script> <script>
$(function() { $(function() {

View File

@@ -28,7 +28,7 @@
<caption class="clearfix"> <caption class="clearfix">
{intl l='Currencies'} {intl l='Currencies'}
{loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.currencies.create"} {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.configuration.currencies.create"}
<span class="pull-right"> <span class="pull-right">
<button class="btn btn-default btn-info" title="{intl l='Update rates'}">{intl l='Update rates'} <span class="glyphicon glyphicon-globe"></span></button> <button class="btn btn-default btn-info" title="{intl l='Update rates'}">{intl l='Update rates'} <span class="glyphicon glyphicon-globe"></span></button>
<a class="btn btn-default btn-primary" title="{intl l='Add a new currency'}" href="#add_currency_dialog" data-toggle="modal"> <a class="btn btn-default btn-primary" title="{intl l='Add a new currency'}" href="#add_currency_dialog" data-toggle="modal">
<span class="glyphicon glyphicon-plus-sign"></span> <span class="glyphicon glyphicon-plus-sign"></span>
@@ -114,7 +114,7 @@
<th class="text-right">{intl l='Actions'}</th> <th class="text-right">{intl l='Actions'}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{loop name="currencies" type="currency" backend_context="1" lang=$lang_id order=$order} {loop name="currencies" type="currency" backend_context="1" lang=$lang_id order=$order}
<tr> <tr>
@@ -138,7 +138,7 @@
<td class="text-center"> <td class="text-center">
{admin_position_block {admin_position_block
permission="admin.currencies.edit" permission="admin.currencies.edit"
path="/admin/configuration/currencies" path="/admin/configuration/currencies/update-position"
url_parameter="currency_id" url_parameter="currency_id"
in_place_edit_class="currencyPositionChange" in_place_edit_class="currencyPositionChange"
position="$POSITION" position="$POSITION"
@@ -186,6 +186,7 @@
</form> </form>
</div> </div>
</div> </div>
{module_include location='currencies_bottom'} {module_include location='currencies_bottom'}
@@ -216,18 +217,21 @@
<input type="hidden" name="{$name}" value="{url path='/admin/configuration/currencies/update' currency_id='_ID_'}" /> <input type="hidden" name="{$name}" value="{url path='/admin/configuration/currencies/update' currency_id='_ID_'}" />
{/form_field} {/form_field}
{* We do not allow users to create secured currencies from here *}
<div class="modal-body"> <div class="modal-body">
{if $form_error}<div class="alert alert-block alert-error" id="add_currency_dialog_error">{$form_error_message}</div>{/if} {if $form_error}<div class="alert alert-block alert-error" id="add_currency_dialog_error">{$form_error_message}</div>{/if}
{form_field form=$form field='name'} {form_field form=$form field='name'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
<label>{intl l='Name *'}</label>
{loop type="lang" name="default-lang" default_only="1"} {loop type="lang" name="default-lang" default_only="1"}
<div class="input-group">
<input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency name'}" placeholder="{intl l='Name'}">
<span class="input-group-addon"><img src="{image file="assets/img/flags/{$CODE}.gif"}" alt="{intl l=$TITLE}" /></span>
</div>
<div class="help-block">{intl l="Enter here the currency name in the default language ($TITLE)"}</div>
{* Switch edition to the current locale *} {* Switch edition to the current locale *}
<input type="hidden" name="edit_language_id" value="{$ID}" /> <input type="hidden" name="edit_language_id" value="{$ID}" />
@@ -235,38 +239,30 @@
{form_field form=$form field='locale'} {form_field form=$form field='locale'}
<input type="hidden" name="{$name}" value="{$LOCALE}" /> <input type="hidden" name="{$name}" value="{$LOCALE}" />
{/form_field} {/form_field}
<div class="input-group">
<input type="text" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency name'}" placeholder="{intl l='Name'}">
<span class="input-group-addon"><img src="{image file="assets/img/flags/{$CODE}.gif"}" alt="{intl l=$TITLE}" /></span>
</div>
<div class="help-block">{intl l="Enter here the currency name in the default language ($TITLE)"}</div>
{/loop} {/loop}
</div> </div>
{/form_field} {/form_field}
{form_field form=$form field='code'} {form_field form=$form field='code'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label class="control-label">{intl l='ISO 4217 code *'}</label> <label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
<input type="text" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='ISO 4217 code'}" placeholder="{intl l='Code'}"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='ISO 4217 code'}" placeholder="{intl l='Code'}">
<span class="help-block"><a href="http://fr.wikipedia.org/wiki/ISO_4217" target="_blank">{intl l='More information about ISO 4217'}</a></span> <span class="help-block"><a href="http://fr.wikipedia.org/wiki/ISO_4217" target="_blank">{intl l='More information about ISO 4217'}</a></span>
</div> </div>
{/form_field} {/form_field}
{form_field form=$form field='symbol'} {form_field form=$form field='symbol'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label class="control-label">{intl l='Symbol *'}</label> <label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
<input type="text" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}">
</div> </div>
{/form_field} {/form_field}
{form_field form=$form field='rate'} {form_field form=$form field='rate'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label class="control-label">{intl l='Rate *'}</label> <label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
<input type="text" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency rate'}" placeholder="{intl l='Rate'}"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" class="form-control" value="{$value}" title="{intl l='Currency rate'}" placeholder="{intl l='Rate'}">
<span class="help-block">{intl l="The rate from Euro (Price in Euro * rate = Price in this currency)"}</span> <span class="help-block">{intl l="The rate from Euro (Price in Euro * rate = Price in this currency)"}</span>
</div> </div>
{/form_field} {/form_field}
@@ -338,7 +334,7 @@
{* Always reset create dialog on close *} {* Always reset create dialog on close *}
$('#add_currency_dialog').on('hidden',function() { $('#add_currency_dialog').on('hidden',function() {
// Hide error currency // Hide error currency
$('#add_currency_dialog_error').remove(); $('#add_currency_dialog_error').remove();
@@ -359,7 +355,7 @@
placement : 'left', placement : 'left',
success : function(response, newValue) { success : function(response, newValue) {
// The URL template // The URL template
var url = "{url path='/admin/configuration/currencies/updatePosition' currency_id='__ID__' position='__POS__'}"; var url = "{url path='/admin/configuration/currencies/update-position' currency_id='__ID__' position='__POS__'}";
// Perform subtitutions // Perform subtitutions
url = url.replace('__ID__', $(this).data('id')) url = url.replace('__ID__', $(this).data('id'))

View File

@@ -28,10 +28,10 @@
<div class="form-container"> <div class="form-container">
<div class="col-md-12"> <div class="col-md-12">
{form name="thelia.admin.currency.modification"} {form name="thelia.admin.currency.modification"}
<form method="POST" action="{url path='/admin/configuration/currencies/save'}" {form_enctype form=$form} class="clearfix"> <form method="POST" action="{url path='/admin/configuration/currencies/save'}" {form_enctype form=$form} class="clearfix">
{* Be sure to get the currency ID, even if the form could not be validated *} {* Be sure to get the currency ID, even if the form could not be validated *}
<input type="hidden" name="currency_id" value="{$currency_id}" /> <input type="hidden" name="currency_id" value="{$currency_id}" />
@@ -47,26 +47,27 @@
<input type="hidden" name="{$name}" value="{$edit_language_locale}" /> <input type="hidden" name="{$name}" value="{$edit_language_locale}" />
{/form_field} {/form_field}
{if $form_error}<div class="alert alert-block alert-error">{$form_error_message}</div>{/if} {if $form_error}<div class="alert alert-danger">{$form_error_message}</div>{/if}
<div class="col-md-6"> <div class="col-md-6">
{form_field form=$form field='name'} {form_field form=$form field='name'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label>{intl l='Name *'}</label> <label for="{$label_attr.for}" class="control-label">{intl l="{$label}"} : </label>
<input type="text" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency name'}" placeholder="{intl l='Currency name'}" class="form-control"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency name'}" placeholder="{intl l='Currency name'}" class="form-control">
<span class="help-block">&nbsp;</span>
</div> </div>
{/form_field} {/form_field}
{form_field form=$form field='code'} {form_field form=$form field='code'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label> <label for="{$label_attr.for}" class="control-label">
{intl l='ISO 4217 Code *'} {intl l="{$label}"} :
<span class="label-help-block">
<a title="{intl l='More information about ISO 4217'}" href="http://fr.wikipedia.org/wiki/ISO_4217" target="_blank">List of ISO 4217 code</a>
</span>
</label> </label>
<input type="text" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency ISO 4217 Code'}" placeholder="{intl l='Code'}" class="form-control"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency ISO 4217 Code'}" placeholder="{intl l='Code'}" class="form-control">
<span class="help-block">
<a title="{intl l='More information about ISO 4217'}" href="http://fr.wikipedia.org/wiki/ISO_4217" target="_blank">List of ISO 4217 code</a>
</span>
</div> </div>
{/form_field} {/form_field}
@@ -76,27 +77,26 @@
{form_field form=$form field='symbol'} {form_field form=$form field='symbol'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label> <label for="{$label_attr.for}" class="control-label">
{intl l='Symbol *'} {intl l="{$label}"} :
<span class="label-help-block">The symbol, sur as $, £, &euro;...</span>
</label> </label>
<input type="text" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}" class="form-control"> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Currency symbol'}" placeholder="{intl l='Symbol'}" class="form-control">
<span class="help-block">{intl l='The symbol, such as $, £, &euro;...'}</span>
</div> </div>
{/form_field} {/form_field}
{form_field form=$form field='rate'} {form_field form=$form field='rate'}
<div class="form-group {if $error}has-error{/if}"> <div class="form-group {if $error}has-error{/if}">
<label> <label for="{$label_attr.for}" class="control-label">
{intl l='Rate from &euro; *'} {intl l="{$label}"} :
<span class="label-help-block">The rate from Euro</span> </label>
</label> <input type="text" id="{$label_attr.for}" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Rate from Euro'}" placeholder="{intl l='Rate'}" class="form-control">
<input type="text" required="required" name="{$name}" value="{$value|htmlspecialchars}" title="{intl l='Rate from Euro'}" placeholder="{intl l='Rate'}" class="form-control"> <span class="help-block">The rate from Euro: Price in Euro x rate = Price in this currency</span>
<span class="help-block">Price in Euro x rate = Price in this currency</span>
</div> </div>
{/form_field} {/form_field}
</div> </div>
</form> </form>
{/form} {/form}