You will need the original package.xml, it can be usefull for package maintainers to provide source of their package as i did for my PHP_Debug pkg
// file function showSource($dir, $file) { $path = $dir. $file; print('<div>'); print('<h1>'. $path. '</h1>'); highlight_file($path); print('</div>'. "\n"); } // Dir function parseDir($dir, $parent) { $path = $parent. ($dir['name'] != '/' ? $dir['name']. '/' : ''); foreach($dir->file as $file) { if (in_array($file['role'], array('test', 'php'))) { showSource($path, $file['name']); } } foreach($dir->dir as $child) { parseDir($child, $path); } return; } $package = simplexml_load_file('package.xml'); $dir = ''; parseDir($package->contents->dir, $dir);
Here is a small patch for the action.class of the admin generator theme :
in executeList() function replace :
$this->pager->setPage($this->getRequestParameter('page', 1));
by :
$this->pager->setPage($this->getRequestParameter('page', $this->getUser()->getAttribute('page', 1, 'sf_admin/<?php echo $this->getSingularName() ?>')));
in executeList() function add at the end :
// Save page if ($this->getRequestParameter('page')) { $this->getUser()->setAttribute('page', $this->getRequestParameter('page'), 'sf_admin/<?php echo $this->getSingularName() ?>'); }
in the process() function, before the 1st removeNamespace statement add :
$this->getUser()->getAttributeHolder()->removeNamespace('sf_admin/<?php echo $this->getSingularName() ?>');
[EN] This is a typical .htaccess file for the french hoster OVH (1)
[FR] Voici un fichier .htaccess typique pour faire fonctionner une application Symfony sur un hébergement mutualisé OVH.
(1)
SetEnv PHP_VER 5 SetEnv REGISTER_GLOBALS 0 SetEnv MAGIC_QUOTES 0 mod_gzip_on Off Options +FollowSymLinks +ExecCGI <IfModule mod_rewrite.c> RewriteEngine On # we skip all files with .something RewriteCond %{REQUEST_URI} \..+$ RewriteCond %{REQUEST_URI} !\.html$ RewriteCond %{REQUEST_URI} !\.php #RewriteCond %{REQUEST_URI} !\.php RewriteRule .* - [L] # we check if the .html version is here (caching) RewriteRule ^$ /index.html [QSA] RewriteRule ^([^.]+)$ /$1.html [QSA] RewriteCond %{REQUEST_FILENAME} !-f # no, so we redirect to our front web controller RewriteRule ^(.*)$ /index.php [QSA,L] # hidden frontoffice controller RewriteRule ^index\.php/(.*)$ /index.php [QSA,L] # fo controllers RewriteRule ^frontend\.php/(.*)$ /frontend.php [QSA,L] RewriteRule ^frontend_dev\.php/(.*)$ /frontend_dev.php [QSA,L] </IfModule> # big crash from our front web controller ErrorDocument 500 "<h2>Application error</h2>symfony application failed to start properly"
[Fr] Traduction à venir.
Well, in fact i have taken the idea from what has already been done with cakePHP, you can find the original blog articles here (1) and thanks to the authors for the work. Now a full project for this purpose is available for cakePHP it is called Jake
(1)
Versions used :
Joomla 1.0.12 stable
Install the plugin (check the plugin Wiki page in Symfony )
Now you must have a sfJoomlaBridge dir in your plugins dir and a com_symfony dir in /web/components
The Symfony application is built as usual and the Joomla installation must be achieved in the /web of the Symfony project.
Open symfony.html.php, line 8 modify the name of Symfony controller file (frontoffice.php by default). Take care to use a production controller, because we should't have any debug information
Un-comment line 34 with file_get_contents if you don't have curl installed on you web server, and comment the curl related line above.
In index.php of Joomla
before (near line 114)
// mainframe is an API workhorse, lots of 'core' interaction routines $mainframe = new mosMainFrame( $database, $option, '.' ); $mainframe->initSession();
add
//Before Joomla initialize its mainframe, lets play… // check missing component — added before $mainframe is being initialized //########################## if (!file_exists($mosConfig_absolute_path . '/components/'. $option )) { $option = 'com_symfony'; //set component to com_cake if a controller is not found. }
Clear the cache
> symfony clear cc
There is nothing really specific except the new link helper to use in the templates. To use the Joomla helpers :
<?php use_helper('Joomla'); ?>
You can also modify the index file of Joomla application in plugins/sfJoomlaBridge/config/config.php
We are going to do a page (module/action) whitch will be called by Joomla with the help of its new component.
Nous are going to create 2 actions, 1 basic (static content) and one with the users list of Joomla administration
index/actions/actions.class.php
/** * Executes index action */ public function executeIndex() { } /** * Test Joomla/Symfony integration * * @author lvernet * @since 26 feb 2007 */ public function executeUsers() { $this->users = JosUsersPeer::doSelect(new Criteria()); }
The plugin introduce a new helper link_to_joomla that allows to do link to others symfony pages but with staying in the Joomla application.
templates/usersSuccess.php
<?php use_helper('Joomla'); ?> <h1>Joomla Users list</h1> <p>This list is retrieved with the help of the ORM of Symfony</p>
<?php if ($users): ?> <table class="contenttoc"> <thead> <th>Id</th> <th>Name</th> <th>User name</th> </thead> <?php foreach($users as $user): ?> <tr> <td><?php echo $user->getId(); ?></td> <td><?php echo $user->getName(); ?></td> <td><?php echo $user->getUsername(); ?></td> </tr> <?php endforeach; ?> </table> <?php endif; ?>
<?php echo link_to_joomla('Back to index', 'index/index', array('query_string' => 'param1=chat¶m2=rat')); ?>
templates/indexSuccess.php
<?php use_helper('Joomla'); ?> <h1>This content is provided through Symfony framework.</h1> <?php echo image_tag('powered_by.png') ?> && <?php echo image_tag('http://www.symfony-project.com/images/symfony_logo.gif') ?>
<?php echo link_to_joomla('Users list', 'index/users'); ?>
Now we must desactivate the view's layout, because we don't want it as we will use the one of Joomla.
In index/config/view.yml :
indexSuccess: has_layout: off usersSuccess: has_layout: off
We can now call our sf pages in Joomla, link are formed as following : index.php?option=index&task=users
Go to the url: http://projectroot/index.php?option=index&task=users, the users list is displayed and you have a link to go to the index page (link created with the joomla helper)
/!\ Be carefull, the option param should not match the name of a Joomla component /!\
I made this quiet quickly, of course there are lot of things to do to have a perfect integration as the Jake project does it for cakePHP. If someone is interested, feel free to take the code and contribute, it would be nice. COil :)
[Fr] Dans la doc j'ai trouvé la liste de fichier à exclure assez peu fournie. Voici une liste plus complète à personnaliser : (1)
[En] In the documentation i found the exclude file list quiet small. Here is a more consistant one to customize : (1)
.svn .CVS .project .settings .settings/* symfony /cache/* /config/config.php /config/propel.ini /config/databases.yml /config/properties.ini /doc/* /log/* /lib/symfony /lib/symfony/* /data/symfony /data/symfony/* /plugins/.channels/ /plugins/.registry/ /plugins/.depdb /plugins/.depdblock /plugins/.filemap /plugins/.lock /plugins/.pearrc /stats/* /test/* /web/.htaccess /web/frontend_dev.php /web/backend_dev.php /web/phpinfo.php /web/test.php /web/uploads/* /web/sf /web/sf/*
[En] After having tested the sync command of Symfony i realised that all the permissions of my /web directory were wrong. The symfony command : (1)
It supposed to correct this, unfortunately the command line php version is 4 on my shared hosting, so i had to to it by hand with a little unix script : (2)
There are lot and different problems on shared testing as can can see in this forum thread
[Fr] Apres avoir testé la commande de synchronisation de symfony, plus rien ne marchait... En fait c'est du au fait que tous mes fichiers de /web n'avait plus les bonnes permission et il fallait les repasser en 755. Une commande symfony permet de regler ce problème : (1)
> symfony fix-perms
Néanmoins la version de PHP sur mon server mutualisé est la 4 ! le script symfony ne peut donc pas fonctionner... On peut néanmoins contourner en executant la commande suivante : (2)
> symfony cc > find . -type f | xargs chmod 644 > find . -type d | xargs chmod 755
La joie des serveurs mutualisés avec Symfony, ce n'est pas toujours de tout repos comme le montre ce post sur le forum
[Fr] Attention ce snippet n'est vraiment utile que pour la version 0.6.3 de Symfony, la version 1.0.0 intégrant déja une fonctionnalité de suppression des commentaires propel. Il suffit d'ajout dans votre propel.ini
(1)
propel.builder.addIncludes = false propel.builder.addComments = false
Voici un batch pour supprimmer les commentaires des classes générées par Propel. On peut gagner entre 5% et 9% du fichier, ce qui n'est pas négligeable surtout sur les gros fichier. Créér simplement un fichier dans '/batch/', par exemple 'strip_comments.php' avec le code suivant (2)
[En] Take care, this snippet is only usefull for 0.6.3 version of Symfony, the 1.0.0 version already inlcudes this functionnality. Juste add in your propel.ini (1)
Here is a batch to delete the genrerated propel comments. On can optimize between 5% and 9% of file size, witch is not negligible. Just create a file in the directory '/batch/', for example 'strip_comments.php' with the following code :
<?php // GEN ========================================================================= define('STR_N', ""); define('CR', "\r\n"); define('SF_ROOT_DIR', realpath(dirname(__file__).'/..')); define('SF_APP', 'backoffice'); define('SF_ENVIRONMENT', 'dev'); define('SF_DEBUG', true); // Tokenizer constants if (!defined('T_ML_COMMENT')) { define('T_ML_COMMENT', T_COMMENT); } else { define('T_DOC_COMMENT', T_ML_COMMENT); } require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR. 'apps'. DIRECTORY_SEPARATOR. SF_APP. DIRECTORY_SEPARATOR. 'config'. DIRECTORY_SEPARATOR. 'config.php'); // Logs $logger = sfContext::getInstance()->getLogger(); // SPEC ======================================================================== $batchName = 'Strip Propel class comments '; // PROCESS ===================================================================== // Titre $info = '== BATCH : '.$batchName. CR; print($info); $logger->log($info); // Start $start = time(); $info = '== Start : '. CR; print($info); $logger->log($info); // Recuperation de la liste des fichiers $modelPath = SF_ROOT_DIR. DIRECTORY_SEPARATOR. 'lib' . DIRECTORY_SEPARATOR. 'model'. DIRECTORY_SEPARATOR; echo '$modelPath : '. $modelPath. CR; $omPath = $modelPath .'om'. DIRECTORY_SEPARATOR; $mapPath = $modelPath. 'map'. DIRECTORY_SEPARATOR; $omPropelFiles = sfFinder::type('file')->name('Base*.php')->relative()->in($omPath); $mapPropelFiles = sfFinder::type('file')->name('*MapBuilder.php')->relative()->in($mapPath); // Om if ($omPropelFiles) { foreach ($omPropelFiles as $file) { strip_comments($omPath. $file); } $info = '== Om : '. count($omPropelFiles). ' fichiers epures.'. CR; print($info); $logger->log($info); } // Map if ($mapPropelFiles) { foreach ($mapPropelFiles as $file) { strip_comments($mapPath. $file); } $info = '== Map : '. count($mapPropelFiles). ' fichiers epures.'. CR; print($info); $logger->log($info); } // End $end = time(); $info = '== End'. CR; print($info); $logger->log($info); /** * Fonction de suppression des commentaires * * @author Vernet Loïc * @since 2 févr. 2007 */ function strip_comments($file) { $source = file_get_contents($file); $tokens = token_get_all($source); $buffer = ''; foreach ($tokens as $token) { if (is_string($token)) { // simple 1-character token $buffer.= $token; } else { // token array list($id, $text) = $token; switch ($id) { case T_COMMENT: case T_ML_COMMENT: // we've defined this case T_DOC_COMMENT: // and this // no action on comments break; default: // anything else -> output "as is" $buffer.= $text; break; } } } file_put_contents($file, $buffer); } ?>
Here we go !
The sort in admin generator is for a single field only, but in some complex list, it can be usefull to sort by multiple criterias. This is the main goal of this snippet. Those functions therefore override the ones of your auto-generated class. (1) And to display what are the ongoing sort criterias, you have to modify your '_list_th_tabular.php' file.
// /** * Add a sort criteria */ protected function processSort () { $sort = $this->getRequestParameter('sort'); $type = $this->getRequestParameter('type'); // Register sort if ($sort) { $this->getUser()->setAttribute($sort, $type, 'sf_admin/produits/sort'); } } /** * Add the sort criterias to the query */ protected function addSortCriteria(&$c) { $multisort = $this->getUser()->getAttributeHolder()->getAll('sf_admin/produits/sort'); if ($multisort) { foreach($multisort as $sort_column => $sort_type) { $sort_column = Propel::getDB($c->getDbName())->quoteIdentifier($sort_column); if ($sort_type == 'asc') { $c->addAscendingOrderByColumn($sort_column); } elseif ($sort_type == 'desc') { $c->addDescendingOrderByColumn($sort_column); } } } else { // Default sort $sort_column = Propel::getDB($c->getDbName())->quoteIdentifier('libelle'); $c->addAscendingOrderByColumn($sort_column); } } /** * Specific function for multi-sort */ protected function processFilters () { if ($this->getRequest()->hasParameter('filter')) { $filters = $this->getRequestParameter('filters'); // Multi-sort initialisation if (!is_array($filters)) { $this->getUser()->getAttributeHolder()->removeNamespace('sf_admin/produits/sort'); } $this->getUser()->getAttributeHolder()->removeNamespace('sf_admin/produits/filters'); $this->getUser()->getAttributeHolder()->add($filters, 'sf_admin/produits/filters'); } }
(for each sortable field)
<?php $multisort = $sf_user->getAttributeHolder()->getAll('sf_admin/produits/sort'); ?> <th id="sf_admin_list_th_libelle"> <?php if (isset($multisort['libelle'])) { echo link_to('Libellé', 'Produits/list?sort=libelle&type='. ($multisort['libelle'] == 'asc' ? 'desc' : 'asc')); echo ' ('. $multisort['libelle'] . ')'; } else { echo link_to('Libellé', 'Produits/list?sort=libelle&type=asc'); } ?> </th>
'Produits' is the module name, 'libelle' is the field to sort.
The 'reset button' of filters also initialize the multi-sort. The sort is made from the first field clicked to the last. That means, if you want a different primary sort you will have to use the reset filter button before.
PS: Obviously, this modification can be easly integrated in your backoffice theme, note that the default sort criteria set in the 'generator.yml' is used in the addSortCriteria function (from line // default sort)
COil :)