![]() |
|
Snippets |
|
Example:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"> <xliff version="1.0"> <file original="global" source-language="en" datatype="plaintext" date="2009-08-12T12:32:00Z"> <header/> <body> ... <trans-unit> <source>from %from_date%<br />to %to_date%</source> <target>de %from_date%<br />até %to_date%</target> </trans-unit> </body> </file> </xliff>
Use '<' and '>' to replace '<' and '>'. respectively.
print 'hello world'
This will allow you to use multiple themes for symfony, each with it's own templates and stylesheets.
Grab the code at GitHub: https://github.com/acetous/Symfony-Themed
The templates have to be stored in sf_root_dir/themes/application/themename/module. A theme named default has to be present. Layouts have to be placed in sf_root_dir/themes/themename/application.
Example:
sf_root_dir/themes/default/frontend/layout.php is the new place for your layoutsf_root_dir/themes/default/frontend/post/indexSuccess.php is the new place for the success-template when calling the index-action on the post-moduleCreate stylesheets for your theme. You'll need to create one for each application.
Example:
default-frontend.cssdefault-backend.cssPlease include this stylesheets in your themes root directory. They need to be copied into the /web/css folder.
All themes (except default) will load the default-templates if a custom one is not found. This way you just need to create the template files you want to alter.
Apart from that the default-stylesheet will be included if a custom one cannot be found.
lib directory (the task to lib/task and filter to lib/filter).filters.yml. Add one filter for the themes logic:
<pre>rendering: ~
security: ~
themes:
class: ThemedFilter
cache: ~
execution: ~
</pre>Just run the ./symfony themed:setup task to setup all themes or ./symfony themed:setup name to setup a specific theme.
$c->addAscendingOrderByColumn( 'NAME COLLATE utf8_bin' );
quite obvious
ReUse of: Conditional object actions for the admin generator By Georg Sorst
The list view of the admin generator currently always displays all defined object actions for each object. There is no way to display an object action for an object only if some condition on this object itself is met.
In order to extend the admin generator with this functionality only a small enhancement is required. You can either apply this change per module or create a new admin generator theme as described in the Symfony book.
The templates/_list_td_actions.php has to be extended to look roughly like this, depending on whether you already have your own modifications in there.
I use sf v1.4.11, for make this enhancement...
First, you should find the partial _list_td_actions.php found in
lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineModule/admin/template/templates/_list_td_actions.php
And modify the original code like this:
<td>
<?php if ($this->configuration->getValue('list.object_actions')): ?>
<ul class="sf_admin_td_actions">
<?php foreach ($this->configuration->getValue('list.object_actions') as $name => $params): ?>
<?php if ( isset( $params['condition'] ) ): ?>
[?php if ( <?php echo ( isset( $params['condition']['invert'] ) && $params['condition']['invert'] ? '!' : '') . '$' . $this->getSingularName( ) . '->' . $params['condition']['function'] ?>( <?php echo ( isset( $params['condition']['params'] ) ? $params['condition']['params'] : '' ) ?> ) ): ?]
<?php endif; ?>
<?php if ('_delete' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToDelete($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php elseif ('_edit' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToEdit($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php elseif ('_show' == $name): ?>
<?php echo $this->addCredentialCondition('[?php echo $helper->linkToShow($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
<?php else: ?>
<li class="sf_admin_action_<?php echo $params['class_suffix'] ?>">
<?php echo $this->addCredentialCondition($this->getLinkToAction($name, $params, true), $params) ?>
</li>
<?php endif; ?>
<?php if ( isset( $params['condition'] ) ): ?>
[?php endif; ?]
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</td>
Second, in the generator.yml you should add aditional lines like
actionName:
label: actionLabel
action: executeAction
#Now the enhancement =D
condition:
# function is the name of function in the model::functionName and must return boolean
function: functionName
# params are the params to send to functionName
params: "$model->getDbField(), $sf_user, 'test'"
# invert is used if you need invert the result of functionName
invert: false
Third and final step, go to
lib/model/doctrine/model.class.php
and create the functionName like:
/** * $param1 is $model->getDbField() * $param2 is $sf_user * $param3 is 'test' */ public function functionName($param1, $param2, $param3){ //code personal return boolean; }
Final, important!
php symfony cache:clear
Well that's all, sorry for my bad English =S Thanks Georg Sorst and Google Translator
Remember to register your hydrator first.
public function executeIndex(sfWebRequest $request) { $manager = Doctrine_Manager::getInstance(); $manager->registerHydrator('xml', 'Doctrine_Hydrator_XmlDriver'); }
See http://www.doctrine-project.org/documentation/manual/1_2/en/data-hydrators
<?php class Doctrine_Hydrator_XmlDriver extends Doctrine_Hydrator_ArrayDriver { public function hydrateResultSet($stmt) { $array = parent::hydrateResultSet($stmt); return $this->arrayToXml($array); } /** * <pre>Converts an Doctrine array graph of the form: * .array * . 0 => * . array * . 'id' => '1' * . 'PersonaDomicilio' => * . array * . 0 => * . array * . 'id' => '1' * To a XML representation. * @param array $array * @return DOMDocument <pre> The XML with following structure: * . <result> * . <rootTable_Collection> * . <rootTable> * . <id></id> * . <field1></field1> * . <relatedTable_Collection> * . <relatedTable> * . </relatedTable> * . <relatedTable> * . </relatedTable> * . :: * . </relatedTable_Collection> * . </rootTable> * . <rootTable> * . :: * . </rootTable> * . :: * . </rootTable_Collection> * . <result> * </pre> */ public function arrayToXml(array $array) { $result = new DOMDocument(); $rootNode = $result->createElement('result'); $result->appendChild($rootNode); $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($array), RecursiveIteratorIterator::SELF_FIRST ); $prevLvl = 0; $component[$prevLvl] = $this->_queryComponents[$this->_rootAlias]['table'] ->getComponentName(); $obj = $result->createElement($component[$prevLvl] . '_Collection'); $rootNode->appendChild($obj); foreach ($iterator as $k => $val) { $depth = $iterator->getDepth(); if ($depth < $prevLvl) { for ($i = 0; $i < ($prevLvl - $depth); $i++) { $obj = $obj->parentNode; } } if (! is_array($val)) { $son = $result->createElement($k, $val); $obj->appendChild($son); } else { if (is_numeric($k)) { $son = $result->createElement($component[$depth]); } else { $component[$depth + 1] = $k; $son = $result->createElement($k . '_Collection'); } $obj->appendChild($son); !empty($val) && $obj = $son; } $prevLvl = $depth; } return $result; } }
Sometimes you'd like to use partial from auto-generated module, like list partial, to keep list presentation unified.
But, before using partial, you add to be sure that cache has been generated or your application will be broken.
This little method (you can place in your tools.class.php) check that cache exists, and if not, generate it.
public static function generateAdminGeneratorModuleCache($module, $configuration) { if(!($configuration instanceof sfApplicationConfiguration)) { throw new sfConfigurationException('You must provide a valid application configuration object'); } if(!file_exists(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module)) { throw new sfConfigurationException('The module specified does not exists for this application'); } if(!file_exists(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'generator.yml')) { throw new sfConfigurationException('This module does not seems to be a valid admin generator module'); } if(!file_exists(sfConfig::get('sf_module_cache_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'actions'.DIRECTORY_SEPARATOR.'actions.class.php')) { // Init generator manager $generator_manager = new sfGeneratorManager($configuration); $config = sfYaml::load(sfConfig::get('sf_app_module_dir').DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'generator.yml'); $config['generator']['param']['moduleName'] = $module; $generator_manager->generate($config['generator']['class'], $config['generator']['param']); } }
Now you can call it from any actions with
myTools::generateAdminGeneratorModuleCache('my_module',$this->getContext()->getConfiguration());
<?php /** * This validator accepts either a four-member array such as * array( 'deg' => 12, 'min' => 34, 'sec' => 56, 'dir' => 'N' ) * or a numeric value such as 12.3456 */ class ynValidatorLatLong extends sfValidatorBase { protected function configure($options = array(), $messages = array()) { parent::configure( $options, $messages ); $this->addRequiredOption('axis'); // 'latitude' || 'longitude' $this->addMessage('invalid_axis', 'The selected axis is invalid.'); } protected function doClean( $value ) { $max = 0; switch ( $this->getOption('axis') ) { case 'latitude': $max = 90; break; case 'longitude': $max = 180; break; default: throw new sfValidatorError($this, 'invalid_axis'); } if ( is_array( $value ) ) { $value = $this->arrayToInt( $value ); } if ( ! is_numeric( $value ) ) { throw new sfValidatorError($this, 'invalid'); } if ( abs( $value ) > $max ) { throw new sfValidatorError($this, 'invalid'); } return $value; } protected function arrayToInt( array $value ) { if ( count( $value ) != 4 ) { throw new sfValidatorError($this, 'invalid'); } if ( ! isset( $value['deg'] ) || ! isset( $value['min'] ) || ! isset( $value['sec'] ) || ! isset( $value['dir'] ) ) { throw new sfValidatorError($this, 'invalid'); } if ( preg_match( '/\D/', $value['deg'].$value['min'].$value['sec'] ) ) { // non-integer component throw new sfValidatorError($this, 'invalid'); } $multiplier = 1; switch ( strtolower( $value['dir'] ) ) { case 'n': $axis = 'latitude'; break; case 's': $axis = 'latitude'; $multiplier = -1; break; case 'e': $axis = 'longitude'; break; case 'w': $axis = 'longitude'; $multiplier = -1; break; default: throw new sfValidatorError($this, 'invalid'); } if ( $axis != $this->getOption('axis') ) { throw new sfValidatorError($this, 'invalid'); } return ( $value['deg'] + $value['min']/60 + $value['sec']/60/60 ) * $multiplier; } } /* * UNIT TEST */ <?php require_once dirname(__FILE__).'/../../bootstrap/unit.php'; $t = new lime_test(); $lat_validator = new ynValidatorLatLong( array( 'axis' => 'latitude' ) ); $lon_validator = new ynValidatorLatLong( array( 'axis' => 'longitude' ) ); $lat_good = array( array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'S' ), 50.456789, 50, -50.456789, -50 ); $lat_bad = array( 'three members' => array( 'deg' => 50, 'min' => 12, 'sec' => 14 ), 'invalid n/s 1' => array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'F' ), 'invalid n/s 2' => array( 'deg' => 50, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), 'non-integer member' => array( 'deg' => 50, 'min' => 12, 'sec' => 14.2, 'dir' => 'N' ), 'out of range' => array( 'deg' => 100, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), ); $lon_good = array( array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'W' ), 50.456789, 50, -50.456789, -50 ); $lon_bad = array( 'three members' => array( 'deg' => 150, 'min' => 12, 'sec' => 14 ), 'invalid e/w 1' => array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'F' ), 'invalid e/w 2' => array( 'deg' => 150, 'min' => 12, 'sec' => 14, 'dir' => 'N' ), 'non-integer member' => array( 'deg' => 150, 'min' => 12, 'sec' => 14.2, 'dir' => 'E' ), 'out of range' => array( 'deg' => 200, 'min' => 12, 'sec' => 14, 'dir' => 'E' ), ); foreach ( $lat_good as $value ) { try { $clean = $lat_validator->clean( $value ); $t->ok( is_numeric( $clean ), 'is numeric' ); if ( is_array( $value ) ) { switch( $value['dir'] ) { case 'E': $multiplier = 1; break; case 'W': $multiplier = -1; break; } $t->ok( abs($clean) >= $value['deg'] && abs($clean) <= $value['deg']+1, "value $clean makes sense" ); $t->ok( $clean * $multipler >= 0, 'direction correct' ); } else { $t->is( $value, $clean, 'value makes sense' ); } } catch( sfValidatorError $e ) { $t->fail( $e->getMessage() ); } } foreach ( $lat_bad as $message => $value ) { try { $lat_validator->clean( $value ); $t->fail( $message ); } catch( sfValidatorError $e ) { $t->pass( $message ); } } foreach ( $lon_good as $value ) { try { $clean = $lon_validator->clean( $value ); $t->ok( is_numeric( $clean ), 'is numeric' ); if ( is_array( $value ) ) { switch( $value['dir'] ) { case 'E': $multiplier = 1; break; case 'W': $multiplier = -1; break; } $t->ok( abs($clean) >= $value['deg'] && abs($clean) <= $value['deg']+1, "value $clean makes sense" ); $t->ok( $clean * $multipler >= 0, 'direction correct' ); } else { $t->is( $value, $clean, 'value makes sense' ); } } catch( sfValidatorError $e ) { $t->fail( $e->getMessage() ); } } foreach ( $lon_bad as $message => $value ) { try { $lon_validator->clean( $value ); $t->fail( $message ); } catch( sfValidatorError $e ) { $t->pass( $message ); } }
The Hebrew alphabet has its own traditional representation for integers. Here is a validator to clean them into conventional Arabic integers. There may be gotchas involved with the Unicode processing. I developed this on a 64-bit Ubuntu system.
<?php class ynValidatorIntegerHebrew extends sfValidatorInteger { protected function configure($options = array(), $messages = array()) { parent::configure( $options, $messages ); // value to add to Hebrew entries, useful for assumed millenia $this->addOption('add_to_hebrew', 0); } public function clean( $value ) { if ( preg_match( '/^\p{Hebrew}+(?:"\p{Hebrew})?$/u', $value ) ) { $value = $this->convertStringToInt( $value ); } return parent::clean( $value ); } /** * Iterates through the Hebrew characters in the string, converting * each to its Unicode code and evaluating for numerical value * * @param string $value The integer represented in Hebrew characters * @return integer */ protected function convertStringToInt( $value ) { if ( ! is_string( $value ) ) { throw new UnexpectedValueException( 'Argument must be a string' ); } preg_match_all( '/\p{Hebrew}/u', $value, $matches ); $value_int = 0; foreach ( $matches[0] as $char ) { $char = iconv( 'UTF-8', 'UTF-32', $char ); $char_dec = unpack( 'L*', $char ); $char_dec = $char_dec[2]; if ( $char_dec > 1510 ) { $base = $char_dec - 1510; $value_int += $base * 100; } elseif ( $char_dec > 1496 ) { $base = $char_dec - 1496; // have to deal with final forms if ( $base > 2 ) $base--; if ( $base > 4 ) $base--; if ( $base > 5 ) $base--; if ( $base > 8 ) $base--; if ( $base > 9 ) $base--; $value_int += $base * 10; } else { $base = $char_dec - 1487; $value_int += $base; } } $value_int += $this->getOption('add_to_hebrew'); return $value_int; } } // UNIT TEST // The Hebrew literals are displaying oddly because of right-to-left; it should // still work require_once dirname(__FILE__).'/../../bootstrap/Doctrine.php'; $t = new lime_test(); $val = new ynValidatorIntegerHebrew(); $t->is( $val->clean('א'), 1 ); $t->is( $val->clean('ב'), 2 ); $t->is( $val->clean('ג'), 3 ); $t->is( $val->clean('ד'), 4 ); $t->is( $val->clean('ה'), 5 ); $t->is( $val->clean('ו'), 6 ); $t->is( $val->clean('ז'), 7 ); $t->is( $val->clean('ח'), 8 ); $t->is( $val->clean('ט'), 9 ); $t->is( $val->clean('י'), 10 ); $t->is( $val->clean('כ'), 20 ); $t->is( $val->clean('ך'), 20 ); $t->is( $val->clean('ל'), 30 ); $t->is( $val->clean('מ'), 40 ); $t->is( $val->clean('ם'), 40 ); $t->is( $val->clean('נ'), 50 ); $t->is( $val->clean('ן'), 50 ); $t->is( $val->clean('ס'), 60 ); $t->is( $val->clean('ע'), 70 ); $t->is( $val->clean('פ'), 80 ); $t->is( $val->clean('ף'), 80 ); $t->is( $val->clean('צ'), 90 ); $t->is( $val->clean('ץ'), 90 ); $t->is( $val->clean('ק'), 100 ); $t->is( $val->clean('ר'), 200 ); $t->is( $val->clean('ש'), 300 ); $t->is( $val->clean('ת'), 400 ); $t->is( $val->clean('תשע'), 770 ); $t->is( $val->clean('תתרתשע'), 1770 ); $val->setOption('add_to_hebrew', 5000); $t->is( $val->clean('תשע'), 5770 );
If I understand correctly, ISO-8601 allows for dates represented as YYYY, YYYY-MM, and YYYY-MM-DD, among others. I did not see the possibility of supporting these date formats with the built-in widgets and validators, so I wrote some.
class ynWidgetFormDateFuzzy extends sfWidgetFormDate { public function render($name, $value = null, $attributes = array(), $errors = array()) { // convert value to an array $default = array('year' => null, 'month' => null, 'day' => null); if (is_array($value)) { $value = array_merge($default, $value); } else if ( preg_match( '/^(\d+)$/', $value, $matches ) ) { $value = array( 'year' => $matches[1], 'month' => '', 'day' => '', ); } else if ( preg_match( '/^(\d+)-(\d+)$/', $value, $matches ) ) { $value = array( 'year' => $matches[1], 'month' => $matches[2], 'day' => '', ); } else { $value = (string) $value == (string) (integer) $value ? (integer) $value : strtotime($value); if (false === $value) { $value = $default; } else { $value = array('year' => date('Y', $value), 'month' => date('n', $value), 'day' => date('j', $value)); } } $date = array(); $emptyValues = $this->getOption('empty_values'); $date['%day%'] = $this->renderDayWidget( $name.'[day]', $value['day'] ? (int) $value['day'] : '', array( 'choices' => array('' => $emptyValues['day']) + $this->getOption('days'), 'id_format' => $this->getOption('id_format') ), array_merge($this->attributes, $attributes) ); $date['%month%'] = $this->renderMonthWidget( $name.'[month]', $value['month'] ? (int) $value['month'] : '', array( 'choices' => array('' => $emptyValues['month']) + $this->getOption('months'), 'id_format' => $this->getOption('id_format') ), array_merge($this->attributes, $attributes) ); $date['%year%'] = $this->renderYearWidget($name.'[year]', $value['year'], array('choices' => $this->getOption('can_be_empty') ? array('' => $emptyValues['year']) + $this->getOption('years') : $this->getOption('years'), 'id_format' => $this->getOption('id_format')), array_merge($this->attributes, $attributes)); return strtr($this->getOption('format'), $date); } } class ynValidatorDateFuzzy extends sfValidatorBase { protected function configure($options = array(), $messages = array()) { $this->addMessage('max', 'The date must be before %max%.'); $this->addMessage('min', 'The date must be after %min%.'); $this->addOption('min', null); $this->addOption('max', null); } protected function doClean($value) { if ( ! is_array( $value ) ) { throw new sfValidatorError($this, 'bad_format'); } // convert array to date string if (is_array($value)) { $value = $this->convertDateArrayToString($value); } if ($max = $this->getOption('max')) { $date_max = new DateTime( $max ); if ( $this->lowestPossible( $value )->format('Ymd') > $date_max->format('Ymd') ) { throw new sfValidatorError($this, 'max', array('value' => $value, 'max' => 'out of range')); } } if ($min = $this->getOption('min')) { $date_min = new DateTime( $min ); if ( $this->highestPossible( $value )->format('Ymd') < $date_min->format('Ymd') ) { throw new sfValidatorError($this, 'max', array('value' => $value, 'min' => 'out of range')); } } return $value; } protected function convertDateArrayToString($value) { // all elements must be empty or a number foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $key) { if (isset($value[$key]) && !preg_match('#^\d+$#', $value[$key]) && !empty($value[$key])) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } } // check empty value correspondence if ( ! $value['year'] && ! $value['month'] && ! $value['day'] ) { return $this->getEmptyValue(); } else if ( ! $value['year'] && ($value['month'] || $value['day']) ) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } else if ( $value['year'] && ! $value['month'] && $value['day'] ) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } if ( $value['month'] && ! in_array( (int) $value['month'], range(1,12) ) ) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } if ( $value['day'] && ! in_array( (int) $value['day'], range(1,31) ) ) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } $clean = ''; if ( $value['year'] ) { $clean .= sprintf( '%04d', intval( $value['year'] ) ); } if ( $value['month'] ) { $clean .= sprintf( '-%02d', intval( $value['month'] ) ); } if ( $value['day'] ) { $clean .= sprintf( '-%02d', intval( $value['day'] ) ); } return $clean; } protected function highestPossible( $date ) { if ( preg_match( '/^\d+$/', $date, $matches ) ) { return new DateTime( $date . '-12-31' ); } else if ( preg_match( '/^\d+-(\d+)$/', $date, $matches ) ) { switch ( $matches[1] ) { case '2': return new DateTime( $date . '-28' ); case '1': case '3': case '5': case '7': case '8': case '10': case '12': return new DateTime( $date . '-31' ); default: return new DateTime( $date . '-30' ); } } else { return new DateTime( $date ); } } protected function lowestPossible( $date ) { if ( preg_match( '/^\d+$/', $date, $matches ) ) { return new DateTime( $date . '-01-01' ); } else if ( preg_match( '/^\d+-(\d+)$/', $date, $matches ) ) { return new DateTime( $date . '-01' ); } else { return new DateTime( $date ); } } }