Implemented new assets generation system (cssembed no more required)

This commit is contained in:
Franck Allimant
2013-11-12 01:32:29 +01:00
parent d2087b994a
commit d941134c84
10 changed files with 677 additions and 981 deletions

View File

@@ -5,13 +5,13 @@
* @file
* Functions needed for Thelia bootstrap
*/
define('THELIA_ROOT' , rtrim(realpath(__DIR__ .'/../'),'/') . "/");
define('THELIA_LOCAL_DIR' , THELIA_ROOT . 'local/');
define('THELIA_CONF_DIR' , THELIA_LOCAL_DIR . 'config/');
define('THELIA_MODULE_DIR' , THELIA_LOCAL_DIR . 'modules/');
define('THELIA_WEB_DIR' , THELIA_ROOT . 'web/');
define('THELIA_TEMPLATE_DIR' , THELIA_ROOT . 'templates/');
define('DS' , DIRECTORY_SEPARATOR);
define('THELIA_ROOT' , rtrim(realpath(dirname(__DIR__)), DS) . DS);
define('THELIA_LOCAL_DIR' , THELIA_ROOT . 'local' . DS);
define('THELIA_CONF_DIR' , THELIA_LOCAL_DIR . 'config' . DS);
define('THELIA_MODULE_DIR' , THELIA_LOCAL_DIR . 'modules' . DS);
define('THELIA_WEB_DIR' , THELIA_ROOT . 'web' . DS);
define('THELIA_TEMPLATE_DIR' , THELIA_ROOT . 'templates' . DS);
$loader = require __DIR__ . "/vendor/autoload.php";

View File

@@ -22,13 +22,16 @@
/*************************************************************************************/
namespace Thelia\Core\Template\Assets;
use Assetic\AssetManager;
use Assetic\FilterManager;
use Assetic\Filter;
use Assetic\Factory\AssetFactory;
use Assetic\Factory\Worker\CacheBustingWorker;
use Assetic\AssetWriter;
use Thelia\Model\ConfigQuery;
use Thelia\Log\Tlog;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOException;
/**
* This class is a simple helper for generating assets using Assetic.
@@ -37,28 +40,196 @@ use Thelia\Model\ConfigQuery;
*/
class AsseticHelper
{
/**
* Generates assets from $asset_path in $output_path, using $filters.
*
* @param string $asset_path the full path to the asset file (or file collection)
* @param string $output_path the full disk path to the output directory (shoud be visible to web server)
* @param string $output_url the URL to the generated asset directory
* @param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
* @param boolean $debug true / false
* @param boolean $dev_mode true / false. If true, assets are not cached and always compiled.
* @throws \InvalidArgumentException if an invalid filter name is found
* @return string The URL to the generated asset file.
*/
public function asseticize($asset_path, $output_path, $output_url, $asset_type, $filters, $debug, $dev_mode = false)
{
$asset_name = basename($asset_path);
$asset_dir = dirname($asset_path);
protected $source_file_extensions = array('less', 'js', 'coffee', 'html', 'tpl', 'htm', 'xml');
$am = new AssetManager();
$fm = new FilterManager();
/**
* Create a stamp form the modification time of the content of the given directory and all of its subdirectories
*
* @param string $directory ther directory name
* @return string the stamp of this directory
*/
protected function getStamp($directory)
{
$stamp = '';
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($iterator as $file) {
$stamp .= $file->getMTime();
}
return md5($stamp);
}
/**
* Check if a file is a source asset file
*
* @param \DirectoryIterator $fileInfo
*/
protected function isSourceFile(\SplFileInfo $fileInfo) {
return in_array($fileInfo->getExtension(), $this->source_file_extensions);
}
/**
* Recursively copy assets from the source directory to the destination
* directory in the web space, ommiting source files.
*
* @param string $from_directory the source
* @param string $to_directory the destination
* @throws \RuntimeException if a problem occurs.
*/
protected function copyAssets(Filesystem $fs, $from_directory, $to_directory)
{
Tlog::getInstance()->addDebug("Copying assets from ", $from_directory, " to ", $to_directory);
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($from_directory, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $item) {
if ($item->isDir()) {
$dest_dir = $to_directory . DS . $iterator->getSubPathName();
if (! is_dir($dest_dir)) {
if ($fs->exists($dest_dir)) {
$fs->remove($dest_dir);
}
$fs->mkdir($dest_dir, 0777);
}
}
// We don't copy source files
else if (! $this->isSourceFile($item)) {
$dest_file = $to_directory . DS . $iterator->getSubPathName();
if ($fs->exists($dest_file)) {
$fs->remove($dest_file);
}
$fs->copy($item, $dest_file);
}
}
}
/**
* Compite the assets path relative to the base template directory
*
* @param string $source_assets_directory the source directory
* @param string $web_assets_directory_base base directory of the web assets
* @return the full path of the destination directory
*/
protected function getRelativeDirectoryPath($source_assets_directory, $web_assets_directory_base)
{
$source_assets_directory = realpath($source_assets_directory);
// Remove base path from asset source path to get a path relative to the template base
// and use it to create the destination path.
return str_replace(
realpath(THELIA_ROOT),
'',
$source_assets_directory
);
}
/**
* Compute the destination directory path, from the source directory and the
* base directory of the web assets
*
* @param string $source_assets_directory the source directory
* @param string $web_assets_directory_base base directory of the web assets
* @return the full path of the destination directory
*/
protected function getDestinationDirectory($source_assets_directory, $web_assets_directory_base)
{
// Compute the absolute path of the output directory
return $web_assets_directory_base . $this->getRelativeDirectoryPath($source_assets_directory, $web_assets_directory_base);
}
/**
* Prepare an asset directory by checking that no changes occured in
* the source directory. If any change is detected, the whole asset directory
* is copied in the web space.
*
* @param string $source_assets_directory the full path to the source asstes directory
* @param string $web_assets_directory_base the base directory of the web based asset directory
* @throws \RuntimeException if something goes wrong.
*/
public function prepareAssets($source_assets_directory, $web_assets_directory_base) {
// Compute the absolute path of the output directory
$to_directory = $this->getDestinationDirectory($source_assets_directory, $web_assets_directory_base);
// Get a path to the stamp file
$stamp_file_path = $to_directory . DS . '.source-stamp';
// Get the last stamp of source assets directory
$prev_stamp = @file_get_contents($stamp_file_path);
// Get the current stamp of the source directory
$curr_stamp = $this->getStamp($source_assets_directory);
if ($prev_stamp !== $curr_stamp) {
$fs = new Filesystem();
//FIXME: lock the stuff ?
/*
$lock_file = "$web_assets_directory_base/assets-".md5($source_assets_directory)."-generation-lock.txt";
if (! $fp = fopen($lock_file, "w")) {
throw new IOException(sprintf('Failed to open lock file %s', $lock_file));
}
if (flock($fp, LOCK_EX|LOCK_NB)) { // do an exclusive lock
*/
$tmp_dir = "$to_directory.tmp";
$fs->remove($tmp_dir);
// Copy the whole source dir in a temp directory
$this->copyAssets($fs, $source_assets_directory, $tmp_dir);
// Remove existing directory
if ($fs->exists($to_directory)) $fs->remove($to_directory);
// Put in place the new directory
$fs->rename($tmp_dir, $to_directory);
/*
// Release the lock
flock($fp, LOCK_UN);
// Remove the lock file
@fclose($fp);
$fs->remove($lock_file);
*/
if (false === @file_put_contents($stamp_file_path, $curr_stamp)) {
throw new \RuntimeException(
"Failed to create asset stamp file $stamp_file_path. Please check that your web server has the proper access rights to do that.");
}
/* }
else {
@fclose($fp);
}
*/
}
}
/**
* Decode the filters names, and initialize the Assetic FilterManager
*
* @param FilterManager $filterManager the Assetic filter manager
* @param string $filters a comma separated list of filter names
* @throws \InvalidArgumentException if a wrong filter is passed
* @return an array of filter names
*/
protected function decodeAsseticFilters(FilterManager $filterManager, $filters) {
if (!empty($filters)) {
$filter_list = explode(',', $filters);
foreach ($filter_list as $filter_name) {
@@ -66,33 +237,33 @@ class AsseticHelper
$filter_name = trim($filter_name);
switch ($filter_name) {
case 'less':
$fm->set('less', new Filter\LessphpFilter());
break;
case 'less':
$filterManager->set('less', new Filter\LessphpFilter());
break;
case 'sass':
$fm->set('sass', new Filter\Sass\SassFilter());
break;
case 'sass':
$filterManager->set('sass', new Filter\Sass\SassFilter());
break;
case 'cssembed':
$fm->set('cssembed', new Filter\PhpCssEmbedFilter());
break;
case 'cssembed':
$filterManager->set('cssembed', new Filter\PhpCssEmbedFilter());
break;
case 'cssrewrite':
$fm->set('cssrewrite', new Filter\CssRewriteFilter());
break;
case 'cssrewrite':
$filterManager->set('cssrewrite', new Filter\CssRewriteFilter());
break;
case 'cssimport':
$fm->set('cssimport', new Filter\CssImportFilter());
break;
case 'cssimport':
$filterManager->set('cssimport', new Filter\CssImportFilter());
break;
case 'compass':
$fm->set('compass', new Filter\CompassFilter());
break;
case 'compass':
$filterManager->set('compass', new Filter\CompassFilter());
break;
default:
throw new \InvalidArgumentException("Unsupported Assetic filter: '$filter_name'");
break;
default:
throw new \InvalidArgumentException("Unsupported Assetic filter: '$filter_name'");
break;
}
}
}
@@ -100,8 +271,38 @@ class AsseticHelper
$filter_list = array();
}
return $filter_list;
}
/**
* Generates assets from $asset_path in $output_path, using $filters.
*
* @param string $asset_path the full path to the asset file (or file collection, e.g. *.less)
*
* @param string $web_assets_directory_base the full disk path to the base assets output directory in the web space
* @param string $output_url the URL to the base assets output directory in the web space
*
* @param string $asset_type the asset type: css, js, ... The generated files will have this extension. Pass an empty string to use the asset source extension.
* @param array $filters a list of filters, as defined below (see switch($filter_name) ...)
*
* @param boolean $debug true / false
* @param boolean $dev_mode true / false. If true, assets are not cached and always compiled.
* @throws \InvalidArgumentException if an invalid filter name is found
* @return string The URL to the generated asset file.
*/
public function asseticize($asset_path, $web_assets_directory_base, $output_url, $asset_type, $filters, $debug, $dev_mode = false)
{
$asset_name = basename($asset_path);
$input_directory = realpath(dirname($asset_path));
$am = new AssetManager();
$fm = new FilterManager();
// Get the filter list
$filter_list = $this->decodeAsseticFilters($fm, $filters);
// Factory setup
$factory = new AssetFactory($asset_dir);
$factory = new AssetFactory($input_directory);
$factory->setAssetManager($am);
$factory->setFilterManager($fm);
@@ -110,113 +311,34 @@ class AsseticHelper
$factory->setDebug($debug);
$factory->addWorker(new CacheBustingWorker('-'));
$asset = $factory->createAsset($asset_name, $filter_list);
// We do not pass the filter list here, juste to get the asset file name
$asset = $factory->createAsset($asset_name);
$input_directory = realpath(dirname($asset_path));
$asset_target_path = $asset->getTargetPath();
$output_directory = $this->getDestinationDirectory($input_directory, $web_assets_directory_base);
$target_file = sprintf("%s/%s", $output_path, $asset_target_path);
// Get the URL part from the relative path
$output_relative_path = $this->getRelativeDirectoryPath($input_directory, $web_assets_directory_base);
// 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 ($dev_mode == true || !file_exists($target_file)) {
$output_relative_web_path = rtrim(str_replace('\\', '/', $output_relative_path), '/') . '/';
if (ConfigQuery::read('process_assets', true)) {
$asset_target_filename = $asset->getTargetPath();
// Delete previous version of the file
list($commonPart, $dummy) = explode('-', $asset_target_path);
// This is the final name of the generated asset
$asset_destination_path = $output_directory . DS . $asset_target_filename;
foreach (glob("$output_path/$commonPart-*") as $filename) {
@unlink($filename);
}
Tlog::getInstance()->addDebug("Asset destination name: ", $asset_destination_path);
// 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)));
}
}
// We generate an asset only if it does not exists, or if the asset processing is forced in development mode
if (! file_exists($asset_destination_path) || ($dev_mode && ConfigQuery::read('process_assets', true)) ) {
$writer = new AssetWriter($output_path);
$writer = new AssetWriter($output_directory);
$writer->writeAsset($asset);
}
Tlog::getInstance()->addDebug("Writing asset to $output_directory");
$writer->writeAsset($asset);
}
return rtrim($output_url, '/') . '/' . $asset_target_path;
}
// Create a hash of the current assets directory
public function getStamp($directory)
{
$stamp = '';
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) continue;
if ($fileInfo->isDir()) {
$stamp .= $this->getStamp($fileInfo->getPathName());
}
if ($fileInfo->isFile()) {
$stamp .= $fileInfo->getMTime();
}
}
return $stamp;
}
public function copyAssets($from_directory, $to_directory)
{
echo "copy $from_directory to $to_directory\n";
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($from_directory, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $item) {
if ($item->isDir()) {
$dest_dir = $to_directory . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
if (!is_dir($dest_dir)) {
if (file_exists($dest_dir)) {
@unlink($dest_dir);
}
if (!mkdir($dest_dir, 0777, true)) {
throw new \RuntimeException(
"Failed to create directory $dest_dir. Please check that your web server has the proper access rights");
}
}
}
else {
$dest_file = $to_directory . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
if (file_exists($dest_file)) {
@unlink($dest_file);
}
if (!copy($item, $dest_file)) {
throw new \RuntimeException(
"Failed to copy $source_file to $dest_file. Please check that your web server has the proper access rights");
}
}
}
return rtrim($output_url, '/') . '/' . $output_relative_web_path . $asset_target_filename;
}
}

View File

@@ -39,7 +39,7 @@ class SmartyAssetsManager
/**
* Creates a new SmartyAssetsManager instance
*
* @param string $web_root the disk path to the web root
* @param string $web_root the disk path to the web root (with final /)
* @param string $path_relative_to_web_root the path (relative to web root) where the assets will be generated
* @param boolean $developmentMode true / false. If true, assets are not cached, and always generated.
*/
@@ -53,6 +53,22 @@ class SmartyAssetsManager
$this->assetic_manager = new AsseticHelper();
}
public function prepareAssets($assets_directory, \Smarty_Internal_Template $template) {
$tpl_dir = dirname($template->source->filepath);
$asset_dir_absolute_path = realpath($tpl_dir . DS . $assets_directory);
if ($asset_dir_absolute_path === false) throw new \Exception("Failed to get real path of '".$tpl_dir . DS . $assets_directory."'");
$modified = $this->assetic_manager->prepareAssets(
$asset_dir_absolute_path,
$this->web_root . $this->path_relative_to_web_root
);
}
public function computeAssetUrl($assetType, $params, \Smarty_Internal_Template $template)
{
$file = $params['file'];
@@ -66,14 +82,24 @@ class SmartyAssetsManager
$tpl_dir = dirname($tpl_path);
// Create absolute dir path
$asset_dir = realpath($tpl_dir.'/'.dirname($file));
$asset_dir = realpath($tpl_dir) . DS . dirname($file);
$asset_file = basename($file);
if ($asset_dir === false) throw new \Exception("Failed to get real path of '".$tpl_dir.'/'.dirname($file)."'");
/*
$url = $this->assetic_manager->asseticize(
$asset_dir.'/'.$asset_file,
$this->web_root."/".$this->path_relative_to_web_root,
$this->web_root . DS . $this->path_relative_to_web_root,
URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE),// PATH only
$assetType,
$filters,
$debug,
$this->developmentMode
);
*/
$url = $this->assetic_manager->asseticize(
$asset_dir . DS . $asset_file,
$this->web_root . $this->path_relative_to_web_root,
URL::getInstance()->absoluteUrl($this->path_relative_to_web_root, null, URL::PATH_TO_FILE /* path only */),
$assetType,
$filters,

View File

@@ -34,11 +34,21 @@ class Assetic extends AbstractSmartyPlugin
public function __construct($developmentMode)
{
$web_root = THELIA_WEB_DIR;
$asset_dir_from_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets');
$asset_dir_from_web_root = ConfigQuery::read('asset_dir_from_web_root', 'assets/');
$this->assetManager = new SmartyAssetsManager(THELIA_WEB_DIR, $asset_dir_from_web_root, $developmentMode == 'dev');
}
$this->assetManager = new SmartyAssetsManager($web_root, $asset_dir_from_web_root, $developmentMode == 'dev');
public function declareAssets($params, \Smarty_Internal_Template $template)
{
if (false !== $asset_dir = $this->getParam($params, 'directory', false)) {
$this->assetManager->prepareAssets($asset_dir, $template);
return '';
}
throw new \InvalidArgumentException('declare_assets: parameter "directory" is required');
}
public function blockJavascripts($params, $content, \Smarty_Internal_Template $template, &$repeat)
@@ -79,10 +89,11 @@ class Assetic extends AbstractSmartyPlugin
public function getPluginDescriptors()
{
return array(
new SmartyPluginDescriptor('block' , 'stylesheets', $this, 'blockStylesheets'),
new SmartyPluginDescriptor('block' , 'javascripts', $this, 'blockJavascripts'),
new SmartyPluginDescriptor('block' , 'images' , $this, 'blockImages'),
new SmartyPluginDescriptor('function', 'image' , $this, 'functionImage')
new SmartyPluginDescriptor('block' , 'stylesheets' , $this, 'blockStylesheets'),
new SmartyPluginDescriptor('block' , 'javascripts' , $this, 'blockJavascripts'),
new SmartyPluginDescriptor('block' , 'images' , $this, 'blockImages'),
new SmartyPluginDescriptor('function', 'image' , $this, 'functionImage'),
new SmartyPluginDescriptor('function', 'declare_assets' , $this, 'declareAssets')
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,12 @@
{check_auth role="ADMIN" resource="{block name="check-resource"}{/block}" access="{block name="check-access"}{/block}" login_tpl="/admin/login"}
{/block}
{* -- Define some stuff for Smarty ----------------------------------------- *}
{* -- Define some stuff for Smarty ------------------------------------------ *}
{config_load file='variables.conf'}
{* -- Declare assets directory, relative to template base directory --------- *}
{declare_assets directory='assets'}
<!DOCTYPE html>
<html lang="{$lang_code}">
<head>
@@ -22,7 +25,7 @@
{block name="before-bootstrap-css"}{/block}
{stylesheets file='assets/less/main.less' filters='less,cssembed'}
{stylesheets file='assets/less/main.less' filters='less'}
<link rel="stylesheet" href="{$asset_url}">
{/stylesheets}

View File

@@ -2,4 +2,4 @@
@import "bootstrap/bootstrap.less";
/* Thelia Admin */
@import "thelia/thelia.less";
@import "thelia/thelia.less";

View File

@@ -12,14 +12,14 @@
{form_field form=$form field='label'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{$label}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{$label}">
</div>
{/form_field}
{form_field form=$form field='company'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Company'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Company'}">
</div>
{/form_field}
@@ -27,7 +27,7 @@
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<select name="{$name}" id="{$label_attr.for}" class="form-control">
<select {if $required}required{/if} name="{$name}" id="{$label_attr.for}" class="form-control">
{loop type="title" name="title1"}
<option value="{$ID}" {if $value == $ID}selected{/if}>{$LONG}</option>
{/loop}
@@ -40,7 +40,7 @@
{form_field form=$form field='firstname'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Firstname'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Firstname'}">
</div>
{/form_field}
</div>
@@ -48,7 +48,7 @@
{form_field form=$form field='lastname'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Lastname'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Lastname'}">
</div>
{/form_field}
</div>
@@ -57,18 +57,18 @@
{form_field form=$form field='address1'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Address'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Address'}">
</div>
<div class="form-group">
{form_field form=$form field='address2'}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
{/form_field}
</div>
<div class="form-group">
{form_field form=$form field='address3'}
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Additional address'}">
{/form_field}
</div>
{/form_field}
@@ -78,7 +78,7 @@
{form_field form=$form field='zipcode'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Zip code'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Zip code'}">
</div>
{/form_field}
</div>
@@ -86,7 +86,7 @@
{form_field form=$form field='city'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='City'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='City'}">
</div>
{/form_field}
</div>
@@ -95,7 +95,7 @@
{form_field form=$form field='country'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<select name="{$name}" id="{$label_attr.for}" class="form-control">
<select {if $required}required{/if} name="{$name}" id="{$label_attr.for}" class="form-control">
{loop type="country" name="country1"}
<option value="{$ID}" {if $value == $ID}selected{/if}>{$TITLE}</option>
{/loop}
@@ -108,7 +108,7 @@
{form_field form=$form field='phone'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Phone number'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Phone number'}">
</div>
{/form_field}
</div>
@@ -116,9 +116,8 @@
{form_field form=$form field='cellphone'}
<div class="form-group {if $error}has-error{/if}">
<label for="{$label_attr.for}" class="control-label">{$label}{if $required} <span class="required">*</span>{/if} : </label>
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Cellular phone number'}">
<input {if $required}required{/if} type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Cellular phone number'}">
</div>
{/form_field}
</div>
</div>
</div>

View File

@@ -8,4 +8,4 @@
@import "thelia/import.less";
/* Theme */
@import "../themes/default/less/import.less";
@import "../themes/default/less/import.less";

View File

@@ -1,3 +1,5 @@
{* Declare assets directory, relative to template base directory *}
{declare_assets directory='assets'}
{block name="no-return-functions"}{/block}
<!doctype html>
<!--
@@ -42,7 +44,7 @@ GNU General Public License : http://www.gnu.org/licenses/
{block name="meta"}{/block}
{* Stylesheets *}
{stylesheets file='assets/less/styles.less' filters='less,cssembed'}
{stylesheets file='assets/less/styles.less' filters='less'}
<link rel="stylesheet" href="{$asset_url}">
{/stylesheets}