Monday, August 24, 2009

sfAdminThemejRollerPlugin: Symfony admin generator on steroid

It's been a while since I wanted to work on my own admin generator theme. In symfony 1.0 the default admin generator was not really nice, it became a little bit better in 1.2 but still not to my taste (purely subjective I must admit ;-). For example, one thing get me mad is the css for the table list and the filter, I believe that the table list should be maximized and filters available only when we need them.
I spent quite some time to modify some admin generator in some projects but you always end up doing the same thing over and over (see this screenshot of what I have done with the list view based on a standard admin generator)...it was time to make things more to my taste. I now have a pretty clear idea of things that I would like out of the box from my new admin generator. Let me be more specific with the complete list of things I would like:

  1. Change css style to a more modern style (again purely subjective ;-) that can be changed easily. For this I am using jQuery UI with their new theme roller system. The plugin is packaged with only one theme but it is extremely (too?) easy to change to your favorite theme (long term will be to use the upcoming html5 grid).

  2. The filter box is hidden by default with a button to show/hide a modal window with the list of available filters. This allows to have a larger space for the table list.

  3. In the new/edit views fieldsets are replaced by tabs (again jQuery UI).

  4. Add a show only view, it makes sense for some application to allow read-only access.

  5. filters ala iTunes smart playlist, meaning you select fields you want to filter and add them (nice to have but not so important, although useful if you have a lot of filter options)

  6. Print preview (configuration in generator.yml): simply a non paginated table easy to print with sortable column (javascript grid style)

  7. live search (configuration in generator.yml): be able to quickly filter the table list while typing a few characters, ajax style. I did that before for one specific module, I now have to make it part of the admin generator.

  8. export to CSV format: we could have a modal window to select the fields we would like to get and export them or simply a list defined in the generator.yml (which can include getters that are not database fields).

  9. export to PDF: does it make sense at all, I am not yet sure!

  10. Import CSV to database: to be able to simply upload a csv file of data to import through the admin generator.



The first 3 items are easy to do just by modifying existing admin generator templates and are indeed already packaged in the plugin. The rest need more work (still learning how admin generator are working deeply).

I am also trying as much as possible to keep all actions accessible without javascript and make sure that I do not break any default admin generator css classes and ids in case you need them.

"Stop talking and show us the code"...here you go! It is published as a symfony plugin at http://www.symfony-project.com/plugins/sfAdminThemejRollerPlugin, meanwhile here are some screenshots:
jroller-list-themes.png

jroller-list-filters.png

jroller-form.png


How to install?


./symfony plugin:install sfAdminThemejRollerPlugin --stability=beta

When I say beta, it is beta ;-) I am not fully satisfied with the code and still thinking about the best approach for some features (not mentioning it's my first play with admin generator). Feel free to give me feedback.
I will publish the code on github later to ease collaboration if any of you are interested. Anyway, it's php right? so you can play with the code right now!

How to use it?


./symfony --theme=jroller doctrine:generate-admin


or if you have an existing admin generator, simply modify your generator.yml file with
theme: jroller
instead of theme: admin.

Configuration


I currently package jQuery 1.3.2 and jQuery UI 1.7.2 to make sure it can work out-of-the-box, but it is also easy to specify in your app.yml file that you do not want to use those versions (or because you already include those libraries in your project) and include your own jquery and UI.
jQuery UI is using at the minimum: Tabs, Dialog (Drag and drop and resizable recommended).


# inside your app.yml file
sf_admin_theme_jroller_plugin:
web_dir: /sfAdminThemejRollerPlugin # specify specific folder where to pick your jquery stuff, ui and themes.
use_jquery: true # default. use the packaged jquery/UI
theme: redmond # default.
css_reset: true # default. reset default css (from Blueprint CSS)


What's next?


I will try to implement the rest in the coming months (I just found out that sfDoctrineAdminGeneratorWithShowPlugin start to implement some of my ideas), again feel free to contribute to speed up the process.

Bugs? no way!



  • I still have a pixel problem with the caption and tfoot of my table, if anyone knows how to solve it?




You will probably find bugs, please report them.

40 comments:

  1. Great man, about this list above, home much are finalizzed? Can I help you?

    http://fellipeeduardo.com

    gtalk: fellipe.eduardo@gmail.com

    ReplyDelete
  2. Can you commit the code to the symfony plugin svn?

    ReplyDelete
  3. Very nice work. I would enjoy to participate a little to the project if you are ok ;)

    ReplyDelete
  4. @felipe, @gimler and @hugo:
    Thanks a lot for your comments. I should have the svn account setup on symfony in the coming day. I am also working on a github.com repository.
    Of course you are more than welcome to contribute. So far, I have not started to work on the rest of my list. Feel free to contact me if you need more information. I'll keep you update as soon as things are ready.

    ReplyDelete
  5. Seems nice ! I'll give a try on my next project. ;)
    See you. :)

    ReplyDelete
  6. _assets.php use app_sf_admin_theme_jroller_* instate of app_sf_admin_theme_jroller_plugin_* config values ;(

    ReplyDelete
  7. another thing in _assets.php line 4 key is not 'include_jquery' it is 'use_jquery'

    ReplyDelete
  8. @gimler: thanks. I fixed the inconsistence between README and app.yml and add a few more things. Everything available in new version 0.1.3 as well as svn and github.

    ReplyDelete
  9. great work. how do you get text fields for dates instead of selects ?

    ReplyDelete
  10. update: for the pixel problem try {padding-left:1px; margin-left:-1px}

    ReplyDelete
  11. hi, i created a project with the propel-init-admin command and installed your plugin but it doesnt work.
    I changed the theme from admin to jroller but in my browsers still shows the default theme. I also added your code to my app.yml and renamed the sfDoctrineModule folder to sfPropelModule.
    pls help

    ReplyDelete
  12. Hello!
    great plugin but does it work with symfony 1.4?
    I receive this warning message before the headers of my "category" admin page (but the script doesn't end and i see the admin page with jroller):
    Strict Standards: Declaration of BaseCategoryGeneratorConfiguration::getForm() should be compatible with that of sfModelGeneratorConfiguration::getForm() in /Users/cosmy/workspace/ssm/cache/backend/dev/modules/autoCategory/lib/BaseCategoryGeneratorConfiguration.class.php on line 11

    ReplyDelete
  13. I think it doesn't work wit sf 1.4, that's bad :(

    ReplyDelete
  14. Hi,
    Great job. Excellent plugin.
    How can I have icon like you on the back to list button please. I try to figure out but i don't successed.

    ReplyDelete
  15. Nice work !
    Any plan to continue the development of this plugin ? Using Symfony 1.4

    ReplyDelete
  16. How can I add or remove fields in the show/list action? I have a table with more than 20 fields and by default I want to show only a few because it show the horizontal scroll.
    Thanks

    ReplyDelete
  17. Dude, this is extremely cool. As for you, John, do something like this:

    list:
    title: Volunteer / Foster list
    display: [first_name, last_name, home_phone, cell_phone, primary_email]

    this will only show the stuff in square brackets. You can do so for different views.

    ReplyDelete
  18. seems great! how about Propel support? Do you have any plans for that?

    ReplyDelete
  19. I have been visiting various blogs for my term papers writing research. I have found your blog to be quite useful. Keep updating your blog with valuable information... Regards

    ReplyDelete
  20. I like it!!

    But... Pass http://validator.w3.org/check?

    ReplyDelete
  21. But some issues in batch actions ((

    ReplyDelete
  22. i got _csrf_token [CSRF attack detected.] error when i trying delete file in list view :) i trying to fix this problem

    ReplyDelete
  23. Hi..
    I think you would benefit from reading the link below.
    After batch delete does work except it only deletes 1 item at a time. I would suggest looking through the cache for the module and see what's happening under the hood.

    http://botchedcode.com/2010/02/12/_csrf_token-csrf-attack-detected-using-sfadminthemejrollerplugin-symfony-1-4/

    ReplyDelete
  24. Great plugin BTW,
    I get the error below after selecting more than one item for batch delete, any ideas why
    SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

    Symfony 1.4

    ReplyDelete
  25. Great plugin!
    Thank you.
    Any tips how to get it working with propel? Please.
    I've been struggling for a few hours trying to replace Doctrine to Propel - where I understood what to do, but it's too complicated for me.

    ReplyDelete
  26. hi
    I want to generate admin page by select from view table, how can i do this?
    ex. i have a view table vw_report , then i want to generate admin by selecting from that, i use doctrine, and plugin sfAdminThemejRollerPlugin.
    #anyway, i just want to have a page which generate by selecting from view

    anyone can help me?

    sorry for my poor english.

    ReplyDelete
  27. After installing the plugin and setting the theme to "jroller", I get this:

    Fatal error: Class 'BaseIncentivesGeneratorConfiguration' not found in /data/work_area/shayr/redchurch/apps/backend/modules/incentives/lib/incentivesGeneratorConfiguration.class.php on line 12

    Anyone know what's going on? I've posted on several boards and heard nothing... Help!

    ReplyDelete
  28. How do I change the default index page? I need to get it to output the related field rather than the Blah_ID number .. so for "IDEventSeverity" I need it to output the actual description field thatt he foreign relation points to. This would be easy with a normal admin page but I cant seem to figure out how to change this in your module.

    ReplyDelete
  29. im reasonably certain its just going to be a quick if statement in the index and show actions.. but I cant find where to put it yet .. I would appreciate the help..

    ReplyDelete
  30. Solution for issue #8396
    _csrf_token [CSRF attack detected.] error while executing a batch deleting in admin generated with sfAdminThemejRollerPlugin
    http://trac.symfony-project.org/ticket/8396

    you must change the form class name at line 9 from your file:
    plugins/sfAdminThemejRollerPlugin/data/generator/sfDoctrineModule/jroller/template/templates/_list_batch_actions.php
    where it say:
    [?php $form = new sfForm(); if ($form->isCSRFProtected()): ?]
    shoud be:
    [?php $form = new BaseForm(); if ($form->isCSRFProtected()): ?]

    and that's it!!!

    ReplyDelete
  31. In fg.buttons.css:-
    button.fg-button-icon-left .ui-icon { left: -20px; }
    is ok for firefox, but for IE it puts the icon off the button.
    for IE ie should be 0px.

    I tried to get something working for both browsers but so far have been unsuccessful.

    ReplyDelete
  32. Hello!

    I tried to use the plugin on my already generated admins but when I do, I got this error "Fatal error: Call to a member function getRawValue() on a non-object ..." Any idea why?

    And do you have any tutorial or something that sais how to change the date in a form to a text box with a datepicker?

    Thanks!

    ReplyDelete
  33. After installing the plugin and setting the theme to "jroller", I get this:

    Parse error: syntax error, unexpected '<', expecting T_FUNCTION in C:\wamp\www\prueba\cache\backend\dev\modules\autoArticulo\lib\BaseArticuloGeneratorConfiguration.class.php on line 13

    Anyone know what's going on? I've posted on several boards and heard nothing... Help please!

    ReplyDelete
  34. Hello many thanks for this plugin but when i'm trying to reset filter by clicking on reset button of filter i get the :
    404 | Not Found | sfError404Exception
    Action "module_name/action" does not exist.

    ReplyDelete
  35. I managed to solve the CSS problem with the buttons in all browsers except firefox... Saddly, I only found the 'fast solution', that is, using a mozilla hack.

    Locate the file 'css/fg.buttons.css' inside the plugin folder (sfAdminTheme... etc)

    Locate this line: button.fg-button-icon-left .ui-icon { left: -20px; }

    Replace it with:

    @-moz-document url-prefix() {
    button.fg-button-icon-left .ui-icon {
    left: -20px;
    }
    }

    That's the ugly FF hack I found. The main problem resides in the custom classes that are added to the button (these are not de jQueryUI ones). I tried replacing them with the jquery roller theme ones, with no luck.

    ReplyDelete
  36. To fix de 'reset' issue inside the filters window, locate the file 'jroller.js' (plugin_folder/web)

    Change line: 67
    location.href = $('#sf_admin_filter_reset').attr('href');

    For this one

    $('#sf_admin_filters_buttons a')[1].click();

    (yes, it's kinda like a hack), the problem is that the filter reset action needs a post parameter (not a get), this is a quick fix.

    Basically, I'm triggering the link to reset that actually works (the one from the list)

    ReplyDelete
  37. if found 2 nasty bugs:

    1 - you're using sfValidatorDoctrineChoiceMany (in data/generator/sfDoctrineModule/jroller/parts/batchAction.php), that is deprecated. You must use sfValidatorDoctrineChoice instead

    2 - getForm() method (in data/generator/sfDoctrineModule/jroller/parts/configuration.php) is missing second parameter $options and so is not compatible with parent class

    ReplyDelete
  38. Good Day,
    I hyave the solution for propel module on delete bug:
    first edit _list_batch_actions.php
    on line:
    [?php $form = new sfForm(); if ($form->isCSRFProtected()): ?]
    to
    [?php $form = new BaseForm(); if ($form->isCSRFProtected()): ?]

    next edit batchActions.php on line 29 replace for

    $validator = new sfValidatorPropelChoice(array('multiple' => true, 'model' => 'getModelClass() ?>'));

    next replace all executeBatchDelete function for:

    protected function executeBatchDelete(sfWebRequest $request)
    {
    $ids = $request->getParameter('ids');

    $count = 0;
    foreach (getModelClass().'::PEER') ?>::retrieveByPks($ids) as $object)
    {
    $this->dispatcher->notify(new sfEvent($this, 'admin.delete_object', array('object' => $object)));

    $object->delete();
    if ($object->isDeleted())
    {
    $count++;
    }
    }

    if ($count >= count($ids))
    {
    $this->getUser()->setFlash('notice', 'The selected items have been deleted successfully.');
    }
    else
    {
    $this->getUser()->setFlash('error', 'A problem occurs when deleting the selected items.');
    }

    $this->redirect('@getUrlForAction('list') ?>');
    }

    ReplyDelete
  39. Hiho all!!

    Another bug solution:

    As Stephen said there is a bug when showing in a model which has more than 1 primary key (for example a many-to-many relationship), in this comment: http://gestadieu.blogspot.com.es/2009/08/sfadminthemejrollerplugin-symfony-admin.html?showComment=1287124144885#c5448752061317719639

    SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens


    The solution is too simple. Go to plugins\sfAdminThemejRollerPlugin\data\generator\sfDoctrineModule\jroller\parts\showAction.php

    In the 3º line, which has a call to the find() method, replace:

    This ...->find(getRetrieveByPkParamsForAction(49) ?>);

    With this ...->find(array(getRetrieveByPkParamsForAction(49) ?>));

    In other words: Involve the index params in an array, to be able to work with a M-N relationships.

    Have a nice day, mates!!

    ReplyDelete