[med-svn] [mobyle] 01/03: Imported Upstream version 1.5.3+dfsg

Andreas Tille tille at debian.org
Fri May 20 20:52:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository mobyle.

commit 33e512a062a36584413c1241f5f5373d681b6988
Author: Andreas Tille <tille at debian.org>
Date:   Fri May 20 22:42:43 2016 +0200

    Imported Upstream version 1.5.3+dfsg
---
 AUTHORS                                            |    9 +
 COPYING                                            |  340 ++
 Doc/Admin/configuration_guide.pdf                  |  Bin 0 -> 220514 bytes
 Doc/Admin/configuration_guide.tex                  |  800 ++++
 Doc/Admin/execution_systems_guide.pdf              |  Bin 0 -> 137236 bytes
 Doc/Admin/execution_systems_guide.tex              |  304 ++
 Doc/Admin/grid_architecture.png                    |  Bin 0 -> 102427 bytes
 Doc/MobLinkExample.html                            |   61 +
 Doc/jme_interface.jpeg                             |  Bin 0 -> 312623 bytes
 Doc/service_description_guide.pdf                  |  Bin 0 -> 643939 bytes
 Doc/service_description_guide.tex                  | 1617 +++++++
 Example/Local/Config/Config.template.py            |  461 ++
 Example/Local/CustomClasses/__init__.py            |    2 +
 Example/Local/Policy.ldap.py                       |   87 +
 Example/Local/Policy.pasteur.py                    |  220 +
 Example/Local/Policy.py                            |  166 +
 INSTALL                                            |  199 +
 Local/Config/Execution/DRMAAConfig.py              |   20 +
 Local/Config/Execution/ExecutionConfig.py          |   15 +
 Local/Config/Execution/LsfDRMAAConfig.py           |   27 +
 Local/Config/Execution/PbsDRMAAConfig.py           |   22 +
 Local/Config/Execution/SGEConfig.py                |   26 +
 Local/Config/Execution/SYSConfig.py                |   15 +
 Local/Config/Execution/SgeDRMAAConfig.py           |   30 +
 Local/Config/Execution/__init__.py                 |   28 +
 Local/Config/__init__.py                           |    1 +
 Local/CustomClasses/__init__.py                    |    2 +
 Local/Policy.py                                    |   48 +
 Local/__init__.py                                  |    1 +
 Local/black_list.py                                |    6 +
 Local/mailTemplate.py                              |  270 ++
 NEWS                                               |  245 ++
 PKG-INFO                                           |   10 +
 README                                             |   51 +
 Schema/biomoby.rnc                                 |   23 +
 Schema/biomoby.rng                                 |   42 +
 Schema/common.rnc                                  |  366 ++
 Schema/common.rng                                  |  777 ++++
 Schema/jobstate.rnc                                |   82 +
 Schema/jobstate.rng                                |  170 +
 Schema/mobyle.sch                                  |  155 +
 Schema/package.rnc                                 |    3 +
 Schema/package.rng                                 |    7 +
 Schema/program.rnc                                 |   65 +
 Schema/program.rng                                 |  133 +
 Schema/program_or_workflow.rnc                     |    1 +
 Schema/program_or_workflow.rng                     |    9 +
 Schema/tutorial.rnc                                |   10 +
 Schema/tutorial.rng                                |   17 +
 Schema/userspace.rnc                               |   72 +
 Schema/userspace.rng                               |  164 +
 Schema/viewer.rnc                                  |   19 +
 Schema/viewer.rng                                  |   36 +
 Schema/workflow.rnc                                |   36 +
 Schema/workflow.rng                                |  109 +
 Src/Mobyle/Admin.py                                |  319 ++
 Src/Mobyle/AnonymousSession.py                     |  137 +
 Src/Mobyle/AuthenticatedSession.py                 |  323 ++
 Src/Mobyle/BMPSWorkflow.py                         |  541 +++
 Src/Mobyle/Captcha/Backgrounds.py                  |   42 +
 Src/Mobyle/Captcha/COPYING                         |   19 +
 Src/Mobyle/Captcha/Captcha.py                      |  103 +
 Src/Mobyle/Captcha/Distortions.py                  |   85 +
 Src/Mobyle/Captcha/Pictures.py                     |   35 +
 Src/Mobyle/Captcha/Text.py                         |   91 +
 Src/Mobyle/Captcha/Words.py                        |   48 +
 Src/Mobyle/Captcha/__init__.py                     |   15 +
 Src/Mobyle/Captcha/data/pictures/abstract/1.png    |  Bin 0 -> 33232 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/10.png   |  Bin 0 -> 44069 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/11.png   |  Bin 0 -> 63021 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/12.png   |  Bin 0 -> 64672 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/2.png    |  Bin 0 -> 35791 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/3.png    |  Bin 0 -> 34327 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/4.png    |  Bin 0 -> 38100 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/5.png    |  Bin 0 -> 36360 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/6.png    |  Bin 0 -> 38920 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/7.png    |  Bin 0 -> 40702 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/8.png    |  Bin 0 -> 43513 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/9.png    |  Bin 0 -> 43633 bytes
 Src/Mobyle/Captcha/data/pictures/abstract/README   |    3 +
 .../Craig_Barrington_ocotillo_and_mountains.png    |  Bin 0 -> 914240 bytes
 .../pictures/nature/Kerry_Carloy_Chisos_Sunset.png |  Bin 0 -> 297206 bytes
 .../data/pictures/nature/Paul_Dowty_Mt_Bross.png   |  Bin 0 -> 1623294 bytes
 Src/Mobyle/Captcha/data/pictures/nature/README     |    2 +
 Src/Mobyle/Captcha/data/words/basic-english        |  852 ++++
 Src/Mobyle/Classes/Alignment.py                    |  109 +
 Src/Mobyle/Classes/Core.py                         |  851 ++++
 Src/Mobyle/Classes/DataType.py                     |  280 ++
 Src/Mobyle/Classes/Sequence.py                     |  106 +
 Src/Mobyle/Classes/Structure.py                    |   60 +
 Src/Mobyle/Classes/Tree.py                         |   17 +
 Src/Mobyle/Classes/__init__.py                     |   20 +
 Src/Mobyle/ClassificationIndex.py                  |   78 +
 Src/Mobyle/CommandBuilder.py                       |  275 ++
 Src/Mobyle/ConfigManager.py                        | 1388 ++++++
 Src/Mobyle/Converter/DataConverter.py              |   62 +
 Src/Mobyle/Converter/__init__.py                   |   28 +
 Src/Mobyle/Converter/clustal_phyml.py              |   62 +
 Src/Mobyle/Converter/fasta_phyml.py                |   62 +
 Src/Mobyle/Converter/nexus_phyml.py                |   62 +
 Src/Mobyle/Converter/phylipi_phyml.py              |   89 +
 Src/Mobyle/Converter/phylips_phyml.py              |   62 +
 Src/Mobyle/Converter/squizz_alignment.py           |  177 +
 Src/Mobyle/Converter/squizz_sequence.py            |  172 +
 Src/Mobyle/Converter/stockholm_phyml.py            |   62 +
 Src/Mobyle/DataInputsIndex.py                      |  120 +
 Src/Mobyle/DataProvider.py                         |   39 +
 Src/Mobyle/DataTypeValidator.py                    |  113 +
 Src/Mobyle/DescriptionsIndex.py                    |   47 +
 Src/Mobyle/Dispatcher.py                           |   85 +
 Src/Mobyle/Evaluation.py                           |  128 +
 Src/Mobyle/Execution/DRMAA.py                      |  300 ++
 Src/Mobyle/Execution/Dummy.py                      |  156 +
 Src/Mobyle/Execution/ExecutionSystem.py            |  162 +
 Src/Mobyle/Execution/LsfDRMAA.py                   |   25 +
 Src/Mobyle/Execution/PbsDRMAA.py                   |   24 +
 Src/Mobyle/Execution/SGE.py                        |  265 ++
 Src/Mobyle/Execution/SYS.py                        |  194 +
 Src/Mobyle/Execution/SgeDRMAA.py                   |   21 +
 Src/Mobyle/Execution/__init__.py                   |    8 +
 Src/Mobyle/IndexBase.py                            |  115 +
 Src/Mobyle/InterfacePreprocessor.py                |  109 +
 Src/Mobyle/Job.py                                  |  317 ++
 Src/Mobyle/JobFacade.py                            |  588 +++
 Src/Mobyle/JobState.py                             | 1149 +++++
 Src/Mobyle/MobyleError.py                          |  125 +
 Src/Mobyle/MobyleJob.py                            | 1063 +++++
 Src/Mobyle/MobyleLogger.py                         |  232 +
 Src/Mobyle/Net.py                                  |  402 ++
 Src/Mobyle/Parser.py                               |  553 +++
 Src/Mobyle/Registry.py                             |  631 +++
 Src/Mobyle/RunnerChild.py                          |  245 ++
 Src/Mobyle/RunnerFather.py                         |  352 ++
 Src/Mobyle/SearchIndex.py                          |  137 +
 Src/Mobyle/Service.py                              | 4548 ++++++++++++++++++++
 Src/Mobyle/Session.py                              | 1186 +++++
 Src/Mobyle/SessionFactory.py                       |  190 +
 Src/Mobyle/Status.py                               |  101 +
 Src/Mobyle/StatusManager.py                        |  298 ++
 .../Test/Converter/DataAlignments/align.CLUSTAL    |  292 ++
 .../Test/Converter/DataAlignments/align.FASTA      |  114 +
 .../Test/Converter/DataAlignments/align.MEGA       |  153 +
 Src/Mobyle/Test/Converter/DataAlignments/align.MSF |  299 ++
 .../Test/Converter/DataAlignments/align.NEXUS      |  202 +
 .../Test/Converter/DataAlignments/align.PHYLIPI    |  196 +
 .../Test/Converter/DataAlignments/align.PHYLIPS    |  148 +
 .../Test/Converter/DataAlignments/align.STOCKHOLM  |  234 +
 .../Test/Converter/DataAlignments/align.UNKNOWN    |  196 +
 .../Test/Converter/DataSequences/sequence.EMBL     |   14 +
 .../Test/Converter/DataSequences/sequence.FASTA    |    5 +
 .../Test/Converter/DataSequences/sequence.GCG      |   15 +
 .../Test/Converter/DataSequences/sequence.GDE      |    5 +
 .../Test/Converter/DataSequences/sequence.GENBANK  |   14 +
 .../Test/Converter/DataSequences/sequence.IG       |    8 +
 .../Test/Converter/DataSequences/sequence.NBRF     |    6 +
 .../Test/Converter/DataSequences/sequence.PIR      |   18 +
 .../Test/Converter/DataSequences/sequence.RAW      |    1 +
 Src/Mobyle/Test/Converter/__init__.py              |    0
 Src/Mobyle/Test/Converter/test_squizz_alignment.py |  110 +
 Src/Mobyle/Test/Converter/test_squizz_sequence.py  |  120 +
 Src/Mobyle/Test/MobyleTest.py                      |  139 +
 Src/Mobyle/Test/__init__.py                        |    5 +
 Src/Mobyle/Test/main.py                            |   39 +
 Src/Mobyle/Test/openTransaction.py                 |   44 +
 Src/Mobyle/Test/program_test.py                    |    8 +
 Src/Mobyle/Test/test_Admin.py                      |  263 ++
 Src/Mobyle/Test/test_AnonymousSession.py           |   70 +
 Src/Mobyle/Test/test_AuthenticatedSession.py       |  222 +
 Src/Mobyle/Test/test_JobState.py                   |  265 ++
 Src/Mobyle/Test/test_Session.py                    |  408 ++
 Src/Mobyle/Test/test_SessionFactory.py             |  131 +
 Src/Mobyle/Test/test_StatusManager.py              |  192 +
 Src/Mobyle/Test/test_Transaction.py                |  610 +++
 Src/Mobyle/Test/test_Workflow.py                   |  118 +
 Src/Mobyle/Test/test_mobdeploy.py                  |   76 +
 Src/Mobyle/Transaction.py                          | 1256 ++++++
 Src/Mobyle/Utils.py                                |  463 ++
 Src/Mobyle/Validator.py                            |  135 +
 Src/Mobyle/Workflow.py                             |  474 ++
 Src/Mobyle/WorkflowDemo.py                         |   92 +
 Src/Mobyle/WorkflowDemo2.py                        |   63 +
 Src/Mobyle/WorkflowJob.py                          |  637 +++
 Src/Mobyle/WorkflowJobDemo.py                      |   33 +
 Src/Mobyle/WorkflowJobFacadeDemo.py                |   29 +
 Src/Mobyle/WorkflowLayout.py                       |   82 +
 Src/Mobyle/WorkflowSessionHTTPDemo.py              |  315 ++
 Src/Mobyle/WorkflowSessionHTTPDemo2.py             |  129 +
 Src/Mobyle/__init__.py                             |   33 +
 Src/Portal/cgi-bin/MobylePortal/bank_get.py        |   61 +
 Src/Portal/cgi-bin/MobylePortal/data_bookmark.py   |   49 +
 Src/Portal/cgi-bin/MobylePortal/data_get.py        |   36 +
 Src/Portal/cgi-bin/MobylePortal/data_remove.html   |    9 +
 Src/Portal/cgi-bin/MobylePortal/data_remove.py     |   22 +
 .../cgi-bin/MobylePortal/data_remove_form.py       |   18 +
 Src/Portal/cgi-bin/MobylePortal/data_rename.py     |   23 +
 Src/Portal/cgi-bin/MobylePortal/data_upload.py     |   67 +
 Src/Portal/cgi-bin/MobylePortal/data_view.py       |   26 +
 Src/Portal/cgi-bin/MobylePortal/error.html         |    8 +
 Src/Portal/cgi-bin/MobylePortal/feed_view.py       |   19 +
 Src/Portal/cgi-bin/MobylePortal/file_load.py       |   30 +
 Src/Portal/cgi-bin/MobylePortal/form.py            |   62 +
 Src/Portal/cgi-bin/MobylePortal/genericService.py  |  747 ++++
 Src/Portal/cgi-bin/MobylePortal/graphml_to_dot.xsl |   92 +
 Src/Portal/cgi-bin/MobylePortal/graphml_to_wf.xsl  |  148 +
 Src/Portal/cgi-bin/MobylePortal/help_request.py    |   29 +
 Src/Portal/cgi-bin/MobylePortal/jobService.py      |  439 ++
 Src/Portal/cgi-bin/MobylePortal/job_kill.py        |   24 +
 Src/Portal/cgi-bin/MobylePortal/job_provjson.py    |   98 +
 Src/Portal/cgi-bin/MobylePortal/job_simulate.py    |   25 +
 Src/Portal/cgi-bin/MobylePortal/job_status.py      |   23 +
 Src/Portal/cgi-bin/MobylePortal/job_subjobs.py     |   23 +
 Src/Portal/cgi-bin/MobylePortal/job_submit.py      |   24 +
 Src/Portal/cgi-bin/MobylePortal/job_view.py        |   85 +
 Src/Portal/cgi-bin/MobylePortal/mb_cgi.py          |  370 ++
 .../cgi-bin/MobylePortal/net_servers_list.py       |   25 +
 Src/Portal/cgi-bin/MobylePortal/net_services.py    |   25 +
 Src/Portal/cgi-bin/MobylePortal/openidconsumer.py  |  405 ++
 Src/Portal/cgi-bin/MobylePortal/openidform.html    |   49 +
 Src/Portal/cgi-bin/MobylePortal/portal.html        |  322 ++
 Src/Portal/cgi-bin/MobylePortal/portal.py          |   78 +
 .../cgi-bin/MobylePortal/portal_properties.py      |   62 +
 .../cgi-bin/MobylePortal/program_validate.py       |   38 +
 Src/Portal/cgi-bin/MobylePortal/programs_list.html |   91 +
 Src/Portal/cgi-bin/MobylePortal/programs_list.py   |   49 +
 Src/Portal/cgi-bin/MobylePortal/registry.py        |   49 +
 .../cgi-bin/MobylePortal/schema_get_flattened.py   |   24 +
 .../cgi-bin/MobylePortal/session_activate.py       |   35 +
 .../cgi-bin/MobylePortal/session_activate_ano.html |   11 +
 .../MobylePortal/session_activate_auth.html        |   24 +
 .../cgi-bin/MobylePortal/session_activate_form.py  |   22 +
 .../cgi-bin/MobylePortal/session_captcha_check.py  |   22 +
 .../cgi-bin/MobylePortal/session_captcha_get.py    |   17 +
 .../cgi-bin/MobylePortal/session_email_form.html   |   13 +
 .../cgi-bin/MobylePortal/session_email_form.py     |   18 +
 .../cgi-bin/MobylePortal/session_job_help.html     |   28 +
 .../cgi-bin/MobylePortal/session_job_help_form.py  |   21 +
 .../cgi-bin/MobylePortal/session_job_remove.html   |    9 +
 .../cgi-bin/MobylePortal/session_job_remove.py     |   22 +
 .../MobylePortal/session_job_remove_form.py        |   19 +
 .../cgi-bin/MobylePortal/session_job_rename.py     |   43 +
 .../cgi-bin/MobylePortal/session_job_submit.py     |   37 +
 .../cgi-bin/MobylePortal/session_register.html     |   28 +
 .../cgi-bin/MobylePortal/session_register.py       |   37 +
 .../cgi-bin/MobylePortal/session_register_form.py  |   17 +
 .../cgi-bin/MobylePortal/session_setemail.py       |   31 +
 .../cgi-bin/MobylePortal/session_signin.html       |   24 +
 Src/Portal/cgi-bin/MobylePortal/session_signin.py  |   38 +
 .../cgi-bin/MobylePortal/session_signin_form.py    |   17 +
 .../cgi-bin/MobylePortal/session_signout.html      |   14 +
 Src/Portal/cgi-bin/MobylePortal/session_signout.py |   29 +
 .../cgi-bin/MobylePortal/session_signout_form.py   |   17 +
 .../cgi-bin/MobylePortal/session_workspace.py      |   39 +
 Src/Portal/cgi-bin/MobylePortal/sitemap.py         |   32 +
 Src/Portal/cgi-bin/MobylePortal/sitemap.xml        |    7 +
 Src/Portal/cgi-bin/MobylePortal/task_xml.xsl       |   63 +
 Src/Portal/cgi-bin/MobylePortal/treenode.py        |   65 +
 Src/Portal/cgi-bin/MobylePortal/tutorial.py        |   57 +
 .../update_graphml_with_graphviz_layout.xsl        |   27 +
 Src/Portal/cgi-bin/MobylePortal/viewer_view.py     |   27 +
 .../cgi-bin/MobylePortal/viewer_xml_input.py       |   37 +
 Src/Portal/cgi-bin/MobylePortal/wf_to_graphml.xsl  |  161 +
 Src/Portal/cgi-bin/MobylePortal/workflow.py        |  357 ++
 .../cgi-bin/MobylePortal/workflow_job_layout.py    |   25 +
 Src/Portal/cgi-bin/MobylePortal/workflow_layout.py |   35 +
 Src/Portal/cgi-bin/MobylePortal/workflow_test.html |   36 +
 Src/Portal/cgi-bin/MobylePortal/workflow_test.py   |   19 +
 Src/Portal/htdocs/MobylePortal/css/local.css       |   83 +
 Src/Portal/htdocs/MobylePortal/css/mobyle.css      |  941 ++++
 .../htdocs/MobylePortal/css/openid/openid.css      |   45 +
 Src/Portal/htdocs/MobylePortal/help/alifmt.html    |  188 +
 Src/Portal/htdocs/MobylePortal/help/register.html  |   23 +
 Src/Portal/htdocs/MobylePortal/help/seqfmt.html    |  155 +
 .../htdocs/MobylePortal/help/stepbystep.html       |   86 +
 .../htdocs/MobylePortal/html/announcement.txt      |    0
 .../htdocs/MobylePortal/images/action_save.gif     |  Bin 0 -> 279 bytes
 Src/Portal/htdocs/MobylePortal/images/add_stat.gif |  Bin 0 -> 94 bytes
 .../images/application_side_expand.png             |  Bin 0 -> 581 bytes
 .../htdocs/MobylePortal/images/arrow_down.gif      |  Bin 0 -> 131 bytes
 .../htdocs/MobylePortal/images/arrow_left.png      |  Bin 0 -> 1142 bytes
 .../htdocs/MobylePortal/images/arrow_rightup.png   |  Bin 0 -> 793 bytes
 .../htdocs/MobylePortal/images/asterisk_orange.png |  Bin 0 -> 760 bytes
 .../htdocs/MobylePortal/images/bullet_orange.gif   |  Bin 0 -> 178 bytes
 .../MobylePortal/images/bullet_toggle_minus.png    |  Bin 0 -> 207 bytes
 .../MobylePortal/images/bullet_toggle_plus.png     |  Bin 0 -> 209 bytes
 Src/Portal/htdocs/MobylePortal/images/cross.gif    |  Bin 0 -> 545 bytes
 Src/Portal/htdocs/MobylePortal/images/cross.png    |  Bin 0 -> 655 bytes
 Src/Portal/htdocs/MobylePortal/images/del_stat.gif |  Bin 0 -> 94 bytes
 .../htdocs/MobylePortal/images/delete_obj.gif      |  Bin 0 -> 351 bytes
 .../htdocs/MobylePortal/images/exclamation.gif     |  Bin 0 -> 588 bytes
 .../htdocs/MobylePortal/images/exportapp_wiz.gif   |  Bin 0 -> 349 bytes
 .../htdocs/MobylePortal/images/hourglass.gif       |  Bin 0 -> 603 bytes
 Src/Portal/htdocs/MobylePortal/images/loading.gif  |  Bin 0 -> 1787 bytes
 .../htdocs/MobylePortal/images/openid/aol.ico      |  Bin 0 -> 2862 bytes
 .../htdocs/MobylePortal/images/openid/blogger.ico  |  Bin 0 -> 3638 bytes
 .../htdocs/MobylePortal/images/openid/claimid.ico  |  Bin 0 -> 3638 bytes
 .../htdocs/MobylePortal/images/openid/flickr.ico   |  Bin 0 -> 1150 bytes
 .../htdocs/MobylePortal/images/openid/google.ico   |  Bin 0 -> 1150 bytes
 .../htdocs/MobylePortal/images/openid/myopenid.ico |  Bin 0 -> 1406 bytes
 .../images/openid/openid_small_logo.png            |  Bin 0 -> 916 bytes
 .../htdocs/MobylePortal/images/openid/orange.ico   |  Bin 0 -> 318 bytes
 .../MobylePortal/images/openid/technorati.ico      |  Bin 0 -> 2294 bytes
 .../htdocs/MobylePortal/images/openid/verisign.ico |  Bin 0 -> 5430 bytes
 .../htdocs/MobylePortal/images/openid/yahoo.ico    |  Bin 0 -> 318 bytes
 .../htdocs/MobylePortal/images/processing.gif      |  Bin 0 -> 10819 bytes
 Src/Portal/htdocs/MobylePortal/images/rem_co.gif   |  Bin 0 -> 350 bytes
 .../MobylePortal/images/session_captcha_wait.png   |  Bin 0 -> 1184 bytes
 Src/Portal/htdocs/MobylePortal/images/tick.gif     |  Bin 0 -> 519 bytes
 Src/Portal/htdocs/MobylePortal/images/tr.gif       |  Bin 0 -> 57 bytes
 Src/Portal/htdocs/MobylePortal/images/tr_open.gif  |  Bin 0 -> 56 bytes
 Src/Portal/htdocs/MobylePortal/js/blank.html       |   39 +
 Src/Portal/htdocs/MobylePortal/js/controls.js      |  965 +++++
 Src/Portal/htdocs/MobylePortal/js/mobyle.js        | 3365 +++++++++++++++
 Src/Portal/htdocs/MobylePortal/js/mobyle_ga.js     |   16 +
 Src/Portal/htdocs/MobylePortal/js/openid/openid.js |   44 +
 Src/Portal/htdocs/MobylePortal/js/rsh.js           |  780 ++++
 Src/Portal/htdocs/MobylePortal/xsl/annotate.xsl    |  186 +
 Src/Portal/htdocs/MobylePortal/xsl/atom.xsl        |   74 +
 Src/Portal/htdocs/MobylePortal/xsl/bookmark.xsl    |   66 +
 .../htdocs/MobylePortal/xsl/clean_import.xsl       |   18 +
 Src/Portal/htdocs/MobylePortal/xsl/form.xsl        |   84 +
 Src/Portal/htdocs/MobylePortal/xsl/form_graph.xsl  |   15 +
 .../htdocs/MobylePortal/xsl/graphviz_simplify.xsl  |   63 +
 Src/Portal/htdocs/MobylePortal/xsl/ident.xsl       |  194 +
 Src/Portal/htdocs/MobylePortal/xsl/job.xsl         |  459 ++
 Src/Portal/htdocs/MobylePortal/xsl/job_graph.xsl   |   52 +
 Src/Portal/htdocs/MobylePortal/xsl/layout.xsl      |  133 +
 Src/Portal/htdocs/MobylePortal/xsl/localize.xsl    |   36 +
 Src/Portal/htdocs/MobylePortal/xsl/parameters.xsl  |  135 +
 Src/Portal/htdocs/MobylePortal/xsl/remove_ns.xsl   |   15 +
 Src/Portal/htdocs/MobylePortal/xsl/results.xsl     |   79 +
 .../htdocs/MobylePortal/xsl/schema_flatten.xsl     |   24 +
 Src/Portal/htdocs/MobylePortal/xsl/tutorial.xsl    |   63 +
 Src/Portal/htdocs/MobylePortal/xsl/viewer.xsl      |  112 +
 Tools/README                                       |  268 ++
 Tools/job_updater.py                               |  268 ++
 Tools/mob_log_rotate                               |  109 +
 Tools/mob_stat                                     |  234 +
 Tools/mobclean                                     |  464 ++
 Tools/mobdeploy                                    | 1134 +++++
 Tools/mobemail                                     |  143 +
 Tools/mobjobw                                      |   86 +
 Tools/mobkill                                      |  134 +
 Tools/mobpasswd                                    |  261 ++
 Tools/mobshell                                     |  103 +
 Tools/mobtypes                                     |  374 ++
 Tools/mobvalid                                     |  101 +
 Tools/mobversion                                   |   33 +
 Tools/session_updater.py                           |  342 ++
 Tools/setsid.c                                     |   12 +
 Tools/validation/iso_abstract_expand.xsl           |  295 ++
 Tools/validation/iso_dsdl_include.xsl              |  989 +++++
 .../iso_schematron_skeleton_for_saxon.xsl          | 1884 ++++++++
 .../iso_schematron_skeleton_for_xslt1.xsl          | 1749 ++++++++
 Tools/validation/iso_svrl.xsl                      |  583 +++
 Tools/validation/remove_base.xsl                   |   30 +
 UPDATE                                             |   71 +
 setup.py                                           |  619 +++
 357 files changed, 60703 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..dbdb83e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+Mobyle authors :
+Bertrand Néron		<bneron at pasteur.fr>
+Hervé Ménager		<hmenager at pasteur.fr>
+Nicolas Joly		<njoly at pasteur.fr>
+Corinne Maufrais	<maufrais at pasteur.fr>
+Sandrine Larroudé	<slarroud at pasteur.fr>
+Pierre Tufféry		<pierre.tuffery at univ-paris-diderot.fr>
+Catherine Letondal	<letondal at pasteur.fr>
+Olivier Sallou    <olivier.sallou at irisa.fr>
\ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 2 of the License, or
+    (at your option) any later version.
+
+    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, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Doc/Admin/configuration_guide.pdf b/Doc/Admin/configuration_guide.pdf
new file mode 100644
index 0000000..7c577e9
Binary files /dev/null and b/Doc/Admin/configuration_guide.pdf differ
diff --git a/Doc/Admin/configuration_guide.tex b/Doc/Admin/configuration_guide.tex
new file mode 100644
index 0000000..4c7c936
--- /dev/null
+++ b/Doc/Admin/configuration_guide.tex
@@ -0,0 +1,800 @@
+\documentclass[11pt,a4paper]{report} 
+\usepackage[latin1]{inputenc} 
+\usepackage[american]{babel}
+\usepackage{verbatim}
+\usepackage{url}
+% %pour creer des lien vers des pages web
+\usepackage[pdftex,colorlinks=true, urlcolor=cyan,pdfstartview=FitH]{hyperref}
+%%pour inserer du code
+\usepackage{listings}\usepackage{ulem}
+
+\setcounter{secnumdepth}{4}
+\setcounter{tocdepth}{6}
+
+\lstset{ %
+         language=XML,
+         showspaces=false,
+         showstringspaces=false,
+         showtabs=false,
+         basicstyle=\footnotesize,
+         }
+
+%% pour afficher des images avec pdflatex utiliser le package pdftex
+\usepackage{graphicx}
+
+%% MARGES
+\oddsidemargin -1cm
+\marginparwidth 0cm \textwidth 18.5cm
+\topmargin -0.5cm
+\headheight -0.8cm \headsep 0cm
+%%\footskip 0cm
+\textheight 26.8cm
+\pagestyle{plain}
+
+\date{ Mobyle 1.0 }
+
+\title{Mobyle Configuration Guide}
+
+\begin{document}
+
+\maketitle
+
+\tableofcontents
+
+\pagebreak 
+
+\chapter{General Configuration}
+The Mobyle configuration is set in the file \textbf{\$MOBYLEHOME/Local/Config/Config.py}. It is written in Python, so be very careful to validate the syntax of your file.
+
+\section{Link Mobyle with a web server}
+Three values have to be carefully updated in the configuration to integrate it correctly with your web server.
+\textbf{ROOT\_URL}: the root url and port of the Mobyle server. (mandatory)\\
+\textbf{HTDOCS\_PREFIX}: the extra path to the htdocs Mobyle directory. (mandatory)\\
+\textbf{CGI\_PREFIX}: the extra path to the cgi-bin Mobyle directory. (mandatory)\\
+
+You can potentially use any web server, just as long as it is multithreaded and CGI-enabled. Following are two configuration examples that use Apache 2, one that hosts Mobyle in a virtual host, the other in a subdirectory.
+
+\subsection{Example of Mobyle configuration using a virtual host}
+
+\subsubsection{Apache configuration}
+\begin{verbatim}
+<VirtualHost 192.168.0.3:83>
+        ServerName server.domain.ext:83
+        ScriptAlias "/cgi-bin" "/var/www/localhost/cgi-bin/mobyle/"
+        DocumentRoot "/var/www/localhost/htdocs/mobyle/"
+        DirectoryIndex index.html index.xml
+        ErrorLog "/var/log/apache2/mobyle_error_log"
+        TransferLog "/var/log/apache2/mobyle_acces_log"
+</VirtualHost>
+\end{verbatim}
+
+\subsubsection{Mobyle installation}
+\begin{verbatim}
+python setup.py install --install-htdocs=/var/www/localhost/htdocs/mobyle/ \
+                                     --install-cgis=/var/www/localhost/cgi-bin/mobyle/\
+                                     --install-core=/any/where
+\end{verbatim}
+
+\subsubsection{Mobyle configuration}
+
+\begin{verbatim}
+ROOT_URL = ``http://server.domain.ext:83''
+HTDOCS_PREFIX = ``''
+CGI_PREFIX = ``cgi-bin/mobyle''
+\end{verbatim}
+
+\subsection{Example of Mobyle configuration using a web subdirectory}
+
+\subsubsection{Apache configuration}
+\begin{verbatim}
+ServerName server.domain.ext
+ScriptAlias "/cgi-bin" "/var/www/localhost/cgi-bin/"
+DocumentRoot "/var/www/localhost/htdocs/"
+DirectoryIndex index.html index.xml
+\end{verbatim}
+
+\subsubsection{Mobyle installation}
+\begin{verbatim}
+python setup.py install --install-htdocs=/var/www/localhost/htdocs/mobyle/ \
+                                     --install-cgis=/var/www/localhost/cgi-bin/mobyle/\
+                                     --install-core=/any/where
+\end{verbatim}
+
+\subsubsection{Mobyle configuration}
+\begin{verbatim}
+ROOT_URL = ``http:mobyle.mydomain.ext/mobyle''
+HTDOCS_PREFIX = ``mobyle''
+CGI_PREFIX = ``cgi-bin/mobyle''
+\end{verbatim}
+
+\subsection{Additional web server configuration}
+
+\subsubsection{Specific MIME types}
+
+In order to be able to upload/visualize some specific data types, such
+as PDB files, you need to overload their mime type. The default is the
+\texttt{chemical/x-pdb} in \texttt{/etc/mime.types}, but this mime type
+causes most browsers to try opening these files with an external tool.
+
+As a workaround, you can force these files to be sent as ``plain text''
+files by declaring them as \texttt{text/plain} files. In Apache, for instance,
+this is done by adding this directive in the \texttt{/etc/apache2/mods-available/mime.conf}
+Apache configuration file:
+
+\begin{verbatim}
+"AddType text/plain .pdb".
+\end{verbatim}
+
+\subsubsection{Download button}
+
+The "save" button that is available in job results and data bookmarks can
+ automatically open a "save as" prompt (instead of opening the file in the browser),
+ given that you use a little trick to the web server configuration, that modifies the 
+HTTP response headers.
+
+For instance, in Apache, you can add these few lines in an \texttt{.htaccess} file (if your general Apache
+configuration permits it ) in the directory where the jobs are stored (--install-hdocs value + '/data/jobs' by default).
+
+\begin{verbatim}
+RewriteEngine on
+RewriteCond    %{REQUEST_URI}	 ^HTDOCS_PREFIX/data/jobs(\.*)
+RewriteCond %{QUERY_STRING}	^save$
+RewriteRule   (.*)/([^/]+)$    $1/$2 [E=SAVEDFILENAME:$2]
+Header set Content-Disposition "attachment; filename=\"%{SAVEDFILENAME}e\"" env=SAVEDFILENAME
+\end{verbatim}
+
+\subsubsection{Directory indexes}
+
+As the jobs are stored in web-accessible subtrees, data confidentiality relies on the use of
+a unique key as the job identifier. Thus, it is strongly recommended to forbid the directory indexes
+ of the \texttt{"[--install-htdocs]/data/jobs"} subtree. You can do it in the Apache general configuration
+ or in \texttt{.htaccess} files with the directive \texttt{Options -Indexes}.
+
+\subsubsection{Hidden files}
+
+In job directories, Mobyle uses some "hidden files" for administration purposes.
+- the unix command generated (the \texttt{.command} file)
+- some information relative to the job (the \texttt{.admin} file)
+- some internal informations to Mobyle execution (the \texttt{.forChild.dump} file or the \texttt{ADMINDIR} subtree)
+
+These files are not intended to be viewed by the users, and it is thus highly recommended to mask them. 
+You can mask them with a rewrite rule placed in an \texttt{.htaccess} file in the \texttt{[--install-htdocs]/data/jobs} subtree.
+  
+\begin{verbatim}
+# Do not show hidden files content
+RewriteCond     %{REQUEST_URI}  /\. [OR]
+RewriteCond     %{REQUEST_URI}  ADMINDIR
+RewriteRule     .*              - [F,L]
+\end{verbatim}
+
+\section{Mail}
+
+Mobyle occasionally sends emails to users, to:
+\begin{itemize}
+ \item validate their email adresses when creating ``authenticated'' accounts,
+ \item send job status or help request notifications. 
+\end{itemize}
+
+It is mandatory to configure correctly this mail to be able to run Mobyle. The values are the following:
+
+\textbf{MAILHOST}: the mail transfert agent used by Mobyle to send an email. (mandatory)\\
+\textbf{MAXMAILSIZE}:  if the results size is over MAXMAILSIZE, job results are not sent. Rather, only a notification of the end 
+of his job is send to the user (containing a link to download it), in bytes. (default=2097152 (2Mo))\\
+\textbf{MAINTAINER}: the emails list which will receive alert emails when problems occur in Mobyle. (mandatory)\\
+\textbf{HELP}: the email adress that receives help requests from users. (mandatory)\\
+\textbf{SENDER}: the email address representing Mobyle which sends long job notifications, results etc\ldots (for further
+details, see the MailTemplate section)\\ 
+
+
+\section{Execution system}
+Mobyle can execute jobs either locally or on the SGE or PBS Distributed Resource Management systems. 
+The execution system has been rewritten to be highly flexible, so its configuration
+has completely changed. Please refer to the execution\_system documentation to learn how to configure it.  
+
+\section{Module}(\textit{new in 1.5})
+Mobyle is abble to execute programs which are manage via Module (see \url{http://modules.sourceforge.net/}).
+To enable Module in Mobyle you must define two variables in Config.py.
+\begin{itemize}
+	\item MODULE\_INIT: The location of the master Modules package file, the module command initialization shell script.
+	\item MODULE\_LOAD: The module load to perform before to execute the command line
+\end{itemize}
+MODULE\_LOAD is a dictionary wher the keys are the service name and the values the load module command to do before to execute the command.
+The MODULE\_LOAD dictionnary accpet a special key wich DEFAULT. when a service name is not find in MODULE\_LOAD the DEFAULT entry is used.
+the MODULE\_LOAD value can be either a string or None. If the value is None the module will not be used. If it's a string it could be a python string template.
+Mobyle provide 4 variables for interpolation of the template.
+\begin{itemize}
+  \item PACK\_NAME: The name of the package if it's defined in the xml service description otherwise the program name is used.
+  \item PACK\_VERS: The version of the Package if it's defined in the xml service description otherwise the program version is used.
+  \item PROG\_NAME: The name of the program as defined in the xml service description.
+  \item PROG\_VERS: The version of the program as defined in the xml service description.
+\end{itemize}
+
+for instance if we have 3 services as following:
+\begin{itemize}
+  \item service1: in the xml the name is service1 and version = 2.0
+  \item service2: in the xml the name is service2 and version = 2.11-beta
+  \item service3: in the xml the name is service3 and version = 1.2.7
+\end{itemize}
+
+and the configuration
+\begin{verbatim}
+MODULE_INIT = "/path/to/Modules/init/sh"
+MODULE_LOAD = { 'service1' : "module load dependency/1 && module load %(PROG_NAME)s/%(PROG_VERS)s",
+                'service2' : None ,
+                'DEFAULT': "module load %(PACK_NAME)s/%(PACK_VERS)s" }
+\end{verbatim}
+
+service2 will not use module system for it's execution.
+Before to execute service1 Mobyle will execute 2 module load instructions: 
+module load dependency/1 then module load service1/2.0 
+Before to execute service3 a module load service3/1.2.7 will be performed.
+
+Warning: If MODULE\_LOAD is defined but not MODULE\_INIT a ConfigError is raised (the portal will not displayed).
+
+
+\section{Logging}
+\textbf{LOGDIR} is the directory where are located the different file loggers. Beware, given that the default directory is \texttt{/dev/null}, tracing potential problems can be hard if you do not set this value.
+The most important log files in this directory are:
+\begin{itemize}
+  \item \texttt{access\_log}: to log the launched jobs,
+  \item \texttt{error\_log}: to log the errors that occur on the Mobyle server,
+  \item \texttt{build\_log}: to log all the steps leading to the construction of the command line (when debug $>=$ 2) 
+\end{itemize}
+If \textbf{ACCOUNTING} is set to True an \texttt{account\_log} will be created to log some statistics about jobs.
+This log file can be used to fine-tune the execution system.\\
+
+\section{Data converter management}
+
+You can define with the \textbf{DATACONVERTER} variable which converter(s) you want to use to
+manage the format conversion and verification properties.\\
+For each Datatype, you can provide an ordered list of converter(s) as follow:
+\begin{verbatim}
+DATACONVERTER = {
+				'Datatype1': [ converter1_class('/path/to/bin/converter1'),
+							  converter2_class('/path/to/bin/converter2')     ] , 
+				'Datatype2': [ converter3_class('/path/to/bin/converter3') ] 
+                }
+\end{verbatim}
+
+Mobyle includes by default two ready-to-use converter classes: \emph{squizz\_alignment} and
+\emph{squizz\_sequence} to manage respectively the Alignment and Sequence formats with the \texttt{squizz} program. 
+The use of \texttt{squizz} is highly recommended in production environments.
+
+The existing converter classes are located in \texttt{\$MOBYLEHOME/Src/Mobyle/Converter}.\\
+
+Example to use the provided converters:\\
+\begin{verbatim}
+DATACONVERTER = {
+                'Sequence': [ squizz_sequence('/path/to/bin/squizz') ] , 
+                'Alignment': [ squizz_alignment('/path/to/bin/squizz') ] 
+                }
+\end{verbatim}
+
+
+\section{Debug}
+\textbf{DEBUG} allows to set the default debug level in Mobyle.
+The default debug level is 0. Beware, only debug levels 0 and 3 allow the execution of a job.
+
+\begin{description}
+  \item[\textit{Level 0}] used in production:
+  \begin{itemize}
+    \item the command line is built
+    \item the build log is NOT filled
+    \item the job is executed
+  \end{itemize}
+  \item[\textit{Level 1}] to test a program definition XML (e.g. python syntax in code, precond\ldots):
+  \begin{itemize}
+    \item the command line is built
+    \item the build log is NOT filled
+    \item the job is NOT executed
+  \end{itemize}  
+  \item[\textit{Level 2}] to debug a program definition XML:
+  \begin{itemize}
+    \item the command line is built
+    \item the build log is filled
+    \item the job is NOT executed
+  \end{itemize} 
+  \item[\textit{Level 3}] to test the program definition XML, the job execution and its results:
+  \begin{itemize}
+    \item the command line is built
+    \item the build log is filled
+    \item the job is executed
+  \end{itemize}     
+\end{description}
+
+To test/debug a program or its interface, the debug level can be overloaded for this service using the feature \textbf{PARTICULAR\_DEBUG}
+in conjunction with \textbf{AUTHORIZED\_SERVICES}, which allows to define restricted access by IP address 
+(see \ref{service_access_restriction}). To test the execution and results, set the \textbf{PARTICULAR\_DEBUG} 
+to 2 or 3 for the program in question and restrict the access to this program to your own machine. This way, you will
+be the only one who can access the interface in the portal, and the \texttt{build\_log} will register all steps of 
+the command line generation, which can be useful to debug an interface. 
+
+In the following example the general debug level is 0 except for \texttt{clustalw} which has a debug level set to 2.
+\begin{verbatim}
+DEBUG= 0
+PARTICULAR_DEBUG={ 'clustalw' : 2 }
+\end{verbatim} 
+
+\noindent \large \textbf{Warnings}:
+\normalsize
+\begin{enumerate}
+ \item during the debugging phase, don't forget to reinstall the XML file after each modification with \texttt{mobdeploy}:
+\begin{verbatim}
+ python mobdeploy.py -s local -p myNewProgram deploy
+\end{verbatim}
+  \item once the debugging phase has been completed, don't forget to remove the restricted access and set the debug level to 0.
+\end{enumerate}
+
+\section{Binary path}
+\textbf{BINARY\_PATH} is a list of strings representing the paths where the program binaries can be found.
+Each element of the list must be a valid path. The order of the elements is kept to build the final path.
+These paths are added before the canonical PATH. By default, it is empty.
+
+Example: here we add \texttt{/usr/local/bin} to \texttt{\$PATH}:
+\begin{verbatim}
+BINARY_PATH = ['/usr/local/bin'] 
+\end{verbatim} 
+
+\section{Data banks}
+\textbf{DATABANKS\_CONFIG} describes the locally available databanks to fetch entries from, using the "databox". It is a dictionary of 
+dictionaries, such that:
+\begin{itemize}
+ \item the \textbf{key} is the \textbf{name of the bank}, and the value is a dictionary where
+ \item \textbf{'dataType'} is the Mobyle dataType of the data stored in the bank,
+ \item \textbf{'bioTypes'} is the list of Mobyle bioTypes for the data stored in the bank,
+ \item \textbf{'label'} is the label of the bank, as shown to the user in the portal,
+ \item and \textbf{'command'} is a string template that describes how to generate a command line that will retrieve the bank entry(ies).
+The \textbf{db} key is the key of the bank (if it is necessary to specify it in the command line), and \textbf{id} key is the requested value.
+\end{itemize}
+
+Example:
+\begin{verbatim} 
+DATABANKS_CONFIG = { 'WGS' : {  'dataType' : 'Sequence', 
+                                'bioTypes' : [ 'Nucleic' ],
+                                'label' : 'Genbank - Whole Genome Shotgun',
+                                'command' : [ 'golden', '%(db)s:%(id)s' ] },
+                      'PDB' : { 'dataType' : '3DStructure', 
+                                'bioTypes' : [ 'Protein' ],
+                                'label' : 'Protein Data Bank',
+                                'command' : [ 'PDBGet.py', '%(id)s' ] } 
+                    }
+\end{verbatim}
+
+\section{Statistics}
+\textbf{GACODE} : Google Analytics. Setting this code will automatically configure Mobyle tracking on Google Analytics. 
+For more information about how to set up a Google Analytics account, please refer to \url{http://www.google.com/analytics/}
+
+\section{Portal and Welcome page configuration}
+
+\subsection{Simple forms}
+The forms can be displayed in two modes, ``simple'' and ``advanced''. Simple forms display only the parameters which are annotated as
+with the ``issimple'' attribute. If the \textbf{SIMPLE\_FORMS} configuration rule is set to True (it is by default set to False), then upon display each form is tested for the
+presence of ``simple'' parameters. If simple parameters are defined, by default only these ones will be displayed in the portal. The ``advanced''
+parameters can then be displayed with a toggle (``simple form''/``advanced form'') located at the top of the form. If some advanced parameters 
+have a modified value in the form, they will not be hidden, even when the form is switched to the ``simple'' mode.
+
+\subsection{Portal appearance customization}
+You can customize the appearance of the portal multiple ways:
+\begin{itemize}
+ \item \textbf{CUSTOM\_PORTAL\_HEADER} enables you to replace the top left "banner" zone which by default contains a "Mobyle" 
+ title with any HTML content.
+ \item \textbf{CUSTOM\_PORTAL\_FOOTER} enables you to add a footer zone below the portal which contains any HTML content.
+\end{itemize}
+
+Additionally, the CSS rules of the Mobyle portal can be enriched/overriden by modifying the \texttt{portal/css/local.css} file 
+located in the htdocs section of the mobyle portal.
+
+\subsection{Welcome page configuration}
+The welcome page of the Mobyle portal (i.e., the contents of the "welcome" tab) can also be customized, to display the contents of an "external" HTML page or an ATOM news feed.
+
+\textbf{WELCOME\_CONFIG} is a dictionary with 2 entries:
+\begin{itemize}
+  \item \textbf{'url'} : the url of the document to include in the portal
+  welcome page,
+  \item \textbf{'format'}: with 2 possible values:\begin{itemize}
+        \item \textbf{'html'} if the url is the location of an html page,
+        \item \textbf{'atom'} if the url is the location of an ATOM feed.
+        \end{itemize}
+\end{itemize}
+An example file, \texttt{news\_ex.atom}, is available in the Example/Local directory.
+
+
+
+\section{User authentication and management}
+
+User actions in the portal, such as job submissions, can be configured to require some informations such as a valid email, to be able to contact the
+ user if necessary.
+
+\textbf{OPT\_EMAIL}: allow users to run jobs without  having to provide an email. ( default = False )\\
+\textbf{PARTICULAR\_OPT\_EMAIL}: per-program override of the previous option.\\
+
+Example: the email is mandatory to run any program, except for golden (a very short program).
+\begin{verbatim}
+OPT_EMAIL = False
+PARTICULAR_OPT_EMAIL = { 'golden' : True }
+\end{verbatim}
+
+\subsection{General e-mail validation configuration}
+
+User-provided emails can be validated beyond syntax check by performing a DNS resolution on the domain part of the user email, to ensure that a valid corresponding mail server exists, in order to limit fake user email addresses. This is done by setting \textbf{DNS\_RESOLVER} to True. In this case the \textbf{dnspython} library must be installed. By default, domain name resolution is not performed.
+
+\subsection{User accounts management}
+
+Each user has a space to store his data. This space can be temporary (during the working session) or persistent. The temporary space is created when a user connects to the portal and is accessible during the work session. These temporary accounts are refered to in the configuration as \textbf{anonymous sessions}. Persistent accounts are refered to as authenticated sessions, as they should require a higher level of validation for user informations (such as emails). Such accounts allow the [...]
+
+Administrators can enable/disable the use of anonymous and authenticated sessions by setting the \textbf{ANONYMOUS\_SESSION} and \textbf{AUTHENTICATED\_SESSION} values.
+
+\textbf{ANONYMOUS\_SESSION} can take 3 values :
+\begin{itemize} 
+\item 'no'      : anonymous sessions are not allowed,
+\item 'yes'     : anonymous sessions are allowed, without any verification,
+\item 'captcha' : anonymous sessions are allowed, but with a captcha challenge (default value).
+\end{itemize} 
+
+\textbf{AUTHENTICATED\_SESSION} can also take 3 values :
+\begin{itemize}
+  \item 'no'    : the authenticated sessions are not allowed,
+  \item 'yes'   : the authenticated sessions are allowed and activated, without any restriction,
+  \item 'email' : the authenticated sessions are allowed but an email confirmation is needed to activate it (default value).
+\end{itemize}
+
+User workspaces are allocated a given amount of disk space. This disk space stores mainly their data bookmarks, which they can reuse across multiple tools. Note that this disk space \textbf{does not} include the job files, which are stored separately, and only ``linked'' from the user account. This size can be set using \textbf{SESSIONLIMIT}, which has a default value of 50Mib.
+
+\textbf{OPENID} : activate OpenID authentication. 
+When the OpenID identification system is enabled, users can connect without any registration: their authenticated session is automatically created and activated (default=False).\\
+
+\subsection{Jobs management}
+
+To limit disk space problems generated by excessively large jobs, or by programming bugs (e.g., execution loops which output ever-growing files), the \textbf{FILELIMIT} value allows to define the maximum size for a file generated by a job \textbf{if and only if the job is executed using the OS batch system, ``SYS``}. If you use a distributed resource management system such as SGE or Torque/PBS, please refer to its documentation.
+
+Jobs can be configured to have a limited ``storage'' time, meaning that once they are finished, they are kept on the server for a limited number of days before being removed. Jobs should be removed using the mobclean tool (see Tools/mobclean and Tools/README), which can be started periodically from a cron. To configure this ``cleaning'' tasks, please set the \textbf{RESULT\_REMAIN} value which stores the lifetime of a finished job in days (default value=10 days).
+
+Users are notified by email that their job is finished only if its execution time is superior to a given limit. This limit is set using \textbf{EMAIL\_DELAY} in seconds, and the default value is 20s.
+
+\section{Portal settings}
+
+The Mobyle portal includes a javascript-based polling mechanism which regularly updates the user workspace by getting its contents from the server. The frequency of these requests can be set with \textbf{REFRESH\_FREQUENCY}, in seconds. Its default value is 240 seconds, meaning the automatic workspace refresh is launched every 4 minutes.
+User data (parameter values or job results) are displayed in textarea elements in the portal (if text-based). However, this preview/editing mechanism can be problematic if the data file to be displayed is too large. Hence, the portal does not display the contents of a data file if its size is above a given limit. The size limit can be set with \textbf{PREVIEW\_DATA\_LIMIT}, and the default size is 1048576 octets (1 Mib).
+
+\section{Services management}
+
+\subsection{Deployment}\label{structure}
+Before deployment, the XML descriptions of the services used in Mobyle are located in two directories: 
+\begin{itemize}
+ \item \texttt{\$MOBYLEHOME/Services/} intended to store service definitions provided by Mobyle maintainers,
+ \item \texttt{\$MOBYLEHOME/Local/Services/} intended to store your own service definitions.
+\end{itemize}
+The use of two directories allows to maintain a list of service definitions from an external source 
+located in \texttt{\$MOBYLEHOME/Services}, without any risk to loose local modifications located  in \texttt{\$MOBYLEHOME/Local/Services}. 
+Beware, if there are 2 services,
+one in \texttt{\$MOBYLEHOME/Local/Services} and the other in \texttt{\$MOBYLEHOME/Services}, 
+with the same name, \underline{only the description in \texttt{\$MOBYLEHOME/Local/Services} will be deployed}.
+
+\texttt{\$MOBYLEHOME/Services/} contains 4 subdirectories: 
+\begin{itemize}
+\item \texttt{Programs}: to store program definitions,
+\item \texttt{Workflows}: to store workflow definitions,
+\item \texttt{Viewers}: to store viewer definitions and \underline{dependencies}. 
+\item \texttt{Entities}: to store pieces of XML shared by several XML files, such as package information. (see \texttt{how\_to\_write\_a\_xml.pdf})
+\end{itemize}
+
+\texttt{\$MOBYLEHOME/Local/Services/} contains 5 subdirectories: 
+\begin{itemize}
+\item \texttt{Programs}: to store program definitions
+\item \texttt{Workflows}: to store workflow definitions
+\item \texttt{Viewers}: to store viewer definitions and dependencies
+\item \texttt{Entities}: to store pieces of XML shared by several XML, like packages or nucleic databanks available
+\item \texttt{Env}: to store some environment variables required by some programs (BLASTDB \ldots)
+\end{itemize}
+
+\subsubsection{Configuration}
+There are 2 kinds of services:
+\begin{description}
+ \item[local services] corresponding to services executed on the local Mobyle server
+ \item[imported services] corresponding to services appearing on the local Mobyle portal but executed on another Mobyle server
+\end{description}
+
+Whatever the service is, it must be deployed to be invoked in the Mobyle portal. 
+This deployment operation will:
+\begin{enumerate}
+  \item get the XML files (on your computer or the distant server for
+  imported services) and resolve the entities
+  \item validate of the service descriptions
+  \item transform the descriptions to make them ready to use by Mobyle (to lower the
+  burden of the portal)
+  \item copy the descriptions in the HTTP-accessible area for web publication
+\end{enumerate}
+
+The \texttt{mobdeploy} script located in \texttt{\$MOBYLEHOME/Tools} allows to deploy or undeploy a service. 
+It needs the Mobyle configuration file \texttt{\$MOBYLEHOME/Local/Config/Config.py} to determine:
+\begin{itemize}
+  \item which services to deploy,
+  \item which services are imported ones.
+\end{itemize}
+
+\subsubsection{Local services}\label{local_services}
+The local service descriptions are stored in 2 directories (as described in
+\ref{structure}). However Mobyle can be configured to deploy only a subset of these
+descriptions by using the following 2 directives:
+\begin{description}
+ \item[LOCAL\_DEPLOY\_INCLUDE] indicating which descriptions must be deployed
+ \item[LOCAL\_DEPLOY\_EXCLUDE] indicating which descriptions must \underline{not} be deployed
+\end{description}
+These 2 directives are python dictionaries with the 3
+following keys: 'programs', 'workflows','viewers' and the values are lists of
+corresponding services to deploy or not. Jokers can be used to defined a set of
+services. By default all definitions found are deployed because of the use
+of the wildcard \texttt{*} in each list of each entry of the
+\textbf{LOCAL\_DEPLOY\_INCLUDE}  dictionary.
+
+Beware, Mobyle evaluates \textbf{LOCAL\_DEPLOY\_INCLUDE} first meaning that if the same service
+appears in the two lists, it will be excluded because \underline{\textbf{LOCAL\_DEPLOY\_EXCLUDE}
+has the final say}. 
+
+\begin{lstlisting}
+LOCAL_DEPLOY_INCLUDE = { 'programs' : [ '*' ] , 
+                         'workflows': [ '*' ] ,
+                         'viewers'  : [ '*' ] 
+                       }
+
+LOCAL_DEPLOY_EXCLUDE = { 'programs' : [ '' ] ,
+                         'workflows': [ '' ] ,
+                         'viewers'  : [ '' ] 
+                       }
+\end{lstlisting}
+
+To illustrate how these two directives work, let's imagine that a portal
+proposes  \texttt{hmmalign}, \texttt{hmmconvert}, \texttt{hmmemit}, \texttt{hmmfetch} and \texttt{muscle} as
+programs. The definitions of \texttt{hmmalign}, \texttt{hmmconvert}, \texttt{hmmemit}, \texttt{hmmfetch}
+\texttt{hmmscan}, \texttt{hmmsim} and \texttt{hmmstat} are provided in the \texttt{pasteur-programs} package.
+\begin{lstlisting}
+LOCAL_DEPLOY_INCLUDE = { 'programs' : [ 'hmm*' , 'muscle' ] ,# will deploy all
+                                                             # programs beginning 
+                                                             # with hmm plus
+                                                             # muscle
+                          'workflows': [ '*' ] , 
+                          'viewers'  : [ '*' ] }
+
+LOCAL_DEPLOY_EXCLUDE = { 'programs' : [ 'hmms*' ] , # all programs beginning with
+                                                    # hmms will be removed from
+                                                    # the list of programs to
+                                                    # deploy so 'hmmscan',
+                                                    # 'hmmsim', 'hmmstat' won't
+                                                    # appear in the portal
+                         'workflows': [ '' ] , 
+                         'viewers'  : [ '' ]  }
+\end{lstlisting}
+
+
+\subsubsection{Imported services}
+Mobyle offers the possibility to import services executed on other portals. The detailed configuration is described in the MobyleNet (\ref{MobyleNet}) section.
+
+\subsubsection{mobdeploy usage}
+
+Before running \texttt{mobdeploy}, make sure that the current user (e.g. apache user) 
+has permissions to read and write the files belonging to the Mobyle user. 
+\begin{verbatim}
+Usage: mobdeploy [options] cmd
+\end{verbatim}
+Here are the available commands:
+\begin{itemize}
+   \item \textbf{deploy}: deploy services available options [-s
+           --server, -p --programs , -w --workflows , -v --viewers , -f --force , -V --verbose] 
+   \item \textbf{clean}: clean the repository  available options [-s
+           --server, -p --programs , -w --workflows , -v --viewers , -f --force , -V --verbose] 
+    \item \textbf{index}: generate indexes      available options [ -V
+    --verbose ]
+\end{itemize}
+And options:
+\begin{itemize} 
+   \item \texttt{-s --server} specifies the names (comma separated list) of the servers on which the command is applied.
+       The keyword \texttt{all} means all servers as defined in the \textbf{PORTALS} dictionary in \texttt{\$MOBYLEHOME/Local/Config/Config.py}
+   \item \texttt{-p --programs} specifies the names (comma separated list) of the programs the command is applied to.
+       The keyword \texttt{all} means all programs as defined in \texttt{\$MOBYLEHOME/Local/Config/Config.py}
+   \item \texttt{-w --workflows} specifies the names (comma separated list) of the workflows the command is applied to.
+       The keyword \texttt{all} means all workflows as defined in \texttt{\$MOBYLEHOME/Local/Config/Config.py}
+   \item \texttt{-v --viewers} specifies the names (comma separated list) of the viewers on which the command is applied.
+       The keyword \texttt{all} means all viewers as defined in \texttt{\$MOBYLEHOME/Local/Config/Config.py}
+   \item \texttt{-V --verbose} increases the verbosity. 3 levels are available: \texttt{warning}, \texttt{info} and \texttt{debug}. Default is \texttt{warning}.
+\end{itemize}
+                    
+\noindent After deployment, the services are stored in a directory hierarchy with the following schema:
+\begin{itemize}
+ \item the root directory is \texttt{\$MOBYLE\_ROOT\_URL/HTDOC\_PREFIX/data/services/}
+ \item inside the root directory, there is one directory for each server:
+ \begin{itemize}
+  \item your Mobyle server is called ``local''
+  \item all other directories are named as specified in the \textbf{PORTALS} dictionary set in the \texttt{\$MOBYLEHOME/Local/Config/Config.py}
+ \end{itemize}
+ \item in each server directory, there are the following subdirectories: programs, workflows and viewers (local server only) if some
+programs, workflows and viewers have respectively been deployed for this server. The descriptions of the corresponding services are inside those directories.
+\end{itemize}
+
+A full description of \texttt{mobdeploy} is available in \texttt{\$MOBYLEHOME/Tools/README}.                                  
+
+\subsection{Disabling services execution}
+It is sometimes necessary to disable jobs execution, for maintenance operations for instance. You can disable a given service by adding it in the \textbf{DISABLED\_SERVICES} list or disable all programs by setting \textbf{DISABLE\_ALL} to True to forbid the execution of any service. This mechanism prevents all new job submissions, portal-wide or service-specific, without any need to remove it from the portal. Conversely, new submissions can be enabled back by setting \textbf{DISABLE\_ALL [...]
+
+\subsection{Services access restriction}\label{service_access_restriction}
+The access to some services can be restricted based on their IP address. By default, all the programs available are usable by any user. If you need to filter this access, based on some license restrictions for example, you can use \textbf{AUTHORIZED\_SERVICES}. This dictionary defines the list of IPs and IP ``masks`` which can access specific services.
+
+Example: here toppred can be accessed by any machine with IP 125.234.60.18 or any IP with ``mask'' 125.234.60.*
+\begin{verbatim}
+AUTHORIZED_SERVICES = {
+   'http://myMobyle.mydomain.fr/data/programs/toppred.xml' : [ '125.234.60.18', '125.234.60.*']
+                       } 
+\end{verbatim}
+
+Example: you have a new version of neighor program and you want to test it so you restrict the access to your IP 125.234.60.18 and set the debug level of the program to 2 doing this:
+\begin{verbatim}
+PARTICULAR_DEBUG={ 'neighbor' : 2 }
+
+AUTHORIZED_SERVICES = {
+   'http://myMobyle.mydomain.fr/data/programs/neighbor.xml' : [ '125.234.60.18']
+                       } 
+\end{verbatim} 
+
+\section{\textit{MobyleNet} functionality\label{MobyleNet}}
+
+Mobyle includes a set of functionalities allowing the use of services available on remote Mobyle servers. \textit{MobyleNet}-ed servers are connected using HTTP, to enable remote job submissions or results retrievals for instance.
+
+\subsection{Motivation}
+
+Administrators of systems such as Mobyle portals are often asked to integrate new services very quickly, to answer the need of a user. The installation and maintenance of such services can be problematic (is this program compatible with my architecture? how does it work? will it be often accessed by my users?).
+The proposed solution is to provide Mobyle server administrators the possibility to share resources, exporting and importing services (programs, workflows) between servers: while users still access the same portal, the jobs corresponding to some programs can be remotely executed and stored.
+
+\subsection{Mobyle Network jobs}
+
+Mobyle Network job submissions are routed to an alternative endpoint, which does not require such an activation process in order to execute a job. This alternative endpoint, instead of checking for the existence of an activated session, only verifies that the invoked program is part of the list of exported services. Consequently, as opposed to the classical submission endpoint, all the submitted data values have to be provided, since no session is used to store data on the \textit{remote [...]
+
+\subsection{Program imports, job submission, status poll and results display}
+
+When a remote program is imported, its definition is downloaded from the \textit{remote server} to the \textit{portal server} to avoid network access overheads, and this definition is indexed during the same deployment process.
+
+Once this task has been performed by the administrator, the \textit{remote server} will not be queried until a user submits a job. When this happens, the user request is first processed by the \textit{portal server}, which performs an initial set of controls and may replace user workspace parameter references by their values, since the user workspace cannot be directly accessed by the \textit{remote server}. The request for job submission is then forwarded to this last server.
+
+Similarly to job submission, job status polls and job results display are forwarded by the portal to the \textit{remote server}, and the \textit{portal server} only handles the display of the data. The data corresponding to the remote job are not stored on the \textit{portal server}.
+
+\subsection{Configuration directives}
+
+The configuration of the MobyleNet functionality is set in the following values:
+\begin{itemize}
+ \item \textbf{PORTALS}: The Mobyle server from which you want to import services. To define a server, you need to specify these 6 fields: 
+  \begin{itemize}
+    \item name: the nickname you give to a Mobyle server. The services imported from this server will be labeled with 
+  this name in the Services panels of the portal.
+    \item url: the url of the portal (minus '/portal.py').
+    \item help: the email address to which help requests should be sent (should probably be the email set in the \textbf{HELP} configuration value of the corresponding server).
+    \item repository: the container url where the remote data (service descriptions, jobs) are stored.
+    \item services: the dictionary of services you want to import from this server, defining two lists, one with the 'programs' key, the other with the 'workflows' key.
+  \end{itemize}
+ \item \textbf{EXPORTED\_SERVICES}: The list of local services you want to enable from other portals.
+ \item \textbf{PORTAL\_NAME} is the name you want to give to your own portal, when seen from other MobyleNet nodes.
+\end{itemize}
+
+Example:
+\begin{verbatim}
+PORTALS={
+         'mobyleA': {
+            'url': 'http://mobyle.domain.ex/cgi-bin/MobylePortal',
+            'help' : 'user at domain.ex',
+            'repository': 'http://mobyle.domain.ex/data/programs/',
+            'programs': ['clustalw-multialign'],
+            'jobsBase': 'http://mobyle.domain.ex/data/jobs' 
+            } ,
+        'univB': {
+            'url': 'http://bio.univB.fr/cgi-bin/',
+            'help' : 'mobyle-help at univB.fr',
+            'repository': 'http://bio.univB.fr/Mobyle/programs',
+            'programs': ['muscle' , 'sspro' ],
+            'jobsBase': 'http://bio.univB.fr/Mobyle/Results' 
+            } 
+         }
+
+EXPORTED_SERVICES = [ 'protpars' , 'dnapars', 'neighbor' ]
+
+PORTAL_NAME = 'me'
+\end{verbatim}
+
+\subsection{Important remarks}
+
+\begin{itemize}
+ \item Any change to the list of imported programs needs to be followed by a re-deployment of the impacted programs, using the \texttt{mobdeploy} tool. 
+ \item Importing a program from a remote server is your responsibility: please ask for the permission of the server administrator. Please do not abuse either any remote resource, and try to comply with the terms of services that server administrators may specify.
+\end{itemize}
+
+
+\chapter{Mail Templates}
+
+Mobyle uses emails in several circumstances. You can tune the contents of each mail by modifying the
+corresponding template in \texttt{Local/mailTemplate.py}.
+
+A template is composed of two parts, the header and the body. The header must contain the fields ``From'' and ``Subject'', and can contain the fields ``Cc'', ``Bcc'', ``Reply-To'' and ``Organization''. For each template, you can use some variables which will be expanded at runtime. All available variables are documented in \texttt{Local/mail.template.py}. 
+
+\begin{itemize}
+  \item \textbf{CONFIRM\_SESSION} : if \textbf{AUTHENTICATED\_SESSION} is set to 'email', an email is sent to the user to confirm his
+  registration.
+  \item \textbf{LONG\_JOB\_NOTIFICATION} : when a job is longer than \textbf{EMAIL\_DELAY}, we consider this job as a ``long`` job, and
+  an email is sent to the user to notify him that his job keeps running.
+  \item \textbf{RESULTS\_FILES} : once a ''long'' job is finished, all results are sent to the user as a zip archive (if an email was
+  specified).
+  \item \textbf{RESULTS\_TOOBIG} : once a ''long'' job is finished, if the size of the results is bigger than \textbf{MAXMAILSIZE}, the
+  URL of the results is sent to the user.
+  \item \textbf{RESULTS\_NOTIFICATION} : once a ''long'' job is finished, if the compression of the results cannot be performed, the
+  URL of the results is sent to the user.
+  \item \textbf{HELP\_REQUEST} : whereas the previous emails are sent by ``Mobyle'' \textbf{SENDER} to the user, this email is sent by the
+  user to the email address set in the \textbf{HELP} directive. The user triggers this action by clicking on the ``ask for help'' button in the results page.
+  \item \textbf{HELP\_REQUEST\_RECEIPT}: this is a receipt sent to the user who asks for help, containing a copy of his request.
+  \item \textbf{NEW\_PASSWD}: text of the password modification notification sent by the \texttt{mobpasswd} tool.
+\end{itemize}
+
+\chapter{Local Policy}
+
+The \texttt{Local/Policy.py} module is used to overload internal Mobyle functions, to adapt them to your administration needs.
+
+\section{emailCheck}
+
+This function checks if the email is valid, according to the local rules. This function takes one argument, the \textit{email} address, and must return one of the following values:
+\begin{itemize}
+  \item \textbf{Mobyle.Net.EmailAddress.VALID}    : if the email is valid,
+  \item \textbf{Mobyle.Net.EmailAddress.INVALID}  : if the email is rejected,
+  \item \textbf{Mobyle.Net.EmailAddress.CONTINUE} : to continue further the email validation process.
+\end{itemize}
+
+This function is called after the syntax checking, black-list filter and, if enabled by your configuration, before the ``DNS'' checking.
+
+\section{authenticate}
+
+This function overloads the Mobyle authentication session method. You can provide here any custom authentication mechanism. This function takes 2 arguments, \textit{login} and \textit{password}, and must return one of the following values:
+\begin{itemize}
+  \item \textbf{Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE}: the method did not authenticate this
+  login/passwd. The fall back method must be applied.
+  \item \textbf{Mobyle.AuthenticatedSession.AuthenticatedSession.VALID}: the login/password has been authenticated and is valid.
+  \item \textbf{Mobyle.AuthenticatedSession.AuthenticatedSession.REJECT}: the login/password is invalid.    
+\end{itemize}
+
+\chapter{Black list}
+The file \texttt{Local/black\_list.py} allows to forbid job submission to ``non-desired'' users. In this file 2 structures are defined to store emails or IP addresses for which you want to forbid job submissions.
+
+\section{users}
+The first structure, \texttt{users}, is a list of emails which are not allowed to run a job on your Mobyle server.
+You can use * as a joker to black list a set of email address in one time
+Example:
+\begin{verbatim}
+users = [ 'foo at bar.com', 'spam at eggs.fr', '*@spam.com']
+\end{verbatim} 
+
+\section{host}
+The second structure, \texttt{host}, is a list of IP addresses from which users are not allowed to run a job on your Mobyle server.
+You can use * as a joker to black list a whole subnet.\
+% 
+Example:
+\begin{verbatim}
+host = [ '192.168.3.1' , '192.168.2.*' ]
+\end{verbatim}
+
+\chapter{Portal referencing in search engines}
+
+If you manage a publicly accessible Mobyle portal, you may be interested in having it referenced by various search engines. To facilitate this task, the Mobyle portal includes a sitemap-generating CGI. You can ``declare'' your sitemap to different search engines (refer to their respective webmaster help pages). This sitemap will facilitate the crawling of your Mobyle portal, by declaring a different page for each available program. For more detailed information about sitemaps, refer to \ [...]
+
+
+\chapter{Rotate and compress mobyle log files}
+
+Due to some limitations in the python ``logging'' library (the library crashes when the log file exceeds 2Go) It's a good practice to rotate the logs.
+However, the ``logging'' library can only rotate a file if it exceeds a given size or on a n days frequency, but is not able to rotate each month, which is the most
+appropriate frequency to rotate mobyle logs. We therefore provide a small script (Tools/mob\_log\_rotate) which rotates the log files and compress them. This script
+can be setup in a cron, so you can choose the frequency of the log rotation.
+
+Example to rotate logs the first day of each mont at 0H5min am
+\begin{verbatim}
+#send statistics
+0 0 1 * * apache /opt/bin/mob_stat -o /tmp/mobyle_stat.txt  /var/log/mobyle/access_log -m 
+#rotate logs
+0 5 1 * * apache /opt/bin/mob_log_rotate
+\end{verbatim}
+
+Note that in this example we send, every month, to the maintainers of the server some very basic statitics 
+of mobyle usage just before to compress the log files.
+
+
+\end{document}
diff --git a/Doc/Admin/execution_systems_guide.pdf b/Doc/Admin/execution_systems_guide.pdf
new file mode 100644
index 0000000..45a7400
Binary files /dev/null and b/Doc/Admin/execution_systems_guide.pdf differ
diff --git a/Doc/Admin/execution_systems_guide.tex b/Doc/Admin/execution_systems_guide.tex
new file mode 100644
index 0000000..2c70c19
--- /dev/null
+++ b/Doc/Admin/execution_systems_guide.tex
@@ -0,0 +1,304 @@
+\documentclass[11pt,a4paper]{article} 
+\usepackage[latin1]{inputenc} 
+\usepackage[american]{babel}
+\usepackage{verbatim}
+\usepackage{url}
+% %pour creer des lien vers des pages web
+\usepackage[pdftex,colorlinks=true, urlcolor=cyan,pdfstartview=FitH]{hyperref}
+%%pour inserer du code
+\usepackage{listings}
+\lstset{ %
+         language=Python,
+         showspaces=false,
+         showstringspaces=false,
+         showtabs=false,
+         basicstyle=\footnotesize,
+         }
+
+%% pour afficher des images avec pdflatex utiliser le package pdftex
+%\usepackage{graphicx}
+
+%% MARGES
+\oddsidemargin -1cm
+\marginparwidth 0cm \textwidth 18.5cm
+\topmargin -0.5cm
+\headheight -0.8cm \headsep 0cm
+%%\footskip 0cm
+\textheight 26.8cm
+\pagestyle{plain}
+
+\date{ Mobyle 1.0 }
+
+\title{ The Mobyle Execution System  }
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+
+\section{concepts}
+There are 3 main actors in the Mobyle execution system:
+\begin{itemize}
+\item The ExecutionSystem is the interface between Mobyle and the low level
+execution system as your local system or your favorite Distributed Resources
+Management System (DRMS),
+\item The ExecutionConfig is an object which provides the basic information
+needed by the ExecutionSystem to be used in your environment,
+\item The Dispatcher chooses which execution system to use for a job.
+\end{itemize}
+Each actor is modelized as a class and several implementations are provided in
+Mobyle distribution.
+
+\subsection{The ExecutionSystem}
+All execution System classes inherit from the abstract class ExecutionSystem
+and are located in the MOBYLEHOME/Src/Mobyle/Execution package. 5 ExecutionSystem 
+classes are available:
+\begin{itemize}
+  \item SYS which is the interface between Mobyle and your local system.
+  \item SGE which is the interface between Mobyle and the Sun Grid Engine DRMS.
+  \item SgeDRMAA which is the interface between Mobyle and the Sun Grid Engine
+  DRMS using the drmaa library.
+  \item PbsDRMAA which is the interface between Mobyle and the PBS/torque
+  DRMS using the drmaa library. 
+  \item Lsf DRMAA which is the interface between Mobyle and the LSF
+  DRMS using the drmaa library.
+\end{itemize}
+
+For SGE and PBS, two execution systems are proposed: one which wraps
+the shell commands and another which deals with Distributed Resource Management
+Application Api (DRMAA) library. The benefits to use the DRMAA library is that
+there is no need to use intermediate shell to run, get the status or
+kill a job. 
+For those who cannot or do not want to install libdrmaa, the legacy
+SGE execution systems is kept.
+
+\subsection{The ExecutionConfig}
+Mobyle is highly flexible towards the execution of jobs, you can use several
+Execution System in parallel. For instance you can use SGE for most of jobs and
+SYS for some very small jobs or one cluster managed by SGE for a set of jobs and 
+an other cluster managed by PBS for the other jobs on so on. 
+For each execution system you want to use you must define an ExecutionConfig. 
+this instanciation must be done in EXECUTION\_SYSTEM\_ALIAS. 
+To each class of ExecutionSystem a class of ExecutionConfig is associated. So
+the choice of an ExecutionConfig determines which ExecutionSystem will be
+used.
+
+\subsubsection{SgeDRMAAConfig}
+is associated to SgeDRMAA ExecutionSystem, which is the interface with SGE
+using libdrmaa. This class takes 3 mandatory parameters:
+\begin{enumerate}
+  \item the path of the drmaa library (eg. /usr/local/sge/lib/lx26-amd64/libdrmaa.so )
+  \item root the content of the SGE\_ROOT variable.
+  \item cell the content of the SGE\_CELL variable.
+\end{enumerate}
+
+\subsubsection{PbsDRMAAConfig}
+is associated to PbsDRMAA ExecutionSystem, which is the interface with Pbs/torque
+using libdrmaa. This class takes 2 mandatory parameters:
+\begin{enumerate}
+  \item the path of the drmaa library (eg.  /usr/local/lib64/libdrmaa.so)
+  \item le fully qualified name of the host where is located the PBS/torque
+  daemon server
+\end{enumerate}
+
+\subsubsection{LsfDRMAAConfig}
+is associated to LsfDRMAA ExecutionSystem, which is the interface with
+LSF using libdrmaa. This class takes 3 mandatory parameters:
+\begin{enumerate}
+	\item the path of the drmaa library (eg.  /usr/local/lib64/libdrmaa.so)
+  	\item lsf\_envdir the content of the variable ENVDIR of LSF
+  	\item lsf\_serverdir the content of the variable SERVERDIR of LSF
+\end{enumerate}
+
+\subsubsection{SGEConfig}
+is associated to SGE ExecutionSystem, which is the interface with SGE using the shell 
+commands wrapping. 
+This class takes 2 mandatory parameters:
+\begin{enumerate}
+  \item root the content of the SGE\_ROOT variable
+  \item cell the content of the SGE\_CELL variable
+\end{enumerate}
+
+\subsubsection{SYSConfig}
+is associated to SYS ExecutionSystem. It is used to launch job without any DRMS.
+There is no argument to run a job in ``local''.
+
+\subsection{The Dispatcher}
+After having defined all your execution system, you have to specify what system
+must be used for a given program. This is the role of the dispatcher.\\
+A DefaultDispatcher is provided. 
+
+\subsubsection{The DefaultDispatcher}
+associates statically one program to an ExecutionSystem and a queue. 
+The DefaultDispatcher takes as argument a dictionary where the
+name of the programs are the keys and the values are tuple with 2 arguments.
+the first one is an EXECUTION\_SYSTEM\_ALIAS entry and the second a queue name.
+There is a joker program name : ``DEFAULT'' to define a system for all programs
+which are not listed in the keys.
+
+
+\section{how to configure}
+
+\subsection{First step: define the execution system you want to use.}
+The key is a symbolic name you give to an execution system.\\
+The value is an instance of ExecutionConfig.
+following an example of EXECUTION\_SYSTEM\_ALIAS using all ExecutionConfig
+we provide. 
+\begin{verbatim}
+from Execution import  *
+EXECUTION_SYSTEM_ALIAS = {
+          'DRMAA_sge'  : SgeDRMAAConfig( '/usr/local/sge/lib/lx26-amd64/libdrmaa.so' ,
+                                         root ='/usr/local/sge',
+                                         cell = 'default' ) ,
+          'DRMAA_torque': PbsDRMAAConfig('/usr/local/lib64/libdrmaa.so' ,
+                                         'marygay.sis.pasteur.fr'),
+
+          'SGE'         : SGEConfig( root = '/usr/local/sge',
+                                     cell= 'default' ) ,
+          'SYS'        : SYSConfig()  ,
+          'LSF'         : LsfDRMAAConfig( '/home/bneron/Sys/lib/libdrmaa.so' ,  
+                                                          lsf_envdir = '/home/bneron/Sys/share/lsf/conf' ,
+                                                          lsf_serverdir = '/home/bneron/Sys/share/lsf/7.0/linux2.6-glibc2.3-x86_64/etc')
+                          }
+          }
+\end{verbatim}
+
+here a second example to illustrate that you can use the same Execution System with different Config.  
+
+\begin{verbatim}
+from Execution import  *
+EXECUTION_SYSTEM_ALIAS = {
+          'cluster1'  : SgeDRMAAConfig( '/usr/local/sge/lib/lx26-amd64/libdrmaa.so' ,
+                                         root ='/usr/local/sge',
+                                         cell = 'cluster1' ) ,
+          'cluster2' : SgeDRMAAConfig( '/usr/local/sge/lib/lx26-amd64/libdrmaa.so' ,
+                                         root ='/usr/local/sge',
+                                         cell = 'cluster2' ) ,       
+                           }
+\end{verbatim}
+in this example, the cluster1 and cluster2 have differents features, 
+number of nodes, memory \ldots and some jobs must run on cluster1 and the other on cluster2.
+ 
+\subsection{Second step: define which SystemExecution will be used for given program.}
+ 
+After having defined your ExecutionSystem you must specify in what conditions you will use it.
+We illustrate here the configuration of the DefaultDispatcher which links a job
+name to an ExecutionConfig and a queue.
+\begin{verbatim}
+from Mobyle.Dispatcher import DefaultDispatcher
+
+DISPATCHER = DefaultDispatcher( {
+       'blast2'    : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_sge' ] , 'mobyle' ),
+       'fastdnaml' : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_torque' ] , 'mobyle' ),
+       'toppred'   : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_torque' ] , 'short' ),
+       'dnapars'   : ( EXECUTION_SYSTEM_ALIAS[ 'SGE' ]    , 'long' ),
+       'golden'    : ( EXECUTION_SYSTEM_ALIAS[ 'SYS' ]   ,   ''    ),
+       'kitch'     : ( EXECUTION_SYSTEM_ALIAS[ 'LSF' ] , 'mobyle' ),
+       'DEFAULT'   : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_sge' ]    , 'mobyle' )
+                             } )
+\end{verbatim}
+or
+\begin{verbatim}
+from Mobyle.Dispatcher import DefaultDispatcher
+
+DISPATCHER = DefaultDispatcher( {
+       'job1' : ( EXECUTION_SYSTEM_ALIAS[ 'cluster1' ] , 'short' ),
+       'job2' : ( EXECUTION_SYSTEM_ALIAS[ 'cluster1' ] , 'long' ),
+       'DEFAULT'   : ( EXECUTION_SYSTEM_ALIAS[ 'cluster2' ] , 'mobyle' )
+                           } )
+\end{verbatim}
+
+Don't be afraid by this configuration once it is done you do not have to
+change it very often, and for most of you, you will have only one ExecutionConfig.
+For instance if you use SGE and one queue 'mobyle' for all jobs, the
+configuration will be:
+\begin{verbatim}
+EXECUTION_SYSTEM_ALIAS = { 'SGE'  : SGEConfig( root = '/usr/local/sge', cell='default' ) } 
+DISPATCHER = DefaultDispatcher( { 'DEFAULT' : ( EXECUTION_SYSTEM_ALIAS[ 'SGE'] , 'mobyle' )
+\end{verbatim}
+
+\section{add new execution system}
+\label{sec:new_execution_system}
+If you have an other execution system not supported by Mobyle, 
+you can develop our own ExecutionSystem. This Class must inherits from the abstract ExecutionSystem Class.
+The module must contain a class named as the module. Only this class can be used by Mobyle.
+Your module must be located in MOBYLEHOME/Src/Execution package.
+The new Class must implement 4 methods \_\_init\_\_, \_run , getStatus and kill.
+
+\subsection{\_\_init\_\_}
+The init method has one argument which is the ExecutionConfig and is used to do 
+all requirements to comunicate with the DRM. 
+For instance set some variables in the environment \ldots usually these informations are
+contained in the ExecutionConfig. The \_\_init\_\_ method is not necessary if
+you don't need any configuration like SYS class.
+
+\subsection{\_run}
+This method is the most complex you have to write to implement, and it has several responsabilities.
+\begin{itemize}
+  \item This method is responsible for submitting the job to the DRMS.
+  \item This method must be synchron with the job execution.
+  \item As we do not use a server which can keep a record of all jobs, we must store some informations to retrieve a 
+given job from an other process (cgi) to get the status of a job or to kill it.
+These informations are stored in a '.admin' file located in each job directory.
+The \_run method is responsible for setting the value of the execution Sytem used and the key to retrieve this job 
+on this Execution system to the an Admin object. The name is always accesible through self.execution\_config\_alias attribute 
+and the key is the pid of the job for SYS or the job identifier in SGE\ldots.   
+  \item The ADMINDIR directory is a kind of table of all jobs currently in execution in Mobyle. 
+It contains a symbolic link toward each job currently running.
+The \_run method must make this link when it submits the job to the DRMS and remove it when the job is finished.
+  \item Finally, when the job is finished the \_run must map the status job to a Mobyle.Status and return it.
+\end{itemize}
+There is a Dummy class in the Execution package to help the developer to write his own Execution class.
+
+\subsection{getStatus}
+Has one argument, the identifier of the job for this DRMs ( one that you store in .admin file in the \_run method ).
+This method queries the DMRS about the status of this job and maps this DRMS status to a Mobyle Status. 
+If the job cannot be found in the DRMS the Status ``unknown'' must be returned.
+
+\subsection{kill}
+Has one argument, the identifier of the job for this DRMs ( one that you store in .admin file in the \_run method ). 
+This method asks the drms to kill the job and return None.
+
+\subsection{ExecutionConfig}
+You must implement also the ExecutionConfig which will be associated to this Class. 
+The ExecutionConfig Class must be named as the new ExecutionSystem you develop with ``Config'' 
+at the end. The module containing this ExecutionConfig
+class must be called also as the Class and located in MOBYLEHOME/Local/Config/Execution package.\\
+
+Your ExecutionConfig class must inherit from ExecutionConfig and implement
+all requirements needed by your ExecutionSystem you have coded.
+At this point you can use your new Execution suitable to your need from the general Config as any 
+other provided classes.
+   
+\section{add new dispatcher}
+The Default dispatcher allows to associate one program to one ExecutionSystem
+and one queue. This queue is determined statically in the config. If you
+need something more dynamic, like compute the quqe based on the email of the
+user for instance, you must develop a new Dispatcher. This new dispatcher will
+inherit from Dispatcher Class and must implement 2 methods:
+\begin{itemize}
+  \item getQueue
+  \item getExecutionConfig
+\end{itemize}
+
+\subsection{getQueue}
+Has one argument which is a JobState instance. You can easily access to all
+job characteristics (the name of the job, the email of the user\ldots) 
+to compute the queue name and return it.   
+
+\subsection{getExecutionConfig}
+returns the ExecutionConfig which will used to execute a job.
+
+\section{DRMS requirement if DRMAA is used}
+
+\subsection{python-drmaa}
+
+\subsection{torque}
+To work with DRMAA and Mobyle ( to run a synchronous job ) torque must be able
+to report the status of a completed jobs. This feature is enabled by setting the keep\_completed attribute on the job execution 
+queue or server configuration.
+
+\subsection{LSF}
+We need lsf-drmaa from the FedStage DRMAA for LSF project
+http://sourceforge.net/projects/lsf-drmaa/
+\end{document}
\ No newline at end of file
diff --git a/Doc/Admin/grid_architecture.png b/Doc/Admin/grid_architecture.png
new file mode 100644
index 0000000..5b09171
Binary files /dev/null and b/Doc/Admin/grid_architecture.png differ
diff --git a/Doc/MobLinkExample.html b/Doc/MobLinkExample.html
new file mode 100644
index 0000000..ac0cdd4
--- /dev/null
+++ b/Doc/MobLinkExample.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>MobLink Example</title>
+<meta name="author" content="Hervé Ménager">
+<style type="text/css">
+body{
+  font-family: Verdana,Arial,Helvetica,sans-serif;
+}
+h1, h2{
+  color: #4682B4;
+}
+</style>
+</head>
+<body>
+<h1>MobLink functionnality demo</h1>
+
+<p><tt>MobLinks</tt> are a way to link a website to a Mobyle website using a HTML anchor or an HTML form. 
+  To do this you need to provide parameters that specify the value of each parameter for each form you want to prefill.
+  For instance, if you want to link to a preloaded golden form that specifies the bank and the identifier, you will
+  provide the corresponding parameters in the link or form:</p>
+<ul>
+  <li><tt>load::golden::db</tt>=<tt>uniprot</tt></li>
+  <li><tt>load::golden::query</tt>=<tt>104K_THEPA</tt></li>
+</ul>
+<p>Each of the parameter names is composed of three parts:</p> 
+<ul>
+  <li><tt>load</tt> which tells mobyle that it should load the following value into a form</li>
+  <li><tt>[form name]</tt> which is the name of the form where the value is to be loaded</li>
+  <li><tt>[parameter name]</tt> which is the name of the parameter where the value is to be loaded</li>
+</ul>
+<p>The url which should be loaded (in the form's <tt>action</tt> attribute or in the anchor's <tt>href</tt> attribute) 
+is the url of the mobyle portal where you want to load the forms (e.g. http://mobyle.pasteur.fr).</p>
+<p>Please refer to the sourcecode of this page for more details.</p>
+
+<h2>Form example</h2>
+
+<p>Forms require a little more HTML code than anchors, but should be easier to generate, and do not force you to use <tt>GET</tt> requests
+which generate long URLs.</p>
+
+<form action="/cgi-bin/portal.py" method="post" enctype="multipart/form-data" target="_blank">
+<div>
+<label>Here is the db that has to be set in golden prefilled form
+<input name="load::golden::db" value="uniprot"></label>
+</div>
+<div>
+<label>Here is the db that has to be set in golden prefilled form
+<input name="load::golden::query" value="104K_THEPA"></label>
+</div>
+<input type="submit" name="Open" value="open"></div>
+</form>
+
+<h2>Anchor example</h2>
+
+<p>Anchors are more concise but are limited to <tt>GET</tt> requests, and the URLs can be harder to generate, because you need to concatenate the
+target URL and the encoded parameter values.</p>
+
+<a href="http://rita.sis.pasteur.fr:85/cgi-bin/portal.py?load::golden::db=uniprot&load::golden::query=104K_THEPA" target="_blank">Open the <b>golden</b> form in Mobyle</a>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/Doc/jme_interface.jpeg b/Doc/jme_interface.jpeg
new file mode 100644
index 0000000..dfd5f1b
Binary files /dev/null and b/Doc/jme_interface.jpeg differ
diff --git a/Doc/service_description_guide.pdf b/Doc/service_description_guide.pdf
new file mode 100644
index 0000000..bfce3d7
Binary files /dev/null and b/Doc/service_description_guide.pdf differ
diff --git a/Doc/service_description_guide.tex b/Doc/service_description_guide.tex
new file mode 100644
index 0000000..893bb88
--- /dev/null
+++ b/Doc/service_description_guide.tex
@@ -0,0 +1,1617 @@
+\documentclass[11pt,a4paper]{report} 
+\usepackage[latin1]{inputenc} 
+\usepackage[american]{babel}
+\usepackage{verbatim}
+\usepackage{url}
+% %pour créer des lien vers des pages web
+\usepackage[pdftex,colorlinks=true, urlcolor=cyan,pdfstartview=FitH]{hyperref}
+%%pour inserer du code
+\usepackage{listings}
+\usepackage{ulem}
+
+\setcounter{secnumdepth}{2}
+\setcounter{tocdepth}{3}
+
+%\lstset{%
+%basicstyle=\ttfamily
+%}
+\lstdefinelanguage{Mobyle}%
+{
+alsoletter={:,\-},%
+}
+
+\lstset{ %
+         language=Mobyle,
+         showspaces=false,
+         showstringspaces=false,
+         showtabs=false,
+         basicstyle=\footnotesize
+         }
+
+
+%% pour afficher des images avec pdflatex utiliser le package pdftex
+\usepackage{graphicx}
+
+% % MARGES
+\oddsidemargin -1cm
+\marginparwidth 0cm \textwidth 18cm
+\topmargin -0.5cm
+\headheight -0.8cm \headsep 0cm
+% %\footskip 0cm
+\textheight 26.8cm
+\pagestyle{plain}
+
+\renewcommand{\abstractname}{Overview}
+
+\date{ Mobyle 1.0 }
+
+\title{Mobyle Service Description Guide}
+
+\begin{document}
+
+\maketitle
+
+\tableofcontents
+
+\chapter{Introduction}
+Mobyle is a system which provides an access to different software elements, in order to allow
+users to perform bioinformatics analyses. Such software elements are called \textbf{services},
+and belong to four distinct categories:
+\begin{itemize}
+ \item \textbf{programs} describe software that are executed on the server side ``command line'' tools,
+ and return results in the form of files,
+ \item \textbf{workflows} describe compositions of successive and/or parallel calls to other 
+ server-side services (programs and/or subworkflows), and also return results as files,
+ \item \textbf{viewers} describe browser-embedded visualization components, such as applets, that allow
+ users to visualize data (results or bookmarks) stored in the Mobyle server.
+ \item \textbf{tutorials} describe custom pages which are categorized and accessible in left part of portal. 
+ These pages are displayed in the "tutorials" tab of the central part of the portal.
+\end{itemize}
+
+To publish such services on a Mobyle server, you need to install and describe them so that 
+Mobyle offers the adequate user interface and executes them when required. Mobyle stores 
+the description of each service in an XML document, a \textbf{service description}.
+
+The formal description of the format of these XML files is stored in \texttt{relax-ng}
+and \texttt{schematron} formats, in the \texttt{*.rnc}, \texttt{*.rng}, and \texttt{*.sch}
+files, located in the \texttt{Schema} folder. The goal of this document is to describe the
+elements of this format so that you can add new tools or modify existing ones. Even if you do 
+not intend to use them, it is strongly recommended to download the service descriptions 
+maintained and distributed by the Institut Pasteur, which are used to illustrate the most 
+complex elements of the service description format.
+\\
+\\
+\definecolor{lightyellow}{rgb}{1,1,0.7}
+\colorbox{lightyellow}{
+\begin{minipage}{\textwidth}
+The releases of service descriptions are available on the public ftp server of the Institut Pasteur:\\
+\href{ftp://ftp.pasteur.fr/pub/gensoft/projects/mobyle/}{ftp://ftp.pasteur.fr/pub/gensoft/projects/mobyle/}.
+ \begin{itemize}
+  \item Programs-2.0.tar.gz or higher.
+  \item Workflows-??.tar.gz or higher.
+  \item Viewers-??.tar.gz or higher.
+  \item Tutorials-??.tar.gz
+ \end{itemize}
+
+The service descriptions are maintained in 3 subprojects of the Mobyle forge:
+ \begin{itemize}
+  \item \href{pasteur-programs}{https://projets.pasteur.fr/projects/show/pasteur-programs}
+  \item \href{pasteur-workflows}{https://projets.pasteur.fr/projects/show/pasteur-workflows} 
+  \item \href{pasteur-viewers}{https://projets.pasteur.fr/projects/show/pasteur-viewers}
+  \item \href{pasteur-viewers}{https://projets.pasteur.fr/projects/show/pasteur-viewers}
+ \end{itemize}
+
+The development versions of the service descriptions are accessible from the
+ public SVN Mobyle repository. With a command-line subversion client, type these
+ lines:
+\begin{itemize}
+ \item \texttt{svn co https://projets.pasteur.fr/svn/pasteur-programs/}
+ \item \texttt{svn co https://projets.pasteur.fr/svn/pasteur-workflows/}
+ \item \texttt{svn co https://projets.pasteur.fr/svn/pasteur-viewers/}
+ \item \texttt{svn co https://projets.pasteur.fr/svn/pasteur-tutorials/}
+\end{itemize}
+\end{minipage}}
+
+\chapter{Writing a program description}\label{programs}
+
+A program is a software: 
+\begin{itemize}
+ \item running on the server side,
+ \item callable in a shell command line,
+ \item returning some results.
+\end{itemize}
+XML files describe these softwares and make them usable by Mobyle. Hereinafter, ``program description''
+stands for ``XML description of such a software''.\\
+
+\noindent A program description aims at capturing different kinds of information:
+\begin{itemize}
+  \item how to invoke the program (a program wrapper),
+  \item how to display options in submission forms or job results (a UI description),
+  \item how to control values or parameters compatibilities (a ``semantic'' description).
+\end{itemize}
+
+\noindent The root element of a program description is the \textbf{program} tag. 
+A program description is divided into two parts:
+\begin{itemize}
+ \item the \textbf{head} element, described in section~\ref{programs_head}, contains general information about the program,
+ \item the \textbf{parameters} element, described in section \ref{programs_parameters}, contains the different input data, options and results of the program.
+\end{itemize}
+\newpage
+
+\section{head \label{programs_head}}
+
+\begin{itemize}
+  \item \textbf{name} (\textit{required}): name of the interface you are describing.
+  \begin{itemize}
+    \item does not have to be identical to the actual invoked program name (but highly recommended).  
+    \item has to be identical to the XML file name (minus its extension). E.g.: for the file 
+    \texttt{blast2.xml}, the \textbf{name} tag has to be \texttt{<name>blast2</name>}. 
+  \end{itemize} 
+  \item \textbf{version}: version of the program you are interfacing, not of the XML description itself.
+  \item \textbf{package} (\textit{new in 1.0}): description of the package the program belongs to. 
+  Can contain all the elements defined in \textbf{head} (\texttt{name}, \texttt{version}, 
+  \texttt{doc}\ldots ).
+
+  \begin{lstlisting}[title= how to include package information (excerpt from
+  \texttt{dnadist} description), breaklines=true, breakatwhitespace=true, float=p]
+<program>
+  <head>
+    <name>dnadist</name>
+    <package>
+      <name>phylip</name>
+      <version>3.67</version>
+      <doc>
+        <title>phylip</title>
+	<description>
+	  <text lang="en">PHYLIP is a package of programs for inferring phylogenies.</text>
+	</description>
+	<homepagelink>http://evolution.gs.washington.edu/phylip.html</homepagelink>
+	<sourcelink>http://evolution.gs.washington.edu/phylip/getme.html</sourcelink>
+        <authors>Felsenstein Joe</authors>
+	<reference>Felsenstein, J. 1993. PHYLIP (Phylogeny Inference Package) 
+        version 3.5c. 
+	Distributed by the author. Department of Genetics, 
+	University of Washington, Seattle.</reference>
+	<reference>Felsenstein, J. 1989. PHYLIP -- Phylogeny Inference Package 
+        (Version 3.2).
+	Cladistics 5: 164-166.</reference>
+	<doclink>http://bioweb2.pasteur.fr/docs/phylip/phylip.html</doclink>  
+      </doc>
+    </package>
+    <doc>
+      <title>dnadist</title>
+      <description>
+        <text lang="en">Compute distance matrix from nucleotide sequences</text>
+      </description>
+      <doclink>http://bioweb2.pasteur.fr/docs/phylip/doc/dnadist.html</doclink>
+      <comment>
+        <text lang="en">This program uses nucleotide sequences to compute 
+        a distance matrix, under four different models of nucleotide substitution. 
+        It can also compute a table of similarity between the nucleotide
+        sequences. The distance for each pair of species estimates the total 
+        branch length between the two species, and can be used in the distance 
+        matrix programs FITCH, KITSCH or NEIGHBOR.
+        </text> 
+      </comment>
+    </doc>
+    <category>phylogeny:distance</category>
+  </head>
+  [...]
+  \end{lstlisting}
+  
+  \item \textbf{doc}: documentation of the program split among the following elements:
+  \begin{itemize}
+    \item \textbf{description}: brief description of the software, in text or XHTML.
+    This description appears at the top of the submission form.
+    
+    \item \textbf{reference}: scientific reference officially used to cite the program, 
+    in text or XHTML. In addition, you can provide a hyperlink to the users by specifying 
+    a DOI or a direct URL, respectively in the \texttt{doi} and \texttt{url} attributes.
+    
+    \item \textbf{authors}: program authors who must be cited.
+    
+    \item \textbf{doclink}: any URL pointing to useful documentation regarding the program.
+    If a \textbf{doclink} is specified, a button ``Help Pages'' is displayed at the top of
+    the web form:
+    \begin{itemize}
+      \item with only one \textbf{doclink} element, the documentation is be available both
+      as the ``Help Pages'' button and as a link at the bottom of the web form,
+      \item with several \textbf{doclink} elements, the ``Help Pages'' button sets the focus 
+      to the bottom of web form where he can choose among the different links.
+    \end{itemize}
+    
+    \item \textbf{sourcelink}: (\textit{new in 1.0}) any URL where the program can be found. 
+    Essentially used as documentation by server administrators.
+    
+    \item \textbf{homepagelink}: (\textit{new in 1.0}) the URL to the homepage of the 
+    software project.
+    
+    \item \textbf{comment}: text describing the program with more details.
+    This comment is shown by clicking on the red question mark beside the title.
+  \end{itemize}
+  
+  \item \textbf{category}: path within the classification of the programs which determines 
+  where the program name will be displayed in the program tree on the left panel. Thus, the 
+  \textbf{category} must be chosen carefully. The colon is used as separator between nodes 
+  of the classification.
+  \begin{itemize}
+    \item if multiple \textbf{category} elements are specified for a service, the program 
+    will appear in each branch of the tree, 
+    \item if none is provided, it will appear at the ``top level''.
+  \end{itemize}
+  
+  \item \textbf{env}: used to specify an environment variable that will be defined prior to launching any job 
+  for the program. Usually implemented with entities or \texttt{XInclude}. Examples:
+  \begin{itemize}
+    \item to specify the location of blast databases: \texttt{<env name=``BLASTDB''>/usr/local/ncbi/db</env>}
+    \item to give the path of a binary different from  the usual path: \\ \texttt{<env name=``PATH''>/path/to/my/binary</env>}
+  \end{itemize}
+
+  \item \textbf{progressReport}: used to specify a file provided by the execution of the program, which will provide information
+  about the execution status of the program. This can be useful if you have a program running for a long time and executing multiple
+  steps internally. In such cases the user will access progress information as the contents of this file in the job status tab of
+  the portal. The \texttt{prompt} attribute lets you define a program-specific prompt for this file, otherwise the default prompt is 
+  "job progress report". Ex: \texttt{<progressReport prompt="This is your progress report">progress.log</progressReport>}.
+
+\end{itemize}
+
+\newpage
+
+\subsection{XInclude to include external or common information}
+
+It is possible to include site-dependent information (such as the
+above-mentioned \textbf{env} tag) by including external pieces of XML code.
+Although XML entities can achieve the same goal, the recommended way is to use 
+\texttt{XInclude} as it provides error-handling mechanisms not available with 
+XML entities. XInclude can be used to: 
+\begin{itemize} 
+ \item refactor some parts of a set of descriptions,
+ \item specify site-dependent variables.
+\end{itemize}
+ 
+For example, if you have a set of interfaces which wrap different programs from the same package, 
+you can include common package information in an entity that you 
+include in the different XML files. Just make sure that:
+\begin{enumerate}
+ \item the \texttt{XInclude} elements are correctly formatted
+ \item the \texttt{XInclude} namespace is declared somewhere.
+\end{enumerate}
+ 
+
+\begin{lstlisting}[title= Example: how to include databank (excerpt from \texttt{golden} description), breaklines=true, breakatwhitespace=true,
+float=h, morekeywords={xi:include, XMLns:xi, href},keywordstyle=\color{red}\textbf]
+  <parameters XMLns:xi="http://www.w3.org/2001/XInclude">
+    <parameter ismandatory="1" issimple="1" ismaininput="1">
+      <name>db</name>
+      <prompt lang="en">Database</prompt>
+      <type>
+        <datatype>
+          <class>Choice</class>
+        </datatype>
+      </type>
+      <vdef>
+        <value>null</value>
+      </vdef>
+      <xi:include href="../../Local/Services/Programs/Entities/goldendb.xml">
+        <xi:fallback>
+          <vlist>
+            <velem undef="1">
+              <value>null</value>
+              <label>Choose a database</label>
+            </velem>
+          </vlist>
+        </xi:fallback>
+      </xi:include>
+      <format>
+        <code proglang="perl">" $db:"</code>
+        <code proglang="python">" " + db + ":"</code>
+      </format>
+      <argpos>2</argpos>
+    </parameter>
+\end{lstlisting}
+
+\newpage
+
+\label{package_information}
+This mechanism is also used to share package information with other program
+descriptions belonging to the same package. In the following example, the \texttt{Entities} directory 
+(same level as \texttt{Programs}) contains the \texttt{phylip\_package.xml} file shared by all phylip programs 
+(\texttt{dnadist}, \texttt{dnapars}, \texttt{protdist}, \texttt{protpars}\dots )
+
+\begin{lstlisting}[title= Example: how to share package information (excerpt from \texttt{dnadist}
+\underline{program} description), breaklines=true, breakatwhitespace=true, 
+morekeywords={xi:include, XMLns:xi, href},keywordstyle=\color{red}\textbf]
+  <head>
+    <name>dnadist</name>
+    <xi:include XMLns:xi="http://www.w3.org/2001/XInclude" href="Entities/phylip_package.xml"/>
+    <doc>
+      <title>dnadist</title>
+      <description>
+        <text lang="en">Compute distance matrix from nucleotide sequences</text>
+      </description>
+      <doclink>http://bioweb2.pasteur.fr/docs/phylip/doc/dnadist.html</doclink>
+      <comment>
+        <text lang="en">This program uses nucleotide sequences to compute a distance matrix,
+         under four different models of nucleotide substitution. It can also
+         compute a table of similarity between the nucleotide sequences. 
+         The distance for each pair of species estimates the total branch length
+         between the two species, and can be used in the distance matrix programs 
+         FITCH, KITSCH or NEIGHBOR.</text> 
+      </comment>
+    </doc>
+    <category>phylogeny:distance</category>
+  </head>
+\end{lstlisting}
+
+\begin{lstlisting}[title= Example: how to share package information (excerpt from phylip \underline{package} description),
+ breaklines=true, breakatwhitespace=true ]
+<package>
+  <name>phylip</name>
+  <version>3.67</version>
+  <doc>
+    <title>phylip</title>
+    <description>
+      <text lang="en">PHYLIP is a package of programs for inferring phylogenies.</text>
+    </description>
+    <homepagelink>http://evolution.gs.washington.edu/phylip.html</homepagelink>
+    <sourcelink>http://evolution.gs.washington.edu/phylip/getme.html</sourcelink>
+    <authors>Felsenstein Joe</authors>
+    <reference>Felsenstein, J. 1993. PHYLIP (Phylogeny Inference Package) version 3.5c. 
+    Distributed by the author. 
+    Department of Genetics, University of Washington, Seattle.</reference>
+    <reference>Felsenstein, J. 1989. PHYLIP -- Phylogeny Inference Package (Version 3.2). 
+    Cladistics 5: 164-166.</reference>
+    <doclink>http://bioweb2.pasteur.fr/docs/phylip/phylip.html</doclink>  
+  </doc>
+</package>
+\end{lstlisting}
+
+\section{Parameters \label{programs_parameters}}
+
+The \textbf{parameters} element is a direct child of:
+\begin{itemize}
+ \item either the \textbf{program} element: it includes all the defined \textbf{parameter}s for the program,
+ \item or the \textbf{paragraph} element: it defines the \textbf{parameter}s of a given \textbf{paragraph}.
+\end{itemize}
+It can include:
+\begin{itemize}
+ \item \textbf{parameter} elements,
+ \item nested \textbf{paragraph} elements.
+\end{itemize}
+
+
+\section{Paragraph}
+
+The \textbf{paragraph} element has two functions: 
+\begin{enumerate}
+ \item it can group the evaluation of different \textbf{parameter}s, with respect to a set of \textbf{precond}itions for instance,
+ \item it also groups visually these \textbf{parameter}s in the form. 
+\end{enumerate}
+\noindent Thus, a \textbf{paragraph} is both a set of \textbf{parameter}s -- visually speaking -- in the submission form and a way to share some properties such as:
+\begin{itemize}
+  \item \textbf{argpos} elements (see \ref{argpos}) and \textbf{precond} elements (see \ref{precond}).
+  \item \textbf{layout} which allows to override the default display of the
+  \textbf{parameter}s belonging to a \textbf{paragraph}. The default disposition of the \textbf{parameter}s is a vertical succession, where form \textbf{parameter}s and job results are laid out in the same order as in the program description. 
+  The \textbf{layout} tag allows to define horizontal groups (with the \textbf{hbox} tag) and 
+  vertical groups (\textbf{vbox} tag) in a recursive structure. Each group had a \textbf{box} element that 
+  refers to the corresponding \textbf{parameter} using its \textbf{name}. See the JME description below.
+\end{itemize}
+
+\begin{lstlisting}[title=excerpt from JME XML description, breaklines=true, breakatwhitespace=true, float=p,
+emph={jme_applet, smiles_input, mol_input}, emphstyle=\color{blue}\texttt,
+morekeywords={layout, vbox, hbox}, keywordstyle=\color{red}\textbf]
+<paragraph>
+  <name>inputs</name>
+  <prompt>SMILES/MOL data</prompt>
+  <parameters>
+    <parameter>
+      <name>jme_applet</name>
+      <prompt lang="en">Use this applet to edit your SMILES/MOL data</prompt>
+      <type>
+        <datatype>
+          <class>String</class>
+        </datatype>
+      </type>
+      <interface>
+         <!-- To improve the readability of the example, 
+              a part of the code has been removed.
+         -->
+      </interface>
+    </parameter>
+    <parameter>
+      <name>smiles_input</name>
+      <prompt lang="en">SMILES data</prompt>
+      <type>
+        <datatype>
+          <class>Smiles_structure</class>
+          <superclass>AbstractText</superclass>
+        </datatype>
+      </type>
+   </parameter>
+   <parameter>
+     <name>mol_input</name>
+     <prompt lang="en">MOL data</prompt>
+     <type>
+       <datatype>
+         <class>MOL_structure</class>
+         <superclass>AbstractText</superclass>
+       </datatype>
+     </type>
+   </parameter>
+ </parameters>
+ <layout>
+   <hbox>
+     <box>jme_applet</box>
+     <layout>
+       <vbox>
+         <box>smiles_input</box>
+         <box>mol_input</box>
+       </vbox>
+     </layout>
+   </hbox>
+ </layout>
+</paragraph>
+\end{lstlisting}
+
+%\clearpage
+
+\begin{figure}[h]
+\includegraphics[width=17cm]{jme_interface.jpeg}
+\caption{JME interface}
+\end{figure}
+
+\clearpage
+
+\section{Parameter}
+
+The \textbf{parameter} element is an essential piece of the program description. It describes and stores: 
+\begin{itemize}
+ \item the options,
+ \item the inputs,
+ \item and the outputs
+\end{itemize}
+of a program, used to build the command line and retrieve the results.
+
+\subsection{Parameter attributes}
+
+The attributes of the \textbf{parameter} element are:
+\begin{itemize}
+  \item \textbf{ismandatory}: the \textbf{parameter} must be specified by the user, pointed out by
+  a {\color{red}*} next to its prompt in the interface. \underline{Special case}: if the \textbf{parameter} is mandatory and
+  if a \textbf{precond} is defined (see below), the \textbf{parameter} becomes mandatory \underline{only if} the result of the
+  \textbf{precond}ition is true. These parameters are indicated by a {\color{blue}*}.
+  \item \textbf{issimple}: the input parameter is displayed in the \textit{simple} version of the submission form. If there are 
+  one or more \textit{simple} parameters in a form, and the Mobyle portal is configured to allow simple forms (see the configuration
+  guide), then the form in the portal will only display these parameters by default, and the other parameters will be accessible only
+  when clicking on the ``advanced form'' button. Please note that mandatory parameters which have no default value and no precondition
+  attached should be set as \textit{simple}, otherwise the service description will not validate. 
+  \item \textbf{isout}: the \textbf{parameter} is produced by the program. It is used to retrieve the results. Mobyle
+  systematically generates 2 files:
+  \begin{itemize}
+   \item \texttt{[program name].out}, corresponding to the standard output stream,
+   \item \texttt{[program name].err}, corresponding to the standard error stream
+  \end{itemize} 
+  These two implicit results are automatically shown to the user in the result page if they are not empty
+  and are typed as ``Text''.
+  \item \textbf{isstdout}: to provide a more explicit or detailed description of the standard output, 
+  instead of using the \textbf{isout} attribute, define a \textbf{parameter} element 
+  with the desired type and pass it as the value of the attribute \textbf{isstdout}. \textbf{isstdout}
+  and \textbf{isout} are mutually \underline{exclusive}. See \textbf{parameter} golden\_out in \texttt{golden.xml} below. Please also note
+  that if you specify an \textbf{isstdout} parameter with a precondition, you will need to also specify the standard output parameter for 
+  all the cases where this precondition is not met (otherwise the corresponding standard output may not be displayed in the job results).
+  \begin{lstlisting}[title=standard output \textbf{parameter} from \texttt{golden.xml}, breaklines=true, breakatwhitespace=true,
+ morekeywords={isstdout}, keywordstyle=\color{red}\textbf ]
+    <parameter isstdout="1">
+      <name>golden_out</name>
+      <prompt lang="en">Sequence</prompt>
+      <type>
+        <biotype>Protein</biotype>
+        <biotype>Nucleic</biotype>
+        <datatype>
+          <class>Sequence</class>
+        </datatype>
+      </type>
+      <filenames>
+        <code proglang="perl">"golden.out"</code>
+        <code proglang="python">"golden.out"</code>
+      </filenames>
+    </parameter>
+  \end{lstlisting}
+  \item \textbf{ishidden}: hides the \textbf{parameter} from the user, as opposed to the input parameters shown to the user in the submission form,
+  or to the results captured by the output parameters shown in the job results. 
+  When adding a \textbf{parameter} just to control another one, conceal it from the user by setting the attribute \textbf{ishidden} to 1. 
+  The value of hidden parameters can't be set by the user. See \textbf{parameter} rateAll in \texttt{seqgen.xml} below.
+\newpage
+  \begin{lstlisting}[title=hidden \textbf{parameter}, breaklines=true, breakatwhitespace=true, 
+morekeywords={ishidden}, keywordstyle=\color{red}\textbf]
+<parameter ishidden="1">
+  <name>rateAll</name>
+  <prompt lang="en">Rates</prompt>
+  <type>
+    <datatype>
+      <class>Float</class>
+    </datatype>
+  </type>
+  <precond>
+    <code proglang="perl">$rate1  and $rate2 and $rate</code>
+    <code proglang="python">rate1 is not None and rate2 is not None and rate3 is not None</code>
+  </precond>
+  <format>
+    <code proglang="perl">" -c $rate1 $rate2 $rate3"</code>
+    <code proglang="python">" -c %f %f %f " %(rate1,rate2,rate3)</code>
+  </format>
+</parameter>
+\end{lstlisting}
+\end{itemize}
+
+\subsection{Parameter subelements}
+
+\begin{itemize}
+
+ \item \textbf{name} (\textit{required}): mainly used inside Mobyle to refer to this parameter but not seen by the user in the interface.
+  The \textbf{name}:
+  \begin{itemize}
+   \item has to be \underline{unique among all parameters and paragraphs},
+   \item must be a valid python variable name (cannot begin by a number \ldots),
+   \item cannot be the name of a javascript property of the HTMLFormElement object, to avoid unexpected behaviors in the portal. Such names
+   include \textbf{name}, \textbf{nodeName}, \textbf{length}, etc. For a complete listing of the forbidden values, please refer to the \texttt{Mobyle.sch} file, located 
+   in the \texttt{Schema} folder.
+  \end{itemize}
+
+ \item \textbf{prompt}: the human-readable label of the parameter that the user can see in the form
+
+ \item \textbf{argpos}\label{argpos} that specifies the position of the parameter in the command line. 
+ \begin{itemize}
+   \item by convention the \textbf{command} has its \textbf{argpos} set to 0,
+   \item if \textbf{argpos} is not specified at the \textbf{parameter} level, takes the \textbf{argpos} of the immediate upper level (\textbf{paragraph}),
+   \item \underline{required only if} the order of parameters matters.
+ \end{itemize}
+
+ \item a \textbf{precond}\label{precond} which allows to specify under which conditions the \textbf{parameter} must be evaluated. All \textbf{precond}itions are evaluated starting with the \textbf{precond} of the outermost \textbf{paragraph} up to the \textbf{precond} of the innermost nested \textbf{paragraph}, 
+ and finally ending with the \textbf{precond} of the \textbf{parameter}. This mechanism allows to refactor \textbf{precond}ition code.
+
+ \item \textbf{format}: the inside code is evaluated to generate the command line. Mobyle uses python code but
+ playMoby (\url{http://lipm-bioinfo.toulouse.inra.fr/biomoby/playmoby/}) uses perl code. By default, the result 
+ of the code evaluation is used as part of the command line. To write it in a file, in particular
+ to simulate the interactive behavior of some programs such as some from the Phylip suite,
+ use the element \textbf{paramfile}. See \texttt{pars.xml}, \texttt{protdist.xml}, \texttt{fitch.xml}, \ldots
+
+ \item \textbf{ctrl}: used to specify additional constraints on values provided by the user such as being an integer,
+ being odd, not excessing a maximum \ldots. Upon execution, the \textbf{code} is evaluated and the corresponding error \textbf{message}
+ is displayed to the user if the result is False. See parameter identity in \texttt{cons.xml} below.
+
+ \item \textbf{vdef}: the default value for the \textbf{parameter}, mandatory for \textbf{vlist} and \textbf{flist} (see \ref{choice_paragraph}).
+
+\end{itemize}
+
+\begin{lstlisting}[title=control a parameter value, breaklines=true, breakatwhitespace=true, float=p,
+classoffset=0, morekeywords={ctrl, format}, keywordstyle=\color{red}\textbf,
+classoffset=1, morekeywords={code, message}, keywordstyle=\color{blue}\textbf]
+<parameter>
+  <name>identity</name>
+  <prompt lang="en">Required number of identities at a position (value greater than or equal to 0)</prompt>
+  <type>
+    <datatype>
+      <class>Integer</class>
+    </datatype>
+  </type>
+  <vdef>
+    <value>0</value>
+  </vdef>
+  <format>
+    <code proglang="python">("", " -identity=" + str(value))[value is not None and value!=vdef]</code>
+  </format>
+  <ctrl>
+    <message>
+      <text lang="en">Value greater than or equal to 0 is required</text>
+    </message>
+    <code proglang="python">value >= 0</code>
+  </ctrl>
+  <argpos>4</argpos>
+  <comment>
+    <text lang="en">Provides the facility of setting the required number of identities at a site...</text>
+  </comment>
+</parameter>
+\end{lstlisting}
+
+\subsection{Retrieving results}
+
+A result from a program is a parameter with:
+\begin{itemize}
+ \item the \textbf{isout}/\textbf{isstdout} attribute set to 1,
+ \item a \textbf{filenames} element.
+\end{itemize}
+
+Unix masks \label{masks} are defined in the \textbf{filenames} element to create the mapping 
+between one or more file names and the parameter. Use them to represent
+filenames that cannot be known statically or when the parameter must be mapped 
+with several files.
+
+For example, the toppred program can take a file with several 
+FASTA protein sequences. In this case, toppred will produce one hydrophobicity 
+file per sequence appearing in the input file. The name of each file will be the name of 
+the corresponding sequence, suffixed with the \texttt{.hydro} extension.
+To retrieve these files, the Unix mask is : ``*.hydro" (e.g. parameter
+hydrophobicity\_files in \texttt{toppred.xml}). 
+
+\clearpage
+Unix mask are used as follow:
+\begin{itemize}
+ \item a Unix mask is defined in a \textbf{code} element,
+ \item only 1 mask can be defined per \textbf{code} element,
+ \item \textbf{filenames} element may have several children \textbf{code} elements 
+\end{itemize}
+The code is evaluated before being used as Unix mask. 
+
+\begin{lstlisting}[title=how to retrieve results, breaklines=true, breakatwhitespace=true, 
+%float=t, 
+classoffset=0,morekeywords={filenames, isout}, keywordstyle=\color{red}\textbf,
+classoffset=1,morekeywords={code}, keywordstyle=\color{blue}\textbf]
+<parameter isout="1"> 
+ <name>graphicfiles</name> 
+ <prompt lang="en">Graphic output files</prompt>
+ <type>
+    <datatype> 
+      <class>Picture</class>
+      <superclass>Binary</superclass>
+    </datatype>
+    <card>0,n</card>
+  </type>
+  <precond>
+    <code proglang="perl">$graph_output</code>
+    <code proglang="python">graph_output == 1</code>
+  </precond>
+    <filenames>
+      <code proglang="perl">*.$profile_format</code>
+      <code proglang="python">'*.' + profile_format</code>
+    </filenames>
+</parameter>
+\end{lstlisting}
+\noindent The \textbf{comment} and \textbf{example} elements allow to generate in line help and example data for the parameter or paragraph,
+very useful as pieces of documentation to the user.
+\begin{itemize}
+ \item When an help \textbf{comment} is available, a clickable ``{\color{red}?}'' appears beside the prompt.
+ \item When an \textbf{example} is available, a ``use example data'' link appears beside the 
+parameter. By clicking on it, the user fills automatically the corresponding databox
+with this value.
+\end{itemize}
+
+
+\section{Typing \label{typing}}
+
+Choosing the right type for a parameter is an essential point in program
+description authoring, as a lot of features are based on types. The typing
+influences: 
+\begin{itemize}
+ \item the interface display,
+ \item the controls on user values assigned to the input parameters,
+ \item the chaining possibilities between programs and data reusability.
+\end{itemize}
+In Mobyle, typing is ``multidimensional'', meaning that it's based on several fields:
+\begin{itemize}
+  \item the \textbf{card} represents the cardinality, \textit{i.e.} the number of distinct
+  values that can be set for the parameter.
+  \item the \textbf{biotype} \label{biotype} describes the biological object (Nucleic , Protein,
+  Drug \ldots). It is not required as some parameters do not represent any
+  biological object. The \textbf{biotype} values are free text labels, but to chain the
+  data appropriately, they must be recognized so take special care on the spelling and case.
+  \item the \textbf{datatype} describes the computing object.
+  \item the \textbf{dataFormat} (\textit{new in 1.0}) specifies the format of the data for
+  this parameter. If the parameter is an input parameter, it lists the formats
+  accepted by the program, hence the formats into which Mobyle must convert (if possible)
+  an input parameter value. For instance, the data formats accepted by a Sequence 
+  datatype can be FASTA, EMBL, \ldots Several \textbf{dataFormat} elements can be 
+  specified. If the parameter is an output one (\textbf{isout}=``1'' or \textbf{isstdout}=``1'') then the
+  \textbf{dataFormat} element specifies the format for the result. This information can be
+  very useful in order to suggest chaining possibilities, so it's an important 
+  information. Sometimes the data format of a result cannot be predetermined but
+  can be computed at runtime based on the values of other input parameters set by the user. 
+  There are two ways to express this: 
+  \begin{enumerate}
+   \item to test the value of another parameter,
+   \item to reference directly a parameter in \textbf{dataFormat},
+  \end{enumerate}
+  The two following examples illustrate these possibilities:
+  \begin{enumerate}   
+  \item  \label{muscle-flist} \begin{lstlisting}[title=\textbf{dataFormat} by testing the value of another parameter (excerpt from \texttt{muscle.xml}), 
+breaklines=true, breakatwhitespace=true, emph={outformat, $outformat}, emphstyle=\color{blue}\texttt,
+morekeywords={dataFormat, isstdout}, keywordstyle=\color{red}\textbf]
+   <parameter>
+     <name>outformat</name>
+     <prompt lang="en">output format</prompt>
+     <type>
+       <datatype>
+         <class>Choice</class>
+       </datatype>
+     </type>
+     <vdef>
+       <value>fasta</value>
+     </vdef>
+     <flist>
+       <felem>
+         <value>fasta</value>
+         <label>fasta</label>
+         <code proglang="perl">""</code>
+         <code proglang="python">""</code>
+       </felem>
+       <felem>
+         <value>html</value>
+         <label>html</label>
+         <code proglang="perl">" -html "</code>
+         <code proglang="python">" -html "</code>
+       </felem>
+       <felem>
+         <value>msf</value>
+         <label>msf</label>
+         <code proglang="perl">" -msf "</code>
+         <code proglang="python">" -msf "</code>
+       </felem>
+       <felem>
+         <value>phyi</value>
+         <label>phylip</label>
+         <code proglang="perl">" -phyi "</code>
+         <code proglang="python">" -phyi "</code>
+       </felem>
+       <!-- it's a clustalw with a muscle header which is not supported by squizz -->
+       <felem>
+         <value>clw</value>
+         <label>muscle format</label>
+         <code proglang="perl">" -clw "</code>
+         <code proglang="python">" -clw "</code>
+       </felem>
+       <felem>
+         <value>clwstrict</value>
+         <label>clustalw 1.81</label>
+         <code proglang="perl">" -clwstrict "</code>
+         <code proglang="python">" -clwstrict "</code>
+       </felem>
+     </flist>
+     <comment>
+       <text lang="en">fasta : Write output in Fasta format</text>
+       <text lang="en">html  : Write output in HTML format</text>
+       <text lang="en">msf   : Write output in GCG MSF format</text>
+       <text lang="en">phylip   : Write output in Phylip (interleaved) format</text>
+       <text lang="en">muscle   : Write output in CLUSTALW format with muscle header</text>
+       <text lang="en">clustalw : Write output in CLUSTALW format with CLUSTALW 1.81</text>
+     </comment>
+   </parameter>
+        
+   <parameter isstdout="1">
+     <name>alignmentout</name>
+     <prompt lang="en">Alignment</prompt>
+     <type>
+       <datatype>
+         <class>Alignment</class>
+       </datatype>
+       <dataFormat>
+         <test param="outformat" eq="fasta">FASTA</test>
+         <test param="outformat" eq="msf">MSF</test>
+         <test param="outformat" eq="phyi">PHYLIPI</test>
+         <test param="outformat" eq="clwstrict">CLUSTAL</test>
+         <test param="outformat" eq="clw">MUSCLE</test>
+       </dataFormat>
+     </type>
+     <precond>
+       <code proglang="perl">$outformat =~ /^(fasta|msf|phyi|clwstrict|clw)$/ </code>
+       <code proglang="python">outformat in [ 'fasta' , 'msf' , 'phyi' , 'clwstrict' , 'clw'] and outfile is None</code> 
+     </precond>
+     <filenames>
+       <code proglang="perl">"muscle.out"</code>
+       <code proglang="python">"muscle.out"</code>
+     </filenames>
+   </parameter>
+   \end{lstlisting} 
+   The user specifies the format \texttt{muscle} must use to output the alignment result with
+   the first parameter ``outformat''. The second parameter ``alignmentout''
+   captures the alignment result. The \textbf{dataFormat} of ``alignmentout'' is set by testing the value of
+   the parameter ``outformat". To test a value we have a set of operators:
+   \begin{itemize}
+     \item \textbf{eq}: equal.
+     \item \textbf{ne}: not equal.
+     \item \textbf{lt}: less than.
+     \item \textbf{gt}: greater than.
+     \item \textbf{le}: less or equal than.
+     \item \textbf{ge}: greater or equal than.
+   \end{itemize}
+
+   \item \label{clustalw-multialign-vlist} \begin{lstlisting}[title=\textbf{dataFormat} by referencing another parameter (excerpt from \texttt{clustalw-multialign.xml}), 
+emph={outputformat, $outputformat},emphstyle=\color{blue}\texttt,
+breaklines=true, breakatwhitespace=true, morekeywords={dataFormat, isout}, keywordstyle=\color{red}\textbf]
+   <parameter>
+     <name>outputformat</name>
+     <prompt lang="en">Output format (-output)</prompt>
+     <type>
+       <datatype>
+         <class>Choice</class>
+       </datatype>
+     </type>
+     <vdef>
+       <value>null</value>
+     </vdef>
+     <vlist>
+       <velem undef="1">
+         <value>null</value>
+         <label>CLUSTAL</label>
+       </velem>
+       <velem>
+         <value>FASTA</value>
+         <label>FASTA</label>
+       </velem>
+       <velem>
+         <value>GCG</value>
+         <label>GCG</label>
+       </velem>
+       <velem>
+         <value>GDE</value>
+         <label>GDE</label>
+       </velem>
+       <velem>
+         <value>PHYLIP</value>
+         <label>PHYLIP</label>
+       </velem>
+       <velem>
+         <value>PIR</value>
+         <label>PIR</label>
+       </velem>
+       <velem>
+         <value>NEXUS</value>
+         <label>NEXUS</label>
+       </velem>
+     </vlist>
+     <format>
+       <code proglang="perl">(defined $value ) ? " -output=$value" : ""</code>
+       <code proglang="python">( "" , " -output=" + str( value) )[ value is not None ]</code>
+     </format>
+   </parameter>
+        
+   <parameter isout="1">
+     <name>aligfile</name>
+     <prompt>Alignment file</prompt>
+     <type>
+       <datatype>
+         <class>Alignment</class>
+       </datatype>
+       <dataFormat>
+         <ref param="outputformat"/>
+       </dataFormat>
+     </type>
+     <precond>
+       <code proglang="perl">$outputformat =~ /^(NEXUS|GCG|PHYLIP|FASTA)$/</code>
+       <code proglang="python">outputformat in [ "FASTA", "NEXUS", "GCG", "PHYLIP"]</code>
+     </precond>
+     <filenames>
+       <code proglang="perl">(defined $outfile)? ( $outputformat eq 'GCG' )? ( $outputformat eq 'PHYLIP' )?"$outfile":"*.msf" : "*.phy" : "*.nxs"</code>
+       <code proglang="python">{ "FASTA":"*.fasta", "NEXUS": "*.nxs", "PHYLIP": "*.phy" , 'GCG': '*.msf' }[ outputformat ]</code>
+     </filenames>
+   </parameter>     
+   \end{lstlisting}
+   The user specifies the format in which \texttt{clustalw} will produce the result
+   with the first parameter ``outputformat''. The value of this parameter also becomes
+   the \textbf{dataFormat} of the second parameter ``aligfile'' which is the parameter
+   to capture the alignment.
+   \end{enumerate}
+\end{itemize}
+
+
+\subsection{Mobyle DataTypes Tour}
+
+\paragraph{Simple datatypes}
+
+Simple datatypes characterize the parameters which are used to configure the jobs, and are used to generate the command line (or the paramfiles). They do not represent \textit{per se} scientifically meaningful data, and cannot be bookmarked and reused. Parameters which have a ``simple`` datatype are displayed as ``simple`` form controls (text input, select box, radio button, etc...).
+
+\subparagraph{Boolean}
+Represents boolean values. \underline{None value special case}: 
+for boolean, a None value means False whereas for all other types it means undefined.
+
+\subparagraph{Integer}
+Represents integer values.
+
+\subparagraph{Float}
+Represents float values.
+
+\subparagraph{String}
+Represents string values. Since these strings will be on the command line, for
+security reasons some values are not allowed. The acceptable characters are
+alphanumerical words, spaces, quotes, plus and minus, single dots. 2 or more 
+dots in a row are forbidden. 
+
+This restriction could be problematic for some programs e.g: \texttt{fuzznuc}, \texttt{fuzzpro},
+\texttt{fuzztran}, \ldots Indeed, these programs, from the EMBOSS suite, allow to search
+patterns in sequences using a grammar using characters 
+such as \texttt{[}, \texttt{]} or \texttt{*} forbidden in Mobyle.
+If the parameter used to specify the pattern is typed as string, many
+patterns won't be accepted. One possibility, when available, is to specify this
+kind of value in a file. The special characters will not appear on the command
+line and are thus allowed in Text parameter.
+
+\subparagraph{Choice}\label{choice_paragraph}
+Appears as a select list in the interface. The list can be:
+\begin{itemize}
+ \item a \textbf{vlist} containing predefined entries,
+ \item a \textbf{flist} containing entries computed on the fly.
+\end{itemize}
+
+A \emph{\textbf{vlist}} element contains 2 or more \textbf{velem} elements. Each of them allows 
+to define a new entry consisting of: 
+\begin{enumerate}
+ \item a \textbf{value} that will be assigned to the variable \texttt{\$value}, unless \textbf{undef}='1',
+ \item a \textbf{label} meaningful to the user.
+\end{enumerate}
+The  \textbf{undef} attribute is used to indicate
+that the \textbf{value} of the \textbf{velem} element must be considered as undefined.
+In the description of \texttt{clustalw-multialign} (see \ref{clustalw-multialign-vlist}), the block
+\begin{lstlisting}
+  <velem undef="1">
+    <value>null</value>
+    <label>CLUSTAL</label>
+  </velem>
+\end{lstlisting}
+means that if ``CLUSTAL'' is chosen, the variable \texttt{\$value} is not defined.
+In that case, given the \textbf{code} element:
+\begin{lstlisting}
+<code proglang="python">( "" , " -output=" + str( value) )[ value is not None ]</code>
+\end{lstlisting}
+the \texttt{-output=} won't appear in the command line.
+
+To force the user to consciously choose an entry:
+\begin{enumerate}
+ \item set the \textbf{ismandatory} attribute to 1 for the \textbf{parameter},
+ \item set the \textbf{vdef} to ``null'',
+ \item put as first \textbf{velem} of the \textbf{vlist} something like:
+\begin{lstlisting}
+  <velem undef="1">
+    <value>null</value>
+    <label>Choose a value</label>
+  </velem>
+\end{lstlisting}
+\end{enumerate}
+
+An \emph{\textbf{flist}} element contains \textbf{felem} subelements instead of \textbf{velem} elements. It obeys the same mechanisms as the \textbf{vlist}, but instead of specifying the command line (or \textbf{paramfile}) code in a \textbf{format} element, the evaluated code is specified for each corresponding felem item in a \textbf{code} element. Its main purpose is to facilitate the construction of the command line, as the choice between the different codes requires nested \textit{i [...]
+
+\begin{lstlisting}[title=flist evaluates three different python expressions to generate the command line depending on the chosen value (excerpt from
+targetp.xml), emph={outputformat, $outputformat},emphstyle=\color{blue}\texttt,
+breaklines=true, breakatwhitespace=true, morekeywords={dataFormat, isout}, keywordstyle=\color{red}\textbf]
+          <flist>
+            <felem undef="1">
+              <value>null</value>
+              <label>no cutoffs; winner-takes-all (default)</label>
+              <code proglang="perl">''</code>
+              <code proglang="python">''</code>
+            </felem>
+            <felem>
+              <value>cutoff_95</value>
+              <label>specificity >0.95</label>
+              <code proglang="perl">( $type eq 'p')? " -p 0.73 -t 0.86 -s 0.43 -o 0.84 " : " -t 0.78 -s 0.00 -o 0.73 "</code>
+              <code proglang="python">( " -t 0.78 -s 0.00 -o 0.73 " , " -p 0.73 -t 0.86 -s 0.43 -o 0.84 " )[ type == 'p' ]</code>
+            </felem>
+            <felem>
+              <value>cutoff_90</value>
+              <label>specificity >0.90</label>
+              <code proglang="perl">( $type eq 'p')? " -p 0.62 -t 0.76 -s 0.00 -o 0.53 " : " -t 0.65 -s 0.00 -o 0.52 "</code>
+              <code proglang="python">( " -t 0.65 -s 0.00 -o 0.52 " , " -p 0.62 -t 0.76 -s 0.00 -o 0.53 " )[ type == 'p' ]</code>
+            </felem>
+          </flist>
+\end{lstlisting}
+
+\subparagraph{MultipleChoice}
+Similar to the Choice datatype but represented as a select box
+where several values can be selected at once by holding the appropriate key. 
+The selected values appear on the command line separated by the value of the \textbf{separator} element.
+
+\subparagraph{Filename}
+Generally used to specify a result file name when the program 
+offers the corresponding option. For security reasons, the
+user values \texttt{\# " ' $<$ $>$ \& * ; \$ ` $|$ ( ) [ ] \{ \} ?}  are not allowed.
+
+\paragraph{File-based datatypes}
+
+File-based datatypes characterize all the parameters and user data that are stored as files. Such data are very diverse, spanning from simple analysis reports to sequence data. They are displayed in the job submission forms using a specific component, the databox, which facilitates the load and reuse of data across analyses.
+
+\subparagraph{AbstractText}
+Should not be used directly as a datatype class in Mobyle, it only represents any text file that should not be included in chaining options that are associated with .Created to avoid unwanted inheritances. Should only appear as the value of the \textbf{superclass} element of an actual type.
+
+\subparagraph{Text}
+Handles ``generic`` text files. Before writing the data, the content is cleaned up, \textit{i.e.} the windows end-of-line
+$\backslash n\backslash r$ are replaced by Unix $\backslash n$.
+Moreover, some characters of the file name ( \texttt{\# " ' $<$ $>$ \& * ; \$ ` $|$ ( ) [ ] \{ \} ?} ) 
+are replaced by \texttt{\_} for security reasons. If an input parameter is typed as
+\textbf{Text} a lot of outputs will be potentially chained to this parameter.
+Please use a more specific type if possible.
+
+\subparagraph{Binary}
+Handles ''binary'' data files. A good example of the use of binary files as input data is in the \textbf{abiview} description: the input of EMBOSS abiview is an ABI trace. Of course the content is not ``cleaned''.
+
+\subparagraph{Sequence}
+Handles any text-based sequence format, such as FASTA, Uniprot, \ldots A sequence converter based on \texttt{squizz} is already provided as a plug-in (but of course requires that you install squizz). Unlike in some previous Mobyle versions, the \texttt{readseq} support is no longer provided. 
+In case you wish to use \texttt{readseq} or any other format conversion tool instead of \texttt{squizz}, you can write a python wrapper to handle it. For more information about format converters, please refer to the ``Data converter management`` section of the Mobyle configuration guide.
+Usually, the parameter of ``Sequence'' datatype also defines several \textbf{dataFormat} corresponding to the formats handled natively by the program.
+If the format of the provided data is detected and does not match any of the formats handled by the program, Mobyle will try to convert the data into one of them, by sequentially testing the capabilities of the converters to convert the data.
+
+\subparagraph{Alignment}
+Handles its data almost identically to the ``Sequence'' type, but represents an sequence alignment and has its own formats.
+
+\subparagraph{Tree}
+Represents a phylogenetic tree. Does not implement any specific processing for now.
+
+\subparagraph{Report}
+Designed to handle generic programs text results, if those are not handled by a more specific type.
+
+\subsection{XML DataTypes}
+The Mobyle DataTypes are based on python classes (in \texttt{Src/Mobyle/Classes)}. This
+system offers some powerful features such as inheritance, but has some
+limitations. Indeed, given that the chaining between two parameters is based on the
+type, a python class should be written for each type of data to avoid irrelevant
+chaining. In the bioinformatics field, the number of datatypes is too large to
+manage such a list. That's why a mechanism to define a new datatype on the fly, 
+called ``XML typing'', is also available. When ``XML typing'', a \textbf{class} element 
+and a \texttt{superclass} element must be added in the program description. Beware: 
+\begin{itemize}
+ \item the \textbf{class} element is the new desired datatype,
+ \item the \textbf{superclass} element refers to a Mobyle python DataType class
+\end{itemize}
+The desired datatype is thus considered as a subtype, for features such as programs chaining
+(appearing as a new datatype), but it is handled like its parent Mobyle python class
+for conversion and validation steps. For consistency reasons, when a same ``XML data
+type'' is defined in different parameters, it can't refer to different Mobyle python classes. 
+In  other words, if the \textbf{class} element is identical among 2 parameters, 
+the \texttt{superclass} element must be identical too.
+
+\subsection{Chaining}
+
+The Mobyle system provides a suggestion mechanism allowing users to use data in a defined set of programs:
+\begin{itemize}
+ \item either by proposing in the form the data from the user's workspace that are compatible with the parameter,
+ \item or by letting the user interactively chains the result of a job to another program form.
+\end{itemize}
+In the following, ``source'' stands for a result or any bookmarked data, and the ``target'' is the parameter of 
+the service in which this data is about to be used. The selection of the programs and input parameters
+that can accept a source is based on type compatibility: 
+\begin{itemize}
+ \item the datatype of the input of the target parameter has to be the same as the one of the source or a superclass of it,
+ \item besides, if biotypes (see \ref{biotype}) have been defined in the output and input parameters, one of the 
+ source's biotypes has to be included in the biotypes of the target parameter,
+ \item finally, the ``dataFormat'' of the source and target have to be either directly or indirectly compatible:
+ if the source and the target have specified data formats, the source data format must be in the list of target
+ data formats or be convertible to one of the target's accepted data formats using one of the data converters configured
+ on the portal.
+\end{itemize}
+
+\subsection{Extending Mobyle DataTypes}
+
+The Mobyle python DataType can be extended by coding new classes. Any new class must :
+\begin{enumerate}
+ \item inherit from the DataType class or from another class which inherits from DataType,
+ \item implement at least 2 public methods, \textbf{``convert''} and \textbf{``validate''}, following
+the API defined in DataTypeTemplate (see \texttt{Core.py}).
+\end{enumerate}
+To be able to use the new datatypes in program descriptions
+the same way as those provided in \texttt{Src/Mobyle/Classes}, don't forget:
+\begin{enumerate}
+ \item to put the new modules in \texttt{Local/CustomClasses} to prevent them from being erased during further updates,
+ \item to add the new classes in the \texttt{\_\_init\_\_.py}.
+\end{enumerate}
+
+\section{Output}
+Mobyle can handle results only if they are stored as files. Once a job is
+completed, the different output \textbf{parameter}s are evaluated by applying the
+\textbf{filename} masks (see \ref{masks}) on the job directory to list the corresponding
+result files. Given that the output \textbf{parameter}s have a \textbf{datatype}, the mapping 
+between output \textbf{parameter}s and result files is a way to type the results.
+As said in \ref{typing}, typing is essential for chaining and data reusability.
+
+\section{Parameter display customization}
+
+The default display of a parameter is, \textit{by default}, automatically generated based on its characteristics (type, value
+range \ldots). However, this default display can be overridden to specify custom HTML code which will be used to layout specific 
+\textbf{parameter}s, \textbf{paragraphs}, or even all of them. It is advised to use this possibility with extreme caution, and 
+carefully test the resulting interfaces, to avoid potential problems raised by the interference between the custom HTML code and 
+the Mobyle portal client code (HTML, javascript, etc.).
+
+The interface can customize for each input parameter its layout in the form or in the job summary page (where parameters which 
+have been filled by the user will be displayed), and for each result its layout in the job summary page. Each of these possibilities
+can be specified by adding an \textbf{interface} tag with a \textbf{type} attribute set to:
+\begin{itemize}
+ \item \textbf{form} for input parameters control display in service forms,
+ \item \textbf{job\_input} for input parameters value display in job summaries,
+ \item \textbf{job\_output} for result value display in job summaries.
+\end{itemize}
+
+The contents of the interface tag must be one or more xhtml elements, and the xhtml namespace must be declared accordingly in each
+ of them, using the \textbf{xmlns="http://www.w3.org/1999/xhtml"} namespace declaration. Otherwise, your service description file 
+will not be valid.
+
+To specify where data values and URLs should be placed in job summaries (either inputs or outputs), the following keywords can be used 
+in the interface tag:
+\begin{itemize}
+ \item \textbf{data-value} which specifies the value of the parameter (a string for XML-stored ``simple'' types, the file name for file-stored data),
+ \item \textbf{data-url} which specifies the complete URL of the result for file stored data, i.e. \texttt{the job id + '/' + the file name}.
+\end{itemize}
+These keywords can be placed in any text or attribute value. To handle multiple values (e.g., multiple outputs), the father element of the attribute or
+text node where the keyword is placed is repeated for each parameter value.
+
+Here are a few examples:
+\begin{lstlisting}[title=program input string control display override in the service form, 
+breaklines=true, breakatwhitespace=true,  emph={name="toto"}, emphstyle=\color{blue}\texttt,
+classoffset=0, morekeywords={interface}, keywordstyle=\color{red}\textbf]
+<interface type="form">
+<div xmlns="http://www.w3.org/1999/xhtml">
+<b>use this parameter to tweak something somewhere</b>
+<input name="toto" value="default value of the toto parameter"/>
+</div>
+</interface>
+\end{lstlisting}
+
+\begin{lstlisting}[title=program input string value display override in the job summary, 
+breaklines=true, breakatwhitespace=true,  emph={data-value}, emphstyle=\color{blue}\texttt,
+classoffset=0, morekeywords={interface}, keywordstyle=\color{red}\textbf]
+<interface type="job_input">
+  <h1 xmlns="http://www.w3.org/1999/xhtml">This is the value of this input: data-value</h1>
+</interface>
+\end{lstlisting}
+
+\begin{lstlisting}[title=job output value (file) display override in the job summary, 
+breaklines=true, breakatwhitespace=true,  emph={data-url}, emphstyle=\color{blue}\texttt,
+classoffset=0, morekeywords={interface}, keywordstyle=\color{red}\textbf]
+<interface type="job_output">
+  <div xmlns="http://www.w3.org/1999/xhtml">
+    <img src="data-url" alt="preview" width="500px" height="500px"/>
+  </div>
+</interface>
+\end{lstlisting}
+
+\newpage
+
+\chapter{Writing a workflow description (\textit{new in 1.0}) }
+
+A workflow is an ordered sequence of connected \textbf{task}s. Each \textbf{task} can be a
+program or a workflow. The root element of a workflow is the \textbf{workflow} tag. 
+A workflow description is divided into 3 parts:
+\begin{itemize}
+  \item \textbf{head} describes some generalities about the workflow.
+  \item \textbf{flow} describes the different tasks and how to connect them.
+  \item \textbf{parameters} describes the parameters of the workflow, 
+  that is to say the parameters that the user can/must specify as inputs of the
+  workflow and the results of the workflow.
+\end{itemize}
+
+The following phylogeny workflow:\label{phylogeny_wf}
+\begin{enumerate}
+ \item performs a multiple alignment,
+ \item used to compute a distance matrix,
+ \item to finally build a tree using the neighbor joining method.
+\end{enumerate}
+It is used as an example to illustrate this section.
+
+\section{Head} 
+Contains the same information as programs \textbf{head} (see \ref{programs_head}), minus all the command-line
+execution-specific elements (\textbf{command} and \textbf{env}).
+
+\section{Parameters \label{wf_parameters}}
+
+This section describes the input parameters that the user must/can specify, and
+the results of a workflow. Of course each \textbf{task} remains accessible and all
+intermediate results are easily accessible. The ``results`` of the workflow
+indicates which outputs are presented to the user as final results. Workflow
+\textbf{parameter} elements contain the same information as program \textbf{parameter} elements
+(see \ref{programs_parameters}). However, some elements are irrelevant in 
+a workflow context, i.e, all the command-line execution-specific elements: 
+\textbf{format}, \textbf{flist}, \textbf{argpos}, \textbf{paramfile}. 
+Nevertheless, \textbf{datatype} and \textbf{dataFormat} specification remains very important 
+to be able to reuse a result or visualize it with a viewer (see \ref{viewers}) in Mobyle.
+For instance, if the \texttt{archeopteryx} viewer has been deployed, it can be used
+to visualize the result of the phylogeny workflow defined above since the \textbf{parameter} 2 
+is typed as ``Tree''.
+
+\begin{lstlisting}[title= parameters part of a phylogeny workflow, 
+breaklines=true, breakatwhitespace=true, 
+classoffset=0, morekeywords={parameter, id, isout}, keywordstyle=\color{red}\textbf]
+  <parameters>
+    <parameter id="1">
+      <name>sequences</name>
+      <prompt>Input sequences</prompt>
+      <type>
+        <datatype>
+          <class>Sequence</class>
+        </datatype>
+        <biotype>Protein</biotype>
+      </type>
+    </parameter>
+    <parameter id="2" isout="1">
+      <name>tree</name>
+      <prompt>Phylogenetic Tree</prompt>
+      <type>
+        <datatype>
+          <class>Tree</class>
+        </datatype>
+        <biotype>Protein</biotype>
+      </type>
+    </parameter>
+  </parameters>
+\end{lstlisting}
+The parameter with \textbf{id}=``1'' is the input of the workflow, whereas the parameter
+with \textbf{id}=``2'' with its attribute \textbf{isout}=``1'' represents the result of the workflow.
+
+\section{Flow}
+Is divided in two parts:  
+\begin{itemize}
+ \item \textbf{task},
+ \item \textbf{link}.
+\end{itemize}
+
+\subsection{Task} 
+A \textbf{task} element has the following attributes:
+\begin{itemize}
+\item \textbf{id} (\textit{required}): a unique label allowing to identify
+each \textbf{task} in the workflow
+\item \textbf{service} (\textit{required}): the name of a program or workflow
+\item \textbf{suspend}: an attribute with 2 allowed values ``true'' and ``false''. If \textbf{suspend} is true,
+the workflow will be suspended at the end of the task and will wait for an action from
+the user to resume. This allows the user to check intermediate results and
+decide if he wants to resume or cancel the workflow.
+\item \textbf{description}: a one-sentence-description of the task.
+\item \textbf{inputValue}: allows to specify some values for some \textbf{parameter}s of the \textbf{task} (program or
+workflow). This tag is used to specify a value:
+\begin{itemize}
+ \item because the \textbf{parameter}'s attribute \textbf{ismandatory}=``1''
+ \item to replace the default value \textbf{vdef} of the \textbf{parameter}.
+\end{itemize}
+The \textbf{name} attribute is used to refer to the \textbf{parameter}. Thus 
+use a new \textbf{inputValue} element for each \textbf{parameter} to set.
+\begin{lstlisting}[title= tasks section of phylogeny workflow, breaklines=true, breakatwhitespace=true, 
+classoffset=0, morekeywords={task, id, service, suspend}, keywordstyle=\color{red}\textbf,
+classoffset=1, morekeywords={inputValue, name}, keywordstyle=\color{blue}\textbf] 
+    <task id="1" service="clustalw-multialign" suspend="false">
+      <description>Align the sequences using Clustal-W</description>
+      <inputValue name="outputformat">PHYLIP</inputValue>
+    </task>
+    <task id="2" service="protdist" suspend="true">
+      <description>Compute my distance matrix</description>
+    </task>
+    <task id="3" service="neighbor" suspend="false">
+      <description>Perform neighbor-joining</description>
+    </task>
+\end{lstlisting}
+The \textbf{task} with \textbf{id} ``1'' refers to the \texttt{clustalw-multialign} program 
+which \textbf{parameter} ``outputformat'' has ``PHYLIP'' as value. 
+Of course, all files produced by the \texttt{clustalw-multialign}, \texttt{protdist} and
+\texttt{neighbor-joining} \textbf{task}s remain accessible.
+\end{itemize}
+
+\subsection{Link}
+This is an important part of the workflow design. It describes how to connect 
+the different \textbf{task}s. All connections must be explicit. Each \textbf{link} is 
+directional and joins two points, a source and a destination. \\
+
+The source must be:
+\begin{description}
+ \item[\textit{case 1}] an input \textbf{parameter} of the workflow (see \ref{wf_parameters})
+ \item[\textit{case 2}] a \textbf{parameter} of a \textbf{task}.
+\end{description}
+
+The source is unambiguously pointed out: 
+\begin{description}
+ \item[\textit{in case 1}] by a workflow's input \textbf{parameter}'s \textbf{id}
+assigned to the \textcolor{blue}{\textbf{fromParameter}} attribute,
+ \item[\textit{in case 2}] by the combination of:
+  \begin{enumerate} 
+   \item a \textbf{parameter}'s \textbf{name} assigned to the \textcolor{blue}{\textbf{fromParameter}} attribute,
+   \item a \textbf{task}'s \textbf{id} assigned to the \textcolor{blue}{\textbf{fromTask}} attribute.
+  \end{enumerate}
+\end{description}
+
+The destination must be:
+\begin{description}
+ \item[\textit{case 1}] an output \textbf{parameter} of the workflow (see \ref{wf_parameters}) 
+ \item[\textit{case 2}] a parameter of a \textbf{task}.
+\end{description}
+
+The destination is unambiguously pointed out: 
+\begin{description}
+ \item[\textit{in case 1}] by a workflow's output \textbf{parameter}'s \textbf{id} assigned to the \textcolor{red}{\textbf{toParameter}} 
+attribute, 
+ \item[\textit{in case 2}] by the combination of:
+\begin{enumerate}
+ \item a \textbf{parameter}'s \textbf{name} assigned to the \textcolor{red}{\textbf{toParameter}} attribute
+ \item a \textbf{task}'s \textbf{id} assigned to the \textcolor{red}{\textbf{toTask}} attribute.
+\end{enumerate}
+\end{description}
+ 
+\begin{lstlisting}[title= Example of workflow connections, breaklines=true, breakatwhitespace=true, 
+classoffset=0, morekeywords={fromTask, fromParameter}, keywordstyle=\color{blue}\textbf,
+classoffset=1, morekeywords={toTask, toParameter}, keywordstyle=\color{red}\textbf]
+  <link toTask="1" fromParameter="1" toParameter="infile"/>
+  <link fromTask="1" toTask="2" fromParameter="aligfile" toParameter="infile"/>
+  <link fromTask="2" toTask="3" fromParameter="outfile" toParameter="infile"/>
+  <link fromTask="3" fromParameter="treefile" toParameter="2"/>
+\end{lstlisting}
+\begin{itemize}
+  \item The first  \textbf{link} element indicates that the  \textbf{parameter} with  \textbf{id}=``1'' from
+ the workflow will be sent into the  \textbf{parameter} named ``infile'' from the \textbf{task} with  \textbf{id}
+=``1''.
+  \item The second \textbf{link} element indicates that the \textbf{parameter} named ``aligfile''
+ from the \textbf{task} with \textbf{id}=``1'' will be sent into the \textbf{parameter} named ``infile'' of the \textbf{task}
+with \textbf{id}=``2''.
+  \item The last \textbf{link} element indicates that the \textbf{parameter} named ``treefile'' of
+  the \textbf{task} with \textbf{id}=``3'' will be sent into the \textbf{parameter} with \textbf{id}=``2'' of the
+workflow. This means that it's a result of the workflow.
+\end{itemize}
+
+
+\newpage
+
+\chapter{Writing a viewer/widget description (\textit{important updates in version 1.0.5}) \label{viewers} }
+
+
+Viewers are a way to embed type-dependent visualization components for the data
+displayed in the Mobyle portal. Viewers easily overcome the limitations of the basic 
+text-based data previews offered by Mobyle. These XML
+files provide a way to incorporate custom interface code to display data of a
+given type in the browser. Such custom interfaces can incorporate HTML-embeddable 
+components such as Java applets, Flash applets or Javascript code. For instance, using 
+viewers, the Jalview component can automatically be included wherever it is relevant,
+so that the user can immediately visualize the results of multiple alignment programs 
+such as ClustalW or Muscle in the portal. 
+
+\textbf{As of version 1.0.5,} these widgets now also offer the possibility 
+to bookmark the edited data back to the portal.
+
+A viewer is composed of two elements:
+\begin{enumerate}
+ \item an XML description in many points similar to program descriptions
+ \item a set of dependencies, which are client-required files stored in a directory
+ (e.g., jar files for a Java applet).
+\end{enumerate}
+
+The root element of a viewer description is the \textbf{viewer} tag (instead of the previously-cited 
+\textbf{program} or \textbf{workflow} tags). It is divided in two parts: 
+\begin{itemize}
+  \item the \textbf{head} (\textit{required}) which describes some generalities
+  about the viewer and how to display it.
+  \item the \textbf{parameters} (\textit{required}) which describes (1) the kind
+  of data Mobyle can apply the viewer to, and (2) the data that can be extracted back (if possible)
+  from the viewer.
+\end{itemize}
+ 
+\section{Head}
+Contains the same information as a program, 
+minus invocation-specific elements such as \textbf{command}, \textbf{env}, etc\ldots In addition to this, 
+the author can provide the viewer XHTML template, although it is now possible and recommended to specify each part
+of it at the parameter level since version 1.0.5.
+
+\section{Parameters}
+The \textbf{parameters} section contains all the input and output parameters that will be used by the component to,
+generate the visualization and handle bookmarking. It's very similar to the \textbf{program parameters}, minus the 
+server-side invocation details.
+
+\subsection{Input parameters: displaying/loading the data}
+Input parameters represent the data that are displayed in the viewer/widget, and may provide, in addition to the common elements of input parameters, 
+the viewer XHTML template. Please note that as an alternative to providing the XHTML template for parts of the viewer at each parameter's level, 
+it is also possible to provide a global template in the viewer head element (this is the only way which was supported up to version 1.0.4). More details
+describing how to write this template are given in the Jalview example section (\ref{jalview}).
+
+\subsection{Output parameters: bookmarking and chaining the edited data}
+Output parameters represent the data edited in the viewer/widget that can be bookmarked in the user's Mobyle workspace (or directly chained to another service).
+They include all the common elements of an output (isout) parameter, with the addition of a javascript code chunk that specifies how to communicate with the embedded
+component to retrieve the edited data. 
+
+\section{Example: Jalview}\label{jalview}
+Jalview is a component allowing to graphically display, explore and analyze multiple sequence alignments.
+The aim of this viewer is to enable users to visualize any compatible multiple alignment data. 
+``Compatible'' means that the file format of the output alignment is compatible with those accepted by the \texttt{Jalview} applet.
+The \texttt{jalview.xml} file is provided in the \texttt{pasteur-viewers} descriptions package.
+
+\begin{lstlisting}[title={ \texttt{jalview.xml} file}, breaklines=true, breakatwhitespace=true,
+classoffset=0, morekeywords={data-parametername}, keywordstyle=\color{red}\textbf,
+emph={aligfile, data-url}, emphstyle=\color{blue}\texttt]
+<viewer>
+  <head>
+    <name>jalview</name>
+    <version>2.7</version>
+[...]
+  </head>
+[...]
+  <parameters>
+    <parameter>
+    <!-- this is the description of the input alignment file -->
+      <name>aligfile</name>
+      <prompt>Alignment file</prompt>
+      <type>
+	<datatype>
+	  <class>Alignment</class>
+	</datatype>
+	<!-- below the list of the alignment formats accepted by Jalview -->
+	<dataFormat>FASTA</dataFormat>
+	<dataFormat>GCG</dataFormat>
+	<dataFormat>CLUSTAL</dataFormat>
+	<dataFormat>PIR</dataFormat>
+	<dataFormat>STOCKHOLM</dataFormat>
+      </type>
+      <!-- below the XHTML template that allows to embed Jalview -->
+      <interface type="viewer">
+	<!-- template keyword viewer-codebase is automatically translated to the URL of the viewer/widget's dependencies, that here contains the jalviewApplet.jar file -->
+	<applet xmlns="http://www.w3.org/1999/xhtml" codebase="viewer-codebase" name="Jalview" code="jalview.bin.JalviewLite" archive="jalviewApplet.jar" width="100%" height="100%" id="jalview_applet">
+	  <!-- template keyword data-parametername specifies which parameter this element is processing, here aligfile -->
+	  <!-- template keyword data-url is automatically replaced by the url of the parameter data that have to be loaded -->
+	  <param data-parametername="aligfile" name="file" value="data-url"/>
+	  <param name="embedded" value="true"/>
+	  <param name="linkUrl_1" value=""/>
+	  <param name="srsServer" value=""/>
+	</applet>
+      </interface>
+    </parameter>
+    <parameter isout="1">
+    <!-- this is the description of the edited alignment retrievable in the FASTA format -->
+      <name>edited_fasta_alignment</name>
+      <prompt>Edited FASTA alignment</prompt>
+      <type>
+	<datatype>
+	  <class>Alignment</class>
+	</datatype>
+	<dataFormat>FASTA</dataFormat>
+      </type>
+      <format base64encoded="false">
+	<!-- this is the code which will be used by the portal to retrieve the data -->
+        <!-- the base64encoded attribute above is used to specify wether the retrieved -->
+	<!-- data is text-based or binary and base64encoded -->
+	<code proglang="javascript">$('jalview_applet').getAlignment('fasta')</code>
+      </format>
+    </parameter>
+[...]
+  </parameters>
+</viewer>
+\end{lstlisting}
+
+\subsection{Chaining in}
+Given that in this example:
+\begin{itemize} 
+ \item the \textbf{datatype} of the input \textbf{parameter} ``aligfile'' is ``Alignment''
+ \item the \textbf{dataFormat} of the input \textbf{parameter} ``aligfile'' is ``FASTA''
+\end{itemize}
+the portal offers to visualize any FASTA alignment file with Jalview by providing 
+an additional ``Jalview'' button in the web interface. Clicking on that button triggers
+ the display of the Jalview alignment in a modal dialog box.
+
+\subsection{Chaining out}
+The edited\_fasta\_alignment output parameter is translated in the portal as a ``bookmark/pipe'' component. Clicking
+it, as for other services, bookmarks or pipes the edited alignment in the FASTA format.
+
+\chapter{Writing a tutorial description (\textit{new in version 1.5}) \label{tutorials} }
+ 
+These XML files provide a way to incorporate custom web pages in the tutorials section of Mobyle portal that provide additional help or information on the usage of the portal and the available services.
+
+A tutorial is composed of one or two elements:
+\begin{enumerate}
+ \item an XML description in many points similar to program descriptions.
+ \item a set of dependencies, which may be required to display the tutorial and stored locally. these files (e.g., pictures, movies, \ldots) are stored in a directory.
+\end{enumerate}
+
+The root element of a tutorial description is the \textbf{tutorial} tag (instead of the previously-cited 
+\textbf{program}, \textbf{workflow} or \textbf{viewers} tags). It only contains a head part.
+
+\section{Head}
+
+Contains the same information as a program, minus invocation-specific elements such as \textbf{command}, \textbf{env},\ldots. But the element \textbf{interface} with a \textbf{type} attribute set to ``tutorials'' is required.
+The contents of the interface tag must be one or more xhtml elements, and the xhtml namespace must be declared accordingly in each of them, using the \textbf{xmlns="http://www.w3.org/1999/xhtml"} namespace declaration. Otherwise, your service description file will not be valid.
+
+Here are a few examples:
+\begin{lstlisting}[title=tutorial based on simple xhtml page, 
+breaklines=true, breakatwhitespace=true, emphstyle=\color{blue}\texttt,
+classoffset=0, morekeywords={interface}, keywordstyle=\color{red}\textbf]
+<tutorial>
+   <head>
+      <name>seqfmt</name>
+      <version>1.0</version>
+      <doc>
+         <title>Sequences formats</title>
+         <description>
+            <text lang="en">common used formats for sequences</text>
+         </description>
+         <authors>N. Joly</authors>
+      </doc>
+      <interface type="tutorial">
+         <div xmlns="http://www.w3.org/1999/xhtml">
+            <p>This document illustrates some common formats used for sequences representation.</p>
+            <dl>
+               <dt>
+                  <strong>
+                     <a name="embl" class="item">
+                        <strong>EMBL</strong>
+                     </a>
+                  </strong>
+               </dt>
+
+               <dd>
+                  <pre>ID MMVASPHOS standard; RNA; EST; 140 BP.
+                     AC X97897;
+                     DE M.musculus mRNA for protein homologous to
+                     DE vasodilator-stimulated phosphoprotein
+                     SQ Sequence 140 BP; 25 A; 58 C; 39 G; 17 T; 1 other;
+                     ttctcccaga agctgactct atggngaccc cgagagagac tgagcagaac 60
+                     ccccgcaccc ctgcacttcc aatcaggggc gccccgggag cactccccgt 120
+                     ccgccctccg cgcagccatg 140
+                     //
+                  </pre>
+               </dd>
+             </dl>
+           </div>
+       </interface>
+    </head>
+</tutorial>       
+\end{lstlisting}
+
+The tutorial can use extra materials as pictures or movies. These dependencies, if stored locally, must be placed in a directory with the same name as the xml description
+(minus the extension). In the xml to refer to these extra-materials you must use "tutorial-codebase'' as root for the src path.
+The following example displays a picture in a tutorial:
+\begin{lstlisting}[title=tutorial based on simple xhtml page, 
+breaklines=true, breakatwhitespace=true, emph={name="tutorial-codebase"}, emphstyle=\color{blue}\texttt,
+classoffset=0, morekeywords={interface}, keywordstyle=\color{red}\textbf]
+<tutorial>
+   <head>
+      <name>test_picture</name>
+      <version>1.0-alpha</version>
+      <doc>
+         <title>My beautiful picture</title>
+         <description>
+            <text lang="en">how to display a picture in tutorial</text>
+         </description>
+         <authors>Me</authors>
+      </doc>
+      <category>test:display</category>
+      <interface type="tutorial">
+         <div xmlns="http://www.w3.org/1999/xhtml">
+             <h1>how to display a picture in a tutorial</h1>
+             <img src="tutorial-codebase/my_picture.png">my beautiful picture</img>
+         </div>
+      </interface>
+    </head>
+<tutorial>      
+\end{lstlisting}
+In the example above the description file must be named test\_picture (as the name tag content plus the ``.xml'' extension) and a file ``my\_picture.png'' must be placed in a directory called test\_picture.
+
+
+\chapter{Validation}
+
+When creating or modifying a service description, validating the XML code is highly recommended 
+to detect mistakes which can cause problems, in particular at display or job execution time. 
+To validate a service description, use the \texttt{mobvalid} script located in the \texttt{\$MOBYLEHOME/Tools} directory\footnote{This
+tool will simply automate the validation of the XML file, according to its grammar based in the \texttt{*.rnc}/\texttt{rng} files
+and a set of additional schematron rules. However, it should not be necessary to be familiar with these files to be able
+to write a service description.}.
+
+As the typing system is central to the chaining process, the consistency between the types is crucial.
+A same ``XML data type'' appearing in different service descriptions must always refer to the same python superclass.
+Some typographical mistakes in the XML class or in a \textbf{biotype} can prevent the desired chaining 
+or lead to an unexpected chaining.  The \texttt{mobtypes} script located in \texttt{\$MOBYLEHOME/Tools} 
+scans service descriptions and python classes to create a repository of all types used in your portal. 
+This tool helps the Mobyle administrator to maintain the consistency of his portal or the service description author to choose the right types.
+The usage of \texttt{mobtypes} script is explained in the associated \texttt{README} file.
+
+When the \texttt{Tools/mobtypes} script analyzes a service description, it generates a report with 3 sections:
+\begin{itemize}
+\item the Mobyle \textbf{datatype} based on python class,
+\item the DataTypes defined by ``XML typing'',
+\item the \textbf{biotypes} (see \ref{biotype}) used.
+\end{itemize}
+By default this script analyzes all installed service descriptions. It helps the Mobyle administrator to keep
+a consistent set of types for the portal.
+
+To be used in the portal, the services must be deployed and debugged if necessary.
+Those operations are described in \texttt{how\_to\_configure\_Mobyle.pdf}.
+
+\chapter{Upgrade from version 0.97}
+
+Upgrading service definitions from previous versions to v1.0 should be reasonably straightforward. The only two incompatibilities are:
+
+\begin{itemize}
+
+ \item in the \textbf{dataFormat} element: whereas in versions 0.9x \textbf{dataFormat} elements are sometimes placed in a container \textbf{acceptedDataFormats} element, this container has now been removed and dataFormat elements should be direct children of the \textbf{type} element.
+
+ \item the \textbf{interface} element has to have a \textbf{type} attribute: the details of this mechanism which extends the customization possibilities are described in the detailed documentation, but legacy 0.97 XML \textbf{interface} elements should be updated with a \textbf{type="form"} attribute for inputs and a \textbf{type="job\_output"} attribute for results.
+
+\end{itemize}
+
+The other evolutions of the grammar are additions are annotated with the ``\textit{new in 1.0}`` label.
+
+
+
+\end{document}
\ No newline at end of file
diff --git a/Example/Local/Config/Config.template.py b/Example/Local/Config/Config.template.py
new file mode 100644
index 0000000..6790d26
--- /dev/null
+++ b/Example/Local/Config/Config.template.py
@@ -0,0 +1,461 @@
+import os , os.path
+# this a python module the syntax used is the python syntax
+
+
+############################################
+#                                          #
+#            Mandatory values              #
+#                                          #
+############################################
+
+
+# the root url of mobyle 
+# warning: do NOT include the port number if it is 80, it is useless
+# and creates problems on the server
+ROOT_URL = "http://mydomain.fr:port"
+
+#base project url for mobyle cgis = ROOT_URL + CGI_PREFIX
+CGI_PREFIX = 'cgi-bin/mobyle/xxxxx'
+
+#base project url for mobyle htdocs = ROOT_URL + HTDOCS_PREFIX
+HTDOCS_PREFIX = 'mobyle/xxxxx'
+
+#########
+#       #
+# mails #
+#       #
+#########
+
+#a list of email addresses who will received messages when problems occur
+MAINTAINER = [ "name at mydommain.fr" ]
+
+#a list of email addresses where the users could have help on this Mobyle portal
+HELP= [ "name2 at mydommain.fr" , "name3 at mydommain.fr" ]
+
+# local mailhost
+MAILHOST= "mailhost.mydommain.fr"
+
+##################
+#                #
+# queuing system #
+#                # 
+##################
+
+from Execution import  *
+
+#EXECUTION_SYSTEM_ALIAS = {
+#                          'DRMAA_sge'   : SgeDRMAAConfig( '/path/to/sge/libdrmaa.so' , 
+#                                                          root = 'path to sge root', 
+#                                                          cell = 'cell name' ) ,
+#                          'DRMAA_torque': PbsDRMAAConfig( '/path/to/torque-pbs/libdrmaa.so' , 
+#                                                          'marygay.sis.pasteur.fr' ),
+#                          'SGE'         : SGEConfig( root = '/path/to/sge_root', 
+#                                                     cell= 'cell name' ) ,
+#                          'SYS'         : SYSConfig() , 
+#                          'LSF'         : LsfDRMAAConfig( '/path/to/LSF/libdrmaa.so' ,  
+#                                                          lsf_envdir = 'the contained of LSF_ENVDIR' ,
+#                                                          lsf_serverdir = 'the contained of LSF_SERVERDIR')
+#                          }
+
+EXECUTION_SYSTEM_ALIAS = { 'SYS'         : SYSConfig()  }
+
+from Mobyle.Dispatcher import DefaultDispatcher
+
+#DISPATCHER = DefaultDispatcher( { 
+#                                  'service1' : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_sge' ]    , 'queue name' ),
+#                                  'service2' : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_torque' ] , 'queue name' ),
+#                                  'service3'  : ( EXECUTION_SYSTEM_ALIAS[ 'LSF' ]         , 'queue name' ),
+#                                  'service4' : ( EXECUTION_SYSTEM_ALIAS[ 'SGE' ]          , 'queue name' ),
+#                                  'service5'    : ( EXECUTION_SYSTEM_ALIAS[ 'DRMAA_sge' ]    , 'queue name' ),
+#                                  'DEFAULT'   : ( EXECUTION_SYSTEM_ALIAS[ 'SYS' ]          , '' )
+#                           } )
+
+DISPATCHER = DefaultDispatcher( { 'DEFAULT'   : ( EXECUTION_SYSTEM_ALIAS[ 'SYS' ]  , '' ) } )
+#######################
+#                     #
+#       logging       #
+#                     #
+#######################
+
+#the directory were will stored the log files
+LOGDIR = '/var/log/mobyle/'
+
+
+####################################################
+#                                                  #
+#    data format detection and conversion          # 
+#                                                  #
+####################################################
+
+# for Sequence and Alignment, the HIGHLY recommended squizz converters 
+# are available
+#DATA_CONVERTER={
+#      'Datatype1': [converter_class1(path_to_the_corresponding_converter_program), converter_class2(path)],
+#      'Datatype2': [converter_class1(path_to_the_corresponding_converter_program), converter_class2(path)]
+#      }
+from Mobyle.Converter import *
+
+DATA_CONVERTER={
+      'Sequence': [ squizz_sequence('/path/to/squizz') ] ,
+      'Alignment': [ squizz_alignment('/path/to/squizz')]
+                }
+
+
+          ######################################################################## 
+          ######################################################################## 
+          ##                                                                    ##
+          ##                           Optionals Values                         ##
+          ##                                                                    ##
+          ########################################################################
+          ########################################################################
+
+#########
+#       #
+# Debug #
+#       #
+#########
+
+#used in production
+# 0    - the command line is build
+#      - the build log is NOT fill
+#      - the job is executed
+
+#to test a xml ( python syntax in code , precond ... )
+# 1    - the command line is build 
+#      - the build log is NOT fill
+#      - the job is NOT executed
+
+#to know what's wrong in the xml I wrote.
+# 2    - the command line is build
+#      - the build log is fill
+#      - the job is NOT executed
+
+#to test the xml and the job execution and the results retrieving
+# 3    - the command line is build
+#      - the build log is fill
+#      - the job is executed
+
+DEBUG = 3
+
+#to set a different debug level for a particular service
+#PARTICULAR_DEBUG = { 'serviceName' : 3 }
+
+
+###############
+#             #
+# Directories #
+#             #
+###############
+
+## were are the binary corresponding to the services
+## a list of string
+## each element must be a valid path
+## the element order is kept to build the final PATH ( the binary path is add before the canonical PATH )
+
+BINARY_PATH = ["/usr/local/bin"]
+
+
+#DATABANKS_CONFIG = {
+#    'wgs':{
+#     'dataType' : 'Sequence', 
+#     'bioTypes' : ['Nucleic'],    
+#     'label'    : 'Genbank - Whole Genome Shotgun',
+#     'command': ['golden', '%(db)s:%(id)s']
+#    },
+#    'pdb':{
+#     'dataType':'3DStructure', 
+#     'bioTypes':['Protein'], 
+#     'label': 'Protein Data Bank', 
+#     'command': [ "/path/to/PDBGet.py", "%(id)s" ]
+#    }
+#    }
+
+DATABANKS_CONFIG = {}
+
+#####################
+#
+#   Statistics
+#
+#####################
+
+## Google Analytics code - set to use GA
+#GACODE = 'XXXXXXXXXX'
+
+
+
+######################
+#                    #
+#   Authentication   #
+#                    #
+######################
+
+## Enable OpenID
+# default value = False
+
+# OPENID = True
+
+#####################
+#
+#   Welcome page configuration
+#
+#####################
+
+## news_ex illustrating example feed is in Example/Local directory.
+#WELCOME_CONFIG = {'url':'http://localhost:85/portal/news_ex.atom',
+#                 'format':'atom'}
+
+#####################
+#
+#   Portal custom content for header and footer
+#
+#####################
+#
+#CUSTOM_PORTAL_HEADER="<h1>myOwnPortalName</h1>"
+#
+#CUSTOM_PORTAL_FOOTER="<div>insert the name of your sponsors or your legal disclaimers</div>
+
+## to make email optional for all programs, set this to True
+## default value = False
+
+#OPT_EMAIL = False
+
+## to have a more control on the mandatory email
+## it able to have the general option but
+## it could be set to another value for particular service
+## example :
+## OPT_EMAIL could be set at False ( the email is mandatory )
+## but set a True for some very short services.
+# the key is the portal id of a service and value is a boolean
+# for local services the portal id the server name can be ommited
+# eg 'local.golden' == 'golden'
+
+#PARTICULAR_OPT_EMAIL = {'portal_id' : True }
+
+#if a job is longer than EMAIL_DELAY Mobyle send the results by email (if user give it's email)
+#its express in second, 0 mean no email (default = 60)
+#EMAIL_DELAY = 60
+
+## anonymous session there is 3 available values 
+##  'no'      : the anonymous sessions are not allowed
+##  'yes'     : the anonymous sessions are allowed, without any verification 
+##  'captcha' : the anonymous sessions are allowed, but with a captcha challenge ( default )
+
+#ANONYMOUS_SESSION = "captcha"
+
+
+## authenticated session there is 3 available values
+##  'no'    : the authenticated session are not allowed.
+##  'yes'   : the authenticated session are allowed and activated without any restriction.
+##  'email' : the authenticated session are allowed but an email confirmation is needed to activate it (default).
+
+#AUTHENTICATED_SESSION = "email"
+
+##############
+#            #
+#    misc    #
+#            #
+##############
+
+## the time to consider that a job is long ( in sec default = 60 , min = 10 )
+#TIMEOUT = 60
+
+## refresh frequency for user data in the web portal, in seconds
+## default: 240
+
+#REFRESH_FREQUENCY = 240
+
+## max size for any file (2 Go )
+#this limit is honored only if the EXECUTION_SYSTEM_ALIAS is set to Sys  (SYSConfig)
+#if SGE, PBS, ... is used this must be set directly in the drm configuration.
+#FILELIMIT = 2147483648
+
+## max size for a session in bytes ( default = 50Mo = 52428800 )
+#SESSIONLIMIT = 52428800 
+
+##max size to preview the results 
+##if the results size exceed this limit the results appear as a link ( default value = 1048576 = 1Mib )
+#PREVIEW_DATA_LIMIT = 1048576
+
+#the number of "similar" jobs a user is allowed to submit at a given time.
+#similar means same email, same command line. ( default = 1 )
+#0 disable this control, then the user may submit as many "same" jobs he wants.
+#if the email is not provided (depend of the configuration) this control is disabled.    
+
+# !! this new directive replace SIMULTANEOUS_JOBS !!
+#MAX_SIMILAR_JOB_PER_USER =1
+
+
+#the number of jobs than a user can submit at a given time
+#0 disable this control, then the user may submit as many "same" jobs he wants (default).
+#if the email is not provided (depend of the configuration) this control is disabled.
+#MAX_JOB_PER_USER = 0
+
+
+
+##########
+#        #
+#  Mail  #
+#        #
+##########
+
+##from: sender email address ( default = HELP )
+#SENDER = "myname-noreply at mydomain.fr"
+
+##set this True if you don't want results to be sent by email.
+##This does not make email optional
+#DONT_EMAIL_RESULTS = False
+
+## max size for results by email ( in bytes default 2 Mib )
+#MAXMAILSIZE = 2097152
+
+## how long should results be available on the server ( in days default = 10)?
+#RESULT_REMAIN = 10
+
+
+## if you want to resolve the domain name of the user email
+## and if it has a mail exchanger field
+## (to avoid fake user email address)
+## by default DNS_RESOLVER = False
+## if DNS_RESOLVER = True dnspython must be installed
+
+#DNS_RESOLVER = False
+
+
+
+################
+#              #
+#   logging    #
+#              #
+################
+
+## to monitored the elapsed time per job ( default = False )
+#ACCOUNTING = True
+
+#######################
+#                     #
+# disabling services  #
+#                     #
+#######################
+
+## some times you need to disable the portal for maintaining operation etc...
+## if DISABLE_ALL is True no new job could be submit, but the running job keep running
+
+#DISABLE_ALL = False
+
+## To disable specifically one service (program or workflow) from any portal, you can append it in  DISABLED_SERVICES.
+## joker can be used, so it's easy to disable all services from a given portal.
+## this portal is call 'local'
+## to re-enable services just toggle DISABLE_ALL to False or remove it from 
+## the DISABLED_SERVICES list
+## example:
+##DISABLED_SERVICES = [ 'portal1.service1' , # disable the service1 from the imported portal1 (as defined in PORTALS )
+##                      'portal2.*' ,        # disable all services from the imported portal2
+##                      'local.clustalw*'    # disable all services begining by clustalw (clutalw-multialign ,clustalw-sequence , clustalw-profile ) from this server.
+##                     ] 
+## By default all services are enabled
+
+#DISABLED_SERVICES = [] 
+
+
+################################
+#                              #
+# restriction services access  #
+#                              #
+################################
+
+## by default all the programs available are usable by all         
+## users who can access your web server. but sometimes,due to      
+## some license restrictions etc... , you need to restrict the     
+## accessibility of some programs to some users.                    
+## to do that use the AUTHORIZED_SERVICES                          
+## The filtering is based on the ip of the requester.              
+## AUTHORIZED_SERVICES is a dictionary with the service names of   
+## programs to restrict as keys and the list of ip which can          
+## access these programs as values                                 
+##                                                                 
+## AUTHORIZED_SERVICES = { serviceURL :[ ip , ip mask ] }         
+##
+## the ip address which can use the service
+##
+## AUTHORIZED_SERVICES = 'http://myMobyle.mydomain.fr/data/programs/toppred.xml' : [ '125.234.60.18' , # only the machine with this ip could access to toppred
+##                                                                                   '125.234.60.*'  , # all the machines in subnet could access to toppred
+##                                                                                   '125.234.*.15   , # all the machine 
+##                                                                                 ] 
+##
+## if there is no entry for a service it's mean that
+## every body can access to this service
+
+
+#AUTHORIZED_SERVICES = {}
+
+
+################################
+#                              #
+#     Services Management      #
+#                              #
+################################
+
+##################################################################################
+#                                                                                #
+# Local program publishing section                                               #
+#                                                                                #
+# This configuration file is used to deploy the xml services descriptions on     # 
+# the Mobyle web part from the Local/Services/* and /Srevices/* directories.     # 
+# all the xml from Local/Services/*/* are deployed                               #
+# the xml from Services/*/* are filtered following the rules below               #  
+# (for more explanations see INSTALL notes and Tools/README)                     #
+#                                                                                #
+# (if this file doesn't exist all the programs from Programs will be published)  # 
+#                                                                                #
+##################################################################################
+
+#the list of programs to deploy is established by filtering the Local/Services/ repository and Services repository
+#with the LOCAL_DEPLOY_INCLUDE following the LOCAL_DEPLOY_EXCLUDE.
+#each value corresponds to a unix mask to filter a directory, so the unix joker * can be used.
+
+#LOCAL_DEPLOY_INCLUDE = { 'programs' : [ 'blast*' ] ,             #all programs definitions begining by blast will be deployed
+#                          'workflows': [ '*' ] ,                 #all workflows will be deployed
+#                          'viewers'  : [ 'jalview' , 'varna' ] , #only jalview and varna will be deployed
+#                          }
+
+#LOCAL_DEPLOY_EXCLUDE = { 'programs' : [ 'blast2mydb' ] , #blast2mydb will be removed from the list of programs to deploy
+#                          'workflows': [ '' ] ,
+#                          'viewers'  : [ '' ] ,
+#                          }
+
+LOCAL_DEPLOY_INCLUDE = { 'programs' : [ '*' ] ,
+                          'workflows': [ '*' ] ,
+                          'viewers'  : [ '*' ] ,
+                          }
+
+LOCAL_DEPLOY_EXCLUDE = { 'programs' : [ '' ] ,
+                          'workflows': [ '' ] ,
+                          'viewers'  : [ '' ] ,
+                          }
+
+
+######################
+#                    #
+#    Grid aspects    #
+#                    #
+######################
+# PORTAL_NAME is the name of the portal as it is known to others in a MobyleNet network
+#PORTAL_NAME = "my_server"
+
+# SIMPLE_FORMS activates the display of simple forms by default
+# inactive by default
+#SIMPLE_FORMS = False
+
+#PORTALS={'portal1': {
+#            'url': 'http://otherdomain.fr:port/cgi-bin/MobylePortal',
+#            'help' : 'help at otherdomain.fr',
+#            'repository': 'http://otherdomain.fr:port/',
+#            'services': { 'programs' : ['clustalw-multialign'],
+#                          'workflows': [ 'wokflow1_of_portal1']
+#            }
+#        }
+PORTALS={}
+
+#EXPORTED_SERVICES = [ 'golden', 'abiview' ]
+
+
diff --git a/Example/Local/CustomClasses/__init__.py b/Example/Local/CustomClasses/__init__.py
new file mode 100644
index 0000000..19bb5da
--- /dev/null
+++ b/Example/Local/CustomClasses/__init__.py
@@ -0,0 +1,2 @@
+#does mo forget to update this file if you want your module
+#is used in mobyle
diff --git a/Example/Local/Policy.ldap.py b/Example/Local/Policy.ldap.py
new file mode 100644
index 0000000..273a388
--- /dev/null
+++ b/Example/Local/Policy.ldap.py
@@ -0,0 +1,87 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import sys
+import os.path
+
+from Mobyle.MobyleError import *
+
+import logging 
+p_log = logging.getLogger('Mobyle.Policy')
+
+
+
+def queue( queueName ):
+    """
+    @return: the name of the queue to be used to execute the job
+    @rtype: string
+    """
+    return queueName
+
+
+def emailCheck( **args ):
+    """
+    check if the email according to the local rules.
+    @return:
+     - Mobyle.Net.EmailAddress.VALID    if the email is valid
+     - Mobyle.Net.EmailAddress.INVALID  if the email is rejected
+     - Mobyle.Net.EmailAddress.CONTINUE to continue futher the email validation process
+    """
+    import Mobyle.Net
+
+    user , domainName  = args['email'].split('@')
+    if domainName == 'pasteur.fr':
+        try:
+            local = isLocal( args['email'] )
+        except MobyleError , err:
+            p_log.error( "an error is occured during checking local login : "+ str( err ))
+            # I don't stop Mobyle for that. The user continue as an external user
+            return Mobyle.Net.EmailAddress.CONTINUE
+        
+        if local:
+            return Mobyle.Net.EmailAddress.VALID
+        else:
+            return Mobyle.Net.EmailAddress.INVALID
+    else:
+        return Mobyle.Net.EmailAddress.CONTINUE
+        
+
+def isLocal( email ):
+    """
+    @return: True if the userName is a pasteur login, False otherwise.
+    @rtype: boolean
+    """
+    import ldap
+    con = ldap.initialize( 'ldap://ldap.pasteur.fr' )
+    try:
+        con.simple_bind_s()
+    except Exception , err :
+        raise MobbyleError , err
+    user , domainName  = email.split('@')
+    
+    base_dn='ou=personnes,dc=pasteur,dc=fr'
+    if user.find('.') != -1:
+        filter = '(& (objectclass=posixAccount) (mail=%s))' %email
+    else:
+        filter = '(& (objectclass=posixAccount) (uid=%s))' %user
+    attrs =['mail']
+    
+    try:
+        user = con.search_s( base_dn , ldap.SCOPE_SUBTREE , filter , attrs )  
+    except Exception , err :
+        raise MobyleError , err
+    if user:
+        try:
+            ldapMail = user[0][1][ 'mail' ][0]
+        except KeyError , err:
+            #some one try to use a uid which have not mail attribute like dbmaint or sge
+            p_log.critical( "some one try to connect with an uid which have not mail : " + str( email ) )
+            return False
+        return True
+    else:
+        return False
diff --git a/Example/Local/Policy.pasteur.py b/Example/Local/Policy.pasteur.py
new file mode 100644
index 0000000..38902fa
--- /dev/null
+++ b/Example/Local/Policy.pasteur.py
@@ -0,0 +1,220 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import sys
+import os.path
+
+import logging 
+p_log = logging.getLogger('Mobyle.Policy')
+
+
+
+def queue( queueName ):
+    """
+    @return: the name of the queue to be used to execute the job
+    @rtype: string
+    """
+    return queueName
+
+
+def emailCheck( **args ):
+    """
+    check if the email according to the local rules.
+    @return:
+     - Mobyle.Net.EmailAddress.VALID    if the email is valid
+     - Mobyle.Net.EmailAddress.INVALID  if the email is rejected
+     - Mobyle.Net.EmailAddress.CONTINUE to continue futher the email validation process
+    """
+    import Mobyle.Net
+    from Mobyle.MobyleError import MobyleError
+    
+    user , domainName  = args['email'].split('@')
+    if domainName == 'pasteur.fr':
+        try:
+            local = isLocal( args['email'] )
+        except MobyleError , err:
+            p_log.error( "an error is occured during checking local login : "+ str( err ))
+            # I don't stop Mobyle for that. The user continue as an external user
+            return Mobyle.Net.EmailAddress.CONTINUE
+        
+        if local:
+            return Mobyle.Net.EmailAddress.VALID
+        else:
+            return Mobyle.Net.EmailAddress.INVALID
+    else:
+        return Mobyle.Net.EmailAddress.CONTINUE
+
+        
+def emailUserMessage( method ):
+    messages = {
+                'host'       : "you have abused our service. Your are not allowed to run on this server for now. For more informations contact mobyle at pasteur.fr",
+                'syntax'     : "you are not allowed to run on this server for now" ,
+                'blackList'  : "you have abused our service. Your are not allowed to run on this server for now. For more informations contact mobyle at pasteur.fr",
+                'LocalRules' : "you are not allowed to run on this server for now",
+                'dns'        : "you are not allowed to run on this server for now" ,
+    }
+    try:
+        return messages[ method ]
+    except KeyError:
+        return "you are not allowed to run on this server for now"
+    
+def isLocal( email ):
+    """
+    @return: True if the userName is a pasteur login, False otherwise.
+    @rtype: boolean
+    """
+    import ldap
+    con = ldap.initialize( 'ldap://ldap.pasteur.fr' )
+    try:
+        con.simple_bind_s()
+    except Exception , err :
+        raise MobbyleError , err
+    user , domainName  = email.split('@')
+    
+    base_dn='ou=personnes,dc=pasteur,dc=fr'
+    if user.find('.') != -1:
+        filter = '(& (objectclass=posixAccount) (mail=%s))' %email
+    else:
+        filter = '(& (objectclass=posixAccount) (uid=%s))' %user
+    attrs =['mail']
+    
+    try:
+        user = con.search_s( base_dn , ldap.SCOPE_SUBTREE , filter , attrs )  
+    except Exception , err :
+        raise MobyleError , err
+    if user:
+        try:
+            ldapMail = user[0][1][ 'mail' ][0]
+        except KeyError , err:
+            #some one try to use a uid which have not mail attribute like dbmaint or sge
+            p_log.critical( "some one try to connect with an uid which have not mail : " + str( email ) )
+            return False
+        return True
+    else:
+        return False
+
+
+
+def authenticate( login , passwd ):
+    """
+    Mobyle administrator can put authentification code here. this function must return either 
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE: 
+                the method does not autentified this login/passwd. The fall back method must be applied.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.VALID:    
+               the login/password has been authenticated.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.REJECT:    
+              the login/password is not allow to continue.
+    """
+    import Mobyle.AuthenticatedSession
+    
+    return Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE
+
+
+def allow_to_be_executed( job ):
+    """
+    check if the job is allowed to be executed 
+    if a job is not allowed must raise a UserError
+    @param job: the job to check before to submit it to Execution
+    @type job: L{Job} instance  
+    @raise UserValueError: if the job is not allowed to be executed
+    """
+    #place here the code you want to be executed to allow a job to be executed.
+    #this is the last control be fore DRM submission
+    #if you want to limit simultaneous job according some criteria as IP or user email, ...
+    #this this the right place to put your code 
+    user_email= job.getEmail()
+    if str(user_email).endswith("@pasteur.fr") :
+        #the email has been already check
+        #and for email @pasteur.fr we already check the LDAP 
+        #so it's not usefull to check the LDAP again at this step
+        return True
+    else:
+        from Mobyle.MobyleError import UserValueError , MobyleError
+        try:
+            return over_limit( job )
+        except UserValueError, err:
+            raise UserValueError( parameter = None, msg = str(err) )
+        except Exception , err:
+            p_log.error( str(err) , exc_info = True )
+            raise MobyleError, "Internal Server Error"
+    
+def over_limit (job):
+    """
+    check if the user (same email) has a similar job (same command line or same workflow name) running 
+    @param job: the job to check before to submit it to Execution
+    @type job: L{Job} instance  
+    @raise UserValueError: if the number of similar jobs exceed the Config.SIMULTANEOUS_JOBS
+    """
+    from hashlib import md5
+    import glob
+    from Mobyle.Admin import Admin
+    from Mobyle.MobyleError import UserValueError
+    
+    max_same_jobs = job.cfg.max_similar_job_per_user()
+    max_user_jobs = job.cfg.max_job_per_user()
+    
+    user_email = str( job.getEmail() )
+    newMd5 = md5()
+    newMd5.update( job.getCommandLine() )
+    newDigest = newMd5.hexdigest()
+    
+    work_dir = job.getDir()
+    thisJobAdm = Admin( work_dir )
+    thisJobAdm.setMd5( newDigest )
+    thisJobAdm.commit()
+    
+    if thisJobAdm.getWorkflowID():
+        #this job is a subtask of a workflow 
+        #we allow a workflow to run several identical job in parallel
+        return True
+    mask = os.path.normpath( "%s/*.*" % ( job.cfg.admindir()) )
+    jobs = glob.glob( mask )
+        
+    same_jobs_nb = 0
+    jobs_nb = 0
+    msg = None
+    for one_job in jobs:
+        try:
+            oldAdm = Admin( one_job )
+        except MobyleError, err :
+            if os.path.lexists( one_job ):
+                p_log.critical( "%s/%s: invalid job in ADMINDIR : %s" %( job.getServiceName() ,  job.getKey() , err ) )
+            continue
+        if( oldAdm.getWorkflowID() ):
+            #this job is NOT a workflow but
+            #this job is the "same" than a workflow subtasks 
+            #we allow a workflow to run several identical job in parallel
+            continue
+        old_email = oldAdm.getEmail() 
+        oldDigest = oldAdm.getMd5()
+        if user_email == old_email:
+            oldStatus = job.getStatus() 
+            if oldStatus.isEnded() :
+                p_log.debug( "oldStatus.isEnded() = True")
+                continue
+            jobs_nb += 1
+            if max_user_jobs and  jobs_nb >= max_user_jobs:
+                msg = "%d jobs (%s) have been already submitted (md5 = %s)" % ( jobs_nb ,
+                                                                                os.path.basename( one_job ),
+                                                                                newDigest
+                                                                                )
+                userMsg = " %d job(s) have been already submitted, and are(is) not finished yet. Please wait for the end of these jobs before you resubmit." % ( jobs_nb )
+                p_log.warning( msg + " : run aborted " )
+                raise UserValueError( parameter = None, msg = userMsg )
+        if newDigest == oldDigest :
+            same_jobs_nb += 1
+            if max_same_jobs and same_jobs_nb >= max_same_jobs:
+                msg = "%d similar jobs (%s) have been already submitted (md5 = %s)" % (
+                                                                                same_jobs_nb ,
+                                                                                os.path.basename( one_job ),
+                                                                                newDigest
+                                                                                )
+                userMsg = " %d similar job(s) have been already submitted, and are(is) not finished yet. Please wait for the end of these jobs before you resubmit." % ( same_jobs_nb )
+                p_log.warning( msg + " : run aborted " )
+                raise UserValueError( parameter = None, msg = userMsg )
+    return True
diff --git a/Example/Local/Policy.py b/Example/Local/Policy.py
new file mode 100644
index 0000000..46d252d
--- /dev/null
+++ b/Example/Local/Policy.py
@@ -0,0 +1,166 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import sys
+import os.path
+
+from logging  import getLogger
+p_log = getLogger('Mobyle.Policy')
+
+
+
+def queue( queueName ):
+    """
+    @return: the name of the queue to be used to execute the job
+    @rtype: string
+    """
+    return queueName
+
+
+
+#if you want to defined a local policy to check user
+# you must put you code here 
+def emailCheck( **args ):
+     """
+     check if the email according to the local rules.
+     @return:
+      - Mobyle.Net.EmailAddress.VALID    if the email is valid
+      - Mobyle.Net.EmailAddress.INVALID  if the email is rejected
+      - Mobyle.Net.EmailAddress.CONTINUE to continue futher the email validation process
+     """
+     import Mobyle.Net
+
+     return Mobyle.Net.EmailAddress.CONTINUE
+
+
+#to change the message when a host or email is blocked
+# host : the ip is in black list
+# invalid : syntax for email
+# blackList : the email provide by the user is in black list
+# dns : the email domain provide by the user haven't any mail server
+
+def emailUserMessage( method ):
+    messages = {
+                'host'       : "you are not allowed to run on this server for now",
+                'syntax'     : "you are not allowed to run on this server for now" ,
+                'blackList'  : "you are not allowed to run on this server for now",
+                'LocalRules' : "you are not allowed to run on this server for now",
+                'dns'        : "you are not allowed to run on this server for now" ,
+    }
+    try:
+        return messages[ method ]
+    except KeyError:
+        return "you are not allowed to run on this server for now"
+
+def authenticate( login , passwd ):
+    """
+    Mobyle administartor can put authentification code here. this function must return either 
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE: 
+                the method does not autentified this login/passwd. The fall back method must be applied.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.VALID:    
+               the login/password has been authenticated.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.REJECT:    
+              the login/password is not allow to continue.
+    """
+    import Mobyle.AuthenticatedSession
+    
+    return Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE
+
+
+def allow_to_be_executed( job ):
+    """
+    check if the job is allowed to be executed 
+    if a job is not allowed must raise a UserError
+    @param job: the job to check before to submit it to Execution
+    @type job: L{Job} instance  
+    @raise UserValueError: if the job is not allowed to be executed
+    """
+    #place here the code you want to be executed to allow a job to be executed.
+    #this is the last control be fore DRM submission
+    #if you want to limit simultaneous job according some criteria as IP or user email, ...
+    #this this the right place to put your code 
+    from Mobyle.MobyleError import UserValueError
+    return over_limit( job )
+    
+    
+    
+def over_limit (job):
+    """
+    check if the user (same email) has a similar job (same command line or same workflow name) running 
+    @param job: the job to check before to submit it to Execution
+    @type job: L{Job} instance  
+    @raise UserValueError: if the number of similar jobs exceed the Config.SIMULTANEOUS_JOBS
+    """
+    from hashlib import md5
+    import glob
+    from Mobyle.Admin import Admin
+    from Mobyle.MobyleError import UserValueError
+    
+    newMd5 = md5()
+    newMd5.update( str( job.getEmail() ) )
+    try:
+        remote = os.environ[ 'REMOTE_HOST' ]
+        if not remote :
+            try:
+                remote = os.environ[ 'REMOTE_ADDR' ]
+            except KeyError :
+                remote = 'no web'
+    except KeyError:
+        try:
+            remote = os.environ[ 'REMOTE_ADDR' ]
+        except KeyError:
+            remote = 'no web'
+                
+    newMd5.update( remote )
+    newMd5.update( job.getCommandLine() )
+    newDigest = newMd5.hexdigest()
+    
+    work_dir = job.getDir()
+        
+    thisJobAdm = Admin( work_dir )
+    thisJobAdm.setMd5( newDigest )
+    thisJobAdm.commit()
+
+    mask = os.path.normpath( "%s/%s.*" %(
+        job.cfg.admindir() ,
+        job.getServiceName()
+        )
+                                 )
+    jobs = glob.glob( mask )
+    max_jobs = job.cfg.simultaneous_jobs()
+        
+    if max_jobs == 0 :
+        return 
+    nb_of_jobs = 0
+    msg = None
+    for one_job in jobs:
+        try:
+            oldAdm = Admin( one_job )
+        except MobyleError, err :
+            if os.path.lexists( one_job ):
+                p_log.critical( "%s/%s: invalid job in ADMINDIR : %s" %( job.getServiceName() ,  job.getKey() , err ) )
+            continue
+        if( oldAdm.getWorkflowID() ):
+            #we allow a workflow to run several identical job in parallel
+            continue
+            
+        oldDigest = oldAdm.getMd5()
+        if newDigest == oldDigest :
+            oldStatus = job.getStatus() 
+            if not oldStatus.isEnded() :
+                nb_of_jobs += 1
+                if nb_of_jobs >= max_jobs:
+                    msg = "%d similar jobs (%s) have been already submitted (md5 = %s)" % (
+                                                                                    nb_of_jobs ,
+                                                                                    os.path.basename( one_job ),
+                                                                                    newDigest
+                                                                                    )
+                    userMsg = " %d similar job(s) have been already submitted, and are(is) not finished yet. Please wait for the end of these jobs before you resubmit." % ( nb_of_jobs )
+                    p_log.warning( msg + " : run aborted " )
+                    raise UserValueError( parameter = None, msg = userMsg )
+    return True
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..58d1a3a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,199 @@
+INSTALLATION INSTRUCTIONS FOR MOBYLE
+************************************
+
+
+1 - Requirements:
+=================
+
+- Any machine running a unix-like Operating System.
+- A CGI-enabled web server (such as Apache with a loaded mod_cgi and optionally the rewrite engine).
+- Python, >=2.5 & <3.0
+- The following Python libraries:
+ 	+ simpletal, >= 4.1 & <5.0
+ 	+ simplejson, >= 1.7.1
+ 	+ python imaging library (with libpng, zlib and freetype support), >= 1.1.5  
+	+ lxml >=2.2.4 ,
+
+(remark: pyCaptcha is not required anymore as it was integrated in Mobyle )
+
+- Optional:
+        + a biological sequence/alignment format converter software. We recommend squizz
+          (ftp://ftp.pasteur.fr/pub/gensoft/projects/squizz-0.99b) and we provide a plugin for it.
+        + a batch system, such as SGE (http://gridengine.sunsource.net/), PBS/Torque 
+          (http://www.clusterresources.com/pages/products/Torque-resource-manager.php), or LSF.
+        + python-drmaa http://drmaa-python.googlecode.com/ if you use SGE via DRMAA, PBS/Torque
+          via DRMAA, or LSF via DRMAA. We do not provide a non-DRMAA access to Torque or LSF.
+        + FedStage DRMAA for LSF (http://sourceforge.net/projects/lsf-drmaa/) if you use LSF
+        + if you use Torque/PBS via DRMA enable this option at compilation time. 
+        + dnspython >=1.5.0 is helpful to check user emails domain validity.
+        + golden (ftp://ftp.pasteur.fr/pub/gensoft/projects/golden/) is helpful to directly load 
+          biological sequences from databanks into the web portal.
+        + python-openid >=2.2.4 (http://openidenabled.com/files/python-openid/packages/python-openid-2.2.4.tar.gz)
+          to enable OpenId authentication.
+        + the service descriptions (ftp://ftp.pasteur.fr/pub/gensoft/projects/Mobyle/)
+          are collections of "ready-to-use" service descriptions (programs, workflows, or viewers)
+          maintained by the Institut Pasteur.
+        + the jing java library and dependencies (http://code.google.com/p/jing-trang/downloads/list)
+          in case you want to modify service descriptions or create your own. It is not required
+          to operate Mobyle, but provides detailed error messages that can help you create valid
+          service descriptions.
+          
+2 - Technical overview:
+=======================
+
+A "Mobyle" server does not run any specific daemon (apart from
+the web server). When a user launches a job, it is actually running a cgi that
+runs a bioinformatics program in a subprocess. If the subprocess runs
+for more than a certain time, it detaches itself, and continues
+monitoring the execution until its end. The "web server user" is therefore
+the one that runs every request and user permissions should be done so
+that it can access and run every data, program, and parameter of the
+Mobyle configuration.
+
+3 - The Mobyle archive tree:
+============================
+
+Doc => Documentation
+
+Example => A few sample files.
+
+Local => Configuration, local parameters and code for the Mobyle
+         system.
+
+Schema => The schema files that define the XML data model and the validation
+         rules for service descriptions.
+
+Src => Mobyle source code:
+	* the Mobyle folder contains the "core" code for the Mobyle
+          Server.
+	* the Portal folder contains the code for the web portal that
+          provides an access to the system.
+
+Tools => A few utilities and scripts.
+
+niad => (only if you choose a Mobyle+BCBB tarball) root of 2 mobyle companion: 
+  * BCBB Mobyle Interface Designer (BMID)
+  * BCBB Mobyle Pipeline System (BMPS)
+
+4 - Installation steps:
+=======================
+
+4.1 - Make sure every required dependence/software is present.
+--------------------------------------------------------------
+
+4.2 - Perform the installation.
+-------------------------------
+
+To install Mobyle, you need to provide 3 different paths, where all
+files will be installed, and 2 flags to install BMID and BMPS (these 2 flags are only
+available if you download a Mobyle+BCBB tarball) :
+
+    python setup.py install \
+      --install-core=/path/where/to/install/core/files \
+      --install-cgis=/path/where/to/install/cgis/files \
+      --install-htdocs=/path/where/to/install/html/files
+      --install-bmps    
+      --install-bmid
+      
+  - The `--install-core' option specifies the directory where to install the Mobyle
+    core files (code, tools, example, documentation, ...).
+
+  - The `--install-cgis' option specifies the directory where to install the Mobyle
+    portal cgis to be executed by the web server.
+
+  - The `--install-htdocs' option, will be used as the Mobyle document
+    root, which will contain the portal static documents (in a 'portal' subfolder),
+    the users sessions, the jobs and the programs definitions (in a 'data'
+    subfolder). Make sure this subtree is readable by the web server
+    user. Furthermore, the permissions on subfolders 'sessions' and
+    'jobs' have to be writable by Apache.
+    
+   - The --install-bmps flag, indicate that the installer will install BMPS, 
+     the BCBB Mobyle Pipeline System. BMPS will be installed in Mobyle cgis location.
+     (this option is available for Mobyle+BCBB tarball only).
+     
+   - The --install-bmid flag, indicate that the installer will install BMID, 
+     the BCBB Mobyle Interface Designer. BMID will be installed in Mobyle cgis location.      
+     (this option is available for Mobyle+BCBB tarball only).
+     
+4.3 - Configure Mobyle and Apache.
+----------------------------------
+
+Go to your freshly installed "Mobyle core" directory (from now on, you can remove
+ the sources), and create the configuration file using the provided template:
+
+cp Example/Local/Config/Config.template.py  Local/Config/Config.py
+
+In order to have a working installation, you need to edit the various configuration directives.
+The documentation describing the configuration of Mobyle and Apache is the configuration manual 
+Doc/Admin/configuration_guide.pdf.
+
+4.4 - Services descriptions deployment.
+---------------------------------------
+
+The deployment of services using Mobyle does not install the programs, it just makes them 
+accessible on the web. You must install separately the bioinformatics software corresponding 
+to the descriptions.
+
+We provide a set of "ready-to-use" services descriptions (programs, workflows, viewers), which 
+are available in the following ftp website: ftp://ftp.pasteur.fr/pub/gensoft/projects/Mobyle/. 
+For Mobyle 1.0, use Programs-1.3.tgz or higher. Workflows and Viewers are new to version 1.0.
+
+For instance, to deploy pasteur-provided program descriptions:
+  - download the "Programs-xx.tgz" file,
+  - expand the archive in the Programs subfolder,
+  - configure Mobyle according to the bioinformatics software installed on
+    your platform (see LOCAL_DEPLOY_* directives in the configuration manual),
+  - use the mobdeploy script which is located in the Tools subfolder (for more details see associated
+    README).
+     
+Make sure, once the programs descriptions are deployed, that they are readable by the web server. 
+For more explanations about services deployment see the associated Tools/README file and the 
+configuration manual. 
+
+5 - Tests and troubleshoot:
+===========================
+
+Try to connect to your portal. The url of the portal is:
+    ROOT_URL/CGI_PREFIX/portal.py
+
+You should see the welcome page at the center and, on left, the
+available programs presented in a hierarchical tree.
+
+If you have any trouble, it will be very useful to check :
+ - the apache error log, if you have a "500 Internal Server Error"
+ - the Mobyle error_log (located in previously the LOGDIR configuration directive), 
+   if something seems goes wrong but there is no error 500. 
+
+Here are a few frequent problems that can occur:
+ 
+* Instead of the welcome page I see 'Internal Server Error'.
+    -> Make sure the web server has the writable permissions on the
+       --install-htdocs /sessions subfolder.
+
+* I do not see the installed services appear in the left menus and/or their forms.
+    -> Make sure the files in data/programs/ are readable the web server user.
+
+* After filling the CAPTCHA, the Portal freezes: 
+    -> Check the Mobyle error log for an "IOError: decoder jpeg not available" message.
+    If so, the PIL package was not compiled with jpeg support. You must have libjeg installed
+    before to install PIL. Check if the PIL install script detects your libjpeg, PIL shows a
+    summary after building (python setup.py build_ext -i).
+
+* After launching a job, I have : Mobyle Internal server error.
+    -> Check the Mobyle error log for an "AsynchronRunner : CRITICAL ... __init__: exec child
+    caught an error" message. If you find it, make sure the python module Src/Mobyle/RunnerChild.py
+    is executable by the web server user.
+ 
+If your problem is not among those covered above then please contact
+us at mobyle-support at pasteur.fr.
+
+6 - Mailing list:
+=================
+
+There is a mailing list dedicated to Mobyle server administrators, called 
+"mobyle-users". This list discusses new releases, related software announcements,
+administration and development issues, etc. This is a moderated and low traffic list.
+
+You can subscribe to Mobyle users at:
+    http://sympa.pasteur.fr/wws/subrequest/mobyle-users
diff --git a/Local/Config/Execution/DRMAAConfig.py b/Local/Config/Execution/DRMAAConfig.py
new file mode 100644
index 0000000..d2c993d
--- /dev/null
+++ b/Local/Config/Execution/DRMAAConfig.py
@@ -0,0 +1,20 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+
+from ExecutionConfig import ExecutionConfig
+
+class DRMAAConfig( ExecutionConfig ):
+    
+    def __init__( self, drmaa_library_path , nativeSpecification = '' ):
+        super( DRMAAConfig , self ).__init__()
+        self.drmaa_library_path = drmaa_library_path
+        self.nativeSpecification = nativeSpecification
+         
diff --git a/Local/Config/Execution/ExecutionConfig.py b/Local/Config/Execution/ExecutionConfig.py
new file mode 100644
index 0000000..c26cd1a
--- /dev/null
+++ b/Local/Config/Execution/ExecutionConfig.py
@@ -0,0 +1,15 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+
+class ExecutionConfig(object):
+    def __init__( self ):
+        self.execution_class_name =  self.__class__.__name__[:-6]
+    
diff --git a/Local/Config/Execution/LsfDRMAAConfig.py b/Local/Config/Execution/LsfDRMAAConfig.py
new file mode 100644
index 0000000..f3109f9
--- /dev/null
+++ b/Local/Config/Execution/LsfDRMAAConfig.py
@@ -0,0 +1,27 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+from Mobyle.MobyleError import MobyleError
+from DRMAAConfig import DRMAAConfig
+
+class LsfDRMAAConfig( DRMAAConfig ):
+    
+        def __init__( self , drmaa_library_path , server_name= '' , lsf_envdir = None , lsf_serverdir = None , nativeSpecification = ''):
+            if lsf_envdir is None or lsf_serverdir is None:
+                msg = "lsf_envdir and lsf_serverdir must be specified for LsfDRMAAConfig instanciation in Config.EXECUTION_SYSTEM_ALIAS"
+                _log.critical( msg )
+                raise MobyleError( msg )
+            super( LsfDRMAAConfig , self ).__init__( drmaa_library_path, nativeSpecification = nativeSpecification  )
+            self.contactString = server_name
+            self.envdir = lsf_envdir
+            self.serverdir = lsf_serverdir
+                        
+            
+        
\ No newline at end of file
diff --git a/Local/Config/Execution/PbsDRMAAConfig.py b/Local/Config/Execution/PbsDRMAAConfig.py
new file mode 100644
index 0000000..d1abac2
--- /dev/null
+++ b/Local/Config/Execution/PbsDRMAAConfig.py
@@ -0,0 +1,22 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+
+from DRMAAConfig import DRMAAConfig
+
+class PbsDRMAAConfig( DRMAAConfig ):
+    
+        def __init__( self , drmaa_library_path , server_name , nativeSpecification = '' ):
+            super( PbsDRMAAConfig , self ).__init__( drmaa_library_path , nativeSpecification = nativeSpecification)
+            self.contactString = server_name
+            
+                        
+            
+        
\ No newline at end of file
diff --git a/Local/Config/Execution/SGEConfig.py b/Local/Config/Execution/SGEConfig.py
new file mode 100644
index 0000000..80cbbba
--- /dev/null
+++ b/Local/Config/Execution/SGEConfig.py
@@ -0,0 +1,26 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+from Mobyle.MobyleError import MobyleError
+from ExecutionConfig import ExecutionConfig
+
+class SGEConfig( ExecutionConfig ):
+    
+    def __init__( self , root = None , cell = None ):
+        if root is None or cell is None:
+            msg = "root and cell must be specified for SGEConfig instanciation in Config.EXECUTION_SYSTEM_ALIAS"
+            _log.critical( msg )
+            raise MobyleError( msg )
+        
+        super( SGEConfig , self ).__init__()
+        self.root = root
+        self.cell = cell
+        
+    
\ No newline at end of file
diff --git a/Local/Config/Execution/SYSConfig.py b/Local/Config/Execution/SYSConfig.py
new file mode 100644
index 0000000..6db1a7e
--- /dev/null
+++ b/Local/Config/Execution/SYSConfig.py
@@ -0,0 +1,15 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+from ExecutionConfig import ExecutionConfig
+
+class SYSConfig( ExecutionConfig ):
+    pass
+        
\ No newline at end of file
diff --git a/Local/Config/Execution/SgeDRMAAConfig.py b/Local/Config/Execution/SgeDRMAAConfig.py
new file mode 100644
index 0000000..349ccc6
--- /dev/null
+++ b/Local/Config/Execution/SgeDRMAAConfig.py
@@ -0,0 +1,30 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import logging 
+_log = logging.getLogger(__name__)
+from Mobyle.MobyleError import MobyleError
+from DRMAAConfig import DRMAAConfig
+         
+class SgeDRMAAConfig( DRMAAConfig ):
+    
+        def __init__( self , drmaa_library_path , root = None , cell = None , nativeSpecification = '' ):
+            if root is None or cell is None:
+                msg = "root and cell must be specified for SgeDRMAAConfig instanciation in Config.EXECUTION_SYSTEM_ALIAS"
+                _log.critical( msg )
+                raise MobyleError( msg )
+            super( SgeDRMAAConfig , self ).__init__( drmaa_library_path , nativeSpecification = nativeSpecification )
+            # sge drmaa does not support several server at same time
+            # so contactString must remain empty
+            # but SGE_CELL must be defined
+            self.contactString = ''
+            self.cell = cell
+            self.root = root
+            
+
+        
\ No newline at end of file
diff --git a/Local/Config/Execution/__init__.py b/Local/Config/Execution/__init__.py
new file mode 100644
index 0000000..ecb3cf5
--- /dev/null
+++ b/Local/Config/Execution/__init__.py
@@ -0,0 +1,28 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import glob 
+import os 
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    import sys
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+for _file in glob.glob( os.path.join( MOBYLEHOME , 'Local' , 'Config' , 'Execution' , '*Config.py' ) ):
+    module_name = os.path.splitext( os.path.basename( _file ) )[0]
+    if module_name != '__init__':
+        try: 
+            module = __import__( module_name , globals(), locals(), [ module_name ])
+            klass = getattr( module , module_name)
+            locals()[ module_name ] = klass
+        except Exception , err :
+            print >> sys.stderr , "the module %s cannot be loaded : %s" %( module_name , err )
+            continue
\ No newline at end of file
diff --git a/Local/Config/__init__.py b/Local/Config/__init__.py
new file mode 100644
index 0000000..4456749
--- /dev/null
+++ b/Local/Config/__init__.py
@@ -0,0 +1 @@
+__all__=['Config' , 'Execution']
diff --git a/Local/CustomClasses/__init__.py b/Local/CustomClasses/__init__.py
new file mode 100644
index 0000000..19bb5da
--- /dev/null
+++ b/Local/CustomClasses/__init__.py
@@ -0,0 +1,2 @@
+#does mo forget to update this file if you want your module
+#is used in mobyle
diff --git a/Local/Policy.py b/Local/Policy.py
new file mode 100644
index 0000000..49e880e
--- /dev/null
+++ b/Local/Policy.py
@@ -0,0 +1,48 @@
+import sys
+import os.path
+
+
+from logging  import getLogger
+p_log = getLogger('Mobyle.Policy')
+
+
+
+def queue( queueName ):
+    """
+    @return: the name of the queue to be used to execute the job
+    @rtype: string
+    """
+    return queueName
+
+
+
+#if you want to defined a local policy to check user
+# you must put you code here 
+def emailCheck( **args ):
+     """
+     check if the email according to the local rules.
+     @return:
+      - Mobyle.Net.EmailAddress.VALID    if the email is valid
+      - Mobyle.Net.EmailAddress.INVALID  if the email is rejected
+      - Mobyle.Net.EmailAddress.CONTINUE to continue futher the email validation process
+     """
+     import Mobyle.Net
+
+     return Mobyle.Net.EmailAddress.CONTINUE
+ 
+
+
+def authenticate( login , passwd ):
+    """
+    Mobyle administartor can put authentification code here. this function must return either 
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE: 
+                the method does not autentified this login/passwd. The fall back method must be applied.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.VALID:    
+               the login/password has been authenticated.
+     - Mobyle.AuthenticatedSession.AuthenticatedSession.REJECT:    
+              the login/password is not allow to continue.
+    """
+    import Mobyle.AuthenticatedSession
+    
+    return Mobyle.AuthenticatedSession.AuthenticatedSession.CONTINUE
+
diff --git a/Local/__init__.py b/Local/__init__.py
new file mode 100644
index 0000000..e70a711
--- /dev/null
+++ b/Local/__init__.py
@@ -0,0 +1 @@
+#__all__=['Config' , 'CustomClasses', 'mailTemplate' ]
diff --git a/Local/black_list.py b/Local/black_list.py
new file mode 100644
index 0000000..067808b
--- /dev/null
+++ b/Local/black_list.py
@@ -0,0 +1,6 @@
+
+users = [ ]
+
+
+
+host = [ ]
diff --git a/Local/mailTemplate.py b/Local/mailTemplate.py
new file mode 100644
index 0000000..244b475
--- /dev/null
+++ b/Local/mailTemplate.py
@@ -0,0 +1,270 @@
+#####################################################
+# Here are the different email templates sent to the user
+# Each template is structured as follows:
+#
+# From:         required
+# Cc:           optional  
+# Subject:      required
+# Reply-To:     optional
+#               an empty line
+# the body of the email containing some keys to expand
+#
+###################################################
+
+
+
+############################################################
+#
+# CONFIRM_SESSION email is sent to the user to confirm his 
+# registration. 
+# if config.AUTHENTICATED_SESSION = 'email'
+# 
+# the available keys are:
+# SENDER      : the email address which sends the mail as defined in the configuration
+# HELP        : the email of the Mobyle "hot line" as defined in the configuration
+# SERVER_NAME : the url of the Mobyle portal
+# CGI_URL     : the url of the portal cgi directory
+# ACTIVATING_KEY : the key which allows the user to activate its session 
+###########################################################
+
+CONFIRM_SESSION = """
+From: %(SENDER)s
+Cc: 
+Bcc: 
+Subject: [ Mobyle server ( %(SERVER_NAME)s ) ] - new user confirmation" 
+Reply-To: %(SENDER)s
+Organization: Institut Pasteur, Paris.
+
+You have requested an account on the %(SERVER_NAME)s Mobyle server.
+Your login is: your email
+Your activation key is: %(ACTIVATING_KEY)s 
+To activate this account please click on the
+following link (or paste its URL in your favourite browser):
+%(CGI_URL)s/portal.py#user::activate
+"""
+
+####################################################################
+#
+# RESULTS_TOOBIG email is sent to the user when a job is finished 
+# and the size of the results files is greater than Config.MAXMAILSIZE
+# 
+# the available keys are:
+# SENDER      : the email address which sends the mail as defined in the configuration 
+# HELP        : the email of the Mobyle "hot line" as defined in the configuration
+# SERVER_NAME : the url of the Mobyle portal
+# JOB_URL     : the url of the job
+# JOB_NAME    : the name of the service ex blast2 , clustalw-multialign
+# RESULTS_REMAIN : the number of days before the job is removed from the server, as defined in the configuration
+#
+####################################################################
+
+RESULTS_TOOBIG = """
+From: %(SENDER)s
+Reply-To: %(SENDER)s
+Subject: Your %(JOB_NAME)s job is finished. Access your results
+
+Your results are too big to be sent by email.
+They are accessible at the following address:
+%(JOB_URL)s
+
+Your results will be kept on our server for %(RESULTS_REMAIN)d days from now.
+"""
+
+
+####################################################################
+#
+# RESULTS_FILES email is sent to the user when a job is finished 
+# Config.OPT_EMAIl = False
+# 
+# the available keys are:
+# SENDER : the email address which sends the mail as defined in the configuration 
+# HELP   : the email of the mobyle "hot line" as defined in the configuration
+# SERVER_NAME : the url of the mobyle portal
+# JOB_URL     : the url of the job
+# JOB_NAME    : the name of the service ex blast2 , clustalw-multialign
+# RESULTS_REMAIN : the number of days before the jobs will be erase as defined in the configuration
+#
+####################################################################
+
+RESULTS_FILES = """
+From: %(HELP)s
+Reply-To: %(SENDER)s
+Subject: Your %(JOB_NAME)s job is finished. Access your results
+
+Your results are accessible at the following address:
+%(JOB_URL)s
+
+Your results will be kept on our server for %(RESULTS_REMAIN)d days from now.
+
+An archive of your job is attached to this email. 
+It includes the index.xml file which is a summary of your job submission 
+(you can view it in your favorite web browser).
+""" 
+
+####################################################################
+#
+# RESULTS_NOTIFICATION email is sent to the user when a job is finished 
+# but the results files zipping failed.
+# 
+# the available keys are:
+# SENDER : the email address which sends the mail as defined in the configuration 
+# HELP   : the email of the mobyle "hot line" as defined in the configuration
+# SERVER_NAME : the url of the mobyle portal
+# JOB_URL     : the url of the job
+# JOB_NAME    : the name of the service ex blast2 , clustalw-multialign
+# RESULTS_REMAIN : the number of days before the jobs will be erase as defined in the configuration
+#
+####################################################################
+
+RESULTS_NOTIFICATION = """
+From: %(HELP)s
+Reply-To: %(SENDER)s
+Subject: Your %(JOB_NAME)s job is finished. Access your results.
+
+Your results are accessible at the following address:
+%(JOB_URL)s
+
+Your results will be kept on our server for %(RESULTS_REMAIN)d days from now.
+"""
+
+####################################################################
+#
+# LONG_JOB_NOTIFICATION email is sent to the user when a job is longer
+# than Config.TIMEOUT
+# 
+# the available keys are:
+# SENDER   : the email address which sends the mail as defined in the configuration 
+# HELP     : the email of the mobyle "hot line" as defined in the configuration
+# SERVER_NAME : the url of the mobyle portal
+# JOB_URL  : the url of the job
+# JOB_NAME : the name of the service ex blast2 , clustalw-multialign
+# JOB_KEY  : the unique key wich permit to identify a job
+# RESULTS_REMAIN : the number of days before the jobs will be erase as defined in the configuration
+#
+####################################################################
+
+LONG_JOB_NOTIFICATION = """
+From: %(HELP)s
+Reply-To: %(SENDER)s
+Subject: Your job %(JOB_NAME)s/%(JOB_KEY)s is running.
+
+Your %(JOB_URL)s job is running on %(SERVER_NAME)s server.
+You will receive the results by email.
+You can access the results or check the job status at the following address:
+%(JOB_URL)s
+"""
+
+####################################################################
+#
+#  email is send From the user to the mobyle help adress to request some help 
+# 
+# the available keys are:
+# USER     : the user email address
+# SENDER   : the email address which sends the mail as defined in the configuration 
+# HELP     : the email of the mobyle "hot line" as defined in the configuration
+# MSG      : the message written by the USER 
+# SERVER_NAME   : the url of the mobyle portal
+# SESSION_ID    : the session unique identifier
+# SESSION_EMAIL : the user email store in the session
+# SESSION_ACTIVATED : if the session is activated
+# SESSION_AUTHENTICATED : if the session is authenticated
+# JOB_URL         : the url of the job
+# JOB_DATE        : the date at which this job was launch
+# JOB_STATUS      : the mobyle job status ( finished, error ... )
+# JOB_ERROR_PARAM : if there is an error, the parameter name that provoke the error
+# JOB_ERROR_MSG   : the error message associated with the error
+####################################################################
+
+HELP_REQUEST = """
+From: %(USER)s
+Subject: [mobyle help request] help on job %(JOB_URL)s.
+
+User Message:
+%(MSG)s
+    
+from: %(USER)s
+
+Session information:
+- id: %(SESSION_ID)s
+- email: %(SESSION_EMAIL)s
+- activated?: %(SESSION_ACTIVATED)s
+- authenticated?: %(SESSION_AUTHENTICATED)s
+
+Job information:
+- id: %(JOB_URL)s
+- date: %(JOB_DATE)s
+- status: %(JOB_STATUS)s
+- user error in parameter: %(JOB_ERROR_PARAM)s
+- user error message: %(JOB_ERROR_MSG)s
+"""
+
+####################################################################
+#
+#  email is sent to the user as a receipt of its help demand
+# 
+# the available keys are:
+# USER     : the user email address
+# SENDER   : the email address which sends the mail as defined in the configuration 
+# HELP     : the email of the mobyle "hot line" as defined in the configuration
+# MSG      : the message written by the USER 
+# SERVER_NAME   : the url of the mobyle portal
+# SESSION_ID    : the session unique identifier
+# SESSION_EMAIL : the user email store in the session
+# SESSION_ACTIVATED : if the session is activated
+# SESSION_AUTHENTICATED : if the session is authenticated
+# JOB_URL         : the url of the job
+# JOB_DATE        : the date at which this job was launch
+# JOB_STATUS      : the mobyle job status ( finished, error ... )
+# JOB_ERROR_PARAM : if there is an error, the parameter name that provoke the error
+# JOB_ERROR_MSG   : the error message associated with the error
+#
+####################################################################
+HELP_REQUEST_RECEIPT = """
+From: %(HELP)s
+Reply-To: %(SENDER)s
+Subject: [mobyle help request receipt] help on job %(JOB_URL)s.
+
+We have received your help request (the message is included below). 
+We will try to answer it as soon as possible.
+
+User Message:
+%(MSG)s
+    
+from: %(USER)s
+
+Session information:
+- id: %(SESSION_ID)s
+- email: %(SESSION_EMAIL)s
+- activated?: %(SESSION_ACTIVATED)s
+- authenticated?: %(SESSION_AUTHENTICATED)s
+
+Job information:
+- id: %(JOB_URL)s
+- date: %(JOB_DATE)s
+- status: %(JOB_STATUS)s
+- error parameter: %(JOB_ERROR_PARAM)s
+- error message: %(JOB_ERROR_MSG)s
+"""
+
+######################################################################
+#
+# email is sent by mobpasswd ( if -m option ) to notify the user of a 
+# password modification. 
+#
+# HELP          : the email of the mobyle "hot line" as defined in the configuration
+# SENDER        : the email address which sends the mail as defined in the configuration 
+# SERVER_NAME   : the url of the mobyle portal
+# PASSWD        : the new password
+#
+######################################################################
+
+NEW_PASSWD = """
+From: %(HELP)s
+Reply-To: %(SENDER)s
+Subject: [ %(SERVER_NAME)s ] new password.
+
+You have requested to change the password for your account on the
+%(SERVER_NAME)s server.
+
+Your new password is %(PASSWD)s
+"""
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..426c291
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,245 @@
+CHANGES IN RELEASE 1.5.3
+
+# Portal
+  * fix bug preventing results from loading when a running job finishes (#1928)
+  * fix bug where a job results page sometimes displays twice the resultbox (#1764)
+
+# Utilities
+  * NEW mob_stat: script that provides basic statistics about portal usage
+  * NEW mob_log_rotate: script to rotate and compress Mobyle logs
+
+CHANGES IN RELEASE 1.5.2
+
+# fix issue (#2044)
+  When program parameters have example data but no comment, the [use example data] button does not work.
+
+# fix issue (#2022)
+  Error when submitting workflows that run tasks with multiple inputs
+
+# fix issue (#1926)
+  the status of the jobs with a retun code != 0
+  are never updated so they are stuck in submitted, pending, running, ...
+
+# fix issue (#1761)
+  inline help is not displayed when coded in HTML
+  
+# add User-set per-job email notification for job completion (#1744)
+  Users should be able to specify on a per-job basis if they want to get an email job completion notification.
+  only show the control if dont_email_results is not set to true and if the user email is defined.
+
+
+
+CHANGES IN RELEASE 1.5.1
+
+# New converters for Alignment formats
+	* We provide a set of new converters to convert alignments in fasta, nexus, phylip, stockholm in phylip-relaxed format.
+	  phylip-relaxed is the format used by Phyml and morePhyml. These converters needs biopython to perform the conversion
+	  so you NEED to install biopython if you want to use them (It's not need to install numpy or reportlab for these functionalities).
+    The converters will automatically be installed with the Mobyle installation/upgrade, but to activate them you must
+    add them in DATA_CONVERTER in the Mobyle configuration, like this:
+       DATA_CONVERTER = {
+           'Sequence': [ squizz_sequence('/usr/bin/squizz') ],
+           'Alignment': [ 
+                        squizz_alignment('/usr/bin/squizz'), 
+                        fasta_phyml(None), 
+                        phylipi_phyml(None),
+                        phylips_phyml(None),
+                        clustal_phyml(None),
+                        nexus_phyml(None),
+                        stockholm_phyml(None)
+                        ]
+                    }
+    To take full advantage of these new converters, you also need to update your program definitions (>=5.1.1).
+
+# BMPS
+  * Workflow description update fixed (#1636)
+  * Default values sometimes not displayed in form task panels fixed (#1762)
+  
+# Portal
+  * Tutorial images fixed in debian package-like configurations (#1631)
+  * Example data can now also contain XML (#1640)
+  * Portal freeze upon loading fixed (#1647)
+  * accents supported in job names (#1673)
+  * renamed jobs correctly sorted (#1674)
+  * Font size for workflows is now ok (#1709)
+  * "Tutorials" and "My Workflows" menus can be customized again (#1737)
+  * Portal freeze when closing widget window after bookmarking fixed (#1739)
+  * Forms displaying sometimes when refreshing the portal fixed (#1757)
+  * Parameter values unmodifiable after loading from history fixed (#1758)
+
+# Execution server
+  * BMPS job error fixed (#1742)
+  
+# Documentation
+  * chaining rules description updated (#1675)
+  
+  
+CHANGES IN RELEASE 1.5.0
+
+# BMPS: BCBB Mobyle Pipeline System is a graphic interface which lets users create their own workflows, which can be launched directly
+  from the BMPS interface but also from the Mobyle portal.
+
+# Portal
+  * bookmarks and jobs can now be renamed in left menus (#1162)
+  * tutorials are now customizable. please note that to keep previous tutorials you have to download and deploy the pasteur-tutorials
+    package (#1165).
+  * the values chosen in Choice and MultipleChoice controls are now displayed in the "job inputs" panel using their label instead of
+    their "command-line value" (#1220).
+  * services now support multiple-file inputs.
+
+# Execution server
+  * Job submissions control configuration: !! MAX_SIMILAR_JOB_PER_USER replaces SIMULTANEOUS_JOBS !!
+	  MAX_SIMILAR_JOB_PER_USER represent the number of jobs than a user can submit at a given time
+	  - 0 disable this control, then the user may submit as many "same" jobs he wants (default).
+	  - if the email is not provided (depend of the configuration) this control is disabled.
+  * support for module is now included (http://modules.sourceforge.net/)
+
+CHANGES IN RELEASE 1.0.7
+# Portal
+ *bugfix: Foreign jobs opened in the Mobyle portal freeze the interface (#1182)
+ *bugfix: Program import blocked because of an irrelevant XSL transformation (#1224) 
+ *bugfix: Interactive pipes broken in Mobyle (#1227)
+ *bugfix: job submission fails silently in case of error (#1232)
+# Execution server
+ *bugfix: Help request emails sent only to the first recipient if multiple recipients are specified (#1123)
+
+CHANGES IN RELEASE 1.0.6
+# Portal
+ * bugfix: "further analysis" button not working from data bookmarks (#1155).
+ * bugfix: simple bookmark click induces chaining if a service is selected in "further analysis" (#1159)
+ * bugfix: recurring problem in AJAX workspace update (#1156)
+ * bugfix: advanced options button is not displayed when all the parameters are "issimple" (#1175)
+# Admin tools
+ * it is no longer possible to publish services with mandatory parameters and no vdef as advanced parameters (i.e. not "issimple") (#1166).
+# Execution server
+ * bugfix in DRMAA error handling (r4157-r4158).
+
+CHANGES IN RELEASE 1.0.5
+# Portal
+ * possibility to display only "simple" parameters by default, and toggle between simple and advanced forms. "Modified"
+   parameter values are also outlined in the portal (#1064).
+ * data edited in "viewers" can now also be reused directly (piped/bookmarked) in the portal (beta/hidden feature since r3610)
+ * viewers custom code now also supports more flexible custom operations (#859)
+ * it is now possible to specify a progress report file: the contents of this file are now displayed and updated even during 
+   its execution (#867)
+ * several minor bugs fixed: jumps in the portal upon history refresh (#1070), no error message sent when the user workspace 
+   is full (#1065), pipe problems (#932), job outputs interface customization (#929), documentation links (#1078) 
+# Execution server
+ * the configuration of the acceptance or refusal of jobs based on the number of similar jobs is now completely configurable
+  (#1122)
+ * the handling and reporting of drmaa errors has been improved (#1076)
+ * logs do not disappear partially anymore when they are rotated (#1075) 
+# Admin tool fixes
+ * custom messages can be specified when blacklisting a user per IP or email (#1123)
+ * updating does not overwrite your CustomClasses __init__.py! (#1136)
+ * mobclean does not crash when processing "dirty" job folders (#857)
+ * mobdeploy minor fix (#835)
+# Documentation
+ * interface customization documentation improved (#921)
+ * portal header and footer documentation corrected (#800)
+
+CHANGES IN RELEASE 1.0.4
+
+# Portal:
+  * improved workflow display (#745 , #747 , #753)
+  * nested workflow jobs do not display in a new tab (#751)
+# Execution server:
+  * Mobyle allows multiple job executions if they are subtasks of the same workflow (#738)
+  * allow a workflow to execute a workflow as a subtask (#734)
+# Admin tool fixes:
+  * mobvalid and mobdeploy apply new rules for parameter name. 
+  from now on, some words are forbidden as parameter names (because they can create conflicts in the resulting HTML DOM) (#683).
+  * mobjobw displays workflows as other jobs (#742)
+  * mobyle can deploy several imported services in one time (#756)
+
+CHANGES IN RELEASE 1.0.3
+
+# A few bugfixes in the portal interface:
+  * user parameter and error messages are sent with user help requests (#699, 
+    broken in prior 1.x releases).
+  * double submissions caused by a defective waiting screen fixed (#682)
+  * broken MobLinks functionality is back (#725).
+  * portal static document links are now versioned, allowing to bypass browser
+    cache mechanisms upon portal update (#681).
+  * users can simultaneously log-in from multiple browsers (#680)
+# Execution server:
+  * workflow status is updated on each subtask status update (#686).
+  * workflow status updates are now performed less frequently (#693).
+  * user error messages now explicitely mention the expected datatype (#709).
+  * user parameter values can now only be ascii-compatible (#727).
+  * job status is properly set even if the DRM is unreachable (#728).
+# Admin tool fixes:
+  * mobclean now does not fail if the '.htaccess' file exists (#703),
+    checks that no other instance of itself is running prior to launch (#710),
+    and uses a lot less memory to run (r3931, r3932, r3933).
+# HTTP API fix:
+  * job simulation can be used (#723, but still needs to behave correctly with files #724)  
+
+CHANGES IN RELEASE 1.0.2
+
+# Mobyle does not require the PyCaptcha anymore (r3864,r3865,r3870)
+# A few bugfixes in the Execution server:
+  * job emails are now always sent if configuration specifies it (#679).
+  * workflow execution engine does not fail if linking two parameters between the same tasks(#649),
+    and handles correctly no-output style workflows (r3853).
+  * workflow jobs now seamlessly handle the removal of remote jobs (#640).
+# A few bugfixes in the portal interface:
+  * comments can be expanded in the standalone job result pages (#673)
+    (warning: this requires that you redeploy your services, and existing jobs won't be fixed).
+  * freezed screens after job submission (#669).
+  * paragraph comments do not appear automatically upon paragraph "maximizing" (#666).
+  * modal dialogs (e.g. captcha problem) do not remain on screen after validation (#647).
+  * input fields such as captcha answer now automatically have the focus, even on old (e.g.,FF3.5)
+    browsers (#571).
+# Admin tool fixes:
+  * mobclean does not complain anymore about any .htaccess file in the jobs directory (#667). 
+  * mobdeploy does not deploy anymore invalid service descriptions (#589).
+# API improvement:
+  * it is now possible to get a service description using JobFacade.parseService() instead of
+    JobFacade.create() (#583).
+
+CHANGES IN RELEASE 1.0.1
+
+# minor features:
+  * portal customization improvement (#585): the header and footer of the portal
+    can now be customized, check the configuration guide for more details.
+  * a download button is now directly available for data bookmarks (#596).
+# multiple bugfixes in the portal interface:
+  * the jobs tab removal checkboxes now work properly (#573).
+  * the simple parameters can now be modified after a refused submission fails (#574).
+  * job results and workspace data can now be directly piped to workflows (#581).
+  * jobs can now be accessed from another session to facilitate sharing and maintenance (#588, #625).
+  * interface is more informative about workspace refreshes and other actions (#592,#593).
+  * multiple workflow execution problems (#594, #605, #606, #608, #624).
+  * multiple workflow diagram browser compatibility issues fixed (#292, #567).
+
+NEW IN MOBYLE v1.0
+
+# Viewers: viewers offer the possibility to visualize data in the Mobyle portal
+using web-compatible components, to provide a more readable representation. 
+For instance, multiple alignments can be visualized with Jalview, Protein 3D 
+structures can be visualized with JMol, RNA Secondary structures with VARNA, or
+phylogenetic trees with Archaeopteryx.
+
+# Workflows (beta): Mobyle services chainings can now be automated by providing
+predefined workflows.
+
+# Many portal improvements, including the possibility to customize your welcome page
+and user workspace management interfaces that facilitate the visualization and cleanup
+of user jobs and data. 
+
+# A new plugin-like architecture for the converter modules: you can associate
+to each data type and format a validator/converter module, that will detect,
+validate and convert the format of user data when appropriate.
+
+# The execution system has been rewritten to be highly flexible: 
+Mobyle now supports new DRMs, including LSF (via the DRMAA API). It is also 
+possible to manage multiple execution configurations, allowing for instance
+to send jobs to different DRMs based on your needs.
+
+# OpenID support: you can now configure Mobyle to let users authenticate themselves
+using OpenID, in order for them to avoid the management of a new Mobyle-specific
+password.
+
+# Google Analytics support: you can configure Mobyle to track users
+activity with a Google Analytics account.
\ No newline at end of file
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..efcd979
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: Mobyle
+Version: 1.5.3
+Summary: UNKNOWN
+Home-page: https://projets.pasteur.fr/wiki/mobyle/
+Author: The Mobyle team
+Author-email: mobyle at pasteur.fr
+License: GNU General Public License, version 2 (GPLv2)
+Description: UNKNOWN
+Platform: Unix
diff --git a/README b/README
new file mode 100644
index 0000000..138edfa
--- /dev/null
+++ b/README
@@ -0,0 +1,51 @@
+Mobyle is a framework and web portal specifically aimed at the
+integration of bioinformatics software and databanks.
+
+
+
+For setup instructions, please refer to the INSTALL or/and UPDATE files.
+
+
+THIRD-PARTY CONTENTS:
+
+This package comes with a few libraries or icons that were developed
+by external users:
+
+- Prototype javascript library.
+
+  File:	    Src/Portal/htdocs/MobylePortal/js/prototype*.js
+  Url:	    http://prototypejs.org
+  Author:   Sam Stephenson
+  License:  MIT (see website for details)
+
+- Scriptaculous javascript library.
+
+  File:     Src/Portal/htdocs/MobylePortal/js/scriptaculous.js
+            Src/Portal/htdocs/MobylePortal/js/builder.js
+            Src/Portal/htdocs/MobylePortal/js/effects.js
+  Url:      http://script.aculo.us
+  Author:   Thomas Fuchs
+  License:  MIT (see website for details)
+
+- Most of the website icons were created as part of the silk icons set
+  from Mark James.
+
+  Files:    in Src/Portal/htdocs/MobylePortal/images:
+            action_save.gif, exclamation.gif, tick.gif, arrow_down.gif,
+            bullet_orange.gif, hourglass.gif, asterisk_orange.png, cross.gif,
+            page_edit.png
+  Url:	    http://www.famfamfam.com/lab/icons/silk/
+  Authors:  Mark James
+  License:  Creative Commons Attribution 2.5 License
+  
+- ISO Schematron validation XSL files.
+  Files:    Tools/validation/iso_*.xsl
+  Url:      http://www.schematron.com/implementation.html
+  Authors:  Rick Jelliffe  
+
+- captcha generation
+  Files:    Src/Mobyle/Captcha/*
+  Url:      http://releases.navi.cx/pycaptcha/
+  Authors:  Micah Dowty
+  License:  see Src/Mobyle/Captcha/COPYING
+
diff --git a/Schema/biomoby.rnc b/Schema/biomoby.rnc
new file mode 100644
index 0000000..ea7d9b6
--- /dev/null
+++ b/Schema/biomoby.rnc
@@ -0,0 +1,23 @@
+include "common.rnc"
+
+start = BioMOBY
+
+BioMOBY = element biomoby {
+	Service
+}
+
+InvocationHead &=		(
+		 ## bioMOBY invocation parameters
+		 # the biomobyInvocation part contains information that is needed in case the XML is used to invoke a
+		 # bioMOBY Web Service
+		 element biomobyInvocation {
+		 	## organization URI
+		 	element authURI {xsd:anyURI}&
+		 	## bioMOBY registry
+		 	element registry {
+		 		## registry endpoint
+		 		element endpoint {xsd:anyURI}&
+		 		## registry namespace
+		 		element namespace{xsd:anyURI}?
+		 		}
+		 })
\ No newline at end of file
diff --git a/Schema/biomoby.rng b/Schema/biomoby.rng
new file mode 100644
index 0000000..21a8ab3
--- /dev/null
+++ b/Schema/biomoby.rng
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start>
+    <ref name="BioMOBY"/>
+  </start>
+  <define name="BioMOBY">
+    <element name="biomoby">
+      <ref name="Service"/>
+    </element>
+  </define>
+  <define name="InvocationHead" combine="interleave">
+    <!--
+      the biomobyInvocation part contains information that is needed in case the XML is used to invoke a
+      bioMOBY Web Service
+    -->
+    <element name="biomobyInvocation">
+      <a:documentation>bioMOBY invocation parameters</a:documentation>
+      <interleave>
+        <element name="authURI">
+          <a:documentation>organization URI</a:documentation>
+          <data type="anyURI"/>
+        </element>
+        <element name="registry">
+          <a:documentation>bioMOBY registry</a:documentation>
+          <interleave>
+            <element name="endpoint">
+              <a:documentation>registry endpoint</a:documentation>
+              <data type="anyURI"/>
+            </element>
+            <optional>
+              <element name="namespace">
+                <a:documentation>registry namespace</a:documentation>
+                <data type="anyURI"/>
+              </element>
+            </optional>
+          </interleave>
+        </element>
+      </interleave>
+    </element>
+  </define>
+</grammar>
diff --git a/Schema/common.rnc b/Schema/common.rnc
new file mode 100644
index 0000000..f5c89cb
--- /dev/null
+++ b/Schema/common.rnc
@@ -0,0 +1,366 @@
+datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" 
+namespace xhtml = "http://www.w3.org/1999/xhtml"
+namespace svg = "http://www.w3.org/2000/svg"
+
+#---------------------------------------------------------------#
+#                     Mobyle Generic Service Elements           #
+#---------------------------------------------------------------#
+
+Service = (
+    element head { Head }&
+	## Parameters list
+	Parameters&
+	Implementation
+)
+
+Head = (
+		## name
+		Name& 
+		## package
+		Package?&
+		## version
+		element version { text }?&
+		## information
+		element doc {
+			## title
+			element title { text }&
+			## description
+			element description { (Text*|anyXHTMLorSVG) }&
+			## authors
+			element authors { anyXHTMLorSVG }?&
+			## reference
+			element reference {
+				## Document Object Identifier
+				attribute doi {xsd:anyURI}?& 
+				## URL
+				attribute url {xsd:anyURI}?& 
+				## formatted reference (text or HTML code)
+				anyXHTMLorSVG
+				}*&
+			## documentation link(s)
+			element doclink { xsd:anyURI }*&
+			## download link(s)
+			element sourcelink { (xsd:anyURI | xsd:string) }*&
+			## homepage link(s)
+			element homepagelink { xsd:anyURI }*&
+			## additional comments
+			Comment?
+			}&
+		## classification category
+		element category { text }*&
+		## EDAM classification category
+		element edam_cat { attribute ref { xsd:string }, attribute label { xsd:string } }*&
+		## bioMOBY service category
+		element biomobyCategory { text }?& #this element stores the biomoby serviceType property value.
+		## invocation elements
+        InvocationHead&
+        Interface*
+    )
+
+Name = element name {xsd:string}
+
+Comment = element comment { (Text*|anyXHTMLorSVG) }
+
+## package element defines elements common to all the interfaces of a package
+Package = element package {
+	Head
+}
+
+Parameters = element parameters {
+	(Parameter | Paragraph)*
+	}
+
+Parameter = element parameter {
+(
+    ## technical ID
+    # id so far is added to facilitate workflow developments
+    attribute id { xsd:string }?&
+	## program main input
+	# ismaininput : if true the parameter is a primary article of the corresponding biomoby service (as used in PlayMoby)
+	attribute ismaininput { xsd:boolean }?&
+	## mandatory parameter
+	# ismandatory : if true this parameter should be specified by the user 
+	attribute ismandatory { xsd:boolean }?&
+	## hidden parameter
+	attribute ishidden { xsd:boolean }?&
+	## show in simple forms	
+	# issimple : specify if this parameter will be displayed only in the simple web form.
+	attribute issimple { xsd:boolean }?&
+	(
+	# isout and isstdout are mutually exclusive
+	## output parameter
+	# isout : if true this parameter is produced by this service 
+	attribute isout { xsd:boolean }? 
+	| 
+	## standard output parameter
+	# isstdout : if true this parameter is the standart output of the service. Only one parameter can be isstdout="1" per service
+	attribute isstdout { xsd:boolean }?
+	)&
+ 	## parameter name
+	Name&
+	## parameter prompt
+	Prompt* &
+	## parameter type
+	element type {
+		## parameter biotype
+		# the biological type of the parameter DNA & Protein ...
+		# the biotypes list represents the list of accepted 'types'
+		# for input parameters and the list of 'types' that an 
+		# output may belong to
+		element biotype { text }*&
+		## parameter datatype
+		# type of the data Sequence & Structure & Matrix ...
+		element datatype { 
+			## datatype class
+			element class { text }&
+			## datatype superclass
+			#  superclass or class if class does not exist
+			#  is a Mobyle parameter Class defined in Src/Mobyle/* or in Local/CustomClasses/*
+			element superclass { text }?
+			}&
+		## accepted data formats
+        # acceptedDataFormats is a deprecated tag, and will be removed in a future version - please use only non-nested dataFormat tags
+		DataFormat*&		
+		## parameter cardinality
+		# the cardinality of the datatype &a number or a couple of value &  int&int or int&n
+		# biotype=Protein datatype=( class = Sequence ) & format=Fasta & card=1  means
+		# the user must provide 1 and only one Protein Sequence in Fasta format to fill this input parameter 
+		# biotype=Protein datatype=( class = Sequence ) & format=Fasta & card=1.n  means
+		# the user must provide 1 or more  Protein Sequence in Fasta format to fill this input parameter
+		element card { text }?&
+		## bioMOBY typing
+                # the biomoby typing accepts only one format for a biomoby service
+		# however many programs accept different formats for a given parameter
+		# thus multiple biomoby datatypes can be specified, and a single mobyle
+		# program can be seen as multiple biomoby services.
+		# the name of the object in the bioMoby ontology. it's implied
+		# that it's a primary and if not specify the parameter is a secondary in biomoby
+		element biomoby { 
+				## bioMOBY datatype
+				element datatype {text}*&
+				## bioMOBY namespace
+				element namespace {text}*
+			}?
+		}&
+	## parameter relevancy condition
+	Precond?&
+	## parameter default value(s)
+	# the default value or list of values (clustalw) for the parameter
+	# the vdef element is mandatory if the parameter has a vlist or a flist.
+    ## EDAM type
+	element edam_type { attribute ref { xsd:string }, attribute label { xsd:string } }*&
+	element vdef { Value+ }?&
+	(
+	  	## list of authorized values
+		# a list of the available values for this parameter?
+		element vlist { 
+			element velem { 
+				ListElem			
+				}+
+			}?
+		|
+		## list of authorized computed values 
+		# flist permit to associate a code with a value which is associate to a label in interface
+		# you could associate different codes with different proglangs for one value
+		element flist {
+			element felem { 
+				ListElem&
+				Code+ }*
+			}?
+	)&
+	## multiple values separator
+	# the character used to split the elements of a vlist for the MultipleChoice?
+	element separator { text }?&
+	## parameter value validation code
+	# associated a message to a code& the code permit to
+	# specify the valids values for a parameter*
+	element ctrl { Message& Code+ }*&
+	## authorized value range
+	element scale { 
+		## minimum value
+		element min { (Value | Code+) }&
+		## maximum value 
+		element max { (Value | Code+) }&
+		## increment 
+		element inc { text }? 
+		}?&
+	## comments
+	Comment?&
+	## example value
+	element example { anyTextOrXML }?&
+	## invocation part
+	InvocationParameterElements?&
+	## custom parameter interface
+	Interface*
+)|
+# this is an automatically generated parameter element needed for the interface generation
+## auto-generated element
+(
+    (
+    ## auto-generated standard error
+    attribute isstderr { xsd:boolean }? 
+    | 
+    ## auto-generated standard output parameter
+    attribute isstdout { xsd:boolean }?
+    )&
+    Interface*
+)
+	
+}
+
+ListElem = 
+	(
+	## undefined value
+	# the undef attribute specifies if an element is an alias for a non specified value.
+	# this links a label to the undefined value in the selectbox of the web form.
+	attribute undef { xsd:boolean }?& 
+	## value
+	Value& 
+	## label
+	Label
+	)
+
+DataFormat =
+	## data format 
+	#  the format of the data 
+	# if datatype == Sequence  format could have this values :
+	# IG & GENBANK & NBRF & EMBL & GCG & DNASTRIDER & FASTA & RAW & 
+	# PIR & XML & SWISSPROT & GDE  &
+	element dataFormat {
+	            ## force data reformatting
+		    	attribute force { xsd:boolean }?&
+		    	DynamicFunction }
+
+DynamicFunction = 
+    (
+    text | 
+    element test {
+        attribute param { text },
+        (attribute eq { text }|
+        attribute ne { text }|
+        attribute lt { text }|
+        attribute gt { text }|
+        attribute le { text }|
+        attribute ge { text }),
+        DynamicFunction
+    } |
+    element ref {attribute param {text}}
+    )*
+
+Prompt =
+	## prompt 
+	element prompt { Lang?& text }
+
+Lang = 
+	## language
+	#  lang as in RFC-1766 (e.g.& en-US& fr& fr-FR& etc.)
+	attribute lang { xsd:language }
+
+Label = 
+	## label
+	# the text associated to a value which will be display on the form
+	element label { text }
+
+Message =
+	## message 
+	# a message displayed when the control fail
+	element message { Text+ }
+
+Precond =
+	## relevancy condition 
+	# in parameter : the parameter is linked to a condition
+	# ex : this parameter could be used only if an other parameter has been specified
+	element precond { Code+ }
+
+Value = 
+	## value
+	element value { text }
+
+Code = 
+	## code snippet in a programming language
+	element code {
+		## programming language (e.g., python, perl, etc.)
+		attribute proglang {text},
+		non-empty-string
+		}
+
+## text element
+Text = 
+	element text {
+		Lang&
+		text 
+		}
+## HTML element
+anyXHTMLorSVG = (
+	element xhtml:* {
+   (attribute * { text }
+    | text
+    | anyXHTMLorSVG)*
+}|text|element svg:* {
+   (attribute * { text }
+    | text
+    | anyXHTMLorSVG)*
+})*
+
+anyTextOrXML = (
+element * {
+   (attribute * { text }
+    | text
+    | anyTextOrXML)*
+}|text
+)
+
+## paragraph
+Paragraph = element paragraph {
+	Name&
+	Prompt*&
+	Precond*&
+	Comment?&
+	Parameters& 
+  InvocationParagraph&
+	## custom paragraph layout/interface
+	(Layout|Interface*)?
+}
+
+## box-model layout
+Layout = element layout {
+	(Hbox | Vbox)+
+}
+
+## horizontal box
+Hbox = element hbox {
+	(Box | Layout) + 
+}
+
+## vertical box
+Vbox = element vbox {
+	(Box | Layout) + 
+}
+
+## box
+Box = element box { text }
+
+## custom interface specification
+Interface = element interface {
+	## override default Choice parameter display
+	(attribute field {"select"|"radio"}|
+    (attribute type {InterfaceType}&
+     attribute generated {"true"}?&
+	## override HTML display code
+	anyXHTMLorSVG))
+}
+
+
+Email = element email {xsd:token {pattern = "[a-z0-9\-\._]+@([a-z0-9\-]+\.)+([a-z]){2,4}"}}
+
+non-empty-string = xsd:token { minLength = "1" }
+
+Implementation = empty
+
+InvocationHead = empty
+
+InvocationParameterElements = empty
+
+InvocationParagraph = empty
+
+InterfaceType = empty
diff --git a/Schema/common.rng b/Schema/common.rng
new file mode 100644
index 0000000..033c1a5
--- /dev/null
+++ b/Schema/common.rng
@@ -0,0 +1,777 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <!--
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+                        Mobyle Generic Service Elements           #
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+  -->
+  <define name="Service">
+    <interleave>
+      <element name="head">
+        <ref name="Head"/>
+      </element>
+      <ref name="Parameters">
+        <a:documentation>Parameters list</a:documentation>
+      </ref>
+      <ref name="Implementation"/>
+    </interleave>
+  </define>
+  <define name="Head">
+    <interleave>
+      <ref name="Name">
+        <a:documentation>name</a:documentation>
+      </ref>
+      <optional>
+        <ref name="Package">
+          <a:documentation>package</a:documentation>
+        </ref>
+      </optional>
+      <optional>
+        <element name="version">
+          <a:documentation>version</a:documentation>
+          <text/>
+        </element>
+      </optional>
+      <element name="doc">
+        <a:documentation>information</a:documentation>
+        <interleave>
+          <element name="title">
+            <a:documentation>title</a:documentation>
+            <text/>
+          </element>
+          <element name="description">
+            <a:documentation>description</a:documentation>
+            <choice>
+              <zeroOrMore>
+                <ref name="Text"/>
+              </zeroOrMore>
+              <ref name="anyXHTMLorSVG"/>
+            </choice>
+          </element>
+          <optional>
+            <element name="authors">
+              <a:documentation>authors</a:documentation>
+              <ref name="anyXHTMLorSVG"/>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <element name="reference">
+              <a:documentation>reference</a:documentation>
+              <interleave>
+                <optional>
+                  <attribute name="doi">
+                    <a:documentation>Document Object Identifier</a:documentation>
+                    <data type="anyURI"/>
+                  </attribute>
+                </optional>
+                <optional>
+                  <attribute name="url">
+                    <a:documentation>URL</a:documentation>
+                    <data type="anyURI"/>
+                  </attribute>
+                </optional>
+                <ref name="anyXHTMLorSVG">
+                  <a:documentation>formatted reference (text or HTML code)</a:documentation>
+                </ref>
+              </interleave>
+            </element>
+          </zeroOrMore>
+          <zeroOrMore>
+            <element name="doclink">
+              <a:documentation>documentation link(s)</a:documentation>
+              <data type="anyURI"/>
+            </element>
+          </zeroOrMore>
+          <zeroOrMore>
+            <element name="sourcelink">
+              <a:documentation>download link(s)</a:documentation>
+              <choice>
+                <data type="anyURI"/>
+                <data type="string"/>
+              </choice>
+            </element>
+          </zeroOrMore>
+          <zeroOrMore>
+            <element name="homepagelink">
+              <a:documentation>homepage link(s)</a:documentation>
+              <data type="anyURI"/>
+            </element>
+          </zeroOrMore>
+          <optional>
+            <ref name="Comment">
+              <a:documentation>additional comments</a:documentation>
+            </ref>
+          </optional>
+        </interleave>
+      </element>
+      <zeroOrMore>
+        <element name="category">
+          <a:documentation>classification category</a:documentation>
+          <text/>
+        </element>
+      </zeroOrMore>
+      <zeroOrMore>
+        <element name="edam_cat">
+          <a:documentation>EDAM classification category</a:documentation>
+          <attribute name="ref">
+            <data type="string"/>
+          </attribute>
+          <attribute name="label">
+            <data type="string"/>
+          </attribute>
+        </element>
+      </zeroOrMore>
+      <optional>
+        <element name="biomobyCategory">
+          <a:documentation>bioMOBY service category</a:documentation>
+          <text/>
+        </element>
+      </optional>
+      <!-- this element stores the biomoby serviceType property value. -->
+      <ref name="InvocationHead">
+        <a:documentation>invocation elements</a:documentation>
+      </ref>
+      <zeroOrMore>
+        <ref name="Interface"/>
+      </zeroOrMore>
+    </interleave>
+  </define>
+  <define name="Name">
+    <element name="name">
+      <data type="string"/>
+    </element>
+  </define>
+  <define name="Comment">
+    <element name="comment">
+      <choice>
+        <zeroOrMore>
+          <ref name="Text"/>
+        </zeroOrMore>
+        <ref name="anyXHTMLorSVG"/>
+      </choice>
+    </element>
+  </define>
+  <define name="Package">
+    <a:documentation>package element defines elements common to all the interfaces of a package</a:documentation>
+    <element name="package">
+      <ref name="Head"/>
+    </element>
+  </define>
+  <define name="Parameters">
+    <element name="parameters">
+      <zeroOrMore>
+        <choice>
+          <ref name="Parameter"/>
+          <ref name="Paragraph"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="Parameter">
+    <element name="parameter">
+      <choice>
+        <interleave>
+          <optional>
+            <!-- id so far is added to facilitate workflow developments -->
+            <attribute name="id">
+              <a:documentation>technical ID</a:documentation>
+              <data type="string"/>
+            </attribute>
+          </optional>
+          <optional>
+            <!-- ismaininput : if true the parameter is a primary article of the corresponding biomoby service (as used in PlayMoby) -->
+            <attribute name="ismaininput">
+              <a:documentation>program main input</a:documentation>
+              <data type="boolean"/>
+            </attribute>
+          </optional>
+          <optional>
+            <!-- ismandatory : if true this parameter should be specified by the user -->
+            <attribute name="ismandatory">
+              <a:documentation>mandatory parameter</a:documentation>
+              <data type="boolean"/>
+            </attribute>
+          </optional>
+          <optional>
+            <attribute name="ishidden">
+              <a:documentation>hidden parameter</a:documentation>
+              <data type="boolean"/>
+            </attribute>
+          </optional>
+          <optional>
+            <!-- issimple : specify if this parameter will be displayed only in the simple web form. -->
+            <attribute name="issimple">
+              <a:documentation>show in simple forms	</a:documentation>
+              <data type="boolean"/>
+            </attribute>
+          </optional>
+          <choice>
+            <optional>
+              <!-- isout and isstdout are mutually exclusive -->
+              <!-- isout : if true this parameter is produced by this service -->
+              <attribute name="isout">
+                <a:documentation>output parameter</a:documentation>
+                <data type="boolean"/>
+              </attribute>
+            </optional>
+            <optional>
+              <!-- isstdout : if true this parameter is the standart output of the service. Only one parameter can be isstdout="1" per service -->
+              <attribute name="isstdout">
+                <a:documentation>standard output parameter</a:documentation>
+                <data type="boolean"/>
+              </attribute>
+            </optional>
+          </choice>
+          <ref name="Name">
+            <a:documentation>parameter name</a:documentation>
+          </ref>
+          <zeroOrMore>
+            <ref name="Prompt">
+              <a:documentation>parameter prompt</a:documentation>
+            </ref>
+          </zeroOrMore>
+          <element name="type">
+            <a:documentation>parameter type</a:documentation>
+            <interleave>
+              <zeroOrMore>
+                <!--
+                  the biological type of the parameter DNA & Protein ...
+                  the biotypes list represents the list of accepted 'types'
+                  for input parameters and the list of 'types' that an 
+                  output may belong to
+                -->
+                <element name="biotype">
+                  <a:documentation>parameter biotype</a:documentation>
+                  <text/>
+                </element>
+              </zeroOrMore>
+              <!-- type of the data Sequence & Structure & Matrix ... -->
+              <element name="datatype">
+                <a:documentation>parameter datatype</a:documentation>
+                <interleave>
+                  <element name="class">
+                    <a:documentation>datatype class</a:documentation>
+                    <text/>
+                  </element>
+                  <optional>
+                    <!--
+                       superclass or class if class does not exist
+                       is a Mobyle parameter Class defined in Src/Mobyle/* or in Local/CustomClasses/*
+                    -->
+                    <element name="superclass">
+                      <a:documentation>datatype superclass</a:documentation>
+                      <text/>
+                    </element>
+                  </optional>
+                </interleave>
+              </element>
+              <zeroOrMore>
+                <!-- acceptedDataFormats is a deprecated tag, and will be removed in a future version - please use only non-nested dataFormat tags -->
+                <ref name="DataFormat">
+                  <a:documentation>accepted data formats</a:documentation>
+                </ref>
+              </zeroOrMore>
+              <optional>
+                <!--
+                  the cardinality of the datatype &a number or a couple of value &  int&int or int&n
+                  biotype=Protein datatype=( class = Sequence ) & format=Fasta & card=1  means
+                  the user must provide 1 and only one Protein Sequence in Fasta format to fill this input parameter 
+                  biotype=Protein datatype=( class = Sequence ) & format=Fasta & card=1.n  means
+                  the user must provide 1 or more  Protein Sequence in Fasta format to fill this input parameter
+                -->
+                <element name="card">
+                  <a:documentation>parameter cardinality</a:documentation>
+                  <text/>
+                </element>
+              </optional>
+              <optional>
+                <!--
+                  the biomoby typing accepts only one format for a biomoby service
+                  however many programs accept different formats for a given parameter
+                  thus multiple biomoby datatypes can be specified, and a single mobyle
+                  program can be seen as multiple biomoby services.
+                  the name of the object in the bioMoby ontology. it's implied
+                  that it's a primary and if not specify the parameter is a secondary in biomoby
+                -->
+                <element name="biomoby">
+                  <a:documentation>bioMOBY typing</a:documentation>
+                  <interleave>
+                    <zeroOrMore>
+                      <element name="datatype">
+                        <a:documentation>bioMOBY datatype</a:documentation>
+                        <text/>
+                      </element>
+                    </zeroOrMore>
+                    <zeroOrMore>
+                      <element name="namespace">
+                        <a:documentation>bioMOBY namespace</a:documentation>
+                        <text/>
+                      </element>
+                    </zeroOrMore>
+                  </interleave>
+                </element>
+              </optional>
+            </interleave>
+          </element>
+          <optional>
+            <ref name="Precond">
+              <a:documentation>parameter relevancy condition</a:documentation>
+            </ref>
+          </optional>
+          <zeroOrMore>
+            <element name="edam_type">
+              <a:documentation>parameter default value(s)</a:documentation>
+              <!--
+                the default value or list of values (clustalw) for the parameter
+                the vdef element is mandatory if the parameter has a vlist or a flist.
+              -->
+              <a:documentation>EDAM type</a:documentation>
+              <attribute name="ref">
+                <data type="string"/>
+              </attribute>
+              <attribute name="label">
+                <data type="string"/>
+              </attribute>
+            </element>
+          </zeroOrMore>
+          <optional>
+            <element name="vdef">
+              <oneOrMore>
+                <ref name="Value"/>
+              </oneOrMore>
+            </element>
+          </optional>
+          <choice>
+            <optional>
+              <!-- a list of the available values for this parameter? -->
+              <element name="vlist">
+                <a:documentation>list of authorized values</a:documentation>
+                <oneOrMore>
+                  <element name="velem">
+                    <ref name="ListElem"/>
+                  </element>
+                </oneOrMore>
+              </element>
+            </optional>
+            <optional>
+              <!--
+                flist permit to associate a code with a value which is associate to a label in interface
+                you could associate different codes with different proglangs for one value
+              -->
+              <element name="flist">
+                <a:documentation>list of authorized computed values </a:documentation>
+                <zeroOrMore>
+                  <element name="felem">
+                    <interleave>
+                      <ref name="ListElem"/>
+                      <oneOrMore>
+                        <ref name="Code"/>
+                      </oneOrMore>
+                    </interleave>
+                  </element>
+                </zeroOrMore>
+              </element>
+            </optional>
+          </choice>
+          <optional>
+            <!-- the character used to split the elements of a vlist for the MultipleChoice? -->
+            <element name="separator">
+              <a:documentation>multiple values separator</a:documentation>
+              <text/>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <!--
+              associated a message to a code& the code permit to
+              specify the valids values for a parameter*
+            -->
+            <element name="ctrl">
+              <a:documentation>parameter value validation code</a:documentation>
+              <interleave>
+                <ref name="Message"/>
+                <oneOrMore>
+                  <ref name="Code"/>
+                </oneOrMore>
+              </interleave>
+            </element>
+          </zeroOrMore>
+          <optional>
+            <element name="scale">
+              <a:documentation>authorized value range</a:documentation>
+              <interleave>
+                <element name="min">
+                  <a:documentation>minimum value</a:documentation>
+                  <choice>
+                    <ref name="Value"/>
+                    <oneOrMore>
+                      <ref name="Code"/>
+                    </oneOrMore>
+                  </choice>
+                </element>
+                <element name="max">
+                  <a:documentation>maximum value </a:documentation>
+                  <choice>
+                    <ref name="Value"/>
+                    <oneOrMore>
+                      <ref name="Code"/>
+                    </oneOrMore>
+                  </choice>
+                </element>
+                <optional>
+                  <element name="inc">
+                    <a:documentation>increment </a:documentation>
+                    <text/>
+                  </element>
+                </optional>
+              </interleave>
+            </element>
+          </optional>
+          <optional>
+            <ref name="Comment">
+              <a:documentation>comments</a:documentation>
+            </ref>
+          </optional>
+          <optional>
+            <element name="example">
+              <a:documentation>example value</a:documentation>
+              <ref name="anyTextOrXML"/>
+            </element>
+          </optional>
+          <optional>
+            <ref name="InvocationParameterElements">
+              <a:documentation>invocation part</a:documentation>
+            </ref>
+          </optional>
+          <zeroOrMore>
+            <ref name="Interface">
+              <a:documentation>custom parameter interface</a:documentation>
+            </ref>
+          </zeroOrMore>
+        </interleave>
+        <!-- this is an automatically generated parameter element needed for the interface generation -->
+        <interleave>
+          <a:documentation>auto-generated element</a:documentation>
+          <choice>
+            <optional>
+              <attribute name="isstderr">
+                <a:documentation>auto-generated standard error</a:documentation>
+                <data type="boolean"/>
+              </attribute>
+            </optional>
+            <optional>
+              <attribute name="isstdout">
+                <a:documentation>auto-generated standard output parameter</a:documentation>
+                <data type="boolean"/>
+              </attribute>
+            </optional>
+          </choice>
+          <zeroOrMore>
+            <ref name="Interface"/>
+          </zeroOrMore>
+        </interleave>
+      </choice>
+    </element>
+  </define>
+  <define name="ListElem">
+    <interleave>
+      <optional>
+        <!--
+          the undef attribute specifies if an element is an alias for a non specified value.
+          this links a label to the undefined value in the selectbox of the web form.
+        -->
+        <attribute name="undef">
+          <a:documentation>undefined value</a:documentation>
+          <data type="boolean"/>
+        </attribute>
+      </optional>
+      <ref name="Value">
+        <a:documentation>value</a:documentation>
+      </ref>
+      <ref name="Label">
+        <a:documentation>label</a:documentation>
+      </ref>
+    </interleave>
+  </define>
+  <define name="DataFormat">
+    <!--
+       the format of the data 
+      if datatype == Sequence  format could have this values :
+      IG & GENBANK & NBRF & EMBL & GCG & DNASTRIDER & FASTA & RAW & 
+      PIR & XML & SWISSPROT & GDE  &
+    -->
+    <element name="dataFormat">
+      <a:documentation>data format </a:documentation>
+      <interleave>
+        <optional>
+          <attribute name="force">
+            <a:documentation>force data reformatting</a:documentation>
+            <data type="boolean"/>
+          </attribute>
+        </optional>
+        <ref name="DynamicFunction"/>
+      </interleave>
+    </element>
+  </define>
+  <define name="DynamicFunction">
+    <zeroOrMore>
+      <choice>
+        <text/>
+        <element name="test">
+          <attribute name="param"/>
+          <choice>
+            <attribute name="eq"/>
+            <attribute name="ne"/>
+            <attribute name="lt"/>
+            <attribute name="gt"/>
+            <attribute name="le"/>
+            <attribute name="ge"/>
+          </choice>
+          <ref name="DynamicFunction"/>
+        </element>
+        <element name="ref">
+          <attribute name="param"/>
+        </element>
+      </choice>
+    </zeroOrMore>
+  </define>
+  <define name="Prompt">
+    <element name="prompt">
+      <a:documentation>prompt </a:documentation>
+      <interleave>
+        <optional>
+          <ref name="Lang"/>
+        </optional>
+        <text/>
+      </interleave>
+    </element>
+  </define>
+  <define name="Lang">
+    <!--  lang as in RFC-1766 (e.g.& en-US& fr& fr-FR& etc.) -->
+    <attribute name="lang">
+      <a:documentation>language</a:documentation>
+      <data type="language"/>
+    </attribute>
+  </define>
+  <define name="Label">
+    <!-- the text associated to a value which will be display on the form -->
+    <element name="label">
+      <a:documentation>label</a:documentation>
+      <text/>
+    </element>
+  </define>
+  <define name="Message">
+    <!-- a message displayed when the control fail -->
+    <element name="message">
+      <a:documentation>message </a:documentation>
+      <oneOrMore>
+        <ref name="Text"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="Precond">
+    <!--
+      in parameter : the parameter is linked to a condition
+      ex : this parameter could be used only if an other parameter has been specified
+    -->
+    <element name="precond">
+      <a:documentation>relevancy condition </a:documentation>
+      <oneOrMore>
+        <ref name="Code"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="Value">
+    <element name="value">
+      <a:documentation>value</a:documentation>
+      <text/>
+    </element>
+  </define>
+  <define name="Code">
+    <element name="code">
+      <a:documentation>code snippet in a programming language</a:documentation>
+      <attribute name="proglang">
+        <a:documentation>programming language (e.g., python, perl, etc.)</a:documentation>
+      </attribute>
+      <ref name="non-empty-string"/>
+    </element>
+  </define>
+  <define name="Text">
+    <a:documentation>text element</a:documentation>
+    <element name="text">
+      <interleave>
+        <ref name="Lang"/>
+        <text/>
+      </interleave>
+    </element>
+  </define>
+  <define name="anyXHTMLorSVG">
+    <a:documentation>HTML element</a:documentation>
+    <zeroOrMore>
+      <choice>
+        <element>
+          <nsName ns="http://www.w3.org/1999/xhtml"/>
+          <zeroOrMore>
+            <choice>
+              <attribute>
+                <anyName/>
+              </attribute>
+              <text/>
+              <ref name="anyXHTMLorSVG"/>
+            </choice>
+          </zeroOrMore>
+        </element>
+        <text/>
+        <element>
+          <nsName ns="http://www.w3.org/2000/svg"/>
+          <zeroOrMore>
+            <choice>
+              <attribute>
+                <anyName/>
+              </attribute>
+              <text/>
+              <ref name="anyXHTMLorSVG"/>
+            </choice>
+          </zeroOrMore>
+        </element>
+      </choice>
+    </zeroOrMore>
+  </define>
+  <define name="anyTextOrXML">
+    <choice>
+      <element>
+        <anyName/>
+        <zeroOrMore>
+          <choice>
+            <attribute>
+              <anyName/>
+            </attribute>
+            <text/>
+            <ref name="anyTextOrXML"/>
+          </choice>
+        </zeroOrMore>
+      </element>
+      <text/>
+    </choice>
+  </define>
+  <define name="Paragraph">
+    <a:documentation>paragraph</a:documentation>
+    <element name="paragraph">
+      <interleave>
+        <ref name="Name"/>
+        <zeroOrMore>
+          <ref name="Prompt"/>
+        </zeroOrMore>
+        <zeroOrMore>
+          <ref name="Precond"/>
+        </zeroOrMore>
+        <optional>
+          <ref name="Comment"/>
+        </optional>
+        <ref name="Parameters"/>
+        <ref name="InvocationParagraph"/>
+        <optional>
+          <choice>
+            <a:documentation>custom paragraph layout/interface</a:documentation>
+            <ref name="Layout"/>
+            <zeroOrMore>
+              <ref name="Interface"/>
+            </zeroOrMore>
+          </choice>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+  <define name="Layout">
+    <a:documentation>box-model layout</a:documentation>
+    <element name="layout">
+      <oneOrMore>
+        <choice>
+          <ref name="Hbox"/>
+          <ref name="Vbox"/>
+        </choice>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="Hbox">
+    <a:documentation>horizontal box</a:documentation>
+    <element name="hbox">
+      <oneOrMore>
+        <choice>
+          <ref name="Box"/>
+          <ref name="Layout"/>
+        </choice>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="Vbox">
+    <a:documentation>vertical box</a:documentation>
+    <element name="vbox">
+      <oneOrMore>
+        <choice>
+          <ref name="Box"/>
+          <ref name="Layout"/>
+        </choice>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="Box">
+    <a:documentation>box</a:documentation>
+    <element name="box">
+      <text/>
+    </element>
+  </define>
+  <define name="Interface">
+    <a:documentation>custom interface specification</a:documentation>
+    <element name="interface">
+      <choice>
+        <a:documentation>override default Choice parameter display</a:documentation>
+        <attribute name="field">
+          <choice>
+            <value>select</value>
+            <value>radio</value>
+          </choice>
+        </attribute>
+        <interleave>
+          <attribute name="type">
+            <ref name="InterfaceType"/>
+          </attribute>
+          <optional>
+            <attribute name="generated">
+              <value>true</value>
+            </attribute>
+          </optional>
+          <ref name="anyXHTMLorSVG">
+            <a:documentation>override HTML display code</a:documentation>
+          </ref>
+        </interleave>
+      </choice>
+    </element>
+  </define>
+  <define name="Email">
+    <element name="email">
+      <data type="token">
+        <param name="pattern">[a-z0-9\-\._]+@([a-z0-9\-]+\.)+([a-z]){2,4}</param>
+      </data>
+    </element>
+  </define>
+  <define name="non-empty-string">
+    <data type="token">
+      <param name="minLength">1</param>
+    </data>
+  </define>
+  <define name="Implementation">
+    <empty/>
+  </define>
+  <define name="InvocationHead">
+    <empty/>
+  </define>
+  <define name="InvocationParameterElements">
+    <empty/>
+  </define>
+  <define name="InvocationParagraph">
+    <empty/>
+  </define>
+  <define name="InterfaceType">
+    <empty/>
+  </define>
+</grammar>
diff --git a/Schema/jobstate.rnc b/Schema/jobstate.rnc
new file mode 100644
index 0000000..bc6c591
--- /dev/null
+++ b/Schema/jobstate.rnc
@@ -0,0 +1,82 @@
+datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" 
+namespace xhtml = "http://www.w3.org/1999/xhtml"
+
+
+include "common.rnc"
+
+start = JobState
+
+#---------------------------------------------------------------#
+#                     Mobyle JobState Definition                #
+#---------------------------------------------------------------#
+
+JobState = element jobState {
+	#this part describes the elements specific to the file that stores job information
+	element name {xsd:anyURI}&
+	#name is the url of the program invoked to run the job 
+	element host {xsd:anyURI}&
+	#host is the host server of the job
+	element id {xsd:anyURI}&
+	#id is the job identifier
+	element date {xsd:token}&
+	#date on which the job has been submitted on the server
+	element status{
+		element value{
+			"building"|"submitted"|"pending"|"running"|"finished"|"error"
+			|"killed"|"hold"|"finishing"
+		}&
+		element message{text}?
+	}&
+	Email?&
+	element sessionKey {text}?&
+	element workflowId {text}?&
+	element data{
+		Input*&
+		Output*
+	}?&
+	(
+    	(
+    	    # this is the "program specific" part of the "job state"
+        	## parameter files
+        	element paramFiles {
+        		File*
+        	}?&
+        	## generated command line
+        	element commandLine{text}?
+    	)
+    	|
+    	(
+    	    # this is the "workflow specific" part of the "job state"
+        	element jobLink{
+        	 attribute taskRef {xsd:anyURI}&
+        	 attribute jobId {xsd:anyURI}
+        	} 
+    	)
+	)
+}
+
+Input = element input{
+	Parameter&
+	((File&
+	  element fmtProgram {text}?&
+	  element formattedFile{
+			FileContent
+	  	}?)
+	  |element value {text})
+}
+
+Output = element output{
+	Parameter&
+	File*
+}
+
+File = element file{
+	FileContent
+}
+
+FileContent = (
+	attribute size{xsd:positiveInteger}?&
+	attribute fmt{xsd:string}?&
+	attribute origName{xsd:string}&
+	text
+)
\ No newline at end of file
diff --git a/Schema/jobstate.rng b/Schema/jobstate.rng
new file mode 100644
index 0000000..698c01a
--- /dev/null
+++ b/Schema/jobstate.rng
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start>
+    <ref name="JobState"/>
+  </start>
+  <!--
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+                        Mobyle JobState Definition                #
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+  -->
+  <define name="JobState">
+    <element name="jobState">
+      <interleave>
+        <!-- this part describes the elements specific to the file that stores job information -->
+        <element name="name">
+          <data type="anyURI"/>
+        </element>
+        <!-- name is the url of the program invoked to run the job -->
+        <element name="host">
+          <data type="anyURI"/>
+        </element>
+        <!-- host is the host server of the job -->
+        <element name="id">
+          <data type="anyURI"/>
+        </element>
+        <!-- id is the job identifier -->
+        <element name="date">
+          <data type="token"/>
+        </element>
+        <!-- date on which the job has been submitted on the server -->
+        <element name="status">
+          <interleave>
+            <element name="value">
+              <choice>
+                <value>building</value>
+                <value>submitted</value>
+                <value>pending</value>
+                <value>running</value>
+                <value>finished</value>
+                <value>error</value>
+                <value>killed</value>
+                <value>hold</value>
+                <value>finishing</value>
+              </choice>
+            </element>
+            <optional>
+              <element name="message">
+                <text/>
+              </element>
+            </optional>
+          </interleave>
+        </element>
+        <optional>
+          <ref name="Email"/>
+        </optional>
+        <optional>
+          <element name="sessionKey">
+            <text/>
+          </element>
+        </optional>
+        <optional>
+          <element name="workflowId">
+            <text/>
+          </element>
+        </optional>
+        <optional>
+          <element name="data">
+            <interleave>
+              <zeroOrMore>
+                <ref name="Input"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="Output"/>
+              </zeroOrMore>
+            </interleave>
+          </element>
+        </optional>
+        <choice>
+          <interleave>
+            <optional>
+              <!-- this is the "program specific" part of the "job state" -->
+              <element name="paramFiles">
+                <a:documentation>parameter files</a:documentation>
+                <zeroOrMore>
+                  <ref name="File"/>
+                </zeroOrMore>
+              </element>
+            </optional>
+            <optional>
+              <element name="commandLine">
+                <a:documentation>generated command line</a:documentation>
+                <text/>
+              </element>
+            </optional>
+          </interleave>
+          <!-- this is the "workflow specific" part of the "job state" -->
+          <element name="jobLink">
+            <interleave>
+              <attribute name="taskRef">
+                <data type="anyURI"/>
+              </attribute>
+              <attribute name="jobId">
+                <data type="anyURI"/>
+              </attribute>
+            </interleave>
+          </element>
+        </choice>
+      </interleave>
+    </element>
+  </define>
+  <define name="Input">
+    <element name="input">
+      <interleave>
+        <ref name="Parameter"/>
+        <choice>
+          <interleave>
+            <ref name="File"/>
+            <optional>
+              <element name="fmtProgram">
+                <text/>
+              </element>
+            </optional>
+            <optional>
+              <element name="formattedFile">
+                <ref name="FileContent"/>
+              </element>
+            </optional>
+          </interleave>
+          <element name="value">
+            <text/>
+          </element>
+        </choice>
+      </interleave>
+    </element>
+  </define>
+  <define name="Output">
+    <element name="output">
+      <interleave>
+        <ref name="Parameter"/>
+        <zeroOrMore>
+          <ref name="File"/>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="File">
+    <element name="file">
+      <ref name="FileContent"/>
+    </element>
+  </define>
+  <define name="FileContent">
+    <interleave>
+      <optional>
+        <attribute name="size">
+          <data type="positiveInteger"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="fmt">
+          <data type="string"/>
+        </attribute>
+      </optional>
+      <attribute name="origName">
+        <data type="string"/>
+      </attribute>
+      <text/>
+    </interleave>
+  </define>
+</grammar>
diff --git a/Schema/mobyle.sch b/Schema/mobyle.sch
new file mode 100644
index 0000000..90e0aa4
--- /dev/null
+++ b/Schema/mobyle.sch
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is a validating schematron file for the mobyle service       -->
+<!-- definitions, heavily inspired by existing python-based            -->
+<!-- validators from Bertrand Néron and Nicolas Joly 			       -->
+<!--                                                			       -->
+<!-- Author: Hervé Ménager					             		       -->
+<!-- 'Biological Software and Databases' Group, Institut Pasteur, Paris.  		-->
+<!-- Distributed under LGPLv2 Licence. Please refer to the COPYING.LIB document.-->
+<schema xmlns="http://purl.oclc.org/dsdl/schematron" >
+     <pattern id="check_program">
+          <rule context="program">
+       	   	   <let name="commandElementsCount" value="./head/command"/>
+       	   	   <let name="iscommandParametersCount" value="count(.//parameter[@iscommand='1' and not(precond) and not(ancestor::paragraph/precond)])"/>
+       	   	   <let name="stdoutParametersCount" value="count(.//parameter[@isstdout='1' and not(precond) and not(ancestor::paragraph/precond)])"/>
+       	   	   <let name="stderrParametersCount" value="count(.//parameter[@isstderr='1' and not(precond) and not(ancestor::paragraph/precond)])"/>
+               <assert test="not(count(.//parameter[@iscommand='1'])=1 and $commandElementsCount=1)">
+               A program cannot contain both a command element and an iscommand parameter
+               </assert>
+               <assert test="not($iscommandParametersCount=0 and $commandElementsCount=0)">
+               A program has to contain one command element or one iscommand parameter:
+               (<value-of select="$iscommandParametersCount"/> iscommand parameters, 
+               <value-of select="$commandElementsCount"/> command elements)
+               </assert>
+               <assert test="$iscommandParametersCount <= 1">
+               	Only one parameter can be iscommand if no precond specified (current number in document: <value-of select="$iscommandParametersCount"/>)
+               </assert>
+               <assert test="$stdoutParametersCount <= 1">
+               	Only one parameter can link to standard output if no precond specified (current number in document: <value-of select="$stdoutParametersCount"/>)
+               </assert>
+               <assert test="$stderrParametersCount <= 1">
+               	Only one parameter can link to standard error if no precond specified (current number in document: <value-of select="$stderrParametersCount"/>)
+               </assert>
+          </rule>
+          <rule context="program/head/name">
+           	   <assert test="not($fileNameParameter) or concat(text(),'.xml')=$fileNameParameter">
+               Inconsistency between file name (<value-of select="$fileNameParameter"/>) and name value (<value-of select="text()"/>).
+          	   </assert>
+    	  </rule>          
+     	<rule context="workflow">
+     		<assert test="count(.//parameter[@isout])>0">
+     			At least one output parameter must be specified in the workflow.
+     		</assert>
+     	</rule>
+     	<rule context="workflow//parameter">
+     		<let name="parameter_id" value="@id"/>
+     		<assert test="@id">
+     			An id attribute must be specified for workflow parameters, missing in parameter "<value-of select="name/text()"/>".
+     		</assert>
+     		<assert test="not(@isstdout)">
+     		  Workflow parameter "<value-of select="name/text()"/>" cannot be @isstdout, if it is an output please use @isout.
+     		</assert>
+     		<assert test="//link[(@toParameter=$parameter_id and not(@toTask)) or (@fromParameter=$parameter_id and not(@fromTask))]">
+     			The workflow parameter "<value-of select="name/text()"/>" is not used in any link.
+     		</assert>
+     	</rule>
+     	<rule context="workflow//link">
+     		<let name="linkString" value="concat(@fromTask,':', at fromParameter,'->', at toTask,':', at toParameter)" />
+     		<assert test="@fromTask or //parameter[@id=current()/@fromParameter]">
+     			The source parameter id "<value-of select="@fromParameter" />" for the link "<value-of select="$linkString" />" does not exist.
+     		</assert>
+     		<assert test="@toTask or //parameter[@id=current()/@toParameter]">
+     			The target parameter id "<value-of select="@toParameter" />" for the link "<value-of select="$linkString" />" does not exist.
+     		</assert>
+     	</rule>
+     	<rule context="parameter/name">
+     		<let name="reservedWords" value="'|acceptCharset|action|autocomplete|elements|encoding|enctype|length|method|name|noValidate|checkValidity|dispatchFormChange|dispatchFormInput|item|namedItem|submit|reset|attributes|baseURI|baseURIObject|childElementCount|childNodes|children|classList|className|clientHeight|clientLeft|clientTop|clientWidth|contentEditable|dataset|dir|firstChild|firstElementChild|id|innerHTML|isContentEditable|lang|lastChild|lastElementChild|localName|name|namespace [...]
+     		<assert test="not(contains($reservedWords,concat('|',text(),'|')))">
+     			Invalid name for parameter '<value-of select="text()"/>': this name cannot be used.
+     		</assert>
+     	</rule>          
+     	<rule context="parameter">
+          	   <let name="parameter" value="."/>
+          	   <let name="parameterName" value="$parameter/name"/>
+          	   <let name="vdef_value" value="vdef/value/text()" /> 
+           	   <assert test="count($parameter//*[@undef='1']) < 2">
+          	   Invalid value list for parameter '<value-of select="$parameterName"/>': this parameter has multiple undefined values.
+          	   </assert>
+               <assert test="not( (not(@issimple) or (@issimple='0')) and (@ismandatory and @ismandatory='1') and not(precond or ancestor::paragraph/precond) and ( not(vdef) or vdef/value/text()=vlist/velem[@undef='1']/value/text() ))">
+          	   a mandatory parameter ('<value-of select="$parameterName"/>') without precond nor default value or with an undefined default value must be tag as @issimple
+               </assert>
+               <assert test="not( (@ishidden and @ishidden='1') and (@issimple and @issimple='1') )">
+               a simple parameter('<value-of select="$parameterName"/>') cannot be hidden too.
+               </assert>
+               <assert test="not( ((@isout and @isout='1') or (@istdout and @istdout='1')) and (@issimple and @issimple='1') )">
+               an output parameter('<value-of select="$parameterName"/>') cannot be simple too.
+               </assert>
+		  </rule>          
+          <rule context="parameter/vlist/velem | parameter/flist/felem">
+          	   <let name="value" value="value/text()"/>
+          	   <let name="label" value="label/text()"/>
+          	   <let name="parameter" value="../.."/>
+          	   <let name="parameterName" value="$parameter/name"/>
+							 <assert test="not(preceding-sibling::*/value[text()=$value])" >
+          	   Invalid value list for parameter '<value-of select="$parameterName"/>': duplicate value '<value-of select="$value"/>'.
+							 </assert>
+							 <assert test="not(preceding-sibling::*/label[text()=$label])" >
+          	   Invalid value list for parameter '<value-of select="$parameterName"/>': duplicate label '<value-of select="$label"/>'.
+							 </assert>
+					</rule>					
+          <rule context="parameter[@isout='1']">
+          	   <let name="parameterName" value="name"/>
+               <assert test="count(filenames) > 0">
+               Invalid parameter: <value-of select="$parameterName"/>: an isout parameter must include a filename
+               </assert>
+          </rule>
+          <rule context="parameter[((not(@isout)) or (@isout='0')) and ((not(@isstdout)) or (@isstdout='0')) and ((not(@isstderr)) or (@isstderr='0'))]">
+          	   <let name="parameterName" value="name"/>
+               <assert test="count(filenames) = 0">
+               Invalid parameter: <value-of select="$parameterName"/>: an input parameter cannot include a filename 
+               </assert>
+          </rule>
+          <rule context="parameter/type/datatype/class">
+          	   <let name="parameter" value="../../.."/>
+          	   <let name="parameterName" value="$parameter/name"/>
+          	   <let name="isOut" value="$parameter/@isout='1'"/>
+          	   <let name="isStdout" value="$parameter/@isstdout='1'"/>
+          	   <let name="isIn" value="not($isOut or $isStdout)"/>          	   
+          	   <let name="isHidden" value="$parameter/@ishidden='1'"/>          	   
+          	   <let name="class" value="text()"/>          	   
+          	   <let name="hasDataFormats" value="count(../..//dataFormat) > 0"/>          	   
+          	   <assert test="not(starts-with($class,'Abstract'))">
+          	   Invalid parameter datatype class for parameter '<value-of select="$parameterName"/>': no parameter datatype class can be abstract.
+          	   </assert>
+          	   <!-- 
+          	   <assert test="not($class='Text' and not($isIn))">
+          	   Invalid parameter datatype class for parameter '<value-of select="$parameterName"/>': no output parameter datatype class can be set to "Text".
+          	   </assert>
+          	    -->
+          	   <assert test="not($class='Text' and $isHidden)">
+          	   Invalid parameter datatype class for parameter '<value-of select="$parameterName"/>': no hidden parameter datatype class can be set to "Text".
+          	   </assert>
+          	   <assert test="$class='MultipleChoice' or count($parameter/vdef/value) < 2">
+          	   Invalid parameter datatype class or default value for parameter '<value-of select="$parameterName"/>': this parameter has multiple default values, yet it is not typed as a MultipleChoice.
+          	   </assert>
+          		<assert test="not(starts-with('Multiple',$class) and $parameter/separator)">
+          	   Invalid parameter datatype class or separator for parameter '<value-of select="$parameterName"/>': this parameter has a value separator specified, yet it is typed as a "Multiple" type.
+          	   </assert>
+          	   <assert test="not(($class='Boolean' or $class='Integer' or $class='Float' or $class='String' or $class='Choice' or $class='MultipleChoice' or $class='FileName') and ($isOut or $isStdout))">
+          	   Invalid parameter datatype class for parameter '<value-of select="$parameterName"/>': no output parameter datatype class can be set to <value-of select="$class"/>.
+          	   </assert>
+          </rule>
+          <rule context="parameter/vdef/value">
+          	   <let name="value" value="text()"/>
+          	   <let name="parameter" value="../.."/>
+          	   <let name="parameterName" value="$parameter/name"/>
+          	   <let name="class" value="$parameter/type/datatype/class"/>
+          	   <let name="list" value="$parameter/vlist | $parameter/flist"/>
+          	   <let name="values" value="$list//value"/>
+          	   <let name="label" value="$list//label"/>
+          	   <assert test="count($list)=0 or count($values[text()=$value])">
+          	   Invalid default value for parameter '<value-of select="$parameterName"/>': default value <value-of select="$value"/> is not part of the possible values list.
+          	   </assert>
+          </rule>
+     </pattern>
+</schema>
\ No newline at end of file
diff --git a/Schema/package.rnc b/Schema/package.rnc
new file mode 100644
index 0000000..fd5eec3
--- /dev/null
+++ b/Schema/package.rnc
@@ -0,0 +1,3 @@
+include "common.rnc" 
+
+start |= Package
\ No newline at end of file
diff --git a/Schema/package.rng b/Schema/package.rng
new file mode 100644
index 0000000..43b5892
--- /dev/null
+++ b/Schema/package.rng
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common.rng"/>
+  <start combine="choice">
+    <ref name="Package"/>
+  </start>
+</grammar>
diff --git a/Schema/program.rnc b/Schema/program.rnc
new file mode 100644
index 0000000..c866421
--- /dev/null
+++ b/Schema/program.rnc
@@ -0,0 +1,65 @@
+include "common.rnc"
+
+start |= Program
+
+Program = element program {
+	Service
+}
+
+InvocationHead &=		(
+		 ## executable name
+		 element command {
+			## custom path	
+			attribute path { text }?&
+			text
+			}?&
+		 ## environment variable for program invocation
+		 element env { 
+			## name
+			attribute name { text }&
+			## value
+			text 
+			}*
+		 )?&
+		 ## progress report output for the program
+		 # displayed even before the program completes
+		 element progressReport {
+		    ## prompt to "label" the progress report
+		    attribute prompt { text }?&
+		    text
+		 }?
+
+InvocationParagraph &= Argpos?
+
+InvocationParameterElements &=
+	(
+	(
+	 ## command parameter
+	 # iscommand : if true& this parameter specify the line of command to run the program  used when the command line is more complicated.
+	 attribute iscommand { xsd:boolean }?|
+	 ## parameter file
+	 element paramfile { text }?
+	)&
+	## command line chunk evaluation code 
+	Format?&
+	## command line chunk position index
+	Argpos?&
+	## result file(s) mask(s) 
+	# this element is relevant only for output parameters, it is used to find the files in which the results are stored
+	# each code must have ONLY ONE file unix mask 
+	element filenames { Code+ }?
+	)
+
+Argpos =
+	## command line chunk position index 
+	# in parameter: specifies the position of this parameter on the command line
+	# in paragraph: specifies the position on the command line for all parameters of this paragraph
+	# by convention the argpos of command must be 0. if we want args before command we could get negative argpos.
+	element argpos { xsd:integer }
+	
+Format =
+	## command line chunk construction code
+	# a code which will be evaluated to form the command line
+	element format { Code+ }
+	
+InterfaceType &= ("form"|"job_input"|"job_output")
\ No newline at end of file
diff --git a/Schema/program.rng b/Schema/program.rng
new file mode 100644
index 0000000..bc1798c
--- /dev/null
+++ b/Schema/program.rng
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start combine="choice">
+    <ref name="Program"/>
+  </start>
+  <define name="Program">
+    <element name="program">
+      <ref name="Service"/>
+    </element>
+  </define>
+  <define name="InvocationHead" combine="interleave">
+    <interleave>
+      <optional>
+        <interleave>
+          <optional>
+            <element name="command">
+              <a:documentation>executable name</a:documentation>
+              <interleave>
+                <optional>
+                  <attribute name="path">
+                    <a:documentation>custom path	</a:documentation>
+                  </attribute>
+                </optional>
+                <text/>
+              </interleave>
+            </element>
+          </optional>
+          <zeroOrMore>
+            <element name="env">
+              <a:documentation>environment variable for program invocation</a:documentation>
+              <interleave>
+                <attribute name="name">
+                  <a:documentation>name</a:documentation>
+                </attribute>
+                <text>
+                  <a:documentation>value</a:documentation>
+                </text>
+              </interleave>
+            </element>
+          </zeroOrMore>
+        </interleave>
+      </optional>
+      <optional>
+        <!-- displayed even before the program completes -->
+        <element name="progressReport">
+          <a:documentation>progress report output for the program</a:documentation>
+          <interleave>
+            <optional>
+              <attribute name="prompt">
+                <a:documentation>prompt to "label" the progress report</a:documentation>
+              </attribute>
+            </optional>
+            <text/>
+          </interleave>
+        </element>
+      </optional>
+    </interleave>
+  </define>
+  <define name="InvocationParagraph" combine="interleave">
+    <optional>
+      <ref name="Argpos"/>
+    </optional>
+  </define>
+  <define name="InvocationParameterElements" combine="interleave">
+    <interleave>
+      <choice>
+        <optional>
+          <!-- iscommand : if true& this parameter specify the line of command to run the program  used when the command line is more complicated. -->
+          <attribute name="iscommand">
+            <a:documentation>command parameter</a:documentation>
+            <data type="boolean"/>
+          </attribute>
+        </optional>
+        <optional>
+          <element name="paramfile">
+            <a:documentation>parameter file</a:documentation>
+            <text/>
+          </element>
+        </optional>
+      </choice>
+      <optional>
+        <ref name="Format">
+          <a:documentation>command line chunk evaluation code </a:documentation>
+        </ref>
+      </optional>
+      <optional>
+        <ref name="Argpos">
+          <a:documentation>command line chunk position index</a:documentation>
+        </ref>
+      </optional>
+      <optional>
+        <!--
+          this element is relevant only for output parameters, it is used to find the files in which the results are stored
+          each code must have ONLY ONE file unix mask 
+        -->
+        <element name="filenames">
+          <a:documentation>result file(s) mask(s) </a:documentation>
+          <oneOrMore>
+            <ref name="Code"/>
+          </oneOrMore>
+        </element>
+      </optional>
+    </interleave>
+  </define>
+  <define name="Argpos">
+    <!--
+      in parameter: specifies the position of this parameter on the command line
+      in paragraph: specifies the position on the command line for all parameters of this paragraph
+      by convention the argpos of command must be 0. if we want args before command we could get negative argpos.
+    -->
+    <element name="argpos">
+      <a:documentation>command line chunk position index </a:documentation>
+      <data type="integer"/>
+    </element>
+  </define>
+  <define name="Format">
+    <!-- a code which will be evaluated to form the command line -->
+    <element name="format">
+      <a:documentation>command line chunk construction code</a:documentation>
+      <oneOrMore>
+        <ref name="Code"/>
+      </oneOrMore>
+    </element>
+  </define>
+  <define name="InterfaceType" combine="interleave">
+    <choice>
+      <value>form</value>
+      <value>job_input</value>
+      <value>job_output</value>
+    </choice>
+  </define>
+</grammar>
diff --git a/Schema/program_or_workflow.rnc b/Schema/program_or_workflow.rnc
new file mode 100644
index 0000000..eb0fc05
--- /dev/null
+++ b/Schema/program_or_workflow.rnc
@@ -0,0 +1 @@
+start = (external "program.rnc" | external "workflow.rnc")
\ No newline at end of file
diff --git a/Schema/program_or_workflow.rng b/Schema/program_or_workflow.rng
new file mode 100644
index 0000000..53e8479
--- /dev/null
+++ b/Schema/program_or_workflow.rng
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <start>
+    <choice>
+      <externalRef href="program.rng"/>
+      <externalRef href="workflow.rng"/>
+    </choice>
+  </start>
+</grammar>
diff --git a/Schema/tutorial.rnc b/Schema/tutorial.rnc
new file mode 100644
index 0000000..86a050f
--- /dev/null
+++ b/Schema/tutorial.rnc
@@ -0,0 +1,10 @@
+include "common.rnc"
+
+start |= Tutorial
+
+Tutorial = element tutorial {
+	element head { Head }
+}
+
+InterfaceType &= "tutorial"
+
diff --git a/Schema/tutorial.rng b/Schema/tutorial.rng
new file mode 100644
index 0000000..aab7393
--- /dev/null
+++ b/Schema/tutorial.rng
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+  <include href="common.rng"/>
+  <start combine="choice">
+    <ref name="Tutorial"/>
+  </start>
+  <define name="Tutorial">
+    <element name="tutorial">
+      <element name="head">
+        <ref name="Head"/>
+      </element>
+    </element>
+  </define>
+  <define name="InterfaceType" combine="interleave">
+    <value>tutorial</value>
+  </define>
+</grammar>
diff --git a/Schema/userspace.rnc b/Schema/userspace.rnc
new file mode 100644
index 0000000..db7ce02
--- /dev/null
+++ b/Schema/userspace.rnc
@@ -0,0 +1,72 @@
+datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" 
+namespace xhtml = "http://www.w3.org/1999/xhtml"
+
+
+include "common.rnc"
+
+start = UserSpace
+
+#---------------------------------------------------------------#
+#                     Mobyle UserSpace Definition               #
+#---------------------------------------------------------------#
+
+UserSpace = element userSpace { 
+	element authenticated{xsd:boolean}&
+	element activated{xsd:boolean}&
+	element activatingKey{xsd:string}&
+	Email&
+	element passwd{xsd:string}&
+	element captchaSolution{xsd:string}?&
+	element dataList{
+		element data{
+  			attribute id {xsd:ID}&
+  			attribute size {xsd:positiveInteger}&
+  			attribute count {xsd:positiveInteger}&
+  			element userName {xsd:string}&
+  			element type { #warning: the type format will soon be unified between program& job and userSpace documents.
+				element biotype { text }*&
+				# the biological type of the parameter DNA & Protein ...
+				# the biotypes list represents the list of accepted 'types'
+				# for input parameters and the list of 'types' that an 
+				# output may belong to
+				element datatype { 
+					# type of the data Sequence & Structure & Matrix ...
+					element class { text }&
+					element superclass { text }?
+					#  superclass& or class if class does not exist
+					# is a Mobyle parameter Class defined in Src/Mobyle/Core.py or in Local/CustomClasses/
+				}&
+				DataFormat*				
+  			}&
+  			element headOfData {xsd:string}&
+  			element inputModes {
+  				element inputMode {"db"|"paste"|"upload"|"result"}
+  			}&
+  			element producedBy {
+	  			attribute id {xsd:IDREF}
+  			}&
+  			element usedBy {
+	  			attribute id {xsd:IDREF}
+  			}
+		}*
+	}&
+	element jobList{
+		element job{
+  			attribute id {xsd:anyURI}&
+  			element userName {xsd:string}&
+  			element programName {xsd:string}&
+  			element userAnnotation {
+  				element description {xsd:string}?&
+  				element labels {xsd:string}*
+  				}*&
+  			element date {xsd:string}&  						
+  			element status {xsd:string}&
+  			element dataUsed {
+	  			attribute id {xsd:string}
+  			}&
+  			element dataProduced {
+	  			attribute id {xsd:string}
+  			}
+		}*
+	}
+}
\ No newline at end of file
diff --git a/Schema/userspace.rng b/Schema/userspace.rng
new file mode 100644
index 0000000..9c6dab7
--- /dev/null
+++ b/Schema/userspace.rng
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start>
+    <ref name="UserSpace"/>
+  </start>
+  <!--
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+                        Mobyle UserSpace Definition               #
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
+  -->
+  <define name="UserSpace">
+    <element name="userSpace">
+      <interleave>
+        <element name="authenticated">
+          <data type="boolean"/>
+        </element>
+        <element name="activated">
+          <data type="boolean"/>
+        </element>
+        <element name="activatingKey">
+          <data type="string"/>
+        </element>
+        <ref name="Email"/>
+        <element name="passwd">
+          <data type="string"/>
+        </element>
+        <optional>
+          <element name="captchaSolution">
+            <data type="string"/>
+          </element>
+        </optional>
+        <element name="dataList">
+          <zeroOrMore>
+            <element name="data">
+              <interleave>
+                <attribute name="id">
+                  <data type="ID"/>
+                </attribute>
+                <attribute name="size">
+                  <data type="positiveInteger"/>
+                </attribute>
+                <attribute name="count">
+                  <data type="positiveInteger"/>
+                </attribute>
+                <element name="userName">
+                  <data type="string"/>
+                </element>
+                <element name="type">
+                  <interleave>
+                    <zeroOrMore>
+                      <!-- warning: the type format will soon be unified between program& job and userSpace documents. -->
+                      <element name="biotype">
+                        <text/>
+                      </element>
+                    </zeroOrMore>
+                    <!--
+                      the biological type of the parameter DNA & Protein ...
+                      the biotypes list represents the list of accepted 'types'
+                      for input parameters and the list of 'types' that an 
+                      output may belong to
+                    -->
+                    <element name="datatype">
+                      <interleave>
+                        <!-- type of the data Sequence & Structure & Matrix ... -->
+                        <element name="class">
+                          <text/>
+                        </element>
+                        <optional>
+                          <element name="superclass">
+                            <text/>
+                          </element>
+                        </optional>
+                      </interleave>
+                      <!--
+                         superclass& or class if class does not exist
+                        is a Mobyle parameter Class defined in Src/Mobyle/Core.py or in Local/CustomClasses/
+                      -->
+                    </element>
+                    <zeroOrMore>
+                      <ref name="DataFormat"/>
+                    </zeroOrMore>
+                  </interleave>
+                </element>
+                <element name="headOfData">
+                  <data type="string"/>
+                </element>
+                <element name="inputModes">
+                  <element name="inputMode">
+                    <choice>
+                      <value>db</value>
+                      <value>paste</value>
+                      <value>upload</value>
+                      <value>result</value>
+                    </choice>
+                  </element>
+                </element>
+                <element name="producedBy">
+                  <attribute name="id">
+                    <data type="IDREF"/>
+                  </attribute>
+                </element>
+                <element name="usedBy">
+                  <attribute name="id">
+                    <data type="IDREF"/>
+                  </attribute>
+                </element>
+              </interleave>
+            </element>
+          </zeroOrMore>
+        </element>
+        <element name="jobList">
+          <zeroOrMore>
+            <element name="job">
+              <interleave>
+                <attribute name="id">
+                  <data type="anyURI"/>
+                </attribute>
+                <element name="userName">
+                  <data type="string"/>
+                </element>
+                <element name="programName">
+                  <data type="string"/>
+                </element>
+                <zeroOrMore>
+                  <element name="userAnnotation">
+                    <interleave>
+                      <optional>
+                        <element name="description">
+                          <data type="string"/>
+                        </element>
+                      </optional>
+                      <zeroOrMore>
+                        <element name="labels">
+                          <data type="string"/>
+                        </element>
+                      </zeroOrMore>
+                    </interleave>
+                  </element>
+                </zeroOrMore>
+                <element name="date">
+                  <data type="string"/>
+                </element>
+                <element name="status">
+                  <data type="string"/>
+                </element>
+                <element name="dataUsed">
+                  <attribute name="id">
+                    <data type="string"/>
+                  </attribute>
+                </element>
+                <element name="dataProduced">
+                  <attribute name="id">
+                    <data type="string"/>
+                  </attribute>
+                </element>
+              </interleave>
+            </element>
+          </zeroOrMore>
+        </element>
+      </interleave>
+    </element>
+  </define>
+</grammar>
diff --git a/Schema/viewer.rnc b/Schema/viewer.rnc
new file mode 100644
index 0000000..91e2e52
--- /dev/null
+++ b/Schema/viewer.rnc
@@ -0,0 +1,19 @@
+include "common.rnc"
+
+start |= Viewer
+
+Viewer = element viewer {
+	Service
+}
+
+InterfaceType &= "viewer"
+
+InvocationParameterElements &=
+	(
+	## javascript code to get back data from the viewer
+	Format?
+	)
+
+Format =
+	## a single javascript chunk
+	element format { attribute base64encoded { xsd:boolean },attribute extension { xsd:string }?, Code }
diff --git a/Schema/viewer.rng b/Schema/viewer.rng
new file mode 100644
index 0000000..17ee429
--- /dev/null
+++ b/Schema/viewer.rng
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start combine="choice">
+    <ref name="Viewer"/>
+  </start>
+  <define name="Viewer">
+    <element name="viewer">
+      <ref name="Service"/>
+    </element>
+  </define>
+  <define name="InterfaceType" combine="interleave">
+    <value>viewer</value>
+  </define>
+  <define name="InvocationParameterElements" combine="interleave">
+    <optional>
+      <ref name="Format">
+        <a:documentation>javascript code to get back data from the viewer</a:documentation>
+      </ref>
+    </optional>
+  </define>
+  <define name="Format">
+    <element name="format">
+      <a:documentation>a single javascript chunk</a:documentation>
+      <attribute name="base64encoded">
+        <data type="boolean"/>
+      </attribute>
+      <optional>
+        <attribute name="extension">
+          <data type="string"/>
+        </attribute>
+      </optional>
+      <ref name="Code"/>
+    </element>
+  </define>
+</grammar>
diff --git a/Schema/workflow.rnc b/Schema/workflow.rnc
new file mode 100644
index 0000000..b0ba45f
--- /dev/null
+++ b/Schema/workflow.rnc
@@ -0,0 +1,36 @@
+include "common.rnc"
+
+start |= Workflow
+
+Workflow = element workflow { Service }
+
+Implementation &= Flow
+
+# Mobyle Dataflow Definition
+Flow = element flow { Task+, Link* }
+
+Task =
+  element task {
+    attribute id { xsd:string }
+    & attribute service { xsd:string }
+    & attribute service_url { xsd:string }?
+    & attribute server { xsd:string }?
+    & attribute suspend { xsd:boolean }?
+    & element description { text }?
+    & element position{ text }?
+    & element inputValue{
+        attribute name { text },
+        (attribute reference { xsd:anyURI } | text)
+    }*
+  }
+
+Link =
+  element link {
+    attribute id { xsd:string }?
+    & attribute fromTask { xsd:string }?
+    & attribute fromParameter { xsd:NMTOKEN }
+    & attribute toTask { xsd:string }?
+    & attribute toParameter { xsd:NMTOKEN }
+  }
+
+InterfaceType &= ("form"|"job_input"|"job_output"|"graph")
\ No newline at end of file
diff --git a/Schema/workflow.rng b/Schema/workflow.rng
new file mode 100644
index 0000000..f4b350c
--- /dev/null
+++ b/Schema/workflow.rng
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href="common.rng"/>
+  <start combine="choice">
+    <ref name="Workflow"/>
+  </start>
+  <define name="Workflow">
+    <element name="workflow">
+      <ref name="Service"/>
+    </element>
+  </define>
+  <define name="Implementation" combine="interleave">
+    <ref name="Flow"/>
+  </define>
+  <!-- Mobyle Dataflow Definition -->
+  <define name="Flow">
+    <element name="flow">
+      <oneOrMore>
+        <ref name="Task"/>
+      </oneOrMore>
+      <zeroOrMore>
+        <ref name="Link"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="Task">
+    <element name="task">
+      <interleave>
+        <attribute name="id">
+          <data type="string"/>
+        </attribute>
+        <attribute name="service">
+          <data type="string"/>
+        </attribute>
+        <optional>
+          <attribute name="service_url">
+            <data type="string"/>
+          </attribute>
+        </optional>
+        <optional>
+          <attribute name="server">
+            <data type="string"/>
+          </attribute>
+        </optional>
+        <optional>
+          <attribute name="suspend">
+            <data type="boolean"/>
+          </attribute>
+        </optional>
+        <optional>
+          <element name="description">
+            <text/>
+          </element>
+        </optional>
+        <optional>
+          <element name="position">
+            <text/>
+          </element>
+        </optional>
+        <zeroOrMore>
+          <element name="inputValue">
+            <attribute name="name"/>
+            <choice>
+              <attribute name="reference">
+                <data type="anyURI"/>
+              </attribute>
+              <text/>
+            </choice>
+          </element>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="Link">
+    <element name="link">
+      <interleave>
+        <optional>
+          <attribute name="id">
+            <data type="string"/>
+          </attribute>
+        </optional>
+        <optional>
+          <attribute name="fromTask">
+            <data type="string"/>
+          </attribute>
+        </optional>
+        <attribute name="fromParameter">
+          <data type="NMTOKEN"/>
+        </attribute>
+        <optional>
+          <attribute name="toTask">
+            <data type="string"/>
+          </attribute>
+        </optional>
+        <attribute name="toParameter">
+          <data type="NMTOKEN"/>
+        </attribute>
+      </interleave>
+    </element>
+  </define>
+  <define name="InterfaceType" combine="interleave">
+    <choice>
+      <value>form</value>
+      <value>job_input</value>
+      <value>job_output</value>
+      <value>graph</value>
+    </choice>
+  </define>
+</grammar>
diff --git a/Src/Mobyle/Admin.py b/Src/Mobyle/Admin.py
new file mode 100644
index 0000000..3c84333
--- /dev/null
+++ b/Src/Mobyle/Admin.py
@@ -0,0 +1,319 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+import os , sys
+import logging
+from time import localtime, strftime , strptime
+
+a_log = logging.getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError
+
+class Admin:
+    """
+    manage the informations in the .admin file.
+    be careful there is no management of concurrent access file
+    thus if there is different instance of Admin with the same path
+    it could be problem of data integrity
+    """
+    FIELDS = ( 'DATE'    ,
+               'EMAIL'   ,
+               'REMOTE'  ,
+               'SESSION' ,
+               'WORKFLOWID' ,
+               'JOBNAME' ,
+               'JOBID'   ,
+               'MD5'     ,
+               'EXECUTION_ALIAS' ,
+               'NUMBER'  ,
+               'QUEUE'
+               )
+    FILENAME = '.admin'
+
+    def __init__( self, path ):
+        self.me = {}
+        path = os.path.abspath( path )
+
+        if os.path.exists( path ):
+            if os.path.isfile( path ):
+                self.path = path
+                self._parse()
+            elif os.path.isdir( path ):
+                self.path = os.path.join( path , Admin.FILENAME )
+                if os.path.isfile( self.path ):
+                    self._parse()
+                else:
+                    raise MobyleError , "invalid job path : " + self.path
+        else:
+            raise MobyleError , "invalid job path : " + path
+
+
+    @staticmethod
+    def create( path , remote , jobName , jobID , userEmail =None , sessionID = None , workflowID = None ):
+        """create a minimal admin file"""
+        adm_file = os.path.join( path , Admin.FILENAME )
+        if os.path.exists( adm_file ):
+            raise MobyleError , "an \"admin\" file already exist in %s. can't create a new one" % ( path )
+
+        args = {'DATE' : strftime( "%x %X" , localtime() ) ,
+                'REMOTE' : remote ,
+                'JOBNAME' : jobName ,
+                'JOBID' : jobID ,
+                }
+        if userEmail:
+            args[ 'EMAIL'] = userEmail
+        if sessionID:
+            args[ 'SESSION' ] = sessionID
+        if workflowID:
+            args[ 'WORKFLOWID' ] = workflowID
+
+        adminFile = open( adm_file , "w" )
+        for key in Admin.FIELDS:
+            try:
+                value = args[ key ]
+            except KeyError :
+                continue
+            adminFile.write( "%s : %s\n" %( key , value ) )
+        adminFile.close()
+
+
+    def __str__( self ):
+        res = ''
+        for key in self.__class__.FIELDS:
+            try:
+                if key == 'DATE':
+                    value = strftime( "%x %X" , self.me['DATE'] )
+                else:
+                    value =  self.me[ key ]
+            except KeyError :
+                continue
+            res = res + "%s : %s\n" % ( key , value )
+        return res
+
+
+    def _parse ( self ):
+        """
+        parse the file .admin
+        """
+        try:
+            fh = open( self.path , 'r' )
+
+            for line in fh:
+                datas = line[:-1].split( ' : ' )
+                key = datas[0]
+                value = ' : '.join( datas[1:] )
+                if key == 'DATE':
+                    value = strptime( value , "%x %X" )
+                self.me[ key ] = value
+        except Exception , err :
+            a_log.critical( "an error occured during %s/.admin parsing (call by: %s) : %s " %( self.path ,
+                                                                                               os.path.basename( sys.argv[0] ) ,
+                                                                                               err ) ,
+                                                                                               exc_info = True  )
+            raise MobyleError , err
+        finally:
+            try:
+                fh.close()
+            except:
+                pass
+            if not self.me:
+                msg = "admin %s object cannot be instantiated: is empty ( call by %s ) " % ( self.path , os.path.basename( sys.argv[0] ) )
+                a_log.critical( msg)
+
+
+    def refresh( self ):
+        self._parse()
+
+    def commit( self ):
+        """
+        Write the string representation of this instance on the file .admin
+        """
+        if not self.me.values():
+            msg = "cannot commit admin file %s admin instance have no values (call by %s) " % ( self.path ,  os.path.basename( sys.argv[0] ) )
+            a_log.critical( msg )
+        try:
+            tmpFile = open( "%s.%d" %( self.path , os.getpid() ) , 'w' )
+            tmpFile.write( self.__str__() )
+            os.rename( tmpFile.name , self.path )
+        except Exception , err :
+            a_log.critical( "an error occured during %s/.admin commit (call by: %s) : %s " %( self.path ,
+                                                                                               os.path.basename( sys.argv[0] ) ,
+                                                                                               err ) ,
+                                                                                               exc_info = True  )
+            raise MobyleError , err
+        finally:
+            try:
+                tmpFile.close()
+            except:
+                pass
+
+    def getDate( self ):
+        """
+        @return: the date of the job submission
+        @rtype: time.struct_time
+        """
+        try:
+            return  self.me[ 'DATE' ]
+        except KeyError :
+            return None
+
+    def getEmail( self ):
+        """
+        @return: the email of the user who run the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'EMAIL' ]
+        except KeyError :
+            return None
+        
+    def setEmail( self , email):
+        """
+        set the email of the user
+        """
+        self.me[ 'EMAIL' ] = email
+        
+    def getRemote( self ):
+        """
+        @return: the remote of the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'REMOTE' ]
+        except KeyError :
+            return None
+
+    def getSession( self ):
+        """
+        @return: the Session key of the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'SESSION' ]
+        except KeyError :
+            return None
+        
+    def setSession(self , session_key ):
+        """
+        set the session key of the job
+        """
+        self.me[ 'SESSION' ] = session_key
+        
+        
+    def getWorkflowID( self , default= None ):
+        """
+        @return: the Workflow owner the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'WORKFLOWID' ]
+        except KeyError :
+            return default
+
+
+    def getJobName( self ):
+        """
+        @return: the name of the job ( blast2 , toppred )
+        @rtype: string
+        """
+        try:
+            return self.me[ 'JOBNAME' ]
+        except KeyError :
+            return None
+
+    def getJobID( self ):
+        """
+        @return: the job identifier.
+        @rtype: string
+        """
+        try:
+            return self.me[ 'JOBID' ]
+        except KeyError :
+            return None
+
+    def getMd5( self ):
+        """
+        @return: the md5 of the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'MD5' ]
+        except KeyError :
+            return None
+
+    def setMd5( self , md5 ):
+        """
+        set the md5 of the job
+        @param : the identifier of the job
+        @type : string
+        """
+        self.me[ 'MD5' ] = md5
+
+    def getExecutionAlias( self ):
+        """
+        @return: the ExecutionConfig alias corresponding to the Execution object used to run the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'EXECUTION_ALIAS' ]
+        except KeyError :
+            return None
+
+    def setExecutionAlias( self , execution_alias ):
+        """
+        set the execution class name  used to run of the job
+        @param :  alias of the ExecutionConfig its value must belong to
+         ExecutionConfig in Config.EXECUTION_CONFIG_ALIAS keys.
+        @type : string
+        """
+        self.me[ 'EXECUTION_ALIAS' ] = execution_alias
+
+    def getNumber( self ):
+        """
+        @return: the number the job the meaning of this value depend of BATCH value.
+         - BATCH = Sys number is the job pid
+         - BATCH = SGE number is the result of qstat
+        @rtype: string
+        """
+        try:
+            return self.me[ 'NUMBER' ]
+        except KeyError :
+            return None
+
+
+    def setNumber( self , number ):
+        """
+        set the number of the job this number depend of the batch value
+        if BATCH = Sys number is the pid
+        if BATCH = SGE number is the
+        @param : the number of the job
+        @type : string
+        """
+        self.me[ 'NUMBER' ] = number
+
+    def getQueue( self ):
+        """
+        @return: return the queue name of the job
+        @rtype: string
+        """
+        try:
+            return self.me[ 'QUEUE' ]
+        except KeyError :
+            return None
+
+
+    def setQueue( self, queue ):
+        """
+        set the queue name of the job
+        @param : the queuename of the job
+        @type : string
+        """
+        self.me[ 'QUEUE' ] = queue
+
+
diff --git a/Src/Mobyle/AnonymousSession.py b/Src/Mobyle/AnonymousSession.py
new file mode 100644
index 0000000..2d8c543
--- /dev/null
+++ b/Src/Mobyle/AnonymousSession.py
@@ -0,0 +1,137 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os 
+import sys
+import random 
+import string
+from time import time
+
+from Mobyle.Session import Session
+from Mobyle.Transaction import Transaction
+from Mobyle.MobyleError import MobyleError , SessionError 
+from logging import getLogger
+
+
+
+class AnonymousSession( Session ):
+
+    DIRNAME = 'anonymous'
+ 
+    def __init__( self ,  cfg  , key = None):
+        self.cfg = cfg
+        self.log = getLogger('Mobyle.Session.AnonymousSession')
+        """the maximum size of a session ( in bytes )"""
+        self.sessionLimit = self.cfg.sessionlimit()
+        anonymousSessionAllowed = self.cfg.anonymousSession()
+        self._modifiedTransaction = False
+       
+        if anonymousSessionAllowed == 'no':
+            self.log.error("can't create anonymous session ANONYMOUS_SESSION is set to \"no\" in Local/Config/Config.py")          
+            raise MobyleError , "can't create anonymous session: permission denied"
+
+        if key :
+            self.Dir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AnonymousSession.DIRNAME , key ) )
+            if not os.path.exists( self.Dir ):
+                self.log.error( "can't retrieve anonymous session, the Key: %s doesn't match with any Session" % key )
+                raise SessionError , "wrong key : %s" % key
+
+            self.key = key
+            self.log.debug( "%f : %s return new annonymousSession based on old dir call by= %s" %( time() ,
+                                                                                                self.getKey()  , 
+                                                                                                os.path.basename( sys.argv[0] ) ,
+                                                                                                ))
+              
+        else: #create a new session
+            self.key = self.__newSessionKey( )
+            """ the user/session  key"""
+            self.Dir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AnonymousSession.DIRNAME , self.key ) )
+            """ the path to this session directory """
+           
+            if os.path.exists( self.Dir ):
+                msg = "Try to make a new anonymous session with key: %s. This directory already exist" % self.key
+                self.log.critical( msg )
+                raise SessionError , "can't create new anonymous session"
+            try:
+                os.makedirs( self.Dir , 0755 ) #create parent directory
+            except Exception, err:
+                self.log.critical( "unable to create anonymous session : %s : %s" %( self.Dir , err) , exc_info = True)
+                raise SessionError , "unable to create anonymous session"
+            
+            if anonymousSessionAllowed == 'yes':
+                Transaction.create( os.path.join( self.Dir, self.FILENAME ) , False , True )
+            else:#anonymousSessionAllowed == 'captcha'
+                Transaction.create( os.path.join( self.Dir , self.FILENAME ) , False , False )
+           
+            self.log.debug( "%f : %s  create a new session call by= %s" %( time() ,
+                                                                        self.getKey()  , 
+                                                                        os.path.basename( sys.argv[0] ) ,
+                                                                        ))
+        self.url = "%s/%s/%s" % (self.cfg.user_sessions_url(), AnonymousSession.DIRNAME, self.key)
+
+    def isAuthenticated( self ):
+        return False
+   
+    
+    
+    def getCaptchaProblem( self ):
+        """
+        @return: a png image which is a captcha
+        @rtype:
+        """
+        from Mobyle.Captcha import Captcha 
+        import StringIO
+        captcha = Captcha.PseudoGimpy()
+       
+        transaction = self._getTransaction( Transaction.WRITE )
+        transaction.setCaptchaSolution( captcha.solutions[0] )
+        transaction.commit()
+        pf = StringIO.StringIO()
+        captcha.render().save( pf , "PNG" )
+       
+        return pf.getvalue()
+  
+             
+    def checkCaptchaSolution( self , solution ):
+        """
+        check if solution is the solution to the current problem
+        @param solution: the solution to the current problem
+        @type solution: string
+        @return: True if solution is the solution of the current captcha problem, False otherwise
+        @rtype: boolean
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        current_captcha_sol = transaction.getCaptchaSolution()
+        transaction.commit()
+        
+        if current_captcha_sol is None :
+            msg = "session key: %s : you must getCaptchaProblem before ask for CaptchaSolution" % self.getKey()
+            self.log.error( msg )
+            raise SessionError , msg
+       
+        if solution == current_captcha_sol :
+            transaction = self._getTransaction( Transaction.WRITE )
+            transaction.activate()
+            transaction.commit()
+            return True
+        else:
+            return False
+     
+     
+    def __newSessionKey( self ):
+        """
+        @return: a unique id for the session
+        @rtype: string
+        """
+        letter = string.ascii_uppercase[ random.randrange( 0 , 26 ) ]
+        strTime = "%.9f" % time()
+        strTime = strTime[-9:]
+        strPid = "%05d" % os.getpid()
+        strPid = strPid.replace( '.' , '' )[ -5 : ]
+        return letter + strPid + strTime
+
diff --git a/Src/Mobyle/AuthenticatedSession.py b/Src/Mobyle/AuthenticatedSession.py
new file mode 100644
index 0000000..72efd6c
--- /dev/null
+++ b/Src/Mobyle/AuthenticatedSession.py
@@ -0,0 +1,323 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from hashlib import md5
+from time import  time , sleep 
+import random
+import shutil
+
+from Mobyle.Session import Session
+from Mobyle.Transaction import Transaction
+from Mobyle.MobyleError import MobyleError , SessionError , AuthenticationError
+from Local.Policy import authenticate as policy_authenticate
+
+from logging import getLogger
+
+
+class AuthenticatedSession( Session ):
+    
+    VALID    = 1
+    REJECT   = 0
+    CONTINUE = 2
+
+    DIRNAME  = 'authentified'
+
+    def __init__( self , cfg , userEmail , passwd=None, ticket_id=None):
+        self.log = getLogger('Mobyle.Session.AuthenticatedSession')
+        self.cfg = cfg
+        assert not(passwd and ticket_id), "please provide either a ticket id or a password" # TODO: clean up the parameters check
+        """the maximum size of a session ( in bytes )"""
+        self.sessionLimit = self.cfg.sessionlimit()
+        self.__userEmail = userEmail
+        authenticatedSessionAllowed = self.cfg.authenticatedSession()        
+        
+        if authenticatedSessionAllowed == "no" :
+            self.log.error("can't create  session AUTHENTICATED_SESSION is set to \"no\" in Local/Config/Config.py")          
+            raise SessionError , "can't create  authenticated session: permission denied"
+              
+        key = self.__newSessionKey()
+        sessionDir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key  ) )
+        self.key = key 
+        """ the user/session  key"""
+        self.Dir = sessionDir
+        """ the path to this session directory """
+       
+        if os.path.exists( sessionDir ): #the session already exist
+            if passwd:
+                if not self.checkPasswd( passwd ):
+                    self.log.info( "authentified/%s : Authentication Failure "% ( self.getKey() ) )
+                    raise AuthenticationError , "Authentication Failure"
+                else:
+                    self._getTicket()
+            elif ticket_id and not self.checkTicket( ticket_id ):
+                raise AuthenticationError , "Invalid ticket or expired ticket"
+            else:
+                self.__generateTicketId(ticket_id or None)
+            
+        else: #creation of new Session
+            resp = self.__userEmail.check()
+            if not resp.status:
+                msg = " %s %s FORBIDDEN can't create authenticated session : %s" % ( self.__userEmail ,
+                                    os.environ[ 'REMOTE_ADDR' ] ,
+                                    resp.message
+                                    )
+                self.log.error( msg )
+                raise SessionError , "you are not allowed to register on this server for now"
+            try:
+                os.makedirs( sessionDir , 0755 ) #create parent directory 
+            except Exception, err:
+                self.log.critical( "unable to create authenticated session : %s : %s" % ( sessionDir , err) , exc_info = True)
+                raise SessionError , "unable to create authenticated session"
+            mymd5 = md5()
+            mymd5.update( passwd )
+            cryptPasswd = mymd5.hexdigest()
+            
+            authenticatedSessionAllowed = self.cfg.authenticatedSession()        
+            if authenticatedSessionAllowed == "yes":  
+                Transaction.create( os.path.join( sessionDir , self.FILENAME ) , 
+                                    True , #authenticated
+                                    True , #activated
+                                    userEmail = str( self.__userEmail ) , 
+                                    passwd = cryptPasswd )
+                self.__generateTicketId()
+            elif authenticatedSessionAllowed == "email" :
+                activatingKey = self.__newActivatingKey()
+                try:
+                    from Mobyle.Net import Email
+                    mail = Email( self.__userEmail )
+                    mail.send( 'CONFIRM_SESSION' , { 'SENDER'         : self.cfg.sender() ,
+                                                     'HELP'           : self.cfg.mailHelp() ,
+                                                     'SERVER_NAME'    : self.cfg.portal_url() ,
+                                                     'ACTIVATING_KEY' : activatingKey ,
+                                                     'CGI_URL'        : self.cfg.cgi_url() } 
+                               )
+                    
+                    Transaction.create( os.path.join( sessionDir , self.FILENAME ) , 
+                                        True ,  #authenticated
+                                        False , #activated
+                                        activatingKey = activatingKey , 
+                                        userEmail = self.__userEmail , 
+                                        passwd = cryptPasswd )
+                    self.__generateTicketId()
+                    # api create( id , authenticated , activated , activatingKey = None , userEmail = None, passwd = None)
+                except MobyleError , err :
+                    msg = "can't send an activation email, session creation aborted"
+                    self.log.error( "%s : %s " % ( msg , err ) )
+                    os.rmdir( self.Dir )
+                    raise SessionError , msg
+        self.url = "%s/%s/%s" % (self.cfg.user_sessions_url(), AuthenticatedSession.DIRNAME, self.key)
+
+    def __generateTicketId( self , ticket_id=None):
+        """
+        create the ticket_id field in session file
+        """
+        self.ticket_id = ticket_id or str(random.randint(0, 1000000))
+        transaction = self._getTransaction( Transaction.WRITE )
+        transaction.setTicket( self.ticket_id, time() + 3600 )
+        transaction.commit()
+         
+    def isAuthenticated( self ):
+        return True   
+    
+    def setPasswd( self , passwd ):
+        """
+        set the pass word for this session
+        @param passwd: the pass word to this session
+        @type passwd: string
+        """
+        newMd5 = md5()
+        newMd5.update( passwd )
+        cryptPasswd = newMd5.hexdigest()
+        transaction = self._getTransaction( Transaction.WRITE )
+        transaction.setPasswd( cryptPasswd )
+        transaction.commit()    
+    
+    
+    def checkPasswd( self , passwd ):
+        """
+        check if passwd is the pass word of this session
+        @param passwd: the session pass word
+        @type passwd: string
+        """
+        if passwd == "":
+            return False
+        try:
+            auth = policy_authenticate( self.__userEmail , passwd )
+        except AttributeError:
+            auth = self.CONTINUE
+            
+        if auth == self.VALID :
+            return True
+        elif auth == self.REJECT:
+            return False
+        else:
+            transaction = self._getTransaction( Transaction.READ )
+            realPasswd = transaction.getPasswd()
+            transaction.commit()
+        
+            newMd5 = md5()
+            newMd5.update( passwd )
+            passwd = newMd5.hexdigest()
+            if passwd == realPasswd :
+                return True
+            else:
+                return False    
+
+    def _getTicket( self ):
+        """
+        get the ticket
+        @param ticket_id: the ticket_id
+        @type ticket_id: string
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        r_ticket_id, r_exp_date = transaction.getTicket()
+        transaction.commit()
+        if r_ticket_id is None or time() < float(r_exp_date):
+            self.__generateTicketId(r_ticket_id)
+        else:
+            self.__generateTicketId()
+        return self.ticket_id
+    
+    def checkTicket( self , ticket_id ):
+        """
+        check if the ticket is valid
+        @param ticket_id: the ticket_id
+        @type ticket_id: string
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        r_ticket_id, r_exp_date = transaction.getTicket()
+        transaction.commit()
+        if r_ticket_id == ticket_id and time() < float(r_exp_date):
+            transaction = self._getTransaction( Transaction.WRITE )
+            transaction.setTicket( ticket_id, time() + 3600 )
+            transaction.commit()            
+            return True
+        else:
+            return False    
+
+
+    
+    def changePasswd( self , oldPasswd , newPasswd ):
+        """
+        change the password for this session
+        @param oldPasswd: the current passwd
+        @type oldPasswd: string
+        @param newPasswd: the new passwd
+        @type newPasswd: string
+        @raise AuthenticationError: if oldPasswd 
+        """
+        newMd5 = md5()
+        newMd5.update( oldPasswd )
+        oldCryptPasswd = newMd5.hexdigest()
+        
+        newMd5 = md5()
+        newMd5.update( newPasswd )
+        newCryptPasswd = newMd5.hexdigest()
+       
+        transaction = self._getTransaction( Transaction.WRITE)
+        currentPasswd = transaction.getPasswd()
+                     
+        if oldCryptPasswd == currentPasswd:
+            transaction.setPasswd( newCryptPasswd )
+            transaction.commit()
+        else:
+            transaction.rollback()
+            raise AuthenticationError , "Authentication failure"
+        
+            
+            
+            
+    def confirmEmail( self , userKey ):
+        """
+        if the activatingkey match the session activatingkey the session is activated
+        @param userKey: the activation key send by email to the user
+        @type userKey: string
+        """ 
+        transaction = self._getTransaction( Transaction.READ )
+        activatingKey = transaction.getActivatingKey()
+        transaction.commit()
+        
+        if userKey == activatingKey:
+            transaction = self._getTransaction( Transaction.WRITE )
+            transaction.activate()
+            transaction.commit()
+            self.log.debug("%f : %s : confirmEmail succeed : %s" % ( time(), self.getKey() , activatingKey ) )
+        else:
+            self.log.info("authentified/%s : wrong key : %s" % ( self.getKey() , activatingKey ) )
+            raise AuthenticationError , "wrong key : %s" % activatingKey
+        
+             
+    def __newSessionKey( self ):
+        """
+        @return: a unique id for the session
+        @rtype: string
+        """
+        newMd5 = md5()
+        newMd5.update( str( self.__userEmail ) )
+        return newMd5.hexdigest()    
+    
+    
+    def __newActivatingKey(self):
+        """
+        @return: a new Session uniq activating key
+        @rtype: string
+        """
+        t1 = time()
+        sleep( random.random() )
+        t2 = time()
+        base = md5( str(t1 +t2) )
+        sid = base.hexdigest()
+        return sid    
+    
+    
+    def mergeWith( self , anonymousSession ):
+        """
+        merge an anonymous session to this authenticated session
+        @param anonymousSession: the session to add this session
+        @type anonymousSession: L{AnonymousSession} object
+        """
+        from BMPSWorkflow import BMPSWorkflow, CopyError
+        if anonymousSession == self:
+            self.log.error( "authentified/%s try to merge with myself" % self.getKey() )
+            raise SessionError , "try to merge with myself"   
+        try:
+            for data in anonymousSession.getAllData():
+                self.addData( data['dataName'] ,  
+                              data['Type'] , 
+                              producer   = anonymousSession , 
+                              inputModes = data['inputModes'] , 
+                              usedBy     = data['usedBy'] , 
+                              producedBy = data['producedBy']
+                            )
+            for job in anonymousSession.getAllJobs():
+                self.addJob( job[ 'jobID' ] ,
+                             userName = job[ 'userName' ] ,
+                             dataUsed     = job[ 'dataUsed' ] ,
+                             dataProduced = job[ 'dataProduced' ] 
+                            )
+                
+            wf_names = anonymousSession.getBMPSWorkflows().keys()
+            for wf_name in wf_names:
+                wf = BMPSWorkflow( wf_name , anonymousSession )
+                try:
+                    wf.copy_wf( self )
+                except CopyError, err:
+                    self.log.debug("%f : %s :cannot copy Workflow named %s from session %s into session %s" % (time(), 
+                                                                                                               self.getKey(),
+                                                                                                               wf_name, 
+                                                                                                               anonymousSession.getDir(), 
+                                                                                                               self.getDir() ) , 
+                                   exc_info = True)
+                    self.log.error( "cannot copy Workflow named %s from session %s into session %s" % (wf_name, anonymousSession.getDir(), self.getDir() ) , exc_info = True)
+                    raise err
+        except Exception, err :
+            self.log.error( "authentified/%s : error during mergeWith : %s" % ( self.getKey() , err ))
+            self.log.debug("%f : %s : error during mergeWith :" % ( time(), self.getKey() ) , exc_info = True )
+            raise err
+ 
diff --git a/Src/Mobyle/BMPSWorkflow.py b/Src/Mobyle/BMPSWorkflow.py
new file mode 100644
index 0000000..6677463
--- /dev/null
+++ b/Src/Mobyle/BMPSWorkflow.py
@@ -0,0 +1,541 @@
+'''
+Created on Dec 17, 2012
+
+ at author: hmenager
+'''
+
+from lxml import etree
+import logging
+import shutil
+import urllib, os
+import pygraphviz as pgv  # @UnresolvedImport
+from glob import glob
+
+from Workflow import Task, Parameter, Paragraph, Link, Type, \
+    Datatype, Biotype, Value, VElem, Vlist, Parser
+from Parser import parseService
+from Mobyle.Registry import registry, WorkflowDef
+from InterfacePreprocessor import InterfacePreprocessor
+from MobyleError import MobyleError
+log = logging.getLogger('Mobyle')
+
+parser = etree.XMLParser(no_network=False)
+
+class CustomResolver(etree.Resolver):
+    """
+    CustomResolver is a Resolver for lxml that allows (among other things) to
+    handle HTTPS protocol, which is not handled natively by lxml/libxml2
+    """
+    def resolve(self, url, public_id, context):
+        return self.resolve_file(urllib.urlopen(url), context)
+
+parser = etree.XMLParser(no_network=False)
+parser.resolvers.add(CustomResolver())
+
+def xslProcess(xsl_file, xml_source, xml_target, params={}):
+    global parser
+    xslt_doc = etree.parse(xsl_file, parser)
+    transform = etree.XSLT(xslt_doc)
+    parser = etree.XMLParser(no_network=False)
+    xml = etree.parse(xml_source, parser)
+    xml = transform(xml, **params)
+    if xml_target:
+        target_file = open(xml_target, "w")
+        target_file.write(str(xml))
+        target_file.close()
+    else:
+        return str(xml)
+
+class ServiceNotFoundError(MobyleError):
+    def __init__(self, pid):
+        self.pid = pid
+
+    @property
+    def message(self):
+        return "Service %s cannot not be found on the server" % self.pid
+
+class RenameError(MobyleError):
+    """
+    Raised when trying to rename a workflow to a name which already exists
+    """
+    def __init__(self, old_name, new_name):
+        """
+        old_name: old workflow name
+        new_name: requested new workflow name
+        """
+        self.old_name = old_name
+        self.new_name = new_name
+
+    @property
+    def message(self):
+        return "Workflow %s cannot not be renamed to %s, which already exists" % (self.old_name, self.new_name)
+
+class CopyError(MobyleError):
+    """
+    Raised when trying to rename a workflow to a name which already exists
+    """
+    def __init__(self, old_name):
+        """
+        old_name: old workflow name
+        """
+        self.old_name = old_name
+
+    @property
+    def message(self):
+        return "Workflow %s cannot not be copy in to new Session, Workflow already exists" % (self.old_name)
+
+
+def get_service(pid):
+    def f(x): return x and x != '.'
+    pid_dict = filter(f, pid.partition('.'))
+    try:
+        try:
+            if len(pid_dict) == 1:
+                service = registry.serversByName['local'].programsByName[pid_dict[0]]    
+            else:
+                service = registry.serversByName[pid_dict[0]].programsByName[pid_dict[1]]
+        except KeyError:
+            if len(pid_dict) == 1:
+                service = registry.serversByName['local'].workflowsByName[pid_dict[0]]            
+            else:
+                service = registry.serversByName[pid_dict[0]].workflowsByName[pid_dict[1]]
+    except KeyError:
+        raise ServiceNotFoundError(pid)
+    return service
+
+class BMPSWorkflow(object):
+
+    BMW_FOLDER = 'BMW'
+
+    PID_PREFIX = 'my_workflow_'
+    
+    MOBYLEXML_SUFFIX = '_mobyle.xml'
+
+    def __init__(self, name, session):
+        """
+        name -- name of the workflow
+        session -- user session containing the workflow
+        """
+        self.name = name
+        self.session = session
+        if not(os.path.exists(self.graphml_filepath)):
+            # create source workflow if it does not exist
+            open(self.graphml_filepath, 'wb').write('<graphml><graph></graph></graphml>')
+            self.graphml_to_mobylexml()
+
+    @classmethod
+    def get_from_mobylexml_filepath(cls, filepath, session):
+        """
+        factory to initialise this object from the path to the mobyle XML path
+        """
+        name = os.path.basename( filepath[:-11] )
+        return BMPSWorkflow(name, session)
+
+    @classmethod
+    def get_from_pid(cls, pid, session):
+        """
+        factory to initialise this object from its pid
+        """
+        name = pid.replace(cls.PID_PREFIX,'')
+        return BMPSWorkflow(name, session)
+    
+    @classmethod
+    def get_user_workflows(cls, session):
+        """
+        factory to list the workflows of a user session
+        """
+        session_workflow_path_list = glob( os.path.join(session.getDir() , cls.BMW_FOLDER,'*'+cls.MOBYLEXML_SUFFIX ) )
+        session_workflow_list = [ BMPSWorkflow.get_from_mobylexml_filepath(filepath, session) for filepath in session_workflow_path_list]
+        return session_workflow_list
+
+    @classmethod    
+    def load_user_workflows( cls, session):
+        """ 
+        This method allows to add BMW-defined workflows to the registry, so that
+        they can be run in Mobyle
+        """
+        user_workflows_list = cls.get_user_workflows(session)
+        if registry.serversByName.has_key('local'):
+            local_server = registry.serversByName['local']
+            #c = "user defined"
+            for w in user_workflows_list:
+                wf = WorkflowDef( name = w.pid,
+                                      url = w.url,
+                                      path = w.mobylexml_filepath,
+                                      server = local_server
+                                    )
+                registry.addWorkflow(wf)
+
+    @property
+    def workflows_folderpath(self):
+        """
+        folder of the user session containing all its workflows
+        """
+        return os.path.realpath(os.path.join(self.session.getDir(), self.BMW_FOLDER))
+
+    @property
+    def graphml_filename(self):
+        """
+        GRAPHML file name for this workflow
+        """
+        return "%s.graphml" % self.name
+
+    @property
+    def tasks_filename(self):
+        """
+        tasks file name for this workflow
+        """
+        return "%s.tasks.xml" % self.name
+
+    @property
+    def mobylexml_filename(self):
+        """
+        Mobyle XML file name for this workflow
+        """
+        return self.name + self.MOBYLEXML_SUFFIX
+
+    @property
+    def url(self):
+        """
+        Mobyle XML URL for this workflow
+        """
+        return "%s/%s/%s" % (self.session.url, self.BMW_FOLDER, self.mobylexml_filename)
+
+
+    @property
+    def graphml_filepath(self):
+        """
+        GRAPHML file path for this workflow
+        """
+        return os.path.realpath(os.path.join(self.workflows_folderpath, self.graphml_filename))
+
+    @property
+    def tasks_filepath(self):
+        """
+        tasks values file name for this workflow
+        """
+        return os.path.realpath(os.path.join(self.workflows_folderpath, self.tasks_filename))
+
+    @property
+    def mobylexml_filepath(self):
+        """
+        Mobyle XML file path for this workflow
+        """
+        return os.path.realpath(os.path.join(self.workflows_folderpath, self.mobylexml_filename))
+
+    @property
+    def pid(self):
+        return self.PID_PREFIX + self.name
+    
+    def graphviz_layout(self):
+        """
+        Perform automatic layout using graphviz for this workflow
+        """
+        wf_dot_filepath = os.path.join(self.workflows_folderpath, self.name + '.dot')
+        wf_css_filepath = os.path.join(self.workflows_folderpath, self.name + 'cmap.css')
+        graphml_to_dot_xsl_path = 'graphml_to_dot.xsl';
+        xslProcess(graphml_to_dot_xsl_path, self.graphml_filepath, wf_dot_filepath)
+        G = pgv.AGraph(wf_dot_filepath)
+        G.layout(prog='dot')
+        G.draw(wf_css_filepath, format='cmapx')
+        xslProcess("update_graphml_with_graphviz_layout.xsl", self.graphml_filepath, self.graphml_filepath, params={'cssPath':"'" + wf_css_filepath + "'"})
+        return open(self.graphml_filepath, 'r').read()
+
+    def update_graphml(self, graphml_contents):
+        """
+        Update the workflow using a new GRAPHML file
+        """
+        open(self.graphml_filepath, 'w').write(graphml_contents)
+        self.graphml_to_mobylexml()
+        
+    def graphml_to_mobylexml(self):
+        """
+        Update the workflow files stored on the server from the corresponding up-to-date graphml file
+        """
+        graphml_to_wf_xsl_path = 'graphml_to_wf.xsl'
+        xslProcess(graphml_to_wf_xsl_path, self.graphml_filepath, self.mobylexml_filepath)
+        yy = open(self.mobylexml_filepath, 'r').read()
+        mobyle_parser = Parser()
+        o = mobyle_parser.XML(yy)
+        if os.path.exists(self.tasks_filepath):
+            doc = etree.parse(self.tasks_filepath, parser)
+            root = doc.getroot()
+            for wf_task_ele in o.findall("flow/task"):
+                task_id = wf_task_ele.get('id')
+                task_inp_values = root.find("task[@id='%s']" % task_id)
+                if (task_inp_values is not None):
+                    for inp in list(task_inp_values):
+                        wf_task_ele.append(inp) 
+        # generate input and output parameters for Mobyle integration
+        mobyle_parser = Parser()
+        wf = mobyle_parser.XML(mobyle_parser.tostring(o))
+        # order tasks for parameter generation for the sake of usability
+        unordered_tasks = wf.tasks
+        ordered_tasks = []
+        def order_next_tasks(task):
+            if task is None:
+                next_tasks = [next_task for next_task in unordered_tasks if next_task.id not in [link.to_task for link in wf.links]]
+            else:
+                next_tasks = [next_task for next_task in unordered_tasks if next_task.id in [link.to_task for link in wf.links if link.from_task==task.id]]
+            for next_task in next_tasks:
+                if next_task not in ordered_tasks:
+                    ordered_tasks.append(next_task)
+                    order_next_tasks(next_task)
+        order_next_tasks(None)
+        for task in ordered_tasks:
+            service = parseService(registry.serversByName['local'].programsByName[task.service].path)
+            # for each task create a paragraph to group its parameters
+            paragraph = Paragraph()
+            paragraph.name = task.id
+            paragraph.prompt = "Task %s" % (task.description if task.description != '' else task.service)
+            # create input parameters
+            for input_parameter_name in service.getUserInputParameterByArgpos():
+                # if the value of the parameter is not provided by a link
+                if not([link for link in wf.links if (input_parameter_name == link.to_parameter and task.id == link.to_task)]):
+                    input_parameter = service.getParameter(input_parameter_name)
+                    parameter = Parameter()
+                    # input parameter name is: task_id + "_" + parameter name to avoid collisions
+                    parameter.name = task.id + "_" + input_parameter.getName()
+                    parameter.prompt = input_parameter.getPrompt()
+                    parameter.type = Type()
+                    parameter.type.biotypes = [Biotype(bt_str) for bt_str in input_parameter.getBioTypes()]
+                    parameter.type.datatype = Datatype()
+                    parameter.type.datatype.class_name = input_parameter.getDataType().name
+                    if input_parameter.getDataType().name != str(input_parameter.getDataType().getRealName()):
+                        parameter.type.datatype.superclass_name = str(input_parameter.getDataType().getRealName())
+                    parameter.id = parameter.name
+                    if input_parameter.ismandatory() and (len(input_parameter.getPreconds()) == 0):
+                        parameter.ismandatory = True
+                    if input_parameter.issimple():
+                        parameter.issimple = True                        
+                    # setting default value (vdef)
+                    vdefs = []
+                    for iv in [iv for iv in task.input_values if iv.name == input_parameter_name]:
+                        value = Value()
+                        if iv.reference is not None:
+                            value.reference = iv.reference
+                            value.safe_name = iv.safe_name
+                            value.user_name = iv.user_name
+                        elif iv.value is not None:
+                            value.value = iv.value
+                        vdefs.append(value)
+                    if not(vdefs) and input_parameter.getVdef():
+                        vdeflist = input_parameter.getVdef()
+                        if isinstance(vdeflist,basestring):
+                            vdeflist = [vdeflist]
+                        for vdef in vdeflist:
+                            value = Value()
+                            value.value = vdef
+                            vdefs.append(value)
+                    if vdefs:
+                        parameter.vdef = vdefs
+                    # setting vdef
+                    if input_parameter.hasVlist():
+                        vlist = Vlist()
+                        for key, value in input_parameter._vlist.items():
+                            velem = VElem()
+                            velem.label = key
+                            velem.value = value
+                            vlist.velems = vlist.velems + [velem]
+                        parameter.vlist = vlist
+                    if input_parameter.hasFlist():
+                        vlist = Vlist()
+                        for key, value in input_parameter._flist.items():
+                            velem = VElem()
+                            velem.label = value[0]
+                            velem.value = key
+                            vlist.velems = vlist.velems + [velem]
+                        parameter.vlist = vlist
+                    # wf.parameters = wf.parameters + [parameter]
+                    paragraph.parameters = paragraph.parameters + [parameter]
+                    link = Link()
+                    link.to_parameter = input_parameter_name
+                    link.to_task = task.id
+                    link.from_parameter = parameter.id
+                    wf.links = wf.links + [link]
+            if len(paragraph.parameters) > 0:
+                wf.paragraphs = wf.paragraphs + [paragraph]
+            # complete the description of workflow output parameters
+            for output_parameter_name in service.getUserOutputParameters():
+                # if the value of the parameter is not provided by a link
+                for link in wf.links:
+                    if (output_parameter_name == link.from_parameter and task.id == link.from_task and link.to_task is None):
+                        output_parameter = service.getParameter(output_parameter_name)
+                        wf_output_parameter = [parameter for parameter in wf.parameters if parameter.id == link.to_parameter][0]
+                        wf_output_parameter.prompt = output_parameter.getPrompt()
+                        wf_output_parameter.type = Type()
+                        wf_output_parameter.type.biotypes = [Biotype(bt_str) for bt_str in output_parameter.getBioTypes()]
+                        wf_output_parameter.type.datatype = Datatype()
+                        wf_output_parameter.type.datatype.class_name = output_parameter.getDataType().name
+                        if input_parameter.getDataType().name != str(output_parameter.getDataType().getRealName()):
+                            wf_output_parameter.type.datatype.superclass_name = str(output_parameter.getDataType().getRealName())
+        fh = open(self.mobylexml_filepath, 'w')
+        fh.write(mobyle_parser.tostring(wf))
+        fh.close()
+        # now generating a Mobyle-style interface to enable display of BMW jobs in Mobyle Portal
+        preprocessor = InterfacePreprocessor()
+        preprocessor.process_interface(self.mobylexml_filepath)
+        return
+    
+    def get_graphml(self):
+        """
+        Get the contents of the GRAPHML file for the workflow
+        """
+        return open(self.graphml_filepath, 'r').read()
+
+    def rename_wf(self, new_name, new_description=None):
+        """
+        Rename the workflow
+        """
+        old_graphml_filepath = self.graphml_filepath
+        old_mobylexml_filepath = self.mobylexml_filepath
+        if os.path.exists(self.tasks_filepath):
+            old_tasks_filepath = self.tasks_filepath
+        else:
+            old_tasks_filepath = None
+        old_name = self.name
+        self.name = new_name
+        if(not(os.path.exists(self.graphml_filepath))):
+            os.rename(old_graphml_filepath, self.graphml_filepath)
+            os.rename(old_mobylexml_filepath, self.mobylexml_filepath)
+            if old_tasks_filepath is not None:
+                os.rename(old_tasks_filepath, self.tasks_filepath)
+        else:
+            raise RenameError(old_name, new_name)
+        # rename in the graphml file directly as well
+        source_file = open(self.graphml_filepath, 'r')
+        root_tree = etree.parse(source_file)
+        source_file.close()
+        graph = root_tree.find('graph')
+        graph.set('userName', self.name)
+        if new_description is not None:
+            graph.set('description', new_description)
+        open(self.graphml_filepath, 'w').write(etree.tostring(root_tree))
+        self.graphml_to_mobylexml()
+
+    def change_wf_description(self, new_description=None):
+        """
+        Change the workflow description
+        """
+        source_file = open(self.graphml_filepath, 'r')
+        root_tree = etree.parse(source_file)
+        source_file.close()
+        graph = root_tree.find('graph')
+        if new_description is not None:
+            graph.set('description', new_description)
+        open(self.graphml_filepath, 'w').write(etree.tostring(root_tree))
+        self.graphml_to_mobylexml()
+        
+
+    
+    def copy_wf(self, dest_session ):
+        """
+        copy the workflow into another session
+        @param dest_session: 
+        @type dest_session: Session object
+        @param new_description:
+        @type new_description: string
+        """
+        src_graphml_filepath = self.graphml_filepath
+        src_mobylexml_filepath = self.mobylexml_filepath
+        src_tasks_filepath = self.tasks_filepath
+        
+        dest_wf_dir = os.path.join(dest_session.getDir(), self.BMW_FOLDER)
+        if not os.path.exists(dest_wf_dir):
+            #in old authenticated sessions the workflow directory doe not exists
+            os.mkdir(dest_wf_dir, 0755)
+            
+        copy = False
+        for ext in ("","_1","_2","_3","_4","_5","_6","_7","_8","_9","_10"):
+            dest_graphml_filename = self.graphml_filename + ext
+            dest_mobylexml_filename = self.mobylexml_filename + ext
+            dest_tasks_filename = self.tasks_filename + ext
+            dest_graphml_filepath = os.path.join( dest_wf_dir, dest_graphml_filename)
+            dest_mobylexml_filepath = os.path.join( dest_wf_dir, dest_mobylexml_filename)
+            dest_tasks_filepath = os.path.join( dest_wf_dir, dest_tasks_filename)
+            if not os.path.exists(dest_graphml_filepath):
+                shutil.copy(src_graphml_filepath, dest_graphml_filepath)
+                shutil.copy(src_mobylexml_filepath, dest_mobylexml_filepath )
+                if os.path.exists(self.tasks_filepath):
+                    shutil.copy(src_tasks_filepath, dest_tasks_filepath)
+                source_file = open(dest_graphml_filepath)
+                root_tree = etree.parse(source_file)
+                source_file.close()
+                graph = root_tree.find('graph')
+                graph.set('userName', self.name)
+                open(dest_graphml_filepath, 'w').write(etree.tostring(root_tree))
+                new_wf = BMPSWorkflow( self.name + ext , dest_session )
+                new_wf.graphml_to_mobylexml()
+                copy = True
+                break
+        if not copy:
+            raise CopyError( self.name )
+            
+        
+    def delete(self):
+        """
+        Delete the workflow
+        """
+        os.remove(self.graphml_filepath)
+        os.remove(self.mobylexml_filepath)
+        if os.path.exists(self.tasks_filepath):
+            os.remove(self.tasks_filepath)
+        return
+        
+    def set_task_values(self, task_id, values):
+        """
+        Change the parameter values for a task of the workflow
+        """
+        if os.path.exists(self.tasks_filepath):
+            doc = etree.parse(self.tasks_filepath, parser)
+            root = doc.getroot()
+            task_inp_values = root.find("task[@id='%s']" % task_id)
+            if (task_inp_values is not None):
+                root.remove(task_inp_values)
+        else:
+            root = etree.Element("tasks")
+        field_list = '<task id="%s">\n' % task_id
+        field_names = values.keys()
+        for field in field_names:
+            if field == 'action' or field == 'form_submit' or field == 'app_id' or field == 'from_app'\
+                                or field == 'wf_name' or '.srcFileName' in field or '.name' in field\
+                                or '.mode' in field or '.srcUrl' in field:
+                continue
+            valuelist = values.getvalue(field)
+            if isinstance(valuelist,basestring):
+                valuelist = [valuelist]
+            for vdef in valuelist:
+                if vdef and len(vdef.strip()) > 0:
+                    if '.ref' in field:
+                        field = field.replace('.ref','')
+                        field_list = field_list + '<inputValue name="%s" reference="%s" mode="%s" userName="%s" safeName="%s"/>\n' % \
+                            (field, values.getvalue(field+'.srcUrl',''), values.getvalue(field+'.mode',''), values.getvalue(field+'.name',''), values.getvalue(field+'.ref',''))
+                        field = field.replace(".ref", ".srcFileName")
+                    else:
+                        field_list = field_list + '<inputValue name="%s">%s</inputValue>\n' % (field, vdef)
+        field_list = field_list + '</task>\n'
+        task_node = etree.fromstring(field_list, parser=parser)
+        root.append(task_node)
+        open(self.tasks_filepath, 'wb').write(etree.tostring(root))
+        return
+    
+    def get_task_xml(self, task_id, service_pid):
+        """
+        Get the Mobyle XML for a task of the workflow, after having modified the default values
+        """
+        mobyle_parser = Parser()
+        wf = mobyle_parser.parse(self.mobylexml_filepath)
+        tasks = [t for t in wf.tasks if t.id == task_id]
+        if(len(tasks)==0):
+            mobyle_parser = Parser()
+            wf = mobyle_parser.parse(self.mobylexml_filepath)
+            task = Task()
+            task.service = service_pid
+            task.id = task_id
+            wf.tasks = wf.tasks + [task]
+        else:
+            task = tasks[0]
+        service_pid = task.service
+        service_path = get_service(service_pid).path
+        msg = xslProcess('task_xml.xsl', service_path, None, {'task_id':"'%s'" % task_id, 'workflow_url':"'%s'" % self.mobylexml_filepath})
+        return msg
diff --git a/Src/Mobyle/Captcha/Backgrounds.py b/Src/Mobyle/Captcha/Backgrounds.py
new file mode 100644
index 0000000..a718b48
--- /dev/null
+++ b/Src/Mobyle/Captcha/Backgrounds.py
@@ -0,0 +1,42 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import random
+try:
+    import Image
+except ImportError:
+    from PIL import Image
+from Mobyle.Captcha import Pictures
+
+class TiledImage(object):
+    """Pick a random image and a random offset, and tile the rendered image with it"""
+    def __init__(self, imageFactory=Pictures.abstract):
+        self.tileName = imageFactory.pick()
+        self.offset = (random.uniform(0, 1),
+                       random.uniform(0, 1))
+
+    def render(self, image):
+        tile = Image.open(self.tileName)
+        for j in xrange(-1, int(image.size[1] / tile.size[1]) + 1):
+            for i in xrange(-1, int(image.size[0] / tile.size[0]) + 1):
+                dest = (int((self.offset[0] + i) * tile.size[0]),
+                        int((self.offset[1] + j) * tile.size[1]))
+                image.paste(tile, dest)
+
+
+class CroppedImage(object):
+    """Pick a random image, cropped randomly. Source images should be larger than the CAPTCHA."""
+    def __init__(self, imageFactory=Pictures.nature):
+        self.imageName = imageFactory.pick()
+        self.align = (random.uniform(0,1),
+                      random.uniform(0,1))
+
+    def render(self, image):
+        i = Image.open(self.imageName)
+        image.paste(i, (int(self.align[0] * (image.size[0] - i.size[0])),
+                        int(self.align[1] * (image.size[1] - i.size[1]))))
diff --git a/Src/Mobyle/Captcha/COPYING b/Src/Mobyle/Captcha/COPYING
new file mode 100644
index 0000000..bf51608
--- /dev/null
+++ b/Src/Mobyle/Captcha/COPYING
@@ -0,0 +1,19 @@
+Copyright (c) 2004 Micah Dowty
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do 
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
+SOFTWARE. 
diff --git a/Src/Mobyle/Captcha/Captcha.py b/Src/Mobyle/Captcha/Captcha.py
new file mode 100644
index 0000000..aed0986
--- /dev/null
+++ b/Src/Mobyle/Captcha/Captcha.py
@@ -0,0 +1,103 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import random, string, time
+try:
+    import Image
+except ImportError:
+    from PIL import Image
+from Mobyle.Captcha import Words , Backgrounds , Text , Distortions
+
+def randomIdentifier(alphabet = string.ascii_letters + string.digits,
+                     length = 24):
+    return "".join([random.choice(alphabet) for i in xrange(length)])
+
+
+class PseudoGimpy():
+    """A relatively easy CAPTCHA that's somewhat easy on the eyes
+       The render() function generates the CAPTCHA image at the given size by
+       combining Layer instances from self.layers.
+       """
+    minCorrectSolutions = 1
+    maxIncorrectSolutions = 0   
+    defaultSize = (256,96)
+  
+    def __init__(self):
+        self.solutions = []
+        self.valid = True
+
+        # Each test has a unique identifier, used to refer to that test
+        # later, and a creation time so it can expire later.
+        self.id = randomIdentifier()
+        self.creationTime = time.time()
+
+        self._layers = self.getLayers()
+    
+    def addSolution(self, solution):
+        self.solutions.append(solution)
+
+    def testSolutions(self, solutions):
+        """Test whether the given solutions are sufficient for this CAPTCHA.
+           A given CAPTCHA can only be tested once, after that it is invalid
+           and always returns False. This makes random guessing much less effective.
+           """
+        if not self.valid:
+            return False
+        self.valid = False
+
+        numCorrect = 0
+        numIncorrect = 0
+
+        for solution in solutions:
+            if solution in self.solutions:
+                numCorrect += 1
+            else:
+                numIncorrect += 1
+
+        return numCorrect >= self.minCorrectSolutions and \
+               numIncorrect <= self.maxIncorrectSolutions
+
+    def getImage(self):
+        """Get a PIL image representing this CAPTCHA test, creating it if necessary"""
+        if not self._image:
+            self._image = self.render()
+        return self._image
+
+
+    def getLayers(self):
+        word = Words.defaultWordList.pick()
+        self.addSolution(word)
+        return [
+            random.choice([
+                Backgrounds.CroppedImage(),
+                Backgrounds.TiledImage(),
+            ]),
+            Text.TextLayer(word, borderSize=1),
+            Distortions.SineWarp(),
+            ]
+
+    def render(self, size=None):
+        """Render this CAPTCHA, returning a PIL image"""
+        if size is None:
+            size = self.defaultSize
+        img = Image.new("RGB", size)
+        return self._renderList(self._layers, Image.new("RGB", size))
+
+    def _renderList(self, l, img):
+        for i in l:
+            if type(i) == tuple or type(i) == list:
+                img = self._renderList(i, img)
+            else:
+                img = i.render(img) or img
+        return img
+
+
+
+
+
+    
diff --git a/Src/Mobyle/Captcha/Distortions.py b/Src/Mobyle/Captcha/Distortions.py
new file mode 100644
index 0000000..ed415b5
--- /dev/null
+++ b/Src/Mobyle/Captcha/Distortions.py
@@ -0,0 +1,85 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import random , math 
+try:
+    import Image
+except ImportError:
+    from PIL import Image
+
+class SineWarp(object):
+    """Warp the image using a random composition of sine waves
+       define a function that maps points in the output image to points in the input image.
+       This warping engine runs a grid of points through this transform and uses
+       PIL's mesh transform to warp the image.
+       """
+    filtering = Image.BILINEAR
+    resolution = 10
+
+    def __init__(self,
+                 amplitudeRange = (3, 6.5),
+                 periodRange    = (0.04, 0.1),
+                 ):
+        
+        self.amplitude = random.uniform(*amplitudeRange)
+        self.period = random.uniform(*periodRange)
+        self.offset = (random.uniform(0, math.pi * 2 / self.period),
+                       random.uniform(0, math.pi * 2 / self.period))
+        
+        
+    def getTransform(self, image):
+        return (lambda x, y,
+                a = self.amplitude,
+                p = self.period,
+                o = self.offset:
+                (math.sin( (y+o[0])*p )*a + x,
+                 math.sin( (x+o[1])*p )*a + y))
+
+    def render(self, image):
+        r = self.resolution
+        xPoints = image.size[0] / r + 2
+        yPoints = image.size[1] / r + 2
+        f = self.getTransform(image)
+
+        # Create a list of arrays with transformed points
+        xRows = []
+        yRows = []
+        for j in xrange(yPoints):
+            xRow = []
+            yRow = []
+            for i in xrange(xPoints):
+                x, y = f(i*r, j*r)
+
+                # Clamp the edges so we don't get black undefined areas
+                x = max(0, min(image.size[0]-1, x))
+                y = max(0, min(image.size[1]-1, y))
+
+                xRow.append(x)
+                yRow.append(y)
+            xRows.append(xRow)
+            yRows.append(yRow)
+
+        # Create the mesh list, with a transformation for
+        # each square between points on the grid
+        mesh = []
+        for j in xrange(yPoints-1):
+            for i in xrange(xPoints-1):
+                mesh.append((
+                    # Destination rectangle
+                    (i*r, j*r,
+                     (i+1)*r, (j+1)*r),
+                    # Source quadrilateral
+                    (xRows[j  ][i  ], yRows[j  ][i  ],
+                     xRows[j+1][i  ], yRows[j+1][i  ],
+                     xRows[j+1][i+1], yRows[j+1][i+1],
+                     xRows[j  ][i+1], yRows[j  ][i+1]),
+                    ))
+
+        return image.transform(image.size, Image.MESH, mesh, self.filtering)
+
+
diff --git a/Src/Mobyle/Captcha/Pictures.py b/Src/Mobyle/Captcha/Pictures.py
new file mode 100644
index 0000000..560fa43
--- /dev/null
+++ b/Src/Mobyle/Captcha/Pictures.py
@@ -0,0 +1,35 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import os, random , glob
+
+
+class ImageFactory(object):
+    """Given a list of files and/or directories, this picks a random file.
+       Directories are searched for files matching any of a list of extensions.
+       Files are relative to our data directory plus a subclass-specified base path.
+       """
+    extensions = [".png"]
+    picturesPath = os.path.abspath(os.path.join(os.path.dirname(__file__), "data" , "pictures" ) )
+    
+    def __init__(self, image_category ):
+        self.image_category = image_category
+        self._fullPaths = None
+
+
+    def _findFullPaths(self):
+        """From our given file list, find a list of full paths to files"""
+        return glob.glob( os.path.join( self.picturesPath , self.image_category , '*.png' ) )
+
+    def pick(self):
+        if self._fullPaths is None:
+            self._fullPaths = self._findFullPaths()
+        return random.choice(self._fullPaths)
+    
+abstract = ImageFactory("abstract")
+nature = ImageFactory("nature")
\ No newline at end of file
diff --git a/Src/Mobyle/Captcha/Text.py b/Src/Mobyle/Captcha/Text.py
new file mode 100644
index 0000000..782002c
--- /dev/null
+++ b/Src/Mobyle/Captcha/Text.py
@@ -0,0 +1,91 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import os , glob , random
+try:
+    import ImageFont, ImageDraw
+except ImportError:
+    from PIL import ImageFont, ImageDraw
+    
+    
+class FontFactory(object):
+    """Picks random fonts and/or sizes from a given list.
+       'sizes' can be a single size or a (min,max) tuple.
+       If any of the given files are directories, all *.ttf found
+       in that directory will be added.
+       """
+    basePath = "fonts"
+    fontsPath = os.path.abspath(os.path.join(os.path.dirname(__file__), "data" , "fonts" ) )
+    
+    
+    def __init__(self, sizes, font_category ):
+        self.font_category = font_category
+        self._fullPaths = None
+        
+        if type(sizes) is tuple:
+            self.minSize = sizes[0]
+            self.maxSize = sizes[1]
+        else:
+            self.minSize = sizes
+            self.maxSize = sizes
+
+    def _findFullPaths(self):
+        """From our given file list, find a list of full paths to files"""
+        return glob.glob( os.path.join( self.fontsPath , self.font_category , '*.ttf' ) )
+
+    def pick(self):
+        """Returns a (fileName, size) tuple that can be passed to ImageFont.truetype()"""
+        if self._fullPaths is None:
+            self._fullPaths = self._findFullPaths()
+        fileName = random.choice(self._fullPaths)
+        size = int(random.uniform(self.minSize, self.maxSize) + 0.5)
+        return (fileName, size)
+    
+
+   
+    
+class TextLayer(object):
+    """Represents a piece of text rendered within the image.
+       Alignment is given such that (0,0) places the text in the
+       top-left corner and (1,1) places it in the bottom-left.
+
+       The font and alignment are optional, if not specified one is
+       chosen randomly. If no font factory is specified, the default is used.
+       """
+    def __init__(self, text,
+                 borderSize  = 0,
+                 ):
+        fontFactory = FontFactory((30, 40), "vera")
+        self.font = fontFactory.pick()
+        self.alignment = (random.uniform(0,1), random.uniform(0,1))
+        self.text        = text
+        self.textColor   = "black"
+        self.borderSize  = borderSize
+        self.borderColor = "white"
+
+    def render(self, img):
+        font = ImageFont.truetype(*self.font)
+        textSize = font.getsize(self.text)
+        draw = ImageDraw.Draw(img)
+
+        # Find the text's origin given our alignment and current image size
+        x = int((img.size[0] - textSize[0] - self.borderSize*2) * self.alignment[0] + 0.5)
+        y = int((img.size[1] - textSize[1] - self.borderSize*2) * self.alignment[1] + 0.5)
+
+        # Draw the border if we need one. This is slow and ugly, but there doesn't
+        # seem to be a better way with PIL.
+        if self.borderSize > 0:
+            for bx in (-1,0,1):
+                for by in (-1,0,1):
+                    if bx and by:
+                        draw.text((x + bx * self.borderSize,
+                                   y + by * self.borderSize),
+                                  self.text, font=font, fill=self.borderColor)
+
+        # And the text itself...
+        draw.text((x,y), self.text, font=font, fill=self.textColor)
diff --git a/Src/Mobyle/Captcha/Words.py b/Src/Mobyle/Captcha/Words.py
new file mode 100644
index 0000000..e7228f3
--- /dev/null
+++ b/Src/Mobyle/Captcha/Words.py
@@ -0,0 +1,48 @@
+############################################################################################
+#                                                                                          #
+# The code below is from the pycaptcha-0.4 package ( http://releases.navi.cx/pycaptcha/ )  #
+# Copyright (c) 2004 Micah Dowty  (see COPYING in this directory for further details )     #                     #
+# and adapted by Bertrand Neron, for the purpose of Mobyle                                 #                     #
+#                                                                                          #
+############################################################################################
+
+import random, os
+
+
+
+class WordList(object):
+    """A class representing a word list read from disk lazily.
+       Blank lines and comment lines starting with '#' are ignored.
+       Any number of words per line may be used. The list can
+       optionally ingore words not within a given length range.
+       """
+    def __init__(self):
+        self.words = None
+        self.fileName = "basic-english"
+
+    def read(self):
+        """Read words from disk"""
+        f = open( os.path.abspath(os.path.join(os.path.dirname(__file__), "data" , "words" , self.fileName )))
+        
+        self.words = []
+        for line in f.xreadlines():
+            line = line.strip()
+            if not line:
+                continue
+            if line[0] == '#':
+                continue
+            for word in line.split():
+                self.words.append(word)
+
+    def pick(self):
+        """Pick a random word from the list, reading it in if necessary"""
+        if self.words is None:
+            self.read()
+        return random.choice(self.words)
+
+
+# Define several shared word lists that are read from disk on demand
+
+defaultWordList = WordList() 
+
+### The End ###
diff --git a/Src/Mobyle/Captcha/__init__.py b/Src/Mobyle/Captcha/__init__.py
new file mode 100644
index 0000000..30dbe69
--- /dev/null
+++ b/Src/Mobyle/Captcha/__init__.py
@@ -0,0 +1,15 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+__all__ = [ 'Captcha',
+            'Backgrounds',
+            'Distorsions',
+            'Pictures',
+            'Text',
+            'Words'
+            ]
\ No newline at end of file
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/1.png b/Src/Mobyle/Captcha/data/pictures/abstract/1.png
new file mode 100644
index 0000000..51ccde8
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/1.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/10.png b/Src/Mobyle/Captcha/data/pictures/abstract/10.png
new file mode 100644
index 0000000..f45eea8
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/10.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/11.png b/Src/Mobyle/Captcha/data/pictures/abstract/11.png
new file mode 100644
index 0000000..01a60e9
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/11.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/12.png b/Src/Mobyle/Captcha/data/pictures/abstract/12.png
new file mode 100644
index 0000000..e40441e
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/12.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/2.png b/Src/Mobyle/Captcha/data/pictures/abstract/2.png
new file mode 100644
index 0000000..f7dab13
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/2.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/3.png b/Src/Mobyle/Captcha/data/pictures/abstract/3.png
new file mode 100644
index 0000000..77e6723
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/3.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/4.png b/Src/Mobyle/Captcha/data/pictures/abstract/4.png
new file mode 100644
index 0000000..dfa843b
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/4.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/5.png b/Src/Mobyle/Captcha/data/pictures/abstract/5.png
new file mode 100644
index 0000000..003a98e
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/5.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/6.png b/Src/Mobyle/Captcha/data/pictures/abstract/6.png
new file mode 100644
index 0000000..4cc4ecf
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/6.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/7.png b/Src/Mobyle/Captcha/data/pictures/abstract/7.png
new file mode 100644
index 0000000..b1558ad
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/7.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/8.png b/Src/Mobyle/Captcha/data/pictures/abstract/8.png
new file mode 100644
index 0000000..078bcfb
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/8.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/9.png b/Src/Mobyle/Captcha/data/pictures/abstract/9.png
new file mode 100644
index 0000000..6475b02
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/abstract/9.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/abstract/README b/Src/Mobyle/Captcha/data/pictures/abstract/README
new file mode 100644
index 0000000..5ce61e5
--- /dev/null
+++ b/Src/Mobyle/Captcha/data/pictures/abstract/README
@@ -0,0 +1,3 @@
+These images were created by the author with Fyre, expressly for PyCAPTCHA.
+
+Copyright (c) 2004 Micah Dowty
diff --git a/Src/Mobyle/Captcha/data/pictures/nature/Craig_Barrington_ocotillo_and_mountains.png b/Src/Mobyle/Captcha/data/pictures/nature/Craig_Barrington_ocotillo_and_mountains.png
new file mode 100644
index 0000000..6f33258
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/nature/Craig_Barrington_ocotillo_and_mountains.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/nature/Kerry_Carloy_Chisos_Sunset.png b/Src/Mobyle/Captcha/data/pictures/nature/Kerry_Carloy_Chisos_Sunset.png
new file mode 100644
index 0000000..02a5469
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/nature/Kerry_Carloy_Chisos_Sunset.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/nature/Paul_Dowty_Mt_Bross.png b/Src/Mobyle/Captcha/data/pictures/nature/Paul_Dowty_Mt_Bross.png
new file mode 100644
index 0000000..b72f544
Binary files /dev/null and b/Src/Mobyle/Captcha/data/pictures/nature/Paul_Dowty_Mt_Bross.png differ
diff --git a/Src/Mobyle/Captcha/data/pictures/nature/README b/Src/Mobyle/Captcha/data/pictures/nature/README
new file mode 100644
index 0000000..8a86305
--- /dev/null
+++ b/Src/Mobyle/Captcha/data/pictures/nature/README
@@ -0,0 +1,2 @@
+These are uncopyrighted images gathered from various sources,
+including the author's family and national park service web sites.
\ No newline at end of file
diff --git a/Src/Mobyle/Captcha/data/words/basic-english b/Src/Mobyle/Captcha/data/words/basic-english
new file mode 100644
index 0000000..7b979c2
--- /dev/null
+++ b/Src/Mobyle/Captcha/data/words/basic-english
@@ -0,0 +1,852 @@
+a
+able
+about
+account
+acid
+across
+act
+addition
+adjustment
+advertisement
+agreement
+after
+again
+against
+air
+all
+almost
+among
+amount
+amusement
+and
+angle
+angry
+animal
+answer
+ant
+any
+apparatus
+apple
+approval
+arch
+argument
+arm
+army
+art
+as
+at
+attack
+attempt
+attention
+attraction
+authority
+automatic
+awake
+baby
+back
+bad
+bag
+balance
+ball
+band
+base
+basin
+basket
+bath
+be
+beautiful
+because
+bed
+bee
+before
+behavior
+belief
+bell
+bent
+berry
+between
+bird
+birth
+bit
+bite
+bitter
+black
+blade
+blood
+blow
+blue
+board
+boat
+body
+boiling
+bone
+book
+boot
+bottle
+box
+boy
+brain
+brake
+branch
+brass
+bread
+breath
+brick
+bridge
+bright
+broken
+brother
+brown
+brush
+bucket
+building
+bulb
+burn
+burst
+business
+but
+butter
+button
+by
+cake
+camera
+canvas
+card
+care
+carriage
+cart
+cat
+cause
+certain
+chain
+chalk
+chance
+change
+cheap
+cheese
+chemical
+chest
+chief
+chin
+church
+circle
+clean
+clear
+clock
+cloth
+cloud
+coal
+coat
+cold
+collar
+color
+comb
+come
+comfort
+committee
+common
+company
+comparison
+competition
+complete
+complex
+condition
+connection
+conscious
+control
+cook
+copper
+copy
+cord
+cork
+cotton
+cough
+country
+cover
+cow
+crack
+credit
+crime
+cruel
+crush
+cry
+cup
+current
+curtain
+curve
+cushion
+cut
+damage
+danger
+dark
+daughter
+day
+dead
+dear
+death
+debt
+decision
+deep
+degree
+delicate
+dependent
+design
+desire
+destruction
+detail
+development
+different
+digestion
+direction
+dirty
+discovery
+discussion
+disease
+disgust
+distance
+distribution
+division
+do
+dog
+door
+down
+doubt
+drain
+drawer
+dress
+drink
+driving
+drop
+dry
+dust
+ear
+early
+earth
+east
+edge
+education
+effect
+egg
+elastic
+electric
+end
+engine
+enough
+equal
+error
+even
+event
+ever
+every
+example
+exchange
+existence
+expansion
+experience
+expert
+eye
+face
+fact
+fall
+false
+family
+far
+farm
+fat
+father
+fear
+feather
+feeble
+feeling
+female
+fertile
+fiction
+field
+fight
+finger
+fire
+first
+fish
+fixed
+flag
+flame
+flat
+flight
+floor
+flower
+fly
+fold
+food
+foolish
+foot
+for
+force
+fork
+form
+forward
+fowl
+frame
+free
+frequent
+friend
+from
+front
+fruit
+full
+future
+garden
+general
+get
+girl
+give
+glass
+glove
+go
+goat
+gold
+good
+government
+grain
+grass
+great
+green
+grey/gray
+grip
+group
+growth
+guide
+gun
+hair
+hammer
+hand
+hanging
+happy
+harbor
+hard
+harmony
+hat
+hate
+have
+he
+head
+healthy
+hearing
+heart
+heat
+help
+here
+high
+history
+hole
+hollow
+hook
+hope
+horn
+horse
+hospital
+hour
+house
+how
+humor
+ice
+idea
+if
+ill
+important
+impulse
+in
+increase
+industry
+ink
+insect
+instrument
+insurance
+interest
+invention
+iron
+island
+jelly
+jewel
+join
+journey
+judge
+jump
+keep
+kettle
+key
+kick
+kind
+kiss
+knee
+knife
+knot
+knowledge
+land
+language
+last
+late
+laugh
+law
+lead
+leaf
+learning
+leather
+left
+leg
+let
+letter
+level
+library
+lift
+light
+like
+limit
+line
+linen
+lip
+liquid
+list
+little
+less
+least
+living
+lock
+long
+loose
+loss
+loud
+love
+low
+machine
+make
+male
+man
+manager
+map
+mark
+market
+married
+match
+material
+mass
+may
+meal
+measure
+meat
+medical
+meeting
+memory
+metal
+middle
+military
+milk
+mind
+mine
+minute
+mist
+mixed
+money
+monkey
+month
+moon
+morning
+mother
+motion
+mountain
+mouth
+move
+much
+more
+most
+muscle
+music
+nail
+name
+narrow
+nation
+natural
+near
+necessary
+neck
+need
+needle
+nerve
+net
+new
+news
+night
+no
+noise
+normal
+north
+nose
+not
+note
+now
+number
+nut
+observation
+of
+off
+offer
+office
+oil
+old
+on
+only
+open
+operation
+opposite
+opinion
+other
+or
+orange
+order
+organization
+ornament
+out
+oven
+over
+owner
+page
+pain
+paint
+paper
+parallel
+parcel
+part
+past
+paste
+payment
+peace
+pen
+pencil
+person
+physical
+picture
+pig
+pin
+pipe
+place
+plane
+plant
+plate
+play
+please
+pleasure
+plough/plow
+pocket
+point
+poison
+polish
+political
+poor
+porter
+position
+possible
+pot
+potato
+powder
+power
+present
+price
+print
+prison
+private
+probable
+process
+produce
+profit
+property
+prose
+protest
+public
+pull
+pump
+punishment
+purpose
+push
+put
+quality
+question
+quick
+quiet
+quite
+rail
+rain
+range
+rat
+rate
+ray
+reaction
+red
+reading
+ready
+reason
+receipt
+record
+regret
+regular
+relation
+religion
+representative
+request
+respect
+responsible
+rest
+reward
+rhythm
+rice
+right
+ring
+river
+road
+rod
+roll
+roof
+room
+root
+rough
+round
+rub
+rule
+run
+sad
+safe
+sail
+salt
+same
+sand
+say
+scale
+school
+science
+scissors
+screw
+sea
+seat
+second
+secret
+secretary
+see
+seed
+selection
+self
+send
+seem
+sense
+separate
+serious
+servant
+sex
+shade
+shake
+shame
+sharp
+sheep
+shelf
+ship
+shirt
+shock
+shoe
+short
+shut
+side
+sign
+silk
+silver
+simple
+sister
+size
+skin
+skirt
+sky
+sleep
+slip
+slope
+slow
+small
+smash
+smell
+smile
+smoke
+smooth
+snake
+sneeze
+snow
+so
+soap
+society
+sock
+soft
+solid
+some
+son
+song
+sort
+sound
+south
+soup
+space
+spade
+special
+sponge
+spoon
+spring
+square
+stamp
+stage
+star
+start
+statement
+station
+steam
+stem
+steel
+step
+stick
+still
+stitch
+stocking
+stomach
+stone
+stop
+store
+story
+strange
+street
+stretch
+sticky
+stiff
+straight
+strong
+structure
+substance
+sugar
+suggestion
+summer
+support
+surprise
+such
+sudden
+sun
+sweet
+swim
+system
+table
+tail
+take
+talk
+tall
+taste
+tax
+teaching
+tendency
+test
+than
+that
+the
+then
+theory
+there
+thick
+thin
+thing
+this
+thought
+thread
+throat
+though
+through
+thumb
+thunder
+ticket
+tight
+tired
+till
+time
+tin
+to
+toe
+together
+tomorrow
+tongue
+tooth
+top
+touch
+town
+trade
+train
+transport
+tray
+tree
+trick
+trousers
+true
+trouble
+turn
+twist
+umbrella
+under
+unit
+use
+up
+value
+verse
+very
+vessel
+view
+violent
+voice
+walk
+wall
+waiting
+war
+warm
+wash
+waste
+watch
+water
+wave
+wax
+way
+weather
+week
+weight
+well
+west
+wet
+wheel
+when
+where
+while
+whip
+whistle
+white
+who
+why
+wide
+will
+wind
+window
+wine
+wing
+winter
+wire
+wise
+with
+woman
+wood
+wool
+word
+work
+worm
+wound
+writing
+wrong
+year
+yellow
+yes
+yesterday
+you
+young
\ No newline at end of file
diff --git a/Src/Mobyle/Classes/Alignment.py b/Src/Mobyle/Classes/Alignment.py
new file mode 100644
index 0000000..5a7efa8
--- /dev/null
+++ b/Src/Mobyle/Classes/Alignment.py
@@ -0,0 +1,109 @@
+########################################################################################
+#                                                                                      #
+#   Author: Sandrine Larroude                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+Content the Mobyle Parameter types related to genomic bioloy
+"""
+ 
+
+from logging import getLogger
+c_log = getLogger(__name__)
+b_log = getLogger('Mobyle.builder')
+
+from Mobyle.MobyleError import MobyleError , UnDefAttrError
+from Mobyle.Classes.Core import AbstractTextDataType , safeMask
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+
+
+class AlignmentDataType( AbstractTextDataType ):
+    
+    def detect( self , value  ):
+        """
+        detects the format of the sequence(s) contained in fileName
+        @param value: the src object and the filename in the src of the data to detect
+        @type value: tuple ( session/Job/MobyleJob instance , string filename )
+        @return: a tuple of the detection run information:
+            - the detected format,
+            - the detected items number,
+            - program name used for the detection.
+        """
+        detected_mt = super( AlignmentDataType , self ).detect( value )
+        squizzDir = _cfg.format_detector_cache_path()
+        if squizzDir :
+            ##############################################
+            # This part of is used to back up all        # 
+            # submitted sequences which are not detected # 
+            # by squizz for further analysis             #
+            ##############################################
+            squizz_detector = None
+            for detector in _cfg.dataconverter( self.__class__.__name__[:-8] ):
+                if detector.program_name == 'squizz':
+                    squizz_detector = detector
+                    break
+            if squizz_detector is not None :
+                detected_data_format = detected_mt.getDataFormat()
+                from os import link
+                from os.path import join as os_join
+                if ( detected_data_format is None ) or ( detected_data_format in squizz_detector.detectedFormat() and not detected_mt.getFormatProgram() == 'squizz' ):
+                    try:
+                        #dump the data to further annalysis 
+                        link(  os_join( value[0].getDir() , value[1] ) , 
+                               os_join( squizzDir , "%s_%s_%s"%( self.__class__.__name__[:-8] ,
+                                                                 value[0].getKey() ,
+                                                                 value[1] ) )
+                               )
+                    except Exception , err : 
+                        c_log.error( "unable to link data in  format_detector_cache_path : %s " % err ) 
+        return detected_mt
+    
+
+    def validate( self , param ):
+        """
+        @return: True if the value is valid, False otherwise
+        """
+        value = param.getValue()
+        if param.isout():
+            if value is not None : #not possible for the user to modify an isout parameter 
+                return False
+            else:               
+                #####################################################
+                #                                                   #
+                #  check if the Parameter have a secure filenames   #
+                #                                                   #
+                #####################################################
+                
+                try:
+                    debug = param.getDebug()
+                    if debug > 1:
+                        b_log.debug( "check if the Parameter have a secure filename" )
+    
+                    #getFilenames returns a list of unix file mask, result of a code evaluation
+                    #None is returned if there is no mask for a parameter.
+                    masks = param.getFilenames( ) 
+                    for mask in masks :
+                        if mask is None:
+                            continue
+                        mySafeMask = safeMask( mask )
+                        if debug > 1:
+                            b_log.debug( "filename= %s    safeMask = %s" % (mask, mySafeMask))
+                        if  not mySafeMask or mySafeMask != mask :
+                            raise MobyleError , "have an unsecure filenames value before safeMask: %s , after safeMask: %s" % (mask, mySafeMask)
+                        elif debug > 1:
+                            b_log.debug( "filename = %s ...........OK" % mask )
+                                               
+                except UnDefAttrError :
+                    b_log.debug("no filenames")
+
+        else:
+            if value is None:
+                return True
+            else:
+                return True            
+             
\ No newline at end of file
diff --git a/Src/Mobyle/Classes/Core.py b/Src/Mobyle/Classes/Core.py
new file mode 100644
index 0000000..e4c8715
--- /dev/null
+++ b/Src/Mobyle/Classes/Core.py
@@ -0,0 +1,851 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+Content the basics Mobyle Parameter types 
+"""
+
+import os 
+import re
+from types import BooleanType , StringTypes
+from logging import getLogger
+c_log = getLogger(__name__)
+b_log = getLogger('Mobyle.builder')
+
+import shutil
+
+from Mobyle.Utils import safeFileName
+from Mobyle.Classes.DataType import DataType
+from Mobyle.Service import MobyleType
+
+from Mobyle.MobyleError import MobyleError , UserValueError , UnDefAttrError , UnSupportedFormatError
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+
+
+
+def safeMask( mask ):
+    import string
+    for car in mask :
+        if car not in string.printable :
+            mask = mask.replace( car , '_')
+    #don't use re.UNICODE because safeFileName don't permit to use unicode char.
+    #we must work whit the same char set    
+    mask = re.sub( "(;|`|$\(\s)+" , '_' , mask )
+    mask = re.sub( "^.*[\\\]", "" ,  mask  )
+    return mask
+
+
+
+class DataTypeTemplate( DataType ):
+
+    def convert( self , value , AcceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        cast the value to the right typeand compute the dataFormat conversion if needed (based on the 
+        detectedMobyleType and the AcceptedMobyleType)
+        @param value: the value provided by the User for this parameter
+        @type value: String
+        @param acceptedMobyleType: the MobyleType with the Dataformat accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing the real dataformat of this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @return: the casted value and the mobyleType describing this data (with the effective DataFormat)
+                 for datatype which have not dataFormat (boolean , integer, ... )
+                 the MobyleType returned is the same as the AcceptedMobyleType.
+        @rtype: ( value any type , MobyleType instance )
+        @raise UseValueError: if the cast failed
+        """
+        realMobyleType = MobyleType( self )
+        return ( "DataTypeTemplate convert: " + str( value ) , realMobyleType )
+ 
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt    
+ 
+    def validate( self, param ):
+        """
+        do type controls. if the value in the param pass the controls
+        return True, False otherwise. 
+        @rtype: boolean  
+        """
+        return "DataTypeTemplate validate"
+        
+        
+        
+class BooleanDataType( DataType ):
+    """
+    """
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        cast the value to a Boolean.
+        The values: "off", "false", "0" or '' (case insensitive) are false,
+        all others values are True.  
+        @param value: the value provided by the User for this parameter
+        @type value: String
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @return: the value converted to Boolean, and a MobyleType discribing this value
+        @rtype: tuple ( boolean , MobyleType instance )
+        """
+        if value is None:
+            # in html form boolean appear as checkbox
+            # if the checkbox is not selected
+            # the parameter is not send in the request
+            return ( False , acceptedMobyleType )
+        if isinstance( value , BooleanType ):
+            return ( value , acceptedMobyleType )
+        elif isinstance( value , StringTypes ):
+            value = value.lower()
+            if value in [ "off", "false", "0", "", "''", '""' ]:
+                return ( False , acceptedMobyleType )
+            elif value in [ "on", "true", "1" ]:
+                return ( True , acceptedMobyleType )
+        else:
+            msg = "Invalid value: " + str( value ) + " is not a boolean."
+            raise UserValueError( msg = msg )
+
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt
+    
+    def validate( self, param ):
+        """
+        do type controls. if the value is True or False or None return True,
+        otherwise return False..        
+        """
+        value = param.getValue()
+        if value == True or value == False:
+            return True
+        else:
+            return False
+
+
+        
+class IntegerDataType( DataType ):
+
+
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        Try to cast the value to Integer. the allowed values are digits
+        and strings.
+        
+        @param value: the value provided by the User for this parameter
+        @type value: string
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @return: the value converted to Integer and the mobyleType discribing this value.
+        @rtype: ( int , MobyleType instance )
+        @raise UserValueError: if the cast fails.
+        Unlike python, this method convert "8.0" to 8 and
+        raise a UserValueError if you try to convert 8.2 .
+        """
+        if value is None:
+            return ( None , acceptedMobyleType )
+        #type controls
+            # int("8.0") failed and a  ValueError is raised
+            # int(8.1) return 8
+        try:
+            f= float( value )
+            i = int( f )
+            if ( (f - i) == 0):
+                return ( i , acceptedMobyleType )
+            else:
+                msg = "\"%s\" : this parameter must be an integer" %value
+                raise  UserValueError( msg = msg)
+        except OverflowError:
+            raise UserValueError( msg = "this value is too big" )
+        except ( ValueError , TypeError ):
+            msg = "\"%s\" : this parameter must be an integer" %value
+            raise UserValueError( msg = msg)
+
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt  
+         
+    def validate( self, param  ):
+        """
+        @return True if the value is an integer, False othewise.
+        """   
+        value = param.getValue()
+
+        if value is None:
+            return True                       
+        try:
+            int( value )
+        except ( TypeError , ValueError ):
+            return False
+
+class FloatDataType( DataType ):
+
+
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        Try to cast the value to Float.
+        @param value: the value provided by the User for this parameter
+        @type value: 
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @return: the value converted to Float
+        @rtype: float
+        """
+        if value is None:
+            return ( None , acceptedMobyleType )
+        try:
+            return ( float( value ) , acceptedMobyleType )
+        except ( ValueError, TypeError ):
+            msg = str( value ) + " this parameter must be a Float"
+            raise UserValueError( msg = msg )
+
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt
+    
+    def validate( self, param ):
+        """
+        @return: True if the value is a float, False othewise.
+        @rtype: boolean
+        """
+        value = param.getValue()
+        if value is None:
+            return True     
+        try:
+            float( value )
+        except ( ValueError , TypeError ):
+            return False
+
+
+class StringDataType( DataType ):
+
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        Try to cast the value to String. 
+        
+        @param value: the value provided by the User for this parameter
+        @type value: 
+        @return: the value converted to String.
+        @rtype: String
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @raise UserValueError: if the cast fails.
+        """
+        if value is None:
+            return ( None , acceptedMobyleType )
+        #type controls
+        try:
+            value = str( value ).encode('ascii')
+        except ValueError :
+            raise UserValueError( msg = "should be an (ascii) string" ) 
+        #strings with space are allowed, but if the string will appear
+        #as shell instruction it must be quoted
+        if value.find(' ') != -1 and not paramFile:
+            value = "'%s'" % value
+        return ( value , acceptedMobyleType )                        
+        
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt        
+    
+    def validate( self, param  ):
+        """
+        @return: True if the value is a string and doesnot contain dangerous characters.
+        ( allowed characters: the words , space, ' , - , + , and dot if is not followed by another dot
+        and eventually surrounded by commas)
+        @rtype: boolean
+        @raise UserValueError: if the value does not statisfy security regexp.
+        """
+        value = param.getValue()
+        if value is None:
+            return True
+        
+        #allowed characters:
+        #the words , space, ' , - , + , and dot if is not followed by another dot 
+        #and eventually surrounded by commas
+        reg = "(\w|\ |-|\+|,|@|\.(?!\.))+"
+        if re.search( "^(%s|'%s')$" % (reg, reg) ,  value ) :         
+            return True
+        else:
+            msg = "this value: \"" + str( value ) + "\" , is not allowed"
+            raise UserValueError( parameter = param , msg = msg )
+        
+
+class ChoiceDataType( StringDataType ):
+    #the values of ChoiceDataType are literals thus they are String
+
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        The values of ChoiceDataType are literals thus this method try to cast
+        the value to string.
+        @param value: the value provided by the User for this parameter
+        @type value:
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance 
+        @return: the value converted in String
+        @rtype: string
+        """
+        if value is None:
+            return ( None , acceptedMobyleType )
+        try:
+            value  = str( value )
+            if hasattr( acceptedMobyleType , '_undefValue' ) and value == acceptedMobyleType._undefValue :
+                return ( None , acceptedMobyleType )
+            else:
+                return ( value, acceptedMobyleType )
+        except ValueError:
+            msg = " this parameter is a Choice its value should be a String" 
+            raise UserValueError( msg = msg )
+   
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt
+
+    def validate( self, param ):
+        """
+        @return: True if the value is valid. that's mean the value
+        should be a string among the list defined in the xml.
+        otherwise a MobyleError is raised.
+        @rtype: boolean
+        @param value: a value from a Choice parameter
+        @type value: Choice value
+        @raise UserValueError: if the value is not a string or is not among the
+        list defined in the xml vlist.
+        """
+        paramName = param.getName()
+        value = param.getValue()
+        if param.hasVlist() :
+            authorizedValues = param.getVlistValues()
+        elif param.hasFlist() :
+            authorizedValues = param.getFlistValues()
+        else:
+            msg = "%s a Choice must have a flist or vlist" %( paramName )
+            c_log.error( msg )
+            raise MobyleError , msg     
+        if value is None or value in authorizedValues:
+            return True
+        else:
+            logMsg = "Unauthorized value for the parameter : %s : authorized values = %s : provided value = %s" %( paramName , 
+                                                                                                                  authorizedValues ,
+                                                                                                                  value 
+                                                                                                                  )
+            c_log.error( logMsg )
+            msg = "Unauthorized value for the parameter : %s" %( paramName )
+            raise UserValueError( parameter = param , msg = msg )
+                
+               
+
+class MultipleChoiceDataType( StringDataType ):
+
+        
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):
+        """
+        The MutipleChoiceDataType value are literals thus this method try to cast
+        the value to a list of string.
+        @param value: the values provided by the User for this parameter
+        @type value: list
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        @return: a string based on each selected value and joined by the separator.
+        @rtype: String .
+        @raise UserValueError: if the value can't be converted to a string.
+        """    
+        if value is None:
+            return ( None , acceptedMobyleType )
+        try:
+            values = [ str( elem ) for elem in value ]
+        except ValueError:
+            msg  = "this parameter is a MultipleChoice its all values must be Strings" %value 
+            raise UserValueError( msg = msg )
+        return ( values , acceptedMobyleType )
+    
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt
+    
+    def validate( self, param ):
+        #values are stored as string in evaluator
+        #the string is computed by service.setValue()
+        #the string is return by getValue and used as is in ComandBuilder 
+        userValues = param.getValue() #it's a string
+        sep = param.getSeparator()
+        if sep == '':
+            userValues = [ i for i in userValues ]
+        else:
+            userValues = userValues.split( sep )
+        authorizedValues =  param.getVlistValues()
+        for value in userValues:
+            if value not in authorizedValues :
+                msg = "the value %s is not allowed (allowed values: %s)" % (
+                str( value ) ,
+                str( param.getVlistValues() )
+                )
+                raise UserValueError( parameter = param , msg = msg )
+        return True
+
+
+    
+
+class AbstractFileDataType(DataType):
+    """
+    AbstractFileDataType is an abstract class that centralizes the
+    code which is common to text files (AbstractTextDataType) and
+    binary files (BinaryDataType)
+    """
+
+    def isFile( self ):
+        return True
+
+    @classmethod
+    def supportedFormat( cls ):
+        """
+        @return: the list supported by the format detector
+        @rtype: list of string
+        """
+        uniq_formats = []
+        for converter in _cfg.dataconverter( cls.__name__[:-8] ):
+            for f in converter.detectedFormat():
+                if f not in uniq_formats:
+                    uniq_formats.append( f )
+        return uniq_formats        
+    
+    @classmethod         
+    def supportedConversion( cls ):
+        """
+        @return: the list of dataFormat conversion available.
+        @rtype: list of tuple [ (string input format, string output formt) , ... ]
+        """
+        uniq_conversion = []
+        for converter in _cfg.dataconverter( cls.__name__[:-8] ):
+            for f in converter.convertedFormat():
+                if f not in uniq_conversion:
+                    uniq_conversion.append( f )
+        return uniq_conversion
+
+    def detect( self , value  ):
+        """
+        detects the format of the sequence(s) contained in fileName
+        @param value: the src object and the filename in the src of the data to detect
+        @type value: tuple ( session/Job/MobyleJob instance , string filename )
+        @return: a tuple of the detection run information:
+            - the detected format,
+            - the detected items number,
+            - program name used for the detection.
+        """
+        if value is None:
+            return None
+        if len( value ) == 2 :
+            src , srcFileName = value
+        else:
+            raise MobyleError ,"value must be a tuple of 2 elements: ( Job/MobyleJob/JobState or Session instance , Job/MobyleJob/JobState or Session instance , srcFileName )"
+        if src and not srcFileName :
+            raise MobyleError , "if src is specified, srcFileName must be also specified"
+        absFileName = os.path.join( src.getDir() , srcFileName )
+        
+        all_converters = _cfg.dataconverter( self.__class__.__name__[:-8] )
+        for converter in all_converters:
+            detected_format , seq_nb = converter.detect( absFileName )
+            prg = converter.program_name
+            if detected_format:
+                detected_mt = MobyleType( self , dataFormat = detected_format , format_program = prg , item_nb = seq_nb) 
+                return detected_mt 
+        return MobyleType( self ) #no dataFormat have been detected
+    
+    
+    def convert( self , value ,acceptedMobyleType , detectedMobyleType = None,  paramFile= False):
+        """
+        convert the sequence contain in the file fileName in the rigth format
+        throws an UnsupportedFormatError if the output format is not supported
+        or a MobyleError if something goes wrong during the conversion.
+
+        @param value: is a tuple ( src , srcFileName) 
+          - srcfilename is the name of the file to convert in the src
+          - src must be a L{Job} instance the conversion are perform only by jobs (not session) .
+        @type value: ( L{Job} instance dest, L{Job} instance, src)
+        @return: the fileName ( basename ) of the  sequence file and the effective MobyleType associated to this 
+        value
+        @rtype: ( string fileName , MobyleType instance )
+        @raise UnSupportedFormatError: if the data cannot be converted in any suitable format
+        """
+        if value is None:
+            return None
+        if len( value ) == 2 :
+            src , srcFileName = value
+        else:
+            raise MobyleError ,"value must be a tuple of 2 elements: ( Job/MobyleJob/JobState or Session instance , Job/MobyleJob/JobState or Session instance , srcFileName )"
+        if src and not srcFileName :
+            raise MobyleError , "if src is specified, srcFileName must be also specified"
+        absFileName = os.path.join( src.getDir() , srcFileName )
+        all_converters = _cfg.dataconverter( self.__class__.__name__[:-8] )
+        in_format = detectedMobyleType.getDataFormat()
+       
+        if not all_converters:
+            fileName = safeFileName( absFileName )
+            if detectedMobyleType :
+                mt = detectedMobyleType
+            else:
+                mt = MobyleType( self )
+            #filename is a basename
+            return ( fileName , mt )
+        else:
+            def unknow_converter( in_format , converters ):
+                result = None 
+                for converter in converters:
+                    try:
+                        result = fixed_converter( in_format , converter )
+                    except UnSupportedFormatError:
+                        continue
+                    if result is not None:
+                        break
+                #if I cannot find any suitable converter result is None    
+                return result
+                
+            def fixed_converter( in_format ,  converter ):
+                allowed_conversions = converter.convertedFormat()
+                for out_format , force in acceptedMobyleType.getAcceptedFormats():
+                    if ( in_format , out_format ) in allowed_conversions:
+                        try:
+                            outFileName = converter.convert( absFileName , out_format , inputFormat = in_format )
+                            outFileName = os.path.basename( outFileName )
+                        except UnSupportedFormatError, err:
+                            raise UnSupportedFormatError( msg = "a problem occurred during the data conversion : "+str( err ) ) 
+                        converted_mt = MobyleType( self  , dataFormat = out_format , format_program = converter.program_name )
+                        return ( outFileName , converted_mt )
+                #it's not a suitable converter
+                return None
+            if detectedMobyleType.format_program is None:
+                result = unknow_converter( in_format , all_converters )
+            else:
+                for converter in all_converters:
+                    if converter.program_name == detectedMobyleType.format_program :
+                        detector_used = converter
+                        break
+                if detector_used is None:
+                    raise MobyleError( "unable to find %s converter" % detectedMobyleType.format_program )
+                result  = fixed_converter( in_format , detector_used )
+                if result is None:# the detector_used is not suitable for the conversion
+                    result= unknow_converter( in_format , [ c for c in all_converters if c != detector_used ] )
+            if result is None:
+                raise UnSupportedFormatError( "unable to convert %s in %s sequence format" %( in_format , 
+                                                                                              [ f[0] for f in acceptedMobyleType.getAcceptedFormats() ] 
+                                                                                               ) )
+            else:
+                return result #( outFileName , converted_mt )
+            
+ 
+    def validate( self , param ):
+        """
+        """
+        value = param.getValue()
+        if param.isout():
+            if value is not None : #un parametre isout ne doit pas etre modifier par l'utilisateur 
+                return False
+            else:
+                #####################################################
+                #                                                   #
+                #  check if the Parameter have a secure filenames   #
+                #                                                   #
+                #####################################################
+                try:
+                    debug = param.getDebug()
+                    if debug > 1:
+                        b_log.debug( "check if the Parameter have a secure filename" )
+                    #getFilenames return a list of strings representing a unix file mask which is the result of a code evaluation
+                    #getFilenames return None if there is no mask for a parameter.
+                    filenames = param.getFilenames( ) 
+                    for filename in filenames :
+                        if filename is None:
+                            continue
+                        mask = safeMask( filename )
+                        if debug > 1:
+                            b_log.debug( "filename= %s    safeMask = %s"%(filename, mask))
+                        if  not mask or mask != filename :
+                            msg = "The Parameter:%s, have an unsecure filenames value: %s " %( param.getName() ,
+                                                                                                filename )
+                            c_log.error("Mobyle Internal Server Error")
+                            if debug == 0:
+                                c_log.critical( "%s : %s : %s" %( self._service.getName(),
+                                                                       self._job.getKey() ,
+                                                                       msg          
+                                                                       )
+                                                                       )
+                            raise MobyleError , "Mobyle Internal Server Error" 
+                        else:
+                            if debug > 1:
+                                b_log.debug( "filename = %s ...........OK" % filename )
+                except UnDefAttrError :
+                    b_log.debug("no filenames")
+        else:
+            if value is None:
+                #un infile Text ne peut pas avoir de vdef mais peut il etre a None?
+                #=> oui s'il n'est pas obligatoire
+                return True
+            else:
+                return os.path.exists( param.getValue() )
+
+
+
+
+
+class AbstractTextDataType( AbstractFileDataType ):
+
+    def head( self , data ):
+        return data[ 0 : 50 ]
+    
+    def cleanData( self , data ):
+        """
+        convert the data in right encoding and replace windows end of line by unix one.
+        """
+        # trying to guess the encoding, before converting the data to ascii
+        try:
+            # trying ascii
+            data = unicode(data.decode('ascii','strict'))
+        except:
+            try:
+                # utf8 codec with BOM support
+                data = unicode(data,'utf_8_sig')
+            except:
+                try:
+                    # utf16 (default Windows Unicode encoding)
+                    data = unicode(data,'utf_16')
+                except:
+                    # latin1
+                    data = unicode(data,'latin1')
+        # converting the unicode data to ascii
+        data = data.encode('ascii','replace')
+        return  re.sub( "\r\n|\r|\n" , '\n' , data )
+
+
+    def toFile( self , data  , dest , destFileName , src , srcFileName ):
+        """
+        Write file (of user data) in dest directory .
+        @param fileName:
+        @type fileName: string
+        @param data: the content of the file
+        @type data: string
+        @param dest: the object in which the data will be copied
+        @type dest: Job, Session object
+        @param src: the object where the data can be found
+        @type src: Job, Session object
+        @param srcFileName: the file namae of the data in the src ( basename )
+        @type srcFileName: string
+        @return: the name of the created file (the basename)
+        @rtype: string
+        @raise: L{UserValueError} when filename is not allowed (for security reason)
+        @raise: L{MobyleError} if an error occured during the file creation
+        """
+        try:
+            destSafeFileName = safeFileName( destFileName )            
+        except UserValueError, err:
+            raise UserValueError( msg = "this value : %s is not allowed for a file name, please change it" % destFileName )
+        abs_DestFileName= os.path.join( dest.getDir() , destSafeFileName )
+        # if the user upload 2 files with the same basename safeFileName
+        # return the same safeFileName
+        # add an extension to avoid _toFile erase the existing file.
+        ext = 1
+        completeName = abs_DestFileName.split( '.' )
+        base = completeName[0]
+        suffixe = '.'.join( completeName[1:] )
+        while os.path.exists( abs_DestFileName ):
+            abs_DestFileName = base + '.' + str( ext ) + '.' + suffixe
+            ext = ext + 1
+        if src:
+            if src.isLocal():
+                try:
+                    srcSafeFileName = safeFileName( srcFileName )
+                except UserValueError, err:
+                    raise UserValueError( msg = "this value : %s is not allowed for a file name, please change it" % srcFileName )
+                #the realpath is because if the abs_SrcFileName is a soft link ( some results are ) the
+                # hardlink point to softlink and it causse ane error : no such file 
+                abs_SrcFileName = os.path.realpath( os.path.join( src.getDir() , srcSafeFileName ) )
+                try:
+                    os.link(  abs_SrcFileName , abs_DestFileName )
+                except OSError :
+                    #if the src and dest are not on the same device
+                    #an OSError: [Errno 18] Invalid cross-device link , is raised
+                    try:
+                        shutil.copy( abs_SrcFileName , abs_DestFileName )
+                    except IOError ,err:
+                        # I don't know  - neither the service ( if it exists )
+                        #               - nor the job or session ID 
+                        # I keep the Job or the Session to log this error 
+                        msg = "can't copy data from %s to %s : %s" %( abs_SrcFileName ,
+                                                                      abs_DestFileName ,
+                                                                      err )
+                        c_log.error( msg )
+                        raise MobyleError , "can't copy data : "+ str(err)
+            else: #src is a job , jobState , MobyleJob instance ( Session is always Local )
+                data = src.getOutputFile( srcFileName )
+                try:
+                    f = open( abs_DestFileName , 'w' )
+                    f.write( data )
+                    f.close()
+                except IOError ,err:
+                    msg = "unable to write data from %s to %s: %s"%(src , dest , err )
+                    c_log.error( msg )
+                    raise MobyleError , msg
+        else:
+            try:
+                clean_content = self.cleanData( data )
+            except Exception, err :
+                msg = "error when cleaning file : " + abs_DestFileName + str( err )
+                c_log.critical( msg , exc_info= True )
+                raise MobyleError , msg
+            try:
+                fh = open( abs_DestFileName , "w" )
+                fh.write( clean_content )
+                fh.close()
+            except IOError , err:
+                msg = "error when creating file : " + abs_DestFileName + str( err )
+                raise MobyleError , msg
+        size = os.path.getsize( abs_DestFileName )
+        return os.path.basename( abs_DestFileName ) , size
+
+
+
+class TextDataType( AbstractTextDataType ):
+    # this trick is to avoid that SequenceDataType is a subclass of TextDataType
+    pass
+
+
+class ReportDataType( AbstractTextDataType ):
+    pass
+
+
+class BinaryDataType( AbstractFileDataType ):
+
+    def head( self , data ):
+        return 'Binary data'
+            
+    def cleanData( self, data ):
+        """
+        prepare data prior to write it on a disk
+        @param data: 
+        @type data:a buffer
+        """
+        return data
+
+    def toFile( self , data , dest , destFileName , src , srcFileName ):
+        """
+        Write file (of user data) in the working directory .
+        @param fileName:
+        @type fileName: string
+        @param content: the content of the file
+        @type content: string
+        @return: the name ( absolute path ) of the created file ( could be different from the arg fileName )
+        @rtype: string
+        @call: L{MobyleJob._fillEvaluator}
+        """
+        try:
+            destSafeFileName = safeFileName( destFileName )
+        except UserValueError, err:
+            raise UserValueError( msg = "this value : %s is not allowed for a file name, please change it" % destFileName )
+        abs_DestFileName = os.path.join( dest.getDir() , destSafeFileName )
+
+        # if the user upload 2 files with the same basename safeFileName
+        # return the same safeFileName
+        # add an extension to avoid _toFile to erase the existing file.
+        ext = 1
+        completeName = abs_DestFileName.split( '.' )
+        base = completeName[0]
+        suffixe = '.'.join( completeName[1:] )
+        while os.path.exists( abs_DestFileName ):
+            abs_DestFileName = base + '.' + str( ext ) + '.' + suffixe
+            ext = ext + 1
+        if src:
+            if src.isLocal():
+                try:
+                    srcSafeFileName = safeFileName( srcFileName )
+                except UserValueError, err:
+                    raise UserValueError( msg = "this value : %s is not allowed for a file name, please change it" % srcFileName  )
+                
+                #the realpath is because if the abs_SrcFileName is a soft link ( some results are ) the
+                #hardlink point to softlink and it cause an error : no such file  
+                abs_SrcFileName = os.path.realpath( os.path.join( src.getDir() , srcSafeFileName ) )
+                try:
+                    os.link(  abs_SrcFileName , abs_DestFileName )
+                except OSError :
+                    #if the src and dest are not on the same device
+                    #an OSError: [Errno 18] Invalid cross-device link , is raised
+                    try:
+                        shutil.copy( abs_SrcFileName , abs_DestFileName )
+                    except IOError ,err:
+                        # je ne connais - ni le service (s'il existe)
+                        #               - ni le l' ID du job ou de la session
+                        # donc je laisse le soin au Job ou la session a logger l'erreur
+    
+                        msg = "can't copy data from %s to %s : %s" %( abs_SrcFileName ,
+                                                                      abs_DestFileName ,
+                                                                      err )
+                        raise MobyleError , "can't copy data : "+ str(err)
+            else: #src is a job , jobState , MobyleJOb instance ( session is Local )
+                data = src.getOutputFile( srcFileName )
+                try:
+                    f = open( abs_DestFileName , 'wb' )
+                    f.write( data )
+                    f.close()
+                except IOError ,err:
+                    pass
+        else:
+            try:
+                fh = open( abs_DestFileName , "wb" )
+                fh.write( data )
+                fh.close()
+            except IOError , err:
+                # je ne connais - ni le service (s'il existe)
+                #               - ni le l' ID du job ou de la session
+                # donc je laisse le soin au Job ou la session a logger l'erreur
+                msg = "error when creating file %s: %s" %( os.path.basename( abs_DestFileName ) ,  err )
+                raise MobyleError , msg
+        size  = os.path.getsize( abs_DestFileName )
+        return os.path.basename( abs_DestFileName ) , size
+    
+class FilenameDataType( DataType ):
+ 
+    def convert( self , value , acceptedMobyleType , detectedMobyleType = None , paramFile= False ):  
+        """
+        @param acceptedMobyleType: the MobyleType  accepted by this service parameter 
+        @type acceptedMobyleType: L{MobyleType} instance
+        @param detectedMobyleType: the MobyleType discribing this data
+        @type detectedMobyleType: L{MobyleType} instance
+        """  
+        if value is None:
+            return ( None , acceptedMobyleType )
+            #raise UserValueError( parameter = param , msg= " this parameter must be a String" )
+        fileName = safeFileName( value )
+        return ( fileName , acceptedMobyleType )
+    
+    def detect( self , value ):
+        mt = MobyleType( self )
+        return mt    
+    
+    def validate( self, param ):
+        value = param.getValue()
+        if value is None :
+            return True
+        safefileName = safeFileName( value )
+        if safefileName != value:
+            msg = "invalid value: %s : the followings characters \:/ ;` {} are not allowed" %( value )
+            raise UserValueError( parameter = param , msg = msg )
+        else:
+            return True
+
+
+    
+        
+
diff --git a/Src/Mobyle/Classes/DataType.py b/Src/Mobyle/Classes/DataType.py
new file mode 100644
index 0000000..7f0d47d
--- /dev/null
+++ b/Src/Mobyle/Classes/DataType.py
@@ -0,0 +1,280 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+
+"""
+
+import os , os.path , re
+import types
+from logging import getLogger
+c_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError
+
+
+class DataType( object ):
+
+    def __init__( self , name = None ):
+        if name:
+            self.name = name
+        else:
+            self.name = self.__class__.__name__[0:-8]
+        
+        self.ancestors = [ k.__name__[0:-8] for k in self.__class__.mro() ][0:-2]
+
+        if self.name not in self.ancestors :
+            self.ancestors.insert( 0 , self.name )
+        
+            
+    def isPipableToDataType( self , targetDataType ):
+        return targetDataType.name in self.ancestors
+
+    def getName( self ):
+        return self.name
+    
+    def getRealName(self):
+        return self.__class__.__name__[0:-8]
+    
+    def isFile( self ):
+        return False
+    
+    def isMultiple(self):
+        return False
+    
+    def toDom( self ):
+        """
+        @return: a dom representation of this datatype
+        @rtype: element
+        """
+        from lxml import etree        
+        
+        if self.name == self.__class__.__name__[0:-8] :
+            klass = self.name
+            superKlass = None 
+        else:
+            klass = self.name
+            superKlass = self.__class__.__name__[0:-8]
+            
+        dataTypeNode = etree.Element( "datatype" )
+        klassNode = etree.Element( "class" )
+        klassNode.text =  klass 
+        dataTypeNode.append( klassNode )
+        if superKlass :
+            superKlassNode = etree.Element( "superclass" )
+            superKlassNode.text =  superKlass 
+            dataTypeNode.append( superKlassNode )
+        return dataTypeNode 
+
+    def __eq__(self , other ):
+        return self.ancestors == other.ancestors
+    
+    def __str__(self):
+        return self.name
+    
+
+class MultipleDataType( object ):
+    """
+    This is a container to handle several files with the same datatype.
+    In the Session these data will see independently as X data with datatype Y.
+    It cannot handle a group of file which must be manage as one data.
+    """
+    
+    def __init__(self , dt , name = None ):
+        """
+        @param name: the name of the datatype as it specified in class when a superclass is specified too 
+        @type name: string
+        """
+        self.dataType = dt
+        if name:
+            self.name = name
+        else:
+            #self.name = self.__class__.__name__[0:-8]+self.dataType.getName()
+            self.name= "Multiple"+self.dataType.getName()
+        
+        self.ancestors = [ k.__name__[0:-8] for k in self.__class__.mro() ][0:-2]
+        if self.name not in self.ancestors :
+            self.ancestors.insert( 0 , self.name )
+        
+    def isPipableToDataType(self , targetDataType):
+        return self.dataType.isPipableToDataType( targetDataType )
+    
+    def getName(self):
+        return self.name
+    
+    def getRealName(self):
+        return self.__class__.__name__[:-8]+self.dataType.__class__.__name__[:-8]
+    
+    def __str__(self):
+        return self.name
+    
+    def isFile(self):
+        return True
+    
+    def isMultiple(self):
+        return True
+    
+    def toDom(self):
+        return self.dataType.toDom()
+
+    def toFile( self, data, dest, destFileName, src, srcFileName):
+        return self.dataType.toFile(data  , dest , destFileName , src , srcFileName)
+    
+    def head( self , data ):
+        return "a definir"
+    
+    def cleanData( self , data ):
+        """
+        convert the data in right encoding and replace windows end of line by unix one.
+        """
+        return self.dataType.cleanData(data)
+        
+    def supportedFormat( self ):
+        """
+        @return: the list supported by the format detector
+        @rtype: list of string
+        """
+        #est ce obligatoire d'etre une methode de class
+        #ici on devrait appler la methode supportedFormat du type que l'on wrap???
+        return self.dataType.supportedFormat()
+    
+    def supportedConversion( self ):
+        """
+        @return: the list of dataFormat conversion available.
+        @rtype: list of tuple [ (string input format, string output formt) , ... ]
+        """
+        #est ce obligatoire d'etre une methode de class
+        #ici on devrait appler la methode supportedConversion du type que l'on wrap???
+        return self.dataType.supportedConversion()
+        
+    def detect( self , value  ):
+        """
+        detects the format of the sequence(s) contained in fileName
+        @param value: the src object and the filename in the src of the data to detect
+        @type value: tuple ( session/Job/MobyleJob instance , string filename )
+        @return: a tuple of the detection run information:
+            - the detected format,
+            - the detected items number,
+            - program name used for the detection.
+        """
+        return self.dataType.detect(value)
+   
+    def convert( self, value ):
+        #value, acceptedMobyleType, detectedMobyleType = None,  paramFile= False
+        """
+        convert the sequence contain in the file fileName in the rigth format
+        throws an UnsupportedFormatError if the output format is not supported
+        or a MobyleError if something goes wrong during the conversion.
+
+        @param values: is a list of tuple ( src , srcFileName) 
+          - srcfilename is the name of the file to convert in the src
+          - src must be a L{Job} instance the conversion are perform only by jobs (not session) .
+        @type value: ( L{Job} instance dest, L{Job} instance, src)
+        @return: the fileName ( basename ) of the  sequence file and the effective MobyleType associated to this 
+        value
+        @rtype: list of tuple [ ( string fileName , MobyleType instance ), ...]
+        @raise UnSupportedFormatError: if the data cannot be converted in any suitable format
+        """            
+        return self.dataType.convert(value)
+
+    def validate( self , param ):
+        """
+        """  
+        values = param.getValue()
+        sep = param.getSeparator()
+        values= values.split( sep )
+        newParam = param.clone( self.dataType )
+        for value in values:
+            newParam.setValue( value )
+            try:
+                valid = newParam.validate()
+            except  UserValueError ,err:
+                raise UserValueError( parameter = param , msg = str(err) )
+        return True   
+            
+      
+class DataTypeFactory( object ):
+    
+    _ref = None
+    
+    def __new__( cls ):
+        if cls._ref is None:
+            cls._ref = super( DataTypeFactory , cls ).__new__( cls )
+        return cls._ref
+    
+    def __init__(self):
+        self.definedDataTypes = {}
+    
+    def newDataType( self, pythonName , xmlName = None):
+        realName = xmlName or pythonName
+        try:
+            realName = realName + "DataType"
+        except TypeError:
+                raise MobyleError , "the argument \"name\" must be a string ( %s received )" %str( type( realName ))     
+            
+        if realName in self.definedDataTypes:
+            dt = self.definedDataTypes[ realName ]
+            if( pythonName != dt.getRealName() ):
+                c_log.error("consistency error:")
+                raise MobyleError , "consistency error: a \"%s\" is already defined with python type \"%s\" instead of \"%s\"" %( dt.getName() ,
+                                                                                                                                  dt.getRealName() ,
+                                                                                                                                  realName[:-8]
+                                                                                                                                 )
+            return dt   
+        else:
+            import Local.CustomClasses
+            import Mobyle.Classes
+            def localDataType( python_name ):
+                
+                if python_name + "DataType" in dir( Local.CustomClasses ):
+                    return "Local.CustomClasses.%sDataType"%python_name
+            def coreDataType( python_name ):
+                if python_name + "DataType" in dir( Mobyle.Classes ):
+                    return "Mobyle.Classes.%sDataType" %python_name
+            
+            fulldts = localDataType( pythonName ) or coreDataType( pythonName )
+            if fulldts:
+                self.definedDataTypes[ realName ] = eval("%s('%s')" % (fulldts,xmlName or ''))
+                return self.definedDataTypes[ realName ] #xmlname
+            elif( pythonName.startswith( "Multiple") ):
+                inside_name = pythonName[8:] 
+                fulldts = localDataType(inside_name) or coreDataType(inside_name)
+                if fulldts:
+                    dt = eval( fulldts+"()" )
+                    if xmlName:
+                        mdt = MultipleDataType( dt , name = xmlName )
+                    else:
+                        mdt = MultipleDataType( dt )
+                    self.definedDataTypes[ realName ] = mdt
+                    return  mdt
+                else:
+                    raise MobyleError , "invalid DataType : %s" %( pythonName )
+            else:
+                raise MobyleError , "invalid DataType : %s" %( pythonName )
+                
+            
+            
+    def issubclass(self , dataType1 , name2 , xmlName2= None ):
+        """
+        @param dataType1: the dataType to test
+        @type dataType1: instance of a Datatype
+        @param name2: the value of element superclass or class if there is no superclass
+        @type name2:  string
+        @param xmlName2: the value of element class when the element superclass is specify
+        @type xmlName2:  string
+        @return: True if dataType1 is an instance of the datatype represente by name2 , xmlName2. False otherwise  
+        @rtype: boolean
+        """
+        dataType2 = self.newDataType( name2 , xmlName= xmlName2 )
+        try:
+            return  issubclass( dataType1.__class__ , dataType2 .__class__ )
+        except AttributeError , err :
+            raise TypeError , "there is no DataType named "+ str( name2 )
+    
+
+
diff --git a/Src/Mobyle/Classes/Sequence.py b/Src/Mobyle/Classes/Sequence.py
new file mode 100644
index 0000000..86f8263
--- /dev/null
+++ b/Src/Mobyle/Classes/Sequence.py
@@ -0,0 +1,106 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+Content the Mobyle Parameter types related to the genomic bioloy
+"""
+ 
+
+from logging import getLogger
+c_log = getLogger(__name__)
+b_log = getLogger('Mobyle.builder')
+
+from Mobyle.MobyleError import MobyleError , UnDefAttrError 
+from Mobyle.Classes.Core import AbstractTextDataType , safeMask
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+
+class SequenceDataType( AbstractTextDataType ):
+    
+    def detect( self , value  ):
+        """
+        detects the format of the sequence(s) contained in fileName
+        @param value: the src object and the filename in the src of the data to detect
+        @type value: tuple ( session/Job/MobyleJob instance , string filename )
+        @return: a tuple of the detection run information:
+            - the detected format,
+            - the detected items number,
+            - program name used for the detection.
+        """
+        detected_mt = super( SequenceDataType , self ).detect( value )
+        squizzDir = _cfg.format_detector_cache_path()
+        if squizzDir :
+            ##############################################
+            # This part of is used to back up all        # 
+            # submitted sequences which are not detected # 
+            # by squizz for further analysis             #
+            ##############################################
+            squizz_detector = None
+            for detector in _cfg.dataconverter( self.__class__.__name__[:-8] ):
+                if detector.program_name == 'squizz':
+                    squizz_detector = detector
+                    break
+            if squizz_detector is not None :
+                detected_data_format = detected_mt.getDataFormat()
+                from os import link
+                from os.path import join as os_join
+                if ( detected_data_format is None ) or ( detected_data_format in squizz_detector.detectedFormat() and not detected_mt.getFormatProgram() == 'squizz' ):
+                    try:
+                        #dump the data to further annalysis 
+                        link(  os_join( value[0].getDir() , value[1] ) , 
+                               os_join( squizzDir , "%s_%s_%s"%( self.__class__.__name__[:-8] ,
+                                                                 value[0].getKey() ,
+                                                                 value[1] ) )
+                               )
+                    except Exception , err : 
+                        c_log.error( "unable to link data in  format_detector_cache_path : %s " % err ) 
+        return detected_mt
+    
+ 
+    def validate( self , param ):
+        """
+        """
+        value = param.getValue()
+
+        if param.isout():
+            if value is not None : #un parametre isout ne doit pas etre modifier par l'utilisateur 
+                return False
+            else:
+                #####################################################
+                #                                                   #
+                #  check if the Parameter have a secure filenames   #
+                #                                                   #
+                #####################################################
+                try:
+                    debug = param.getDebug()
+                    if debug > 1:
+                        b_log.debug( "check if the Parameter have a secure filename" )
+    
+                    #getFilenames return list of strings representing a unix file mask which is the result of a code evaluation
+                    #getFilenames return None if there is no mask for a parameter.
+                    filenames = param.getFilenames( ) 
+                    for filename in filenames :
+                        if filename is None:
+                            continue
+                        mask = safeMask( filename )
+                        if debug > 1:
+                            b_log.debug( "filename= %s    safeMask = %s"%(filename, mask))
+                        if  not mask or mask != filename :
+                            raise MobyleError , "have an unsecure filenames value before safeMask: %s , after safeMask: %s"%( filename , mask )
+                        else:
+                            if debug > 1:
+                                b_log.debug( "filename = %s ...........OK" % filename )
+                                               
+                except UnDefAttrError :
+                    b_log.debug("no filenames")
+        else:#the param is an inFile
+            if value is None:
+                return True
+            else:
+                return True
diff --git a/Src/Mobyle/Classes/Structure.py b/Src/Mobyle/Classes/Structure.py
new file mode 100644
index 0000000..89eef57
--- /dev/null
+++ b/Src/Mobyle/Classes/Structure.py
@@ -0,0 +1,60 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+"""
+Content the Mobyle Parameter types related to the structural biology
+"""
+ 
+
+from Mobyle.MobyleError import MobyleError , UserValueError , UnDefAttrError
+from Mobyle.Classes.Core import *
+
+
+
+
+class StructureDataType(DataType):
+    
+    @staticmethod
+    def convert( value , param ):
+        """
+        Do the general control and cast the value in the right type.
+        if a control or the casting fail a MobyleError is raised.
+        
+        @param value: the value provide by the User for this parameter
+        @type value: String
+        @return: 
+        """
+        raise NotImplementedError ,"ToDo"
+    
+    @staticmethod
+    def validate( param ):
+        raise NotImplementedError ,"ToDo"
+        
+
+class PropertiesDataType(DataType):
+    
+    @staticmethod
+    def convert( value , param ):
+        """
+        Do the general control and cast the value in the right type.
+        if a control or the casting fail a MobyleError is raised.
+        
+        @param value: the value provide by the User for this parameter
+        @type value: String
+        @return: 
+        """
+        raise NotImplementedError ,"ToDo"
+    
+    
+    @staticmethod    
+    def validate( param ):
+        raise NotImplementedError ,"ToDo"
+
+
+
+    
diff --git a/Src/Mobyle/Classes/Tree.py b/Src/Mobyle/Classes/Tree.py
new file mode 100644
index 0000000..c97d478
--- /dev/null
+++ b/Src/Mobyle/Classes/Tree.py
@@ -0,0 +1,17 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+"""
+Content the Mobyle Parameter types related to the genomic bioloy
+"""
+
+from Mobyle.Classes.Core import AbstractTextDataType 
+
+class TreeDataType( AbstractTextDataType ):
+    #todo
+    pass
diff --git a/Src/Mobyle/Classes/__init__.py b/Src/Mobyle/Classes/__init__.py
new file mode 100644
index 0000000..34476a1
--- /dev/null
+++ b/Src/Mobyle/Classes/__init__.py
@@ -0,0 +1,20 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+from Mobyle.Classes.DataType import DataType , DataTypeFactory
+from Mobyle.Classes.Core import BinaryDataType  , BooleanDataType  , FloatDataType
+from Mobyle.Classes.Core import StringDataType  , ChoiceDataType   , MultipleChoiceDataType
+from Mobyle.Classes.Core import IntegerDataType , FilenameDataType , TextDataType , ReportDataType
+from Mobyle.Classes.Core import AbstractTextDataType
+
+from Mobyle.Classes.Sequence import SequenceDataType
+from Mobyle.Classes.Alignment import AlignmentDataType
+from Mobyle.Classes.Tree import  TreeDataType
+
+from Mobyle.Classes.Structure import StructureDataType , PropertiesDataType
+
diff --git a/Src/Mobyle/ClassificationIndex.py b/Src/Mobyle/ClassificationIndex.py
new file mode 100644
index 0000000..9c4a7ee
--- /dev/null
+++ b/Src/Mobyle/ClassificationIndex.py
@@ -0,0 +1,78 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+from Mobyle.Registry import CategoryDef, registry, ServiceTypeDef
+from logging import getLogger
+r_log = getLogger(__name__)
+from Mobyle import IndexBase
+
+queries = {
+           # head//category adds the package categories to potential results
+           'categories': '/*/head//category',
+           'categories_text': 'text()',
+           'package': '/*/head/package/name/text()',
+           'service_type': 'name(/*)',
+          }
+
+class ClassificationIndex(IndexBase.Index):
+    
+    indexFileName = 'classification.dat'
+
+    def buildRegistryCategories(self, field='category',serviceTypeSort='separate'):
+        """
+        Builds the categories hierarchy from
+        the service definitions, using the categories information
+        """
+        for s in getattr( registry, self.type + 's'):
+            if (not(s.disabled) and (s.authorized) and self.index.has_key(s.url)):
+                try:
+                    cats = self.index[s.url][field]
+                    if len(cats)==0: # display services with no "category" as root
+                        if serviceTypeSort=='separate':
+                            s.server.addDescendant([ServiceTypeDef(self.index[s.url]['service_type'])]+[s])
+                            registry.addDescendant([ServiceTypeDef(self.index[s.url]['service_type'])]+[s])
+                        else:
+                            s.server.addDescendant([ServiceTypeDef('Services')]+[s])
+                            registry.addDescendant([ServiceTypeDef('Services')]+[s])                            
+                    for cn in cats:# for each classification of the program
+                        lpath = []
+                        gpath = []
+                        cs = [c for c in cn.split(':') if c!=''] #''=no category (root-level)
+                        #per-server classification setup
+                        for c in cs:
+                            lpath.append(CategoryDef(c))
+                        lpath.append(s)
+                        #global classification setup
+                        for c in cs:
+                            gpath.append(CategoryDef(c))
+                        if serviceTypeSort=='separate':
+                            s.server.addDescendant([ServiceTypeDef(self.index[s.url]['service_type'])]+lpath+[s])
+                            registry.addDescendant([ServiceTypeDef(self.index[s.url]['service_type'])]+gpath+[s])
+                        else:
+                            s.server.addDescendant([ServiceTypeDef('Services')]+lpath+[s])
+                            registry.addDescendant([ServiceTypeDef('Services')]+gpath+[s])                            
+                except Exception:
+                    r_log.error("Error while loading classification for program %s" % s.name, exc_info=True)
+                    continue
+
+    @classmethod
+    def getIndexEntry(cls, doc, program):
+        """
+        Return an classification index entry value
+        @return: the index entry: value
+        @rtype: object
+        """
+        cats = IndexBase._XPathQuery(doc, queries['categories'], 'rawResult')
+        categories=[]
+        for cat in cats:
+            categories.append(IndexBase._XPathQuery(cat, queries['categories_text'], 'valueString'))
+        package = IndexBase._XPathQuery(doc, queries['package'], 'valueList')
+        service_type = IndexBase._XPathQuery(doc, queries['service_type'], 'rawResult').capitalize()+'s'
+        return {'package':package,'category':categories,'service_type':service_type}
\ No newline at end of file
diff --git a/Src/Mobyle/CommandBuilder.py b/Src/Mobyle/CommandBuilder.py
new file mode 100644
index 0000000..6482cec
--- /dev/null
+++ b/Src/Mobyle/CommandBuilder.py
@@ -0,0 +1,275 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+""" 
+Build the command from the parameters chosen by the users
+"""
+
+import os
+from StringIO import StringIO
+
+from Mobyle.MobyleError import MobyleError
+from logging import getLogger
+c_log = getLogger(__name__)
+b_log = getLogger( 'Mobyle.builder' )
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+__extra_epydoc_fields__ = [( 'call', 'Called by','Called by' )]
+
+
+
+
+
+class CommandBuilder:
+    """
+    This class create the command from the parameters chosen by the users
+    3 main methods exist
+      - buildLocalCommand: to build a unix command line for a local job
+      - buildCGI : to build the url, to invoke a cgi
+      - buildWS : to build the ,to call a WebService
+     """
+
+    def __init__( self , job_dir = None ):
+        self._commandLine = "" 
+        self._paramfileHandles = {} # the key is the filename,
+                                    # the value is the StrinIO file object like
+        
+    def buildLocalCommand( self, service ):
+        """
+        Build a unix command line from a - L{Service} instance
+        @param service: the service  which correspond to the programm asked by the user
+        @type service: a - L{Service} instance
+        @return: a String representing the command line
+        """
+        debug = _cfg.debug( service.getName() )
+        commandIsInserted = False
+        commandParameterName = service.getCommandParameterName()
+        if commandParameterName:
+            commandPos = service.getArgpos( commandParameterName )
+        else:
+            commandPos = 0
+
+        if debug > 1:
+            b_log.debug( """\n
+            \t#####################################################
+            \t#                                                   # 
+            \t#               command line building               #
+            \t#                                                   #
+            \t#####################################################
+            \n""" )
+            
+        myEvaluator = service.getEvaluator()
+            
+        for parameter in service.getAllParameterByArgpos():
+            paramName = parameter.getName()
+            if debug > 1:
+                b_log.debug( "--------------- " + paramName + " ---------------")
+                b_log.debug( "commandIsInserted " + str( commandIsInserted ) )
+                b_log.debug( "service.getArgpos( paramName ) " + str( parameter.getArgpos()))
+            if not commandIsInserted and parameter.getArgpos() >= commandPos:
+                if parameter.iscommand():
+                    #the command from parameter is priority vs command
+                    #the command comes from this parameter
+                    if debug > 1 :
+                        b_log.debug( "self._commandLine service.iscommand " + self._commandLine )                            
+                    commandIsInserted = True
+                else:
+                    #I insert the command from command tag in commandLine
+                    if debug > 1:
+                        b_log.debug( "self._commandLine = " + self._commandLine )
+
+                    self._commandLine += " " + service.getCommand()[0]
+                    commandIsInserted = True
+                    if debug > 1:
+                        b_log.debug( "self._commandLine+ command = " + self._commandLine )
+                    
+            #set "vdef" and "value" in the protected namespace (evaluator) 
+            rawVdef = parameter.getVdef()
+            
+            if rawVdef is None:
+                myEvaluator.setVar( 'vdef' , None )
+                convertedVdef = None #TODO a suprimmer qund b_log renove
+            else:
+                convertedVdef , mt = parameter.convert(rawVdef , parameter.getType() )
+                myEvaluator.setVar( 'vdef' , convertedVdef )
+                                    
+            if debug > 1:
+                b_log.debug( "rawVdef = " + str( rawVdef ) )
+                b_log.debug( "convertedVdef = " + str( convertedVdef ) )
+                b_log.debug( "myEvaluator.setVar( 'vdef' , "+ str( convertedVdef ) +" )")
+
+            if ( myEvaluator.isDefined( paramName ) ) :
+                # be careful we can't use the test: if servive.getValue(),
+                # because, the value could be fill with False.
+                # thus we must test if the value exist or not, and not test the value itself!
+                myEvaluator.setVar( 'value', parameter.getValue( ) )
+                if debug > 1:
+                    b_log.debug( "myEvaluator.isDefined( " + paramName + " ) = True" )
+                    b_log.debug( "myEvaluator.setVar( 'value' ,"+ str( parameter.getValue() ) + " )" )
+            else:
+                myEvaluator.setVar( 'value' , convertedVdef )
+                
+                if debug > 1:
+                    b_log.debug( "myEvaluator.isDefined( " + paramName + " ) = False" )
+                    b_log.debug( "rawVdef = " + str( rawVdef ) )
+                    b_log.debug( "convertedVdef = " + str( convertedVdef ) ) 
+                    b_log.debug( "myEvaluator.setVar( 'value' , " + str( convertedVdef ) + " )" ) 
+
+            if parameter.precondHas_proglang( 'python' ):
+                if debug > 1:
+                    b_log.debug("precondHas_proglang( "+ paramName +" , 'python' ) = True")
+                allPrecondTrue = True
+                preconds = parameter.getPreconds( proglang='python' )
+                
+                for precond in preconds:
+                    if not  myEvaluator.eval( precond ):
+                        if debug > 1:
+                            b_log.debug("eval( "+ precond  +" ) = False")
+                        
+                        allPrecondTrue = False
+                        break
+                    else:
+                        if debug > 1:
+                            b_log.debug("eval( "+ precond  +" ) = True")
+                if not allPrecondTrue :
+                    continue #next parameter
+                
+            if parameter.formatHas_proglang( 'python' ):
+                if debug > 1:
+                    b_log.debug("service.formatHas_proglang( "+ paramName +" , 'python' ) = True")
+                format =  parameter.getFormat( 'python' )
+
+            else:
+                value = myEvaluator.getVar( 'value' )
+                if value is not None :
+                    if parameter.flistHas_proglang( value , 'python' ) :
+
+                        if debug > 1:
+                            b_log.debug("service.flistHas_proglang( "+ paramName +" , "+ str( value ) + " , 'python' ) = True")
+
+                        format = parameter.getFlistCode( value , 'python' )
+                    else:
+                        format = None
+                else:
+                    format = None
+
+            if debug > 1:
+                b_log.debug( "value = " + str( myEvaluator.getVar( 'value' )) + "  type = "+ str(type( myEvaluator.getVar( 'value' ) ) ) )
+                b_log.debug( "vdef = " + str( myEvaluator.getVar( 'vdef' )) + "  type = "+ str(type( myEvaluator.getVar( 'value' ) ) )   )
+                b_log.debug(" format = " + str( format ) )
+                
+            if format :
+                if parameter.hasParamfile():
+                    #the Parameter.setParamfile method had already trim the spaces
+                    paramfileName = parameter.getParamfile()
+                    if paramfileName:
+                        if self._paramfileHandles.has_key( paramfileName ):
+                            paramfileHandle = self._paramfileHandles[ paramfileName ]
+                        else:
+                            try:
+                                paramfileHandle = self.openParamFile( paramfileName )
+                                self._paramfileHandles[paramfileName] = paramfileHandle
+                            except IOError:
+                                raise MobyleError, "cannot open the file: "+str( paramfileName )
+                else :
+                    paramfileHandle = None
+            else:
+                if  myEvaluator.getVar( 'value' ) is not None:
+                    if parameter.formatHas_proglang( 'perl' ) or  parameter.flistHas_proglang( parameter.getValue() , 'perl' ) :
+                        if debug > 1:
+                            b_log.debug( "#################### WARNING ##############################################" )
+                            b_log.debug( "the parameter " + paramName + " had a format code in Perl but not in Python" )
+                            b_log.debug( "###########################################################################" )
+                continue
+                
+            try:
+                arg = myEvaluator.eval( format )
+            except Exception, err:
+                msg = "Error during evaluation of \"%s.%s\" format parameter: %s : \"%s\"" % (
+                    service.getName(),
+                    paramName ,
+                    format,
+                    err
+                    )
+                if debug > 1:
+                    b_log.debug( msg )
+                raise MobyleError , msg
+                
+            if paramfileHandle:
+                if debug > 1:
+                    b_log.debug( ">> " + paramfileName + " , " + arg )
+                if arg :
+                    paramfileHandle.write( arg )
+                    paramfileHandle.flush()
+            else:
+                self._commandLine = str( self._commandLine ) + str( arg )
+                if debug > 1:
+                    b_log.debug( "commandLine = " + self._commandLine )
+        if debug > 1:
+            b_log.debug( "------------ end of parameter loop  -------------" )        
+       
+#===============================================================================
+#            
+#        the environment is modified here ( it will be just before to do run in _batch ) to avoid 
+#        dramatic side effects on well of mobyle.
+#        we usr the environment to find the right python everywhere in mobyle and if we modified the path
+#        we could change the python used.
+#                 
+#===============================================================================
+        xmlEnv = {}
+        
+        if commandParameterName : #the command come from a parameter
+            path  = service.getEnv( 'PATH' )            
+        else:#the command come from the element command in head
+            path = service.getCommand()[2]
+            path_env = service.getEnv( 'PATH' )
+            if path and path_env:
+                path = "%s:%s" % ( path, path_env )
+            elif path_env:
+                path = path_env
+        if path :
+            #os.environ['PATH'] = "%s:%s" %( path , os.environ['PATH'])
+            xmlEnv[ 'PATH' ] = path
+            if debug > 1:
+                b_log.debug( "PATH= " + str( path  ) )            
+        else:
+            if debug > 1 :
+                b_log.debug( "PATH= "+ str( os.environ[ 'PATH' ] ) )
+        for varEnv in service.envVars():
+            envArg = service.getEnv( varEnv ).strip()
+            if varEnv == 'PATH':
+                continue
+            else:
+                xmlEnv[ varEnv ] = envArg
+            
+        #trim multi espaces , ...
+        self._commandLine = ' '.join( self._commandLine.split() )
+        self._commandLine.strip()
+        self._commandLine.replace( '"','\\"' )
+        self._commandLine.replace( '@','\@' )
+        self._commandLine.replace( '_SQ_',"\'" )
+        self._commandLine.replace( '_DQ_','\"' )
+
+
+        if debug > 1:
+            b_log.debug( "Environment ="+str( xmlEnv ) )
+            b_log.debug( "command line= " + self._commandLine )
+        return { 'cmd' : self._commandLine , 
+                 'env'  : xmlEnv ,
+                 'paramfiles': self._paramfileHandles
+                }
+        
+
+    def __str__( self ):
+        return str( self._commandLine )
+
+    def openParamFile(self , paramfileName ):
+        return StringIO()
+       
+
diff --git a/Src/Mobyle/ConfigManager.py b/Src/Mobyle/ConfigManager.py
new file mode 100644
index 0000000..f71235b
--- /dev/null
+++ b/Src/Mobyle/ConfigManager.py
@@ -0,0 +1,1388 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under LGPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+
+import logging 
+import sys
+import os 
+import re
+import types
+
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+from Dispatcher import DefaultDispatcher
+from Mobyle.MobyleError import MobyleError , ConfigError
+
+MOBYLEHTDOCS = None
+
+class MetaSingleton(type):
+    def __init__(cls , name , bases , classdict ):    
+        cls._ref = None
+
+    def __call__(cls , *args , **kwargs ):
+        if cls._ref:
+            return cls._ref
+        else:
+            instance = cls.__new__(cls,  *args , **kwargs )
+            instance.__init__( *args , **kwargs )
+            cls._ref = instance
+            return  instance
+        
+        
+class Config( object ):
+    """
+    this class is designed as the singleton pattern
+    (ref: Programmation Python , Tarek Ziade .p 483).
+    there is only one instance at once.
+    this class parse the file local/Config/Config.py
+    raise error fix default values raise error if needed. 
+    all other Mobyle classes must use this class to access
+    to a configuration information. 
+    """
+    
+    __metaclass__ = MetaSingleton
+    
+    def __init__(self):
+            self.__version = '1.5.3'
+            import Local.Config.Config
+            import Local.Config.Execution
+            #############################
+            #
+            # logging
+            #
+            ##############################
+            
+            try:
+                self._logdir = Local.Config.Config.LOGDIR
+            except AttributeError:
+                self._logdir = "/dev/null"
+                msg = "LOGDIR not found in  Local/Config/Config.py"
+                print >> sys.stderr,  "%s . It sets to %s" % ( msg , self._logdir ) 
+
+            self.log = logging.getLogger('Mobyle.Config')
+            self.log.propagate = False
+            try:
+                defaultHandler = logging.FileHandler( os.path.join( self._logdir , 'error_log' ) , 'a')
+            except :
+                print >> sys.stderr , " WARNING : can't access to logfile. Logs will redirect to /dev/null"
+                defaultHandler = logging.FileHandler( '/dev/null' , 'a' )
+
+            defaultFormatter = logging.Formatter(
+                '%(name)-10s : %(levelname)-8s : L %(lineno)d : %(asctime)s : %(message)s' ,
+                '%a, %d %b %Y %H:%M:%S'
+                )
+
+            #defaultHandler.setLevel( logging.DEBUG )
+            defaultHandler.setFormatter( defaultFormatter )
+            self.log.addHandler( defaultHandler )
+
+            if  os.path.exists( MOBYLEHOME ):
+                self._mobylehome = os.path.realpath( MOBYLEHOME )
+            else:
+                raise MobyleError , "MOBYLEHOME is not defined"
+  
+            ######################
+            #
+            # Mandatory values
+            #
+            ######################
+
+            try: #old style config
+                if os.path.exists( Local.Config.Config.DOCUMENT_ROOT ):
+                    self._mobyle_htdocs = os.path.realpath( Local.Config.Config.DOCUMENT_ROOT )
+                else:
+                    msg = "DOCUMENT_ROOT: %s does not exist" % Local.Config.Config.DOCUMENT_ROOT
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            except AttributeError: 
+                if MOBYLEHTDOCS: #define by new installer
+                    if os.path.exists( MOBYLEHTDOCS ):
+                        self._mobyle_htdocs = os.path.realpath( MOBYLEHTDOCS )
+                    else: #never happen ?
+                        msg = "bad installation option --install-htdocs : %s no such directory " % MOBYLEHTDOCS
+                        raise ConfigError , msg
+                else:
+                    try: #define in Config.py witout installer but with new style config
+                        if os.path.exists( Local.Config.Config.MOBYLEHTDOCS ):
+                            self._mobyle_htdocs = os.path.realpath( Local.Config.Config.MOBYLEHTDOCS )
+                        else:
+                            msg = "MOBYLEHTDOCS: %s no such directory" % Local.Config.Config.MOBYLEHTDOCS
+                            self.log.error( msg )
+                            raise ConfigError , msg
+                    except AttributeError:
+                        msg = "error during installation or configuration MOBYLEHTDOCS not found"
+                        self.log.error( msg )
+                        raise ConfigError , msg
+                
+            try:
+                self._root_url = Local.Config.Config.ROOT_URL.strip( '/' )
+            except AttributeError:
+                msg = "ROOT_URL not found in Local/Config/Config.py"
+                self.log.error( msg )
+                raise ConfigError , msg
+            
+            try:
+                self._cgi_prefix = Local.Config.Config.CGI_PREFIX.strip( '/' )
+            except AttributeError:
+                    msg = "CGI_PREFIX not found in Local/Config/Config.py"
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            try:
+                self._htdocs_prefix = Local.Config.Config.HTDOCS_PREFIX.strip( '/' )
+            except AttributeError:
+                    msg = "HTDOCS_PREFIX not found in Local/Config/Config.py" 
+                    self.log.error( "HTDOCS_PREFIX not found in Local/Config/Config.py" )
+                    raise ConfigError , msg
+                    
+            if  self._htdocs_prefix is not None :
+                    self._portal_prefix = self._htdocs_prefix + '/portal'
+                    self._portal_prefix = self._portal_prefix.lstrip( '/' )
+                    self._portal_path = os.path.realpath( os.path.join( self._mobyle_htdocs , 'portal' ) )
+            
+            ######################
+            #
+            #  default values
+            #
+            #######################
+            
+            self._debug = 0
+            self._particular_debug = {}
+            
+            self._accounting = False
+            self._session_debug = None
+            self._status_debug = False
+            
+            if self._htdocs_prefix:
+                self._repository_url = "%s/%s/%s" % ( self._root_url , self._htdocs_prefix , 'data' )
+            else:
+                self._repository_url = "%s/%s" % ( self._root_url , 'data' )
+                
+            self._results_url = "%s/%s" % ( self._repository_url , 'jobs' )
+            self._user_sessions_url = "%s/%s" % ( self._repository_url , 'sessions' )
+            self._servers_url = "%s/%s/%s" % ( self._repository_url , 'services' , 'servers' )
+            
+            self._repository_path = os.path.realpath( os.path.join( self._mobyle_htdocs , 'data' ) ) 
+            self._services_path = os.path.realpath( os.path.join( self._repository_path , 'services' ) )
+            self._servers_path = os.path.realpath( os.path.join( self._services_path, 'servers' ) )
+            self._index_path = os.path.realpath( os.path.join( self._services_path ,'index' ) )
+            
+            self._results_path = os.path.realpath( os.path.join( self._repository_path , 'jobs' ) )
+            self._user_sessions_path = os.path.realpath( os.path.join( self._repository_path , 'sessions' ) )
+            #OPENID
+            self._openidstore_path = os.path.realpath( os.path.join( self._repository_path , 'openidstore' ) )
+            
+            self._binary_path = []  
+            self._format_detector_cache_path = None
+            
+            self._databanks_config = {}
+            
+            self._dns_resolver = False 
+            self._opt_email = False
+            self._particular_opt_email = {}
+            
+            self._anonymous_session = "captcha"
+            self._authenticated_session = 'email'
+
+            #OPENID
+            self._openid = False
+            
+            self._refresh_frequency = 240
+            
+            self._dont_email_result = False
+            
+            self._filelimit = 2147483648     #  2 Gib
+            self._sessionlimit = 52428800    # 50 Mib
+            self._previewDataLimit = 1048576 #  1 Mib
+            self._max_similar_job_per_user = 1
+            self._max_job_per_user = 0
+            
+            self._result_remain = 10 # in day
+            
+            self._lang = 'en'
+            self._email_delay = 20
+            
+            self._dataconverter = {}
+            
+            self._execution_system_alias  = { 'SYS' : Local.Config.Execution.SYSConfig() }
+            self._dispatcher = DefaultDispatcher( { 'DEFAULT'  : ( self._execution_system_alias[ 'SYS' ] , '' ) } )
+            
+            self._services_deployment_include = { 'programs' : [ '*' ] ,
+                                                 'workflows' : [ '*' ] ,
+                                                 'viewers'   : [ '*' ] ,
+                                                 'tutorials' : [ '*' ] ,
+                                                 }
+            self._services_deployment_exclude = { 'programs' : [] ,
+                                                 'workflows' : [] ,
+                                                 'viewers'   : [] ,
+                                                 'tutorials' : [] ,
+                                                 }
+            self._module_init = None
+            self._module_load= {}
+            self._disable_all = False
+            self._disabled_services = []
+            
+            self._authorized_services = {}
+            self._all_portals = {}
+            self._exported_services = []
+            
+            
+            try:
+                self._accounting = Local.Config.Config.ACCOUNTING
+            except AttributeError:
+                self.log.info( "ACCOUNTING not found in  Local/Config/Config.py, set ACCOUNTING= %s" %self._accounting )
+            try:
+                self._session_debug = Local.Config.Config.SESSION_DEBUG
+            except AttributeError:
+                pass
+            try:
+                self._status_debug = Local.Config.Config.STATUS_DEBUG
+            except AttributeError:
+                pass
+            ######################
+            #
+            #  Directories
+            #
+            ######################
+            
+            ## in htdocs ##
+            
+            
+            #OPENID
+            try:
+                if os.path.exists( Local.Config.Config.OPENIDSTORE_PATH ):
+                    self._openidstore_path = os.path.realpath( Local.Config.Config.OPENIDSTORE_PATH )
+                else:
+                    msg = "OPENIDSTORE_PATH: %s does not exit" % Local.Config.Config.OPENIDSTORE_PATH
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            except AttributeError, err:
+                self.log.debug( "OPENIDSTORE_PATH not found in Local/Config/Config.py default value used : " +str( self._openidstore_path) )
+
+ 
+            ## out web ##
+            self._admindir =  os.path.join( self._results_path , 'ADMINDIR' )
+            
+            try:
+                binary_path = []
+                for path in Local.Config.Config.BINARY_PATH:
+                    binary_path.append( path )
+                self._binary_path  = binary_path
+            except AttributeError:
+                self.log.warning( "BINARY_PATH not found in Local/Config/Config.py the default web server path will be used" )
+                
+            try:
+                if os.path.exists( Local.Config.Config.FORMAT_DETECTOR_CACHE_PATH ):
+                    self._format_detector_cache_path = os.path.realpath( Local.Config.Config.FORMAT_DETECTOR_CACHE_PATH )
+                else:
+                    msg = "FORMAT_DETECTOR_CACHE_PATH: %s does not exit" % Local.Config.Config.FORMAT_DETECTOR_CACHE_PATH
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            except AttributeError:
+                pass
+                
+            try:
+                self._databanks_config = Local.Config.Config.DATABANKS_CONFIG
+            except AttributeError:
+                self.log.info( "No databanks are found in Local/Config/Config.py" )
+                    
+                    
+            
+            #######################
+            #
+            #    debug
+            #
+            #######################
+
+            try:
+                debug = Local.Config.Config.DEBUG
+                if debug >= 0 or debug <= 3:
+                    self._debug = debug
+                else:
+                    self.log.warning( "DEBUG must be >= 0 and <= 3 I found DEBUG =%s, DEBUG is set to %i" % ( debug , self._debug ) ) 
+            except AttributeError:
+                self.log.info( "DEBUG not found in Local/Config/Config.py, DEBUG is set to %i" % self._debug )
+
+            try:
+                self._particular_debug = Local.Config.Config.PARTICULAR_DEBUG
+            except AttributeError:
+                self.log.info( "PARTICULAR_DEBUG not found in Local/Config/Config.py" )
+                                        
+            #######################
+            #
+            #  Mail
+            #
+            #######################
+
+            try:
+                if type( Local.Config.Config.MAINTAINER ) == types.ListType or type( Local.Config.Config.MAINTAINER ) == types.TupleType :
+                    self._maintainer = Local.Config.Config.MAINTAINER
+                else:
+                    self._maintainer = [ Local.Config.Config.MAINTAINER ]
+                    
+                if type( Local.Config.Config.HELP ) == types.ListType or type( Local.Config.Config.HELP ) == types.TupleType :    
+                    self._help = Local.Config.Config.HELP
+                else:
+                    self._help = [ Local.Config.Config.HELP ]
+
+                self._mailhost = Local.Config.Config.MAILHOST
+            except AttributeError, err:
+                msg = str(err).split()[-1] + "not found in Local/Config/Config.py"
+                self.log.error( msg )
+                raise ConfigError , msg         
+                
+            try:
+                self._sender = Local.Config.Config.SENDER
+            except AttributeError, err:
+                self._sender = ", ".join( self._help )
+                self.log.info( "SENDER not found in  Local/Config/Config.py , set SENDER to HELP ")
+                
+            try:
+                self._dns_resolver = Local.Config.Config.DNS_RESOLVER
+            except AttributeError:
+                self.log.info( "DNS_RESOLVER not found in  Local/Config/Config.py, set DNS_RESOLVER to False" )
+
+            #######################
+            #
+            # Statistics
+            #
+            #######################
+
+            #GOOGLE ANALYTICS
+            try:
+                self._GAcode = Local.Config.Config.GACODE
+            except AttributeError:
+                self._GAcode = None
+
+            #######################
+            #
+            # Welcome page configuration
+            #
+            #######################
+
+            try:
+                self._welcome_config = Local.Config.Config.WELCOME_CONFIG
+            except AttributeError:
+                self._welcome_config = {}
+                
+            #######################
+            #
+            # Portal customization
+            #
+            #######################
+            
+            try:
+                self._custom_portal_header = Local.Config.Config.CUSTOM_PORTAL_HEADER
+            except AttributeError:
+                self._custom_portal_header = None
+
+            try:
+                self._custom_portal_footer = Local.Config.Config.CUSTOM_PORTAL_FOOTER
+            except AttributeError:
+                self._custom_portal_footer = None
+
+                
+            #######################
+            #
+            #     Authentication
+            #
+            #######################
+
+            #OPENID
+            try:
+                self._openid = Local.Config.Config.OPENID
+            except AttributeError:
+                self.log.info("OPENID not found in Local/Config/Config.py")
+
+            try:
+                self._opt_email = Local.Config.Config.OPT_EMAIL
+            except AttributeError:
+                self.log.info( "OPT_EMAIL not found in  Local/Config/Config.py, set OPT_EMAIL to %s" % self._opt_email )
+                               
+            try:
+                self._particular_opt_email = Local.Config.Config.PARTICULAR_OPT_EMAIL
+            except AttributeError:
+                self.log.info( "PARTICULAR_OPT_EMAIL not found in  Local/Config/Config.py, use OPT_EMAIL for all services" )
+                
+            try:
+                self._anonymous_session = Local.Config.Config.ANONYMOUS_SESSION
+                try:
+                    self._anonymous_session = self._anonymous_session.lower()
+                except AttributeError:
+                    self._anonymous_session = "captcha"
+                    self.log.warning( "ANONYMOUS_SESSION have a wrong value: " + str( Local.Config.Config.ANONYMOUS_SESSION ) + " , set to \"%s\"" %self._anonymous_session)
+               
+                if self._anonymous_session == "yes" or self._anonymous_session == "y":
+                    self._anonymous_session = "yes"
+                elif self._anonymous_session == "no" or self._anonymous_session == "n":
+                    self._anonymous_session = "no"
+                elif self._anonymous_session != "captcha":
+                    self._anonymous_session = "captcha"
+                    self.log.warning( "ANONYMOUS_SESSION have a wrong value: " + str( Local.Config.Config.ANONYMOUS_SESSION ) + " , set to \"captcha\"" )
+
+            except AttributeError:
+                self.log.info( "ANONYMOUS_SESSION not found in  Local/Config/Config.py, set to  default value: \"%s\"" %self._anonymous_session )
+            try:
+                self._authenticated_session = Local.Config.Config.AUTHENTICATED_SESSION
+                    
+                if self._authenticated_session not in ( "email" ,"no" ,"yes" ):
+                    self.log.warning( "AUTHENTICATED_SESSION have a wrong value: %s set to \"%s\""%( Local.Config.Config.AUTHENTICATED_SESSION ,
+                                                                                                      self._authenticated_session ) )
+                        
+                if self._anonymous_session == "no" and not self._authenticated_session :
+                    msg = "anonymous session AND authenticated session are disabled. you can't disabled both"
+                    self.log.error( msg )
+                    raise ConfigError , msg
+                
+            except AttributeError , err :
+                self.log.info( "AUTHENTICATED_SESSION not found in  Local/Config/Config.py , set to default value: \"%s\"" %self._authenticated_session )         
+                
+            try:
+                self._refresh_frequency = Local.Config.Config.REFRESH_FREQUENCY
+            except AttributeError:
+                self.log.info( "REFRESH_FREQUENCY not found in Local/Config/Config.py, set to %s" % self._refresh_frequency )               
+                
+            ########################
+            #
+            #      results
+            #
+            ########################
+               
+            try:
+                self._dont_email_result = Local.Config.Config.DONT_EMAIL_RESULTS
+            except AttributeError:
+                self.log.info( "DONT_EMAIL_RESULTS not found in  Local/Config/Config.py, set DONT_EMAIL_RESULTS to %s" %self._dont_email_result)
+
+            try:
+                self._maxmailsize = Local.Config.Config.MAXMAILSIZE
+            except AttributeError:
+                if not self._dont_email_result :
+                    self.log.info("MAXMAILSIZE not found in  Local/Config/Config.py but DONT_EMAIL_RESULTS is False, I set MAXMAILSIZE = 2 Mo")
+                    self._maxmailsize = 2097152 
+                else:
+                    self._maxmailsize = None
+
+            try:
+                self._filelimit = int( Local.Config.Config.FILELIMIT )
+            except AttributeError:
+                self.log.info( "FILELIMIT not found in  Local/Config/Config.py, set FILELIMIT to %d Gib" %( self._filelimit / 1073741824 ) )
+            except ValueError:
+                msg = "FILELIMIT have an invalid value : %s .\nIt must be an integer" % self._filelimit 
+                self.log.error( msg )
+                raise ConfigError , msg
+ 
+            try:
+                self._sessionlimit = int( Local.Config.Config.SESSIONLIMIT )
+            except AttributeError:
+                self.log.info( "SESSIONLIMIT not found in  Local/Config/Config.py, set SESSIONLIMIT to %d Mib" %( self._sessionlimit / 1048576 ) )
+            except ValueError:
+                msg = "SESSIONLIMIT have an invalid value : %s .\nIt must be an integer" % self._sessionlimit 
+                self.log.error( msg )
+                raise ConfigError , msg
+
+            try:
+                self._previewDataLimit = int( Local.Config.Config.PREVIEW_DATA_LIMIT )
+            except AttributeError:
+                self.log.info( "PREVIEW_DATA_LIMIT not found in  Local/Config/Config.py, set PREVIEW_DATA_LIMIT to %d Mib"%( self._previewDataLimit / 1048576 ) )
+            except ValueError:
+                msg = "PREVIEW_DATA_LIMIT have an invalid value : %s .\nIt must be an integer" % self._sessionlimit 
+                self.log.error( msg )
+                raise ConfigError , msg
+
+            try:
+                self._result_remain = int( Local.Config.Config.RESULT_REMAIN )
+            except AttributeError:
+                self.log.info( "RESULT_REMAIN not found in  Local/Config/Config.py, set RESULT_REMAIN to %d days" %( self._result_remain ))
+            except ValueError:
+                msg = "RESULT_REMAIN have an invalid value : %s .\nIt must be an integer" % Local.Config.Config.RESULT_REMAIN
+                self.log.error( msg )
+                raise ConfigError , msg
+            
+            try:
+                self._max_similar_job_per_user = int( Local.Config.Config.MAX_SIMILAR_JOB_PER_USER )
+                if self._max_similar_job_per_user < 0 :
+                    msg = "MAX_SIMILAR_JOB_PER_USER have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.MAX_SIMILAR_JOB_PER_USER
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            except AttributeError:
+                try:
+                    self._max_similar_job_per_user = int( Local.Config.Config.SIMULTANEOUS_JOBS )
+                    if self._max_similar_job_per_user < 0 :
+                        msg = "SIMULTANEOUS_JOBS have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.SIMULTANEOUS_JOBS
+                        self.log.error( msg )
+                        self.log.warning( "SIMULTANEOUS_JOBS has been replaced by MAX_SIMILAR_JOB_PER_USER please update you config (see UPDATE file)")
+                        raise ConfigError , msg
+                except AttributeError:
+                    self.log.info( "MAX_SIMILAR_JOB_PER_USER not found in  Local/Config/Config.py, set MAX_SIMILAR_JOB_PER_USER to %d " %( self._max_similar_job_per_user ))
+                except ValueError:
+                    msg = "SIMULTANEOUS_JOBS have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.SIMULTANEOUS_JOBS
+                    self.log.error( msg )
+                    raise ConfigError , msg     
+            except ValueError:
+                msg = "MAX_SIMILAR_JOB_PER_USER have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.MAX_SIMILAR_JOB_PER_USER
+                self.log.error( msg )
+                raise ConfigError , msg   
+            
+            
+            try:
+                self._max_job_per_user = int( Local.Config.Config.MAX_JOB_PER_USER )
+                if self._max_job_per_user < 0 :
+                    msg = "MAX_JOB_PER_USER have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.MAX_JOB_PER_USER
+                    self.log.error( msg )
+                    raise ConfigError , msg
+            except AttributeError:
+                self.log.info( "MAX_JOB_PER_USER not found in  Local/Config/Config.py, users can submit as many jobs as they wants")
+            except ValueError:
+                msg = "MAX_JOB_PER_USER have an invalid value : %s .\nIt must be a positive or null integer" % Local.Config.Config.MAX_JOB_PER_USER
+                self.log.error( msg )
+                raise ConfigError , msg  
+            #############################
+            #
+            #       CONVERTER
+            #
+            #############################
+                       
+            try:
+                self._dataconverter = Local.Config.Config.DATA_CONVERTER
+                for dtype in Local.Config.Config.DATA_CONVERTER:
+                    self._dataconverter[ dtype ] = Local.Config.Config.DATA_CONVERTER[ dtype ]
+            except AttributeError:
+                pass
+
+            ##############################
+            #
+            #      Misc
+            #
+            ##############################
+
+
+            try:
+                self._lang = Local.Config.Config.LANG
+            except AttributeError:
+                self.log.info( "LANG not found in  Local/Config/Config.py, set LANG to %s" % self._lang)
+
+            try:
+                self._email_delay = Local.Config.Config.EMAIL_DELAY
+            except AttributeError:
+                try:
+                    self._email_delay = Local.Config.Config.TIMEOUT
+                    self.log.info( "TIMEOUT is deprecated and replaced by EMAIL_DELAY, set EMAIL_DELAY to %d" % self._email_delay)
+                except AttributeError:
+                    self.log.info( "EMAIL_DELAY not found in  Local/Config/Config.py, set EMAIL_DELAY to %d" % self._email_delay)
+
+            ##############################
+            #
+            #      BATCH
+            #
+            ##############################
+            try:
+                self._execution_system_alias = Local.Config.Config.EXECUTION_SYSTEM_ALIAS
+            except AttributeError:
+                msg = "EXECUTION_SYSTEM_ALIAS not found in  Local/Config/Config.py, it is set to %s" % self._execution_system_alias.values()[0]
+                self.log.warning( msg ) 
+                  
+            if not self._execution_system_alias:
+                self._execution_system_alias = { 'SYS' : Local.Config.Execution.SYSConfig() }
+                msg = "EXECUTION_SYSTEM_ALIAS in Local/Config/Config.py is empty. it is fill with %s" % self._execution_system_alias[ 'SYS' ]
+                self.log.warning( msg ) 
+                
+            try:
+                self._dispatcher = Local.Config.Config.DISPATCHER
+            except AttributeError:
+                default_alias_name , default_alias = self._execution_system_alias.items()[0]
+                msg = "DISPATCHER not found in  Local/Config/Config.py, it is set to { 'DEFAULT' : %s }" % default_alias_name
+                self.log.warning( msg )  
+            
+            self._revert_alias = dict( [ ( config , alias ) for alias , config in self._execution_system_alias.items() ] )
+                
+
+            try:
+                self._default_Q = Local.Config.Config.DEFAULT_Q
+            except AttributeError:
+                self._default_Q = 'mobyle'
+                self.log.info( "DEFAULT_Q not found in  Local/Config/Config.py. it is set to mobyle" )
+
+            try:
+                self._particular_Q  = Local.Config.Config.PARTICULAR_Q
+            except AttributeError:
+                self.log.info( "PARTICULAR_Q not found in  Local/Config/Config.py" )
+
+
+            try:
+                self._module_init = Local.Config.Config.MODULE_INIT.strip()
+            except AttributeError:
+                self.log.info( "MODULE_INIT not found in  Local/Config/Config.py" )
+            
+            try:
+                self._module_load = Local.Config.Config.MODULE_LOAD
+            except AttributeError:
+                self.log.info( "MODULE_LOAD not found in  Local/Config/Config.py" )
+                
+            if self._module_load and not self._module_init:
+                msg = "MODULE_LOAD is specified Local/Config/Config.py but not MODULE_INIT"
+                self.log.error(msg)
+                raise ConfigError( msg )
+            elif self._module_init: 
+                self._module_init = os.path.abspath(os.path.realpath( self._module_init ))
+                if not os.path.exists(self._module_init):
+                    msg = "MODULE_INIT= %s: No such file" % self._module_init
+                    self.log.error(msg)
+                    raise ConfigError( msg )
+            ##########################
+            #
+            # services publication
+            #
+            ###########################
+            try:
+                self._services_deployment_exclude.update( Local.Config.Config.LOCAL_DEPLOY_EXCLUDE )
+            except ( AttributeError , NameError ):
+                self.log.info( "LOCAL_DEPLOY_EXCLUDE not found in  Local/Config/Config.py" )
+                
+            try:
+                self._services_deployment_include.update( Local.Config.Config.LOCAL_DEPLOY_INCLUDE )
+            except ( AttributeError , NameError ):
+                self.log.info( "LOCAL_DEPLOY_INCLUDE not found in  Local/Config/Config.py" )
+
+
+            #############################
+            #
+            # disabled service
+            #
+            #############################
+
+            try:
+                self._disable_all = Local.Config.Config.DISABLE_ALL
+            except AttributeError:
+                msg = "DISABLE_ALL not found in  Local/Config/Config.py,set to %s" % self._disable_all
+                self.log.info( msg )
+            try:
+                self._disabled_services = Local.Config.Config.DISABLED_SERVICES
+            except AttributeError:
+                msg = "DISABLED_SERVICES not found in  Local/Config/Config.py,"
+                self.log.info( msg )
+                
+            #########################################
+            #
+            # restriction services access
+            #
+            #########################################
+
+            try:
+                self._authorized_services = Local.Config.Config.AUTHORIZED_SERVICES
+            except AttributeError:
+                self.log.info( "AUTHORIZED_SERVICES not found in  Local/Config/Config.py" )
+            
+            #########################################
+            #
+            # Grid aspects
+            #
+            #########################################    
+            
+            self._portal_name = "anonymous"
+            try:
+                self._portal_name = Local.Config.Config.PORTAL_NAME
+            except AttributeError:
+                pass
+            
+            self._simple_forms_active = False
+            try:
+                self._simple_forms_active = Local.Config.Config.SIMPLE_FORMS
+            except AttributeError:
+                pass
+
+            try:
+                self._all_portals = Local.Config.Config.PORTALS 
+                for portal in self._all_portals .keys():
+                    self._all_portals[ portal ][ 'url' ] = self._all_portals[ portal ][ 'url' ].rstrip( '/')
+                    self._all_portals[ portal ][ 'repository' ] = "%s/%s" % ( self._all_portals[ portal ][ 'repository' ].rstrip( '/') ,
+                                                                             'data' )
+                    #self._all_portals[ portal ][ 'jobsBase' ] = self._all_portals[ portal ][ 'jobsBase' ].rstrip( '/')
+            except KeyError , err:
+                msg = "error PORTALS : %s is not properly defined : %s" % ( portal , err )
+                self.log.warning( msg )
+                try:
+                    del self._all_portals[ portal ]
+                except KeyError:
+                    pass
+                self.log.warning( " the portal: %s will not be imported" % portal )
+            except AttributeError:
+                pass
+
+            try:
+                self._exported_services = Local.Config.Config.EXPORTED_SERVICES
+            except AttributeError:
+                self.log.info( "EXPORTED_SERVICES not found in  Local/Config/Config.py" )
+            
+
+
+
+    ###############################
+    #
+    #    accessors
+    #
+    ##############################
+    def version( self ):
+        """
+        @return: the version number of this Mobyle instance
+        @rtype: 
+        """
+        return self.__version 
+    
+    def debug( self , jobName = None ):
+        """
+        Returns the debug level for a job or the default debug level if jobName is not specified
+        @param jobName:
+        @type jobName: string
+        @return: an int >=0 and <= 3
+        @rtype: int
+        """
+        try:
+            debug = self._particular_debug[ jobName ]
+        except KeyError:
+            debug = self._debug
+        if debug < 0 or debug > 3:
+            if jobName is None:
+                msg =" DEBUG must be >= 0 and <= 3 I found DEBUG =%i, DEBUG is set to %i" % ( debug , self._debug )
+            else:
+                msg = jobName + " debug must be >= 0 and <= 3 I found %i,  PARTICULAR_DEBUG[ '%s' ] is set it to %i" % ( debug ,
+                                                                                                                         jobName , 
+                                                                                                                         self._debug )
+            self.log.warning( msg )
+        return debug
+    
+    
+    def mobylehome( self ):
+        """
+        @return: the absolute path to home of the project
+        @rtype: string
+        """
+        return self._mobylehome
+    
+    
+    def document_root( self ):
+        """
+        @return: the absolute path to the mobyle htdocs directory
+        @rtype: string
+        """
+        return self._mobyle_htdocs
+    
+    
+    def root_url( self ):
+        """
+        @return: the base url of the web server
+        @rtype: string
+        """
+        return self._root_url
+
+
+    def results_url( self ):
+        """
+        @return: the complete url of the jobs
+        @rtype: string
+        """
+        return self._results_url
+
+    def results_path( self ):
+        """
+        @return: the directory absolute path  where are located the jobs
+        @rtype: string
+        """
+        return self._results_path
+    
+    def admindir(self):
+        """
+        @return: the absolute path of the ADMINDIR
+        @rtype: string
+        """
+        return self._admindir
+
+    def servers_url( self ):
+        """
+        @return: the base url of the service definitions
+        @rtype: string
+        """
+        return self._servers_url
+
+    def servers_path( self ):
+        """
+        @return: the directory absolute path  where are located the services
+        @rtype: string
+        """
+        return self._servers_path
+    
+    def services_path( self ):
+        """
+        @return: the directory absolute path  where are located the services
+        @rtype: string
+        """
+        return self._services_path
+    
+    def repository_path( self ):
+        """
+        @return: the absolute path where are located the services, jobs, sessions ... 
+        @rtype: string
+        """
+        return self._repository_path
+    
+    def repository_url( self ):
+        """
+        @return: the url where are located the services, index , jobs, ... 
+        @rtype: string
+        """
+        return self._repository_url
+
+
+    def index_path( self ):
+        """
+        @return: the directory absolute path  where are located the indexes for this portal.
+        @rtype: string
+        """
+        return self._index_path
+    
+    def binary_path( self ):
+        """
+        @return: the list of path where binaries must be search
+        @rtype: list of strings
+        """
+        return self._binary_path
+    
+
+    def format_detector_cache_path( self ):
+        """
+        @return: the absolute path where the invalid sequences and alignment are dumped for analysis
+        @rtype: string
+        """
+        return self._format_detector_cache_path
+
+
+    def user_sessions_path( self ):
+        """
+        @return: the directory absolute path  where are located the sessions
+        @rtype: string
+        """
+        return self._user_sessions_path
+    
+    def user_sessions_url( self ):
+        """
+        @return: the complete url of the sessions
+        @rtype: string
+        """
+        return self._user_sessions_url 
+    
+    def log_dir( self ):
+        """
+        @return: the absolute path where located the log files
+        @rtype: string
+        """
+        return self._logdir
+       
+    
+    def portal_url( self , relative = False ):
+        """
+        @param relative:
+        @type relative: boolean
+        @return: if relative return the relative url of the portal otherwise return the absolute 
+                 url of the portal.
+        @rtype: string
+        """
+        if relative:
+            return self._portal_prefix
+        else:
+            return "%s/%s" % ( self._root_url , self._portal_prefix )
+    
+    def portal_path(self):
+        """
+        @return: the absolute path to the portal
+        @rtype: string
+        """
+        return  self._portal_path
+    
+    
+    def cgi_url( self ):
+        """
+        @return: the absolute url to the cgis
+        @rtype: string
+        """
+        return "%s/%s" % ( self._root_url , self._cgi_prefix )
+    
+
+    def getDatabanksConfig(self):
+        """
+        @return: the list of databanks
+         along with their typing information and label
+        @rtype: dictionary
+        """
+        return self._databanks_config
+        
+    
+    def maintainer( self ):
+        """
+        @return: the email address of the Mobyle server  maintainer
+        @rtype: list of string
+        """
+        return self._maintainer
+
+    def mailHelp( self ):
+        """
+        @return: The email address to the hot line for this Mobyle server
+        @rtype: list of string
+        """
+        return self._help
+    
+    
+    def sender( self ):
+        """
+        the sender address is used as From: for mail sent by mobyle
+        - notification of long job
+        - results
+        - session confirmation  
+        @return: the sender email address ( From: ) of this Mobyle server
+        @rtype: string
+        """
+        return self._sender
+    
+
+    def mailhost( self ):
+        """
+        @return: the mail transfert agent used to send emails
+        @rtype: string
+        """
+        return self._mailhost
+
+
+    def opt_email( self , portalid = None ):
+        """
+        @return: True if the user email is not mandatory to run a job, False otherwise
+        @rtype: Boolean
+        """
+        if portalid is None:
+            return self._opt_email
+        else:
+            if portalid.find( '.' ) == -1:
+                portalid = "local.%s"%portalid
+            try:
+                return self._particular_opt_email[ portalid ]
+            except KeyError:
+                return self._opt_email 
+
+    #OPENID
+    def openid( self ):
+        """
+        @return: True if openid authentication is supported , False otherwise
+        @rtype: boolean
+        """
+        return self._openid
+
+    #OPENID
+    def openidstore_path( self ):
+        """
+        @return: the directory absolute path  where is located the openid store
+        @rtype: string
+        """
+        return self._openidstore_path
+            
+    
+    def anonymousSession( self ):
+        """
+        @return: 
+          - 'no'       if the anonymous sessions are not allowed.
+          - 'yes'      if the anonymous sessions are allowed, without any restrictions.
+          - 'captcha'  if the anonymous sessions are allowed, with a captcha challenge.
+        @rtype: string
+        """
+        return self._anonymous_session
+    
+    
+    def authenticatedSession( self ):
+        """
+        @return: 
+          - 'no'     if authenticated session are not allowed.
+          - 'yes'    if authenticated session are allowed and activated without any restriction.
+          - 'email'  if authenticated session are allowed but an email confirmation is needed to activate it.
+        @rtype: string
+        """
+        return self._authenticated_session
+
+    def refreshFrequency( self ):
+        """
+        @return: portal refresh frequency, in seconds
+        @rtype: integer
+        """
+        return self._refresh_frequency
+    
+    
+    def dnsResolver( self ):
+        """
+        @return: True if the user email domain name is checked to have a mail server , False otherwise 
+        @rtype: boolean
+        """
+        return self._dns_resolver
+
+    
+    def mailResults( self ):
+        """
+        @return: dont_email_result , mailmaxsize in a tuple
+        @rtype: ( boolean , int )
+        """
+        return ( self._dont_email_result , self._maxmailsize )
+
+
+    def remainResults( self ):
+        """
+        @return: how long ( in days ) the results files remains on the server.
+        @rtype: int
+        """
+        return self._result_remain
+
+    def max_similar_job_per_user( self ):
+        """
+        @return: how many "indentical" jobs are allowed to be submit in same time.
+        "indentical" jobs means same email, same IP, same commandline.
+        @rtype: int (>=0)
+        """
+        return self._max_similar_job_per_user
+    
+    def max_job_per_user(self):
+        """
+        @return: how many jobs are allowed to be submit in same time.
+        @rtype: int (>=0)
+        """
+        return self._max_job_per_user
+       
+     
+    def lang( self ):
+        """
+        @return: The default language used by this Mobyle server (used to internationalise the form) 
+        (2 letter code )(default = "en")
+        @rtype: string
+        """
+        return self._lang
+
+
+    def filelimit( self ):
+        """
+        @return: the max size for one file generated by a service ( in byte ). 
+        @rtype: int
+        """
+        return self._filelimit
+
+    def sessionlimit( self ):
+        """
+        @return: the max size for a session directory ( in byte )
+        @rtype: int
+        """
+        return self._sessionlimit
+    
+    def previewDataLimit( self ):
+        """
+        @return: the max size for a result to be preview in job result ( in Byte ).
+        @rtype: int
+        """
+        return self._previewDataLimit   
+
+    def dataconverter(self, datatype):
+        """
+        @param datatype: the name of a datatype as found in the xml of services
+        @type datatype: string
+        @return: list of dataconverter insntance  for a given datatype
+        """
+        try:
+            return self._dataconverter[ datatype ]
+        except KeyError:
+            return []
+        
+    def data_conversions(self):
+        """
+        data_conversions returns the list of conversion possibilities as a list of dictionnaries
+        computed from the configured converters
+        @type datatype: string
+        @return: list of dataconverter insntance  for a given datatype
+        """
+        list = []
+        for dts, entries in self._dataconverter.items():
+            for entry in entries:
+                for fts in entry.convertedFormat():
+                    if fts[0] != fts[1]:
+                        list.append({'dataType':dts, 'fromFormat':fts[0], 'toFormat':fts[1], 'using':entry.program_name})
+        return list
+    
+    def email_delay( self ):
+        """
+        @return: the time during the father process wait its child  ( in sec ).
+        @rtype: int 
+        """
+        return self._email_delay
+
+
+    def getAliasFromConfig(self , config ):
+        try:
+            return self._revert_alias[ config ]
+        except KeyError:
+            raise ConfigError()
+            
+    def getExecutionConfigFromAlias(self , alias ):
+        """
+        @param alias: the symbolic name of a ExecutionConfig object in the Execution_CONFIG_ALIAS
+        @type alias: string
+        @return: the ExecutionConfig corresponding to the symbolic name alias
+        @rtype: ExecutionConfig
+        @raise KeyError: if alias doesn't match with any alias in Config.
+        """
+        try:
+            return self._execution_system_alias[ alias ]
+        except KeyError , err:
+            raise err 
+        
+    def getDispatcher( self):
+        """
+        @return: a L{Dispatcher} 
+        @rtype: a L{Dispatcher} instance
+        """
+        return self._dispatcher
+    
+    def module(self, serviceName ):
+        """
+        @param serviceName: the name of the service
+        @type serviceName: string
+        @return: the path to module init, and the module load to do if module is used or None otherwise
+        @rtype: tuple (string module_init_path , string  module load) or None
+        """
+        if not self._module_init:
+            return None
+        
+        if serviceName in self._module_load:
+            module_load = self._module_load.get(serviceName)
+        elif 'DEFAULT' in self._module_load:
+            module_load = self._module_load['DEFAULT']
+        else:
+            #module_load is not defined for this interface and there is no default
+            #don't use module load
+            module_load = None
+        if module_load is not None:
+            return (self._module_init, module_load)
+        else:
+            return None
+        
+    def accounting( self ):
+        """
+        @return: True if the accounting is on, False otherwise.
+        @rtype: boolean
+        """
+        return self._accounting
+
+    def session_debug( self ):
+        """
+        @return: the debug level for the session specifique logger. 
+        if there is no level specify in Config, return None 
+        @rtype: int
+        """
+        return self._session_debug
+    
+    def status_debug( self ):
+        """
+        @return: True if the debug log on status is on, False otherwise.
+        @rtype: boolean
+        """
+        return self._status_debug
+    
+    def isDisabled( self , portalID = None ):
+        """
+        @param portalID: the portalID of a service. 
+        The portalID is the portal name and service name separated by a dot like portal1.service1
+        local means the local portal  
+        @type: string
+        @return: True if the service is disabled, False otherwise
+        @rtype: boolean
+        """
+        
+        if portalID is None:
+            return self._disable_all
+        else:
+            if self._disable_all :
+                return True
+            else:
+                pattern = self._disabled_services
+                if pattern :
+                    pattern = '|'.join( pattern )
+                    pattern = pattern.replace('.' , '\.' )
+                    pattern = pattern.replace('*' , '.*')
+                    pattern = "^(%s)$" % pattern
+                    auto = re.compile( pattern )
+                else:
+                    # by default if no pattern are specified all services are enabled
+                    return False
+                if auto.search( portalID ):
+                    return True
+                else:
+                    return False
+
+
+
+
+
+    def restrictedServices( self ):
+        """
+        @return: The list of service which have a restrictive access
+        @rtype: list of strings
+        """
+        return self._authorized_services.keys()
+
+
+    def isAuthorized( self , serviceName , addr = None ):
+        """
+        @param service: the name of the service
+        @type service: type
+        @param addr: the ip address to test. if addr is None the environment REMOTE_ADDR will be used
+        @type addr: string
+        @return: True if the user is authorized to used the service (based on IP), False otherwise.
+        @rtype: boolean
+        """
+        if addr is None:
+            try: 
+                addr = os.environ[ 'REMOTE_ADDR' ]
+            except KeyError:
+                return True
+        try:
+            pattern = self._authorized_services[ serviceName ]
+        except KeyError:
+            # by default if no pattern are specified there is no restriction
+            # to access to this service
+            return True
+        
+        if pattern :
+            pattern = '|'.join( pattern )
+            pattern = pattern.replace('.' , '\.' )
+            pattern = pattern.replace('*' , '.*')
+            pattern = "^(%s)$" % pattern
+            auto = re.compile( pattern )
+        else:
+            # by default if no pattern are specified there is no restriction
+            # to access to this service
+            return True
+        
+        if auto.search( addr ):
+            return True
+        else:
+            return False
+
+    def getPrivilegeTable( self ):
+        """
+        @return: a dict where keys are the service names and values a list of Ip mask which are authorized to access to this service
+        @rtype: dict { string : [ strings ] }
+        """
+        return self._authorized_services
+
+
+    def imported_services( self ):
+        """
+        @return: the urls of the services imported by this server
+        @rtype: list of strings
+        """
+        return self._imported_services
+    
+    
+    def exported_services( self ):
+        """
+        @return: the name of the services which are exported by this server
+        @rtype: list of strings
+        """
+        return self._exported_services
+
+    def portal_name( self ):
+        """
+        @return: the name of the portal within a MobyleNet federation
+        @rtype: String
+        """
+        return self._portal_name
+    
+    def simple_forms_active( self ):
+        """
+        @return: tells if "simple forms" should be displayed by default
+        @rtype: Boolean
+        """
+        return self._simple_forms_active
+    
+    def portals( self ):
+        """
+        @return: all the Mobyle portals belonging to the "Mobyle grid"
+        @rtype: list of dictionaries  {     { 'server' : 'url of Mobyle server' ,
+                                            'email' : 'the contact email of this server' }
+                                        }
+        """
+        return self._all_portals
+    
+    
+    def services_deployment_include(self, service_type = None ):
+        """
+        @param service_type: the type of the service to deploy
+        @type service_type: string (for now the available keywords are 'programs' , 'workflows' and 'viewers'
+        @return: the services deployment include masks list
+        @rtype: list of strings
+        """
+        if service_type is None:
+            return self._services_deployment_include
+        else:
+            try:
+                return self._services_deployment_include[ service_type ]
+            except KeyError:
+                raise KeyError( "the '%s' service type is not handled by Mobyle" %service_type )
+        
+    
+    
+    def services_deployment_exclude(self, service_type = None ):
+        """
+        @param service_type: the type of the service to not deploy
+        @type service_type: string (for now the available keywords are 'programs' , 'workflows' and 'viewers'
+        @return: the services deployment exclude masks list
+        @rtype: list of strings
+        """
+        if service_type is None:
+            return self._services_deployment_exclude
+        else:
+            try:
+                return self._services_deployment_exclude[ service_type ]
+            except KeyError:
+                raise KeyError( "the '%s' service type is not handled by Mobyle" %service_type )
+        
+    def GAcode(self):
+        """
+        @return: the Google Analytics code if a GA account is used
+        @rtype: string or None
+        """
+        return self._GAcode
+        
+    def welcome_config(self):
+        """
+        @return: the custom welcome page configuration
+        @rtype: dictionary with keys 'url' and 'format' (format can be html or atom for instance)
+        """
+        return self._welcome_config
+
+    def custom_portal_header(self):
+        """
+        @return: the custom portal header
+        @rtype: string containing an HTML chunk
+        """
+        return self._custom_portal_header
+    
+    def custom_portal_footer(self):
+        """
+        @return: the custom portal footer
+        @rtype: string containing an HTML chunk
+        """
+        return self._custom_portal_footer
+    
diff --git a/Src/Mobyle/Converter/DataConverter.py b/Src/Mobyle/Converter/DataConverter.py
new file mode 100644
index 0000000..784699b
--- /dev/null
+++ b/Src/Mobyle/Converter/DataConverter.py
@@ -0,0 +1,62 @@
+"""
+This module is used as template to build a converter module
+"""
+
+
+
+
+class DataConverter( object ):
+    
+    def __init__(self, path ):
+        self.path = path
+        self.program_name = """the name of the tool used to perform the detection/convertion format"""
+        
+    def detect( self, dataFileName ):
+        """
+        detect the format of the data.
+        @param dataFileName: the absolute filename of the data which the format must be detected
+        @type dataFileName: string
+        @return: the format of this data and the number of entry. 
+               if the format cannot be detected, return None
+               if the number of entry cannot be detected, return None
+        @rtype: ( string format , int number of entry ) 
+        """
+        raise NotImplementedError("this method must be overridden")
+        return None
+    
+    def detectedFormat(self):
+        """
+        @return: the list of detectables formats.
+        @rtype: list of stings
+        """
+        raise NotImplementedError("this method must be overriden")
+        return []
+    
+    
+    def convert( self, dataFileName , outputFormat , inputFormat = None):
+        """
+        convert a data in the format outputFormat
+        @param dataFileName: the absolute filename of the data to convert
+        @type dataFileName: string
+        @param outputFormat: the format in which the data must be convert in.
+        @type outputFormat: string
+        @param inputFormat: the format of the data 
+        @type inputFormat: string
+        @return: the filename of the converted data.
+        @rtype: string
+        @raise UnsupportedFormatError: if the outputFormat is not supported, or if the data is in unsupported format.
+        """
+        raise NotImplementedError("this method must be overriden")
+        return dataFileName 
+    
+    
+    def convertedFormat(self):
+        """
+        @return: the list of allowed conversion ( inputFormat , outputFormat ) 
+        @rtype: [ ( string inputFormat, string outputFormat )  , ... ]
+        """
+        raise NotImplementedError("this method must be overriden")
+        return [ ]
+
+
+
diff --git a/Src/Mobyle/Converter/__init__.py b/Src/Mobyle/Converter/__init__.py
new file mode 100644
index 0000000..3fed318
--- /dev/null
+++ b/Src/Mobyle/Converter/__init__.py
@@ -0,0 +1,28 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+import glob 
+import os 
+
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    import sys
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+for f in glob.glob( os.path.join( MOBYLEHOME , 'Src' , 'Mobyle' , 'Converter' , '*.py' ) ):
+    module_name = os.path.splitext( os.path.basename(f) )[0]
+    if module_name != '__init__':
+        try: 
+            module = __import__( module_name , globals(), locals(), [ module_name ])
+            klass = getattr( module , module_name)
+            locals()[ module_name ] = klass
+        except Exception , e:
+            continue
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/clustal_phyml.py b/Src/Mobyle/Converter/clustal_phyml.py
new file mode 100644
index 0000000..4161a19
--- /dev/null
+++ b/Src/Mobyle/Converter/clustal_phyml.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+
+class clustal_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( clustal_phyml , self ).__init__( path )
+        self.program_name = 'Clustal2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat != 'clustal':
+            raise UnSupportedFormatError("support only clustal as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:    
+                align = AlignIO.read(data_input, 'clustal')
+            except Exception, err:
+                f_log.error( "failed to read clustal alignment", exc_info= True)
+                raise UnSupportedFormatError( "the conversion from clustal to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise UnSupportedFormatError( "the convertion from clustal to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('CLUSTAL', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/fasta_phyml.py b/Src/Mobyle/Converter/fasta_phyml.py
new file mode 100644
index 0000000..a969f94
--- /dev/null
+++ b/Src/Mobyle/Converter/fasta_phyml.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+
+class fasta_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( fasta_phyml , self ).__init__( path )
+        self.program_name = 'Fasta2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat != 'fasta':
+            raise UnSupportedFormatError("support only fasta as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:
+                align = AlignIO.read(data_input, 'fasta')
+            except Exception, err:
+                f_log.error( "failed to read fasta alignment", exc_info= True)
+                raise MobyleError( "the conversion from fasta to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise MobyleError( "the convertion from fasta to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('FASTA', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/nexus_phyml.py b/Src/Mobyle/Converter/nexus_phyml.py
new file mode 100644
index 0000000..33c1c55
--- /dev/null
+++ b/Src/Mobyle/Converter/nexus_phyml.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+
+class nexus_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( nexus_phyml , self ).__init__( path )
+        self.program_name = 'Nexus2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat != 'nexus':
+            raise UnSupportedFormatError("support only nexus as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:    
+                align = AlignIO.read(data_input, 'nexus')
+            except Exception, err:
+                f_log.error( "failed to read nexus alignment", exc_info= True)
+                raise UnSupportedFormatError( "the conversion from nexus to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise UnSupportedFormatError( "the convertion from nexus to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('NEXUS', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/phylipi_phyml.py b/Src/Mobyle/Converter/phylipi_phyml.py
new file mode 100644
index 0000000..53ecc83
--- /dev/null
+++ b/Src/Mobyle/Converter/phylipi_phyml.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+import string
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+from Bio.AlignIO.PhylipIO import RelaxedPhylipWriter
+
+def write_alignment(self, alignment):
+    """
+    Write a relaxed phylip alignment o
+    allow white spaces in sequence name
+    replace white space by '_'
+    """
+    # Check inputs
+    for s in alignment:
+        name = s.id.strip()
+        for c in string.whitespace:
+            if c in name:
+                name = name.replace(c, "_")
+                s.id = name
+    # Calculate a truncation length - maximum length of sequence ID plus a
+    # single character for padding
+    # If no sequences, set id_width to 1. super(...) call will raise a
+    # ValueError
+    if len(alignment) == 0:
+        id_width = 1
+    else:
+        id_width = max((len(s.id.strip()) for s in alignment)) + 1
+    super(RelaxedPhylipWriter, self).write_alignment(alignment, id_width)
+    
+RelaxedPhylipWriter.write_alignment =  write_alignment
+       
+class phylipi_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( phylipi_phyml , self ).__init__( path )
+        self.program_name = 'PhylipI2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat not in ('phylip', 'phylipi'):
+            raise UnSupportedFormatError("support only phylip as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:    
+                align = AlignIO.read(data_input, 'phylip')
+            except Exception, err:
+                f_log.error( "failed to read phylip alignment", exc_info= True)
+                raise UnSupportedFormatError( "the conversion from phylip to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise UnSupportedFormatError( "the convertion from fasta to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('PHYLIP', 'PHYLIP-RELAXED'), ('PHYLIPI', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/phylips_phyml.py b/Src/Mobyle/Converter/phylips_phyml.py
new file mode 100644
index 0000000..0f95ca2
--- /dev/null
+++ b/Src/Mobyle/Converter/phylips_phyml.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+
+class phylips_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( phylips_phyml , self ).__init__( path )
+        self.program_name = 'PhylipS2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat != 'phylips':
+            raise UnSupportedFormatError("support only phylip sequential as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:    
+                align = AlignIO.read(data_input, 'phylip-sequential')
+            except Exception, err:
+                f_log.error( "failed to read phylip sequential alignment", exc_info= True)
+                raise UnSupportedFormatError( "the conversion from phylip sequential to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise UnSupportedFormatError( "the convertion from phylip sequential to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('PHYLIPS', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/Converter/squizz_alignment.py b/Src/Mobyle/Converter/squizz_alignment.py
new file mode 100644
index 0000000..42fe50c
--- /dev/null
+++ b/Src/Mobyle/Converter/squizz_alignment.py
@@ -0,0 +1,177 @@
+########################################################################################
+#                                                                                      #
+#   Author: Sandrine Larroude                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+This is a squizz converter module for alignment
+"""
+
+import os, re
+from subprocess import Popen , PIPE
+
+from logging import getLogger
+s_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+class squizz_alignment( DataConverter ):
+    
+    def __init__(self , path ):
+        super( squizz_alignment , self ).__init__( path )
+        self.program_name = 'squizz'
+
+    def detect( self, dataFileName ):
+        """
+        detect the format of the data.
+        @param dataFileName: the filename of the data which the format must be detected
+        @type dataFileName: string
+        @return: the format of this data and the number of entry. 
+               if the format cannot be detected, return None
+               if the number of entry cannot be detected, return None
+        @rtype: ( string format , int number of entry ) 
+        """
+        squizz_path = self.path
+        
+        if squizz_path is None:
+            s_log.critical( "squizz path is not configured." )
+            raise MobyleError , 'squizz is required to handle Alignment data but not configured.(See section SEQCONVERTER on Config.py)'
+        
+        nb, format = None, None
+        try:
+            squizz_pipe = Popen( [ squizz_path , "-An" , dataFileName ] ,
+                                 shell = False ,
+                                 stdout = None ,
+                                 stdin = None ,
+                                 stderr = PIPE
+                                 )
+        except OSError, err:
+            msg = "squizz exit abnormally: " + str( err )
+            s_log.critical( msg )
+            raise MobyleError, msg
+        
+        squizz_pipe.wait()
+        if squizz_pipe.returncode != 0:
+            msg = ''.join( squizz_pipe.stderr.readlines() )
+            match = re.search( "squizz: invalid option -n" , msg )
+            if match:
+                msg = "Your squizz binary is too old. Please upgrade it."
+                s_log.critical( msg )
+            raise MobyleError , msg
+        
+        for line in squizz_pipe.stderr:
+            match = re.search( ": (.+) format, (\d+) entries\.$" ,  line)
+            if match :
+                format = match.group(1)
+                nb = int( match.group(2))
+                break
+        
+        if format == "UNKNOWN": 
+            format, nb = None, None
+            
+        return (format, nb)
+    
+    def detectedFormat(self):
+        """
+        @return: the list of detectable formats.
+        @rtype: list of strings
+        """
+        return [ 'CLUSTAL',
+                 'PHYLIP',
+                 'PHYLIPI',
+                 'PHYLIPS',
+                 'FASTA',
+                 'MEGA',
+                 'MSF',
+                 'NEXUS',
+                 'STOCKHOLM' 
+                ]
+    
+    
+    def convert( self, dataFileName , outputFormat , inputFormat = None):
+        """
+        convert a data in the format outputFormat
+        @param dataFileName: the filename of the data to convert
+        @type dataFileName: string
+        @param outputFormat: the format in which the data must be convert in.
+        @type outputFormat: string
+        @param inputFormat: the format of the data 
+        @type inputFormat: string
+        @return: the filename of the converted data.
+        @rtype: string
+        @raise UnsupportedFormatError: if the outputFormat is not supported, or if the data is in unsupported format.
+        """
+        squizz_path =  self.path
+    
+        if squizz_path is None:
+            s_log.critical( "squizz path is not configured." )
+            raise MobyleError , 'squizz is required to handle Alignment data but not configured.(See section SEQCONVERTER on Config.py)'
+    
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat.lower()
+        try:
+            outFile = open( outFileName , 'w' )
+        except IOError, err :
+            s_log.error( "Can't write outFile:" + str( err ) )
+            raise MobyleError , "Alignment Conversion Error: "+ str( err )
+        
+        #number of entries is also returned but not useful
+        det_format , _ = self.detect (dataFileName) 
+        
+        if det_format:
+            #Command building
+            cmde =  [ squizz_path , "-c", outputFormat ]
+            if  inputFormat:
+                cmde += [ "-f" , inputFormat ,
+                         dataFileName    ]
+            else:
+                cmde.append( dataFileName )
+                
+            try:
+                squizz_pipe = Popen( cmde ,
+                                     shell  = False ,
+                                     stdout = outFile ,
+                                     stdin  = None ,
+                                     stderr = PIPE
+                                     )
+            except OSError, err:
+                msg = "squizz exit abnormally: " + str( err )
+                s_log.critical( msg )
+                raise MobyleError, msg
+                
+            squizz_pipe.wait()
+            err = ''.join( squizz_pipe.stderr.readlines() )
+            if squizz_pipe.returncode != 0:
+                msg = err
+                match = re.search( ".*: unsupported format" , err )
+                if match:
+                    s_log.error( msg )
+                    raise  UnSupportedFormatError , msg
+                match = re.search( "squizz: invalid option -- n" , err )
+                if match:
+                    msg = "your squizz binary is too old. Please upgrade it"
+                    s_log.critical( msg )
+                raise MobyleError, msg
+            else:
+                outFile.close()
+                return outFileName 
+        else:
+            # the inFormat is not recognize
+            raise UnSupportedFormatError
+    
+    
+    def convertedFormat(self):
+        """
+        @return: the list of allowed conversion ( inputFormat , outputFormat ) 
+        @rtype: [ ( string inputFormat, string outputFormat )  , ... ]
+        """
+        conversions = []
+        formats = self.detectedFormat()
+        for inputFormat in formats:
+            for outputFomat in formats:
+                conversions.append( ( inputFormat , outputFomat) )
+        return conversions
+    
diff --git a/Src/Mobyle/Converter/squizz_sequence.py b/Src/Mobyle/Converter/squizz_sequence.py
new file mode 100644
index 0000000..b24e82f
--- /dev/null
+++ b/Src/Mobyle/Converter/squizz_sequence.py
@@ -0,0 +1,172 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+This module is used as template to build a converter module
+"""
+import os
+import re
+from subprocess import Popen , PIPE
+from logging import getLogger
+_log = getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+class squizz_sequence( DataConverter ):
+    
+    def __init__(self , path ):
+        super( squizz_sequence , self ).__init__( path )
+        self.program_name = 'squizz'
+        
+        
+    def suffixe( self , format ):
+        return '.' + format.lower()
+               
+    def detect( self, dataFileName ):
+        """
+        detect the format of the data.
+        @param dataFileName: the filename of the data which the format must be detected
+        @type dataFileName: string
+        @return: the format of this data. if the format cannot be detected , return None
+        @rtype: string
+        """
+        
+        if self.path is None :
+            _log.critical( "squizz path is not configured" )
+            raise MobyleError , 'squizz is required to handle data Sequence but not configured' 
+        else:
+            try:
+                squizz_pipe = Popen( [ self.path , "-Sn" , dataFileName ] ,
+                                     shell = False ,
+                                     stdout = None ,
+                                     stdin = None ,
+                                     stderr = PIPE
+                                     )
+            except OSError , err :
+                msg = "squizz exit abnormally: " + str(err)
+                _log.critical( msg )
+                raise MobyleError, msg
+            squizz_pipe.wait()
+            if squizz_pipe.returncode != 0:
+                msg = ''.join( squizz_pipe.stderr.readlines() )
+                match = re.search( "squizz: invalid option -- n" , msg )
+                if match:
+                    msg = "your squizz binary is too old. Please upgrade it"
+                    _log.critical( msg )
+                raise MobyleError , msg
+                    
+            for line in squizz_pipe.stderr :
+                match = re.search( ": (.+) format, (\d+) entries\.$" ,  line)
+                if match :
+                    format = match.group(1)
+                    seq_nb = int( match.group(2))
+                    break
+            if match and format != "UNKNOWN":
+                return ( format , seq_nb )
+            else:
+                return ( None  , None )
+         
+    def detectedFormat(self):
+        """
+        @return: the list of detectables formats.
+        @rtype: list of stings
+        """
+       
+        return [ 'SWISSPROT' , 
+                 'EMBL' ,
+                 'GENBANK',
+                 'CODATA',
+                 'NBRF',
+                 'GDE',
+                 'IG',
+                 'FASTA',
+                 'GCG',
+                 'RAW']
+
+        
+    def convert( self , dataFileName , outputFormat , inputFormat = None ):
+        """
+        convert a data in the format outputFormat
+        @param dataFileName: the filename of the data to convert
+        @type dataFileName: string
+        @param outputFormat: the format in which the data must be convert in.
+        @type outputFormat: string
+        @param inputFormat: the format of the data 
+        @type inputFormat: string
+        @return: the filename of the converted data.
+        @rtype: string
+        @raise UnsupportedFormatError: if the outputFormat is not supported, or if the data is in unsupported format.
+        """
+        
+        outFileName = os.path.splitext( dataFileName )[0] + self.suffixe( outputFormat )
+        cmde = [ self.path ,
+                "-S" ,
+                "-n" ,
+                "-c" , outputFormat ]
+        if  inputFormat:
+            cmde += [ "-f" , inputFormat ,
+                     dataFileName
+                     ]
+        else:
+            cmde.append( dataFileName )
+        try:
+            outFile = open( outFileName , 'w' )
+        except IOError ,err :
+            _log.error( "can't write outFile:" + str( err ) )
+            raise MobyleError , "Sequence Convertion Error: "+ str( err )
+        try:
+            squizz_pipe = Popen( cmde ,
+                                 shell  = False ,
+                                 stdout = outFile ,
+                                 stdin  = None ,
+                                 stderr = PIPE
+                                 )
+        except OSError, err:
+            msg = "squizz exit abnormally: " + str(err)
+            _log.critical( msg )
+            raise MobyleError, msg
+            
+        squizz_pipe.wait()
+        err = ''.join( squizz_pipe.stderr.readlines() )
+        if squizz_pipe.returncode != 0:
+            msg = err
+            match = re.search( ".*: unsupported format" , err )
+            if match:
+                _log.error( msg )
+                raise  UnSupportedFormatError , msg
+            match = re.search( "squizz: invalid option -- n" , err )
+            if match:
+                msg = "your squizz binary is too old. Please upgrade it"
+                _log.critical( msg )
+            raise MobyleError , msg
+        else:
+            outFile.close()
+            match = re.search(  "(: \w+)?: (.+) format, (\d+) entries\.$",  err )
+            if match:
+                detectFormat = match.group(2)
+                #seq_nb = int( match.group(3) )
+            else:
+                raise UnSupportedFormatError , str( err )
+            if detectFormat != "UNKNOWN":
+                return outFileName 
+            else:
+                # the inFormat is not recognize  
+                raise UnSupportedFormatError 
+    
+    
+    def convertedFormat(self):
+        """
+        @return: the list of allowed conversion ( inputFormat , outputFormat ) 
+        @rtype: [ ( string inputFormat, string outputFormat )  , ... ]
+        """
+        formats = self.detectedFormat()
+        return [ ( inputFormat , outputFomat) for inputFormat in formats for outputFomat in formats ]
+    
+
+
diff --git a/Src/Mobyle/Converter/stockholm_phyml.py b/Src/Mobyle/Converter/stockholm_phyml.py
new file mode 100644
index 0000000..be7b20a
--- /dev/null
+++ b/Src/Mobyle/Converter/stockholm_phyml.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Néron                                                          #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+from logging import getLogger
+f_log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+from Mobyle.Converter.DataConverter import DataConverter
+
+from Bio import AlignIO
+
+class stockholm_phyml(DataConverter):
+    
+    def __init__(self, path):
+        super( stockholm_phyml , self ).__init__( path )
+        self.program_name = 'Stockholm2Phylip-relaxed'
+
+    def detect(self, dataFileName):
+        #we lay on squizz to detect the format file
+        return None, None
+    
+    def detectedFormat(self):
+        #we lay on squizz to detect the format file
+        return []
+    
+    def convert(self, dataFileName , outputFormat , inputFormat = None):
+        outputFormat = outputFormat.lower()
+        assert outputFormat == 'phylip-relaxed'
+        
+        if inputFormat is None:
+            raise UnSupportedFormatError("the input format must be specify" )
+        inputFormat = inputFormat.lower()
+        
+        if inputFormat != 'stockholm':
+            raise UnSupportedFormatError("support only stockholm as input, provide " + str(inputFormat))
+        with open(dataFileName) as data_input:
+            #read allow only one multiple alignment
+            #for more msa (after bootstarp) use parse
+            #but for now squiz manage only one msa perfile
+            try:    
+                align = AlignIO.read(data_input, 'stockholm')
+            except Exception, err:
+                f_log.error( "failed to read stockholm alignment", exc_info= True)
+                raise UnSupportedFormatError( "the conversion from stockholm to phylip-relaxed failed")
+        outFileName = os.path.splitext( dataFileName )[0] + "." + outputFormat
+        with  open(outFileName, 'w') as output:
+            try:
+                AlignIO.write(align, output , 'phylip-relaxed')
+            except Exception, err:
+                f_log.error( "failed to write phylip-relaxed alignment", exc_info= True)
+                raise UnSupportedFormatError( "the convertion from stockholm to phylip-relaxed failed")    
+        return outFileName 
+    
+    def convertedFormat(self):
+        return [('STOCKHOLM', 'PHYLIP-RELAXED')]
\ No newline at end of file
diff --git a/Src/Mobyle/DataInputsIndex.py b/Src/Mobyle/DataInputsIndex.py
new file mode 100644
index 0000000..28388fb
--- /dev/null
+++ b/Src/Mobyle/DataInputsIndex.py
@@ -0,0 +1,120 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+"""
+Mobyle.Index
+
+This module manages the list of available programs to:
+ - search them (using search fields)
+ - classify them (building the categories tree)
+ - cache this information (on disk as a JSON file)
+"""
+from logging import getLogger
+r_log =getLogger(__name__)
+
+from Mobyle import IndexBase
+
+queries = {
+           'title': '/*/head/doc/description/text/text()', 
+           'name': '/*/head/name/text()', 
+           'parameter': './/parameter[name]',
+           'parameter_isinput': '(not(@isout) and not(@isstdout) and not(@ishidden))',
+           'parameter_name': 'name/text()',
+           'parameter_prompt': 'prompt/text()',
+           'parameter_type': 'type',
+           'parameter_biotype': 'biotype/text()',
+           'parameter_datatype': 'datatype',
+           'parameter_class': 'class/text()',
+           'parameter_superclass': 'superclass/text()',
+           'parameter_cardinality': 'card/text()',
+           'parameter_dataformats': './/dataFormat/text()',
+          }
+
+nifdt = [None, 
+         '',
+         'Boolean', 
+         'Integer', 
+         'Float', 
+         'String', 
+         'Choice', 
+         'MultipleChoice', 
+         'FileNameDataType', 
+         'StructureDataType', 
+         'PropertiesDataType']
+
+class DataInputsIndex(IndexBase.Index):
+    
+    indexFileName = 'inputs.dat'
+    
+    def getList(self):
+        inputs_list = {}
+        for key, entry in self.index.items():
+            for item in entry:
+                inputs_list['%s|%s' % (key, item['name'])] = item
+        return inputs_list
+    
+    @classmethod
+    def getIndexEntry(cls, doc, program):
+        """
+        Return an description index entry value
+        @return: the index entry: value
+        @rtype: object
+        """
+        inputParameters=[]
+        programName = IndexBase._XPathQuery(doc, queries['name'])
+        programPID = programName
+        if program.server.name != 'local':
+            programName += '@'+program.server.name
+            programPID = program.server.name + '.' + programPID
+        programTitle = IndexBase._XPathQuery(doc, queries['title'])
+        pars = IndexBase._XPathQuery(doc, queries['parameter'], 'rawResult')
+        for p in pars:
+            parameter = {}
+            parameter['isInput'] = str(IndexBase._XPathQuery(p, \
+                                            queries['parameter_isinput'],\
+                                            'rawResult'))
+            parameter['name'] = IndexBase._XPathQuery(p, queries['parameter_name'])
+            parameter['programName'] = programName
+            parameter['programPID'] = programPID
+            parameter['programTitle'] = programTitle
+            parameter['prompt'] = IndexBase._XPathQuery(p, queries['parameter_prompt'])
+            parType = IndexBase._XPathQuery(p, \
+                                  queries['parameter_type'], 
+                                  'rawResult')[0]
+            parameter['bioTypes'] = IndexBase._XPathQuery(parType, \
+                                         queries['parameter_biotype'],"valueList")
+            parDataType = IndexBase._XPathQuery(parType, \
+                                      queries['parameter_datatype'],\
+                                      'rawResult')[0]
+            parameter['dataTypeClass'] = IndexBase._XPathQuery(parDataType, \
+                                      queries['parameter_class'])
+            parameter['dataTypeSuperClass'] = IndexBase._XPathQuery(parDataType, \
+                                           queries['parameter_superclass'])
+            parameter['dataTypeFormats'] = IndexBase._XPathQuery(parType, \
+                                         queries['parameter_dataformats'],"valueList")
+            card = IndexBase._XPathQuery(parType, queries['parameter_cardinality'])
+            mincard = 1
+            maxcard = 1
+            if card != '':
+                card = card.split(',')
+                mincard = card[0]
+                if len(card) > 1:
+                    maxcard = card[1]
+                else:
+                    maxcard = card[0]
+            parameter['minCard'] = mincard
+            parameter['maxCard'] = maxcard
+            #parameter['id']=program.url+'|'+parameter['name']
+            for key, value in parameter.items():
+                if value == '':
+                    parameter[key]=None
+            if parameter['isInput']=='True' and parameter['dataTypeClass'] not in nifdt:
+                inputParameters.append(parameter)
+        return inputParameters
\ No newline at end of file
diff --git a/Src/Mobyle/DataProvider.py b/Src/Mobyle/DataProvider.py
new file mode 100644
index 0000000..7b8e941
--- /dev/null
+++ b/Src/Mobyle/DataProvider.py
@@ -0,0 +1,39 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+import urllib #@UnresolvedImport
+from logging import getLogger #@UnresolvedImport
+log = getLogger(__name__)
+
+from Mobyle.MobyleError import MobyleError
+
+
+class DataProvider(object):
+    """DataProvider is an object that mimicks a session or a job from which a data consumer
+    (e.g., another job) gets data files.
+    """
+    
+    @classmethod
+    def get(cls, o):
+        if hasattr(o,"isLocal") and hasattr(o,"getDir"):
+            return o
+        elif isinstance(o,basestring):
+            return DataProvider(o)
+        else:
+            raise MobyleError("please provide either a data provider object (Session or Job-like) or a url string")
+    
+    def __init__(self, url):
+        self.url = url
+        
+    def isLocal(self):
+        return not(self.url.startswith('http')) # false but...
+    
+    def getDir(self):
+        return self.url
+    
+    def getOutputFile(self,fileName): # mimics job's getOutputFile (called by Core toFile() methods)
+        return urllib.urlopen('%s/%s' % (self.url, fileName)).read()
\ No newline at end of file
diff --git a/Src/Mobyle/DataTypeValidator.py b/Src/Mobyle/DataTypeValidator.py
new file mode 100644
index 0000000..f3838c4
--- /dev/null
+++ b/Src/Mobyle/DataTypeValidator.py
@@ -0,0 +1,113 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+DataTypeValidator.py
+
+This module is used to validate the datatypes of a Mobyle service description
+- The DataTypeValidator class
+"""
+from lxml import etree
+import inspect
+
+from Mobyle.ConfigManager import Config
+from Mobyle import Classes
+from Local import CustomClasses
+_cfg = Config()
+
+from logging import getLogger
+v_log = getLogger(__name__)
+
+net_enabled_parser = etree.XMLParser(no_network=False)
+
+class DataTypeValidator(object):
+    """
+    Check if the Mobyle DataTypes announced by a service are valid, w.r.t:
+    * the "Generic" and "Local" Mobyle DataTypes, declared in Python classes
+    * the previously seen Mobyle DataTypes (if the validator object is the same,
+    it is not a Singleton)
+    """
+
+    
+    def __init__(self):
+        """
+        Load the object, which should be reused in successive validations to compare
+        the dynamic "XML" DataTypes.
+        """
+        self.datatypesList = {}
+        """
+        datatypesList is a dictionary containing the different registered DataTypes in the
+        following structure:
+        * the key is the DataType name
+        * the value is:
+          - None if the DataType is "Python-declared"
+          - a dictionary if it is declared on the fly, with a 'superclass' key that stores the
+            declared superclass, and a list of dictionaries storing the parameter and service 
+            where it has been declared.
+        """
+        self.loadPythonDataTypes()
+        
+    def loadPythonDataTypes(self):
+        """
+        load python-defined datatype names
+        """
+        for name, obj in inspect.getmembers(CustomClasses) + inspect.getmembers(Classes):
+            if inspect.isclass(obj) and issubclass(obj,Classes.DataType) and obj!=Classes.DataType:
+                self.datatypesList[name.rpartition('DataType')[0]] = None
+
+    def validateDataTypes(self, docPath=None, docString=None):
+        """
+        validate the datatypes for a service, passed as a path to an XML file or as an XML string.
+        @param docPath: the path to the service
+        @type docPath: {String}
+        @param docString: A string storing the XML corresponding to the service
+        @type docString: {String}
+        @return: the list of error messages which were generated during validation
+        @rtype: {List}
+        """
+        assert (docPath is not None and docString is None) or (docPath is None and docString is not None), "Please specify either a path or a string. Your parameters:\n-path=%s\n-string=%s" %(docPath, docString)
+        if docPath:        
+            self.doc = etree.parse(docPath, parser=net_enabled_parser)
+        else:
+            self.doc = etree.fromstring(docString)
+        self.doc.xinclude()
+        params = self.doc.xpath('//parameter')
+        service_name = self.doc.xpath('/*/head/name/text()')
+        errors = []
+        for r in params:
+            parameter_name = r.xpath('name/text()')[0]
+            ctx = "parameter %s: " % parameter_name
+            try:
+                dt_class = r.xpath('type/datatype/class/text()')[0]
+            except IndexError:
+                errors.append( ctx + 'the "class" element is empty' )
+            dt_sclass = r.xpath('type/datatype/superclass/text()')
+            if len(dt_sclass)>0:
+                dt_sclass = dt_sclass[0]
+            else:
+                dt_sclass = None
+            if self.datatypesList.has_key(dt_class):
+                if self.datatypesList.get(dt_class) is not None and self.datatypesList.has_key(dt_class):
+                    if self.datatypesList.get(dt_class).has_key('superclass'):
+                        if not(self.datatypesList.get(dt_class).get('superclass')==dt_sclass):
+                            where_used_string = ['\n- parameter %s of service %s' % (w['parameter'],w['service']) for w in self.datatypesList.get(dt_class)['where']]
+                            errors.append(ctx + "redefining %s which is already defined as a subclass of %s as a subclass of %s in %s: " %\
+                                (dt_class, self.datatypesList.get(dt_class), dt_sclass, where_used_string))
+                        else:
+                            self.datatypesList[dt_class]['where'].append({'parameter':parameter_name,'service':service_name})
+            else:
+                if not(dt_sclass):
+                    errors.append(ctx + "unknown datatype %s (no superclass and not defined in Python types)" % dt_class)
+                elif not(dt_sclass in self.datatypesList.keys()):
+                    errors.append(ctx + "defining the new \"XML\" datatype %s using the unknown %s superclass" % (dt_class, dt_sclass))
+                elif not(self.datatypesList.get(dt_class)==None):
+                    errors.append(ctx + "defining the new \"XML\" datatype %s using the %s \"XML\" superclass" % (dt_class, dt_sclass))                        
+                else:
+                    self.datatypesList[dt_class] = {'superclass':dt_sclass,'where':[{'parameter':parameter_name,'service':service_name}]}
+        return errors
\ No newline at end of file
diff --git a/Src/Mobyle/DescriptionsIndex.py b/Src/Mobyle/DescriptionsIndex.py
new file mode 100644
index 0000000..a2fe67b
--- /dev/null
+++ b/Src/Mobyle/DescriptionsIndex.py
@@ -0,0 +1,47 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+Mobyle.Index
+
+This module manages the list of available programs to:
+ - search them (using search fields)
+ - classify them (building the categories tree)
+ - cache this information (on disk as a JSON file)
+"""
+from Mobyle.Registry import registry
+
+from logging import getLogger
+r_log = getLogger(__name__)
+
+from Mobyle import IndexBase
+
+queries = {
+           'description': '/*/head/doc/description//text()',
+          }
+
+class DescriptionsIndex(IndexBase.Index):
+    
+    indexFileName = 'descriptions.dat'
+    
+    def fillRegistry(self):
+        for url, description in self.index.items():
+            if registry.programsByUrl.has_key(url):
+                registry.programsByUrl[url].description = description
+            if registry.workflowsByUrl.has_key(url):
+                registry.workflowsByUrl[url].description = description
+    
+    @classmethod
+    def getIndexEntry(cls, doc, program):
+        """
+        Return an description index entry value
+        @return: the index entry: value
+        @rtype: object
+        """
+        return IndexBase._XPathQuery(doc, queries['description'])
\ No newline at end of file
diff --git a/Src/Mobyle/Dispatcher.py b/Src/Mobyle/Dispatcher.py
new file mode 100644
index 0000000..fc4b0d1
--- /dev/null
+++ b/Src/Mobyle/Dispatcher.py
@@ -0,0 +1,85 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+
+class Dispatcher( object ):
+    """choose the right ExecutionSystem and queue for a program"""
+    
+    def _getJobNameFromJobState(self , jobState ):
+        job_name = jobState.getName().split( '/' )[-1]
+        job_name = job_name[:-4]
+        return job_name
+    
+    def getQueue(self , jobState ):
+        """
+        @param jobState: the jobState of a job
+        @type jobState: a MobyleJobState instance
+        @return: the queue for this job
+        @rtype: string
+        """
+        raise NotImplementedError( "you must redefined getQueue method in your subclass")
+    
+    def getExecutionConfig(self , jobState ):
+        """
+        @param jobState: the jobState of a job
+        @type jobState: a MobyleJobState instance
+        @return: the execution system to use to launch this job
+        @rtype: ExecutionSystem instance
+        """
+        raise NotImplementedError( "you must redefined getExecutionConfig method in your subclass")
+    
+    
+    
+class DefaultDispatcher( Dispatcher ):
+    
+    
+    def __init__(self, routes ):
+        """
+        @param routes: represent all execution system and queue associated to each programs name
+        @type routes: dict which keys are the names of programs and values a tuple
+        the first value of the tuple is an EXECUTION_SYSTEM_ALIAS entry and the 2nde value a queu name
+        routes = { string program name : (  ExecutionConfig object from EXECUTION_SYSTEM_ALIAS , string queue name ) , ...}
+        """
+        self.routes = routes
+    
+    def getExecutionConfig(self , jobState ):
+        """
+        @param jobState: the jobState of a job
+        @type jobState: a MobyleJobState instance
+        @return: the execution config need to launch this job
+        @rtype: ExecutionConfig instance
+        """
+        job_name = self._getJobNameFromJobState( jobState )
+        if job_name in self.routes:
+            exec_sys = self.routes[ job_name ][0] 
+        else:
+            exec_sys = self.routes[ 'DEFAULT' ][0]
+        return exec_sys
+        
+    def getQueue(self , jobState ):
+        """
+        @param jobState: the jobState of a job
+        @type jobState: a MobyleJobState instance
+        @return: the queue for this job
+        @rtype: string
+        """
+        job_name = self._getJobNameFromJobState( jobState )
+        if job_name in self.routes:
+            queue = self.routes[ job_name ][1] 
+        else:
+            queue = self.routes[ 'DEFAULT' ][1]
+        
+        return queue
+    
+    def routes(self):
+        """
+        @return: all routes for this configuration
+        @rtype: list of tuples ( ExecutionSystem instance , queue )
+        """
+        return self.routes.keys()
\ No newline at end of file
diff --git a/Src/Mobyle/Evaluation.py b/Src/Mobyle/Evaluation.py
new file mode 100644
index 0000000..36351aa
--- /dev/null
+++ b/Src/Mobyle/Evaluation.py
@@ -0,0 +1,128 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import imp
+from logging import getLogger
+e_log = getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError , EvaluationError
+
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+
+class Evaluation:
+    """
+    The Evaluation Class stock the parameter values and permit to evaluate expression (Ctrl precond format ...) in a protected environment avoidig names collision
+    """
+    def __init__(self , dict= None ):
+        try:
+            fp , pathname , description = imp.find_module("re")
+            self.re = imp.load_module( "re" , fp , pathname , description )
+            
+        finally:
+            if fp:
+                fp.close()
+        if dict:
+            for key in dict.keys():
+                setattr(self , key , dict[key])
+
+        
+    def isFill( self ):
+        """
+        @return: returns True if the Evaluation is already filled, if there isn't any user value in evalution return False.
+        @rtype: boolean
+        """
+        
+        for var in self.__dict__.keys():
+            if var == "re" or var == "__builtins__":
+                continue
+            else:
+                return True
+        return False
+
+
+    def _2dict_( self ):
+        """
+        @return: Returns a dictionary containing the attributes creates by L{setVar}
+        @rtype: dictionary
+        """
+        dict = {}
+        
+        for param in self.__dict__.keys():
+            if param == "__builtins__" or param == "vdef" or param == "value" or param == "re" :
+                pass
+            else:
+                dict[ param ] = self.__dict__[ param ]
+                
+        return dict
+
+    def setVar(self, varName , value):
+        """
+        Creates an attribute varName with the value value
+             
+        @param varName: the name of the attribute to create
+        @type varName: String
+        @param value: the value of the attribute varName
+        @type value: any
+      
+        """
+        setattr(self , varName , value)
+  
+
+    def getVar(self, varName):
+        """
+        @param varName: the name of the parameter 
+        @type varName: String
+        @return: the value of the varName attribute or None if there is no attribute varName  
+        """
+        try:
+            return getattr(self, varName)
+        except AttributeError:
+            return None
+   
+          
+    def eval(self, expr):
+        """
+        eval the expr in self namespace. be careful to specify the value and vdef before calling eval.
+        @param expr: the expression to evalute
+        @type expr: String
+        @return: the expression evaluated in the Evalution namespace
+        @todo: evaluer le temps passe a importe re a chaque fois, le faire que si re dans la string a evalue?
+        """
+        try:
+            myEval = eval( expr , vars(self) )
+            return myEval
+        
+        except Exception , err:
+            raise EvaluationError , err
+                  
+
+
+    def isDefined(self, varName):
+        """
+        we decided to fill the evaluation instance with all the parameter of the service.
+        if the user provide a value or there is a vdef for the parameter the value is affected
+        to the parameter.
+        else the parameter will be affected at None
+        @return: True if the varName has been defined (not None), False Otherwise
+        """
+        try:
+            if getattr(self, varName) == None:
+                return False
+            else:
+                return True
+        except NameError:
+            msg = "the variable: "+str(varName)+" is unknown"
+            e_log.error(msg)
+            raise MobyleError , msg
+
+                  
+
diff --git a/Src/Mobyle/Execution/DRMAA.py b/Src/Mobyle/Execution/DRMAA.py
new file mode 100755
index 0000000..7b817cb
--- /dev/null
+++ b/Src/Mobyle/Execution/DRMAA.py
@@ -0,0 +1,300 @@
+
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os 
+import imp
+from logging import getLogger, shutdown as logging_shutdown
+_log = getLogger(__name__)
+from Mobyle.MobyleLogger import MLogger
+
+from Mobyle.Status import Status
+from Mobyle.Admin import Admin
+from Mobyle.Execution.ExecutionSystem import ExecutionSystem 
+
+from Mobyle.MobyleError import MobyleError
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+class DRMAA(ExecutionSystem):
+    """
+    Run the commandline with batch system DRMAA bindings
+    """
+    def __init__( self, drmaa_config ):
+        super( DRMAA , self ).__init__( drmaa_config )
+        self.drmaa_library_path = drmaa_config.drmaa_library_path
+        os.environ[ 'DRMAA_LIBRARY_PATH' ] = self.drmaa_library_path
+        fp , pathname , description = imp.find_module("drmaa")
+        self.drmaa = imp.load_module( "drmaa" , fp , pathname , description )
+        self.contactString = drmaa_config.contactString
+        
+    def _drmaaStatus2mobyleStatus( self , drmaaStatus ):
+        if drmaaStatus == self.drmaa.JobState.RUNNING:
+            return Status( 3 ) #running
+        elif drmaaStatus == self.drmaa.JobState.UNDETERMINED:
+            return Status( -1 ) #unknown
+        elif drmaaStatus == self.drmaa.JobState.QUEUED_ACTIVE:
+            return Status( 2 ) #pending
+        elif drmaaStatus == self.drmaa.JobState.DONE:
+            return Status( 4 ) #finished
+        elif drmaaStatus == self.drmaa.JobState.FAILED:
+            return Status( 5 ) # error
+        elif drmaaStatus == self.drmaa.JobState.SYSTEM_ON_HOLD:
+            return Status( 7 ) # hold
+        elif drmaaStatus == self.drmaa.JobState.USER_ON_HOLD:
+            return Status( 7 ) # hold
+        elif drmaaStatus == self.drmaa.JobState.USER_SYSTEM_ON_HOLD:
+            return Status( 7 ) # hold
+        elif drmaaStatus == self.drmaa.JobState.SYSTEM_SUSPENDED:
+            return Status( 7 ) # hold
+        elif drmaaStatus == self.drmaa.JobState.USER_SUSPENDED:
+            return Status( 7 ) # hold
+        elif drmaaStatus == self.drmaa.JobState.USER_SYSTEM_SUSPENDED:
+            return Status( 7 ) # hold
+        else:
+            return Status( -1 )
+
+
+    def _run( self , commandLine , dirPath , serviceName , jobKey , jobState , queue , xmlEnv ):
+        """
+        Run the commandLine 
+        redirect the standard error and output on service.name.out and service.name.err, then restore the sys.stderr and sys.stdout
+        @param execution_config: a configuration object for this execution system
+        @type execution_config: a L{DRMAAConfig}  instance
+        @return: the L{Status} of this job and a message
+        @rtype: Status
+        """
+        if (os.getcwd() != os.path.abspath(dirPath) ):
+            msg = "the process is not in the working directory"
+
+            self._logError( dirPath , serviceName , jobKey ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = msg )
+
+            raise MobyleError , msg
+
+        else:
+            fout = open( serviceName + ".out" , 'w' )
+            ferr = open( serviceName + ".err" , 'w' )
+            try:
+                drmaaSession = self.drmaa.Session( contactString = self.contactString )
+                try:
+                    drmaaSession.initialize()
+                except self.drmaa.errors.AlreadyActiveSessionException:
+                    pass
+                except Exception, err:
+                    self._logError( dirPath , serviceName , jobKey ,
+                                    userMsg = "Mobyle internal server error" ,
+                                    logMsg = None )
+                    import sys
+                    _log.critical( "error during drmaa intitialization for job %s/%s: %s (call by %s pid= %d)" %(serviceName, jobKey , err, sys.argv[0] , os.getpid() ) ,  exc_info= True )
+                jt = drmaaSession.createJobTemplate()
+                jt.workingDirectory = dirPath
+                jt.jobName = jobKey
+                jt.outputPath = ":" + os.path.join( dirPath , fout.name )
+                jt.errorPath  = ":" + os.path.join( dirPath , ferr.name )
+                jt.joinFiles = False
+                jt.jobEnvironment = xmlEnv
+                jt.remoteCommand = "sh"
+                jt.args = [ os.path.join( dirPath , ".command" ) ]
+                nativeSpecification = ''
+                if self.execution_config.nativeSpecification:
+                    nativeSpecification = self.execution_config.nativeSpecification
+                elif queue:
+                    nativeSpecification = "%s -q %s" % ( nativeSpecification , queue )
+                if nativeSpecification:
+                    jt.nativeSpecification = nativeSpecification
+                jt.blockEmail = True
+                drmJobid = drmaaSession.runJob( jt )
+            except self.drmaa.errors , err :
+                _log.error( "cannot exit from drmaa properly try to deleting JobTemplate: " + str( err ) )
+                try:
+                    drmaaSession.deleteJobTemplate( jt )
+                    drmaaSession.exit()
+                except Exception , err :
+                    _log.error( "cannot exit from drmaa properly : " + str( err ) )
+                msg= "System execution failed: " +str( err ) 
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                _log.critical( "%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+                raise MobyleError , msg 
+            except Exception , err :
+                msg= "System execution failed: " +str( err )
+                _log.critical("%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+                raise MobyleError( "Internal Server Error")
+                    
+            adm = Admin( dirPath )
+            adm.setExecutionAlias( self.execution_config_alias ) 
+            adm.setNumber( drmJobid ) 
+            adm.commit()
+            
+            linkName = ( "%s/%s.%s" %( self._cfg.admindir() ,
+                                       serviceName ,
+                                       jobKey
+                                    )
+                         )
+            try:
+                os.symlink(
+                    os.path.join( dirPath , '.admin') ,
+                    linkName
+                    )
+            except OSError , err:
+                try:
+                    drmaaSession.deleteJobTemplate( jt )
+                    drmaaSession.exit()
+                except Exception , err :
+                    _log.error( "cannot exit from drmaa properly : " + str( err ) )
+                    
+                msg = "can't create symbolic link %s in ADMINDIR: %s" %( linkName , err )
+
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                _log.critical( "%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+
+                raise MobyleError , msg
+            logging_shutdown() #close all loggers           
+            #JobInfo =( jobId , hasExited , hasSignal , terminatedSignal, hasCoreDump, wasAborted, exitStatus, resourceUsage)
+            #            0          1          2              3               4            5           6           7
+            try:
+                jobInfos = drmaaSession.wait( drmJobid , self.drmaa.Session.TIMEOUT_WAIT_FOREVER )
+            except Exception , err :
+                MLogger(child = True )
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                
+                _log.critical( "%s/%s : %s" %( serviceName ,
+                                               jobKey ,
+                                               "cannot wait the completion of job : %s" % err
+                                            )
+                                 )
+            finally:
+                MLogger(child = True )
+                try:
+                    drmaaSession.deleteJobTemplate( jt )
+                    drmaaSession.exit()
+                except Exception , err :
+                    MLogger(child = True )
+                    _log.error( "cannot exit from drmaa properly : " + str( err ) )
+            try:
+                os.unlink( linkName )
+            except Exception , err:
+                msg = "cannot remove symbolic link %s in ADMINDIR: %s" %( linkName , err )
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                _log.critical( "%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+            try:
+                fout.close()
+            except Exception , err:
+                msg = "cannot close file: %s" %( fout.name , err )
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                _log.critical( "%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+            try:
+                ferr.close()
+            except Exception , err:
+                msg = "cannot close file: %s" %( ferr.name , err )
+                self._logError( dirPath , serviceName , jobKey ,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                _log.critical( "%s/%s : %s" %(serviceName, jobKey, msg ), exc_info= True )
+                
+            status = self.status_manager.getStatus( dirPath )
+            
+            if not status.isEnded():
+                if jobInfos[ 5 ] :#wasAborted
+                    status = Status( code = 6 , message = "Your job has been cancelled" ) #killed
+                else:
+                    if jobInfos[ 1 ]:#hasExited
+                        if jobInfos[ 6 ] == 0:#exitStatus
+                            status = Status( code = 4 ) #finished
+                        elif jobInfos[ 6 ] < 0 or jobInfos[ 6 ] > 128:
+                            #all the signals that we don't know where they come from
+                            status = Status( code = 5 , message = "Your job execution failed ( %s )" %jobInfos[ 6 ] )    
+                        else:
+                            status = Status( code = 4 , message = "Your job finished with an unusual status code ( %d ), check your results carefully." % jobInfos[ 6 ] )
+                    else:
+                        status = Status( code = 6 , message = "Your job execution failed ( %d )" %jobInfos[ 6 ] ) 
+            return status
+        
+
+    def getStatus( self , key ):
+        """
+        @param execution_config: a configuration object for this execution system
+        @type execution_config: a L{DRMAAConfig}  instance
+        @param key: the value associate to the key "NUMBER" in Admin object (and .admin file )
+        @type key: string
+        @return: the status of the job corresponding to the key 
+        @rtype: Status instance
+        @call: by L{Utils.getStatus}
+        """
+        try:
+            s = self.drmaa.Session( contactString = self.contactString )
+        except Exception , err:
+            _log.error( "getStatus(%s) cannot open drmma session : %s " %( key , err ) )
+            return Status( -1 ) #unknown 
+        try:
+            s.initialize()
+        except self.drmaa.errors.AlreadyActiveSessionException:
+            pass
+        except Exception, err:
+            s.exit()
+            import sys
+            _log.critical( "error during drmaa intitialization for getStatus job %s: %s (call by %s pid=%d)" %(key , err,sys.argv[0], os.getpid()) ,  exc_info= True )
+            return Status( -1 ) 
+        try:
+            drmaaStatus = s.jobStatus( key )
+        except :
+            s.exit()
+            return Status( -1 ) #unknown 
+        s.exit()
+        return self._drmaaStatus2mobyleStatus( drmaaStatus ) 
+
+
+    def kill( self , key ):
+        """
+        kill a job
+        @param execution_config: a configuration object for this execution system
+        @type execution_config: a L{DRMAAConfig}  instance
+        @param key : the value associate to the key "NUMBER" in Admin object (and .admin file )
+        @type key: string
+        @raise MobyleError: if can't kill the job
+        @call: by L{Utils.Mkill}
+        """
+        try:
+            s = self.drmaa.Session( contactString = self.contactString )
+        except Exception , err:
+            _log.error( "kill( %s ) cannot open drmma session : %s " %( key , err ) )
+            return
+        try:
+            s.initialize()
+        except self.drmaa.errors.AlreadyActiveSessionException:
+            pass
+        except Exception, err:
+            import sys
+            _log.critical( "error during drmaa intitialization for kill job %s: %s (call by %s)" %(key , err,sys.argv[0]) ,  exc_info= True )
+            return
+            
+        try:
+            s.control( key , self.drmaa.JobControlAction.TERMINATE )
+        except Exception , err :
+            msg = "error when trying to kill job %s : %s" %( key , err )
+            _log.error( msg )
+            raise MobyleError( msg )
+        finally:    
+            s.exit()
+        return
+
diff --git a/Src/Mobyle/Execution/Dummy.py b/Src/Mobyle/Execution/Dummy.py
new file mode 100755
index 0000000..2df2f3b
--- /dev/null
+++ b/Src/Mobyle/Execution/Dummy.py
@@ -0,0 +1,156 @@
+
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os
+
+from logging import getLogger, shutdown as logging_shutdown
+_log = logging.getLogger(__name__)
+from Mobyle.MobyleLogger import MLogger
+
+from Mobyle.Execution.ExecutionSystem import ExecutionSystem 
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Admin import Admin
+from Mobyle.Status import Status
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+class Dummy(ExecutionSystem):
+    """
+    Run the commandline by ???
+    """
+
+    def run(self, commandLine , dirPath , serviceName , jobKey , jobState , queue , xmlEnv ):
+        """
+        Run the commandLine 
+        redirect the standard error and output on service.name.out and service.name.err, then restore the sys.stderr and sys.stdout
+        @return: the L{Mobyle.Status.Status} of this job and a message
+        @rtype: Status  object
+        """
+        jobKey = self.getKey()
+        fout = open( serviceName + ".out" , 'w' )
+        ferr = open( serviceName + ".err" , 'w' )
+
+        try:
+            ## execute the commandline through your favorite execution system
+            pass
+        except OSError, err:
+            msg= "System execution failed: " + str(err)
+            self._logError( dirPath , serviceName ,jobKey,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+                
+            _log.critical( "%s/%s : %s" %( self.serviceName ,
+                                                jobKey ,
+                                                msg
+                                                )
+                                )
+
+            raise MobyleError , msg 
+                
+            adm = Admin( dirPath )
+            adm.setExecutionAlias( self.execution_config_alias )  ## store the alias of execution config used for this job 
+            adm.setNumber( jobKey ) ## store the key to query/retrieve this job on this system execution
+            adm.commit()
+
+            ## link the .admin file in ADMINDIR which looklike to a "process table"
+            linkName = ( "%s/%s.%s" %(self. _cfg.admindir() ,
+                                      serviceName ,
+                                      jobKey
+                                    )
+                         )
+            
+            try:
+                os.symlink(
+                    os.path.join( self.dirPath , '.admin') ,
+                    linkName
+                    )
+            except OSError , err:
+                msg = "can't create symbolic link %s in ADMINDIR: %s" %( linkName , err )
+                self._logError( dirPath , serviceName ,jobKey,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                
+                _log.critical( "%s/%s : %s" %( serviceName ,
+                                               jobKey ,
+                                               msg
+                                            )
+                                 )
+                raise MobyleError , msg
+
+            logging_shutdown() #close all loggers see issue #1075
+            ## wait the completion of the job 
+            ## THIS CLASS MUST BE SYNCHRON WITH THE JOB  
+            MLogger(child = True ) #reopen the logger
+
+            try:
+                ## remove the job from the ADMINDIR ( "process table" )
+                os.unlink( linkName )
+            except OSError , err:
+                msg = "can't remove symbolic link %s in ADMINDIR: %s" %( linkName , err )
+                self._logError( dirPath , serviceName ,jobKey,
+                                userMsg = "Mobyle internal server error" ,
+                                logMsg = None )
+                
+                _log.critical( "%s/%s : %s" %( self.serviceName ,
+                                                jobKey ,
+                                                msg
+                                                )
+                                 )
+                raise MobyleError , msg
+            ##close the standard output and standard error file
+            fout.close()
+            ferr.close()
+            
+            oldStatus = self.status_manager.getStatus( dirPath )
+ 
+            #if the oldStatus is terminal ( finish error killed )
+            #I don't modify this status 
+            if oldStatus.isEnded():
+                return oldStatus
+            else:
+                ## analyse the returncode of the job
+                ## and instanciate a Status according the returncode
+                ## then return the Status
+                status = Status( code = 4 ) #finished
+            return status
+        
+
+    def getStatus( self , key ):
+        """
+        @param key: the value associate to the key "NUMBER" in Admin object (and .admin file )
+        @type key: string
+        @return: the status of the job corresponding to the key 
+        @rtype: Mobyle.Status.Status instance
+        @call: by L{Utils.getStatus}
+        """
+        ##query your execution system about the job with this key
+        ##translate the answer in mobyle compliant status and message
+        
+        ## for mobyle status see Mobyle.Status.Status
+        mobyle_status_code = 3 # running
+        message = "my job is running"
+        return Status( code = mobyle_status_code , message = message )
+
+
+    def kill( self , key ):
+        """
+        kill a job
+        @param key : the value associate to the key "NUMBER" in Admin object (and .admin file )
+        @type key: string
+        @raise MobyleError: if can't kill the job
+        @call: by L{Utils.Mkill}
+        """
+        return None
+        
diff --git a/Src/Mobyle/Execution/ExecutionSystem.py b/Src/Mobyle/Execution/ExecutionSystem.py
new file mode 100755
index 0000000..f6f19e3
--- /dev/null
+++ b/Src/Mobyle/Execution/ExecutionSystem.py
@@ -0,0 +1,162 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os
+import time
+
+from logging import getLogger
+_log = getLogger(__name__)
+
+from Mobyle.ConfigManager import Config
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Admin import Admin
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+class ExecutionSystem(object):
+    """
+    abstract class
+    manage the status by updating the file index.xml
+    """
+
+    def __init__( self , execution_config  ):
+        """
+        @param execution_config: the configuration of the Execution 
+        @type execution_config: ExecutionConfig instance
+        """
+        self._cfg = Config()
+        self.execution_config = execution_config
+        self.execution_config_alias = self._cfg.getAliasFromConfig( self.execution_config )
+        self.status_manager = StatusManager()
+    
+    def run( self , commandLine , dirPath , serviceName , jobState , xmlEnv = None):
+        """
+        @param execution_config: the configuration of the Execution 
+        @type execution_config: ExecutionConfig instance
+        @param commandLine: the command to be executed
+        @type commandLine: String
+        @param dirPath: the absolute path to directory where the job will be executed (normaly we are already in)
+        @type dirPath: String
+        @param serviceName: the name of the service
+        @type serviceName: string
+        @param jobState:
+        @type jobState: a L{JobState} instance
+        """
+        self.jobState = jobState
+        if dirPath[-1] == '/':
+            dirPath = dirPath[:-1]
+        jobKey = os.path.split( dirPath )[1]
+        if os.getcwd() != os.path.abspath( dirPath ):
+            msg = "the child process execute itself in a wrong directory"
+            self._logError( dirPath , serviceName ,jobKey,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = msg  )
+            raise MobyleError , msg 
+        
+        protectedCommandLine = ''
+        for c in commandLine:
+            protectedCommandLine += '\\'+ c
+            
+        if xmlEnv is None:
+            xmlEnv = {}
+            
+        dispatcher = self._cfg.getDispatcher()
+        queue = dispatcher.getQueue( jobState )
+        adm = Admin( dirPath )
+        adm.setQueue( queue )
+        adm.commit()
+        
+        new_path = ''
+        binary_path = self._cfg.binary_path()
+        if binary_path :
+                new_path = ":".join( binary_path )        
+
+        if xmlEnv.has_key( 'PATH' ) :      
+                new_path = "%s:%s" %( xmlEnv[ 'PATH' ] , new_path )
+        if new_path :
+            xmlEnv[ 'PATH' ] = "%s:%s" %( new_path , os.environ[ 'PATH' ] ) 
+        else:
+            xmlEnv[ 'PATH' ] = os.environ[ 'PATH' ] 
+        for var in os.environ.keys():
+            if var != 'PATH':
+                xmlEnv[ var ] = os.environ[ var ]
+        self._returncode = None
+        accounting = self._cfg.accounting()
+        if accounting:
+            beg_time = time.time()
+
+        ###################################
+        mobyleStatus = self._run( commandLine , dirPath , serviceName , jobKey , jobState , queue , xmlEnv )
+        ###################################
+        if accounting:
+            end_time = time.time()
+            elapsed_time = end_time - beg_time
+            a_log = getLogger( 'Mobyle.account' )
+            #%d trunc time to second
+            #%f for millisecond
+            a_log.info("%(serviceName)s/%(jobkey)s : %(exec_class)s/%(queue)s : %(beg_time)d-%(end_time)d %(ela_time)d : %(status)s" %{ 'serviceName':serviceName ,
+                                                                                                                          'jobkey':jobKey,
+                                                                                                                          'exec_class':self.execution_config.execution_class_name ,
+                                                                                                                          'queue': queue,
+                                                                                                                          'beg_time':beg_time ,
+                                                                                                                          'end_time':end_time ,
+                                                                                                                          'ela_time':elapsed_time ,
+                                                                                                                          'status': mobyleStatus ,
+                                                                                                                          }
+                                                                                                              )
+        self.status_manager.setStatus( dirPath , mobyleStatus )
+
+    def getStatus( self ,  number ):
+        """
+        @param execution_config: a configuration object for this execution system
+        @type execution_config: an ExecutionConfig subclass instance
+        @param number:
+        @type number:
+        @return the status of the job
+        @rtype:
+        abstract method. this method must be implemented in child classes
+        """
+        raise NotImplementedError, "Must be Implemented in child classes"
+
+    def kill(  self , number ):
+        """
+        kill the Job
+        @param execution_config: a configuration object for this execution system
+        @type execution_config: an ExecutionConfig subclass instance
+        @param number:
+        @type number:
+        abstract method. this method must be implemented in child classes
+        """
+        raise NotImplementedError, "Must be Implemented in child classes"
+            
+
+
+
+    def _logError( self , dirPath , serviceName , jobKey , userMsg = None , logMsg = None ):
+
+
+        if userMsg :
+            self.status_manager.setStatus( dirPath, Status( code = 5 , message = userMsg ) )
+
+        if logMsg :
+            _log.error( "%s/%s : %s" %( serviceName ,
+                                        jobKey ,
+                                        logMsg
+                                      )
+                        )
+          
+          
+          
+          
diff --git a/Src/Mobyle/Execution/LsfDRMAA.py b/Src/Mobyle/Execution/LsfDRMAA.py
new file mode 100644
index 0000000..0840f4e
--- /dev/null
+++ b/Src/Mobyle/Execution/LsfDRMAA.py
@@ -0,0 +1,25 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os 
+import logging
+_log = logging.getLogger(__name__)
+
+from Mobyle.Execution.DRMAA import DRMAA
+
+
+class LsfDRMAA(DRMAA):
+    def __init__( self, drmaa_config ):
+        os.environ[ 'LSF_ENVDIR']    = drmaa_config.envdir
+        os.environ[ 'LSF_SERVERDIR'] = drmaa_config.serverdir
+        super( LsfDRMAA , self ).__init__( drmaa_config )
+        self.nativeSpecification = '' 
+        #I set a more permissive umask
+        #to allow LSF to generate some internal scripts
+        #( see JOB_SPOOL_DIR ) with x permissions 
+        os.umask(0012)
\ No newline at end of file
diff --git a/Src/Mobyle/Execution/PbsDRMAA.py b/Src/Mobyle/Execution/PbsDRMAA.py
new file mode 100644
index 0000000..39150d5
--- /dev/null
+++ b/Src/Mobyle/Execution/PbsDRMAA.py
@@ -0,0 +1,24 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os 
+import logging
+_log = logging.getLogger(__name__)
+
+from Mobyle.Execution.DRMAA import DRMAA
+
+
+class PbsDRMAA(DRMAA):
+    """
+    Run the commandline by a system call
+    """
+    def __init__( self, drmaa_config ):
+        super( PbsDRMAA , self ).__init__( drmaa_config )
+        # workaround the bug in librmaa from opendsp
+        if 'HOME' not in os.environ:
+            os.environ[ 'HOME' ] = '/dev/null'
diff --git a/Src/Mobyle/Execution/SGE.py b/Src/Mobyle/Execution/SGE.py
new file mode 100755
index 0000000..e0306ea
--- /dev/null
+++ b/Src/Mobyle/Execution/SGE.py
@@ -0,0 +1,265 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os
+from subprocess import Popen, PIPE
+
+from logging import getLogger, shutdown as logging_shutdown
+_log = getLogger(__name__)
+from Mobyle.MobyleLogger import MLogger
+
+from Mobyle.Admin import Admin
+from Mobyle.Execution.ExecutionSystem import ExecutionSystem 
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Status import Status
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+        
+                
+class SGE (ExecutionSystem):
+    """
+    Run the commandline with Sun GridEngine commands
+    """
+        
+    def __init__( self, sge_config ):
+        super( SGE , self ).__init__( sge_config )
+        arch_path= os.path.join( sge_config.root , 'util' , 'arch' )
+        try:
+            arch_pipe = Popen(arch_path         ,
+                              shell     = False ,
+                              stdout    = PIPE  ,
+                              stdin     = None  ,
+                              stderr    = None  ,
+                              close_fds = True  )
+            arch_pipe.wait() 
+            arch_rc = arch_pipe.returncode
+        except OSError , err:
+            #this error is log by calling method because I can't access to jobKey , adm status ... from static method
+            msg = "SGE: I can't determined the system arch:"+str(err)
+            raise MobyleError( msg )
+        if arch_rc != 0 :
+            msg = "I can't determined the system arch (return code = " + str( arch_rc ) + " )"
+            raise MobyleError( msg )
+        
+        arch = ''.join( arch_pipe.stdout.readlines() ).strip()
+
+        self.sge_prefix = os.path.join( sge_config.root , 'bin' , arch )
+        self.sge_env = {'SGE_CELL': sge_config.cell , 'SGE_ROOT': sge_config.root }   
+
+
+    def _run( self , commandLine , dirPath , serviceName , jobKey , jobState , queue , xmlEnv ):
+        """
+        @param execution_config: the configuration of the Execution 
+        @type execution_config: ExecutionConfig instance
+        @param commandLine: the command to be executed
+        @type commandLine: String
+        @param dirPath: the absolute path to directory where the job will be executed (normaly we are already in)
+        @type dirPath: String
+        @param serviceName: the name of the service
+        @type serviceName: string
+        @param jobState:
+        @type jobState: a L{JobState} instance
+        """
+        
+        options = { '-cwd': ''           ,  #set the working dir to current dir
+                    '-now': 'n'          ,  #the job is not executed immediately
+                    '-N' : jobKey        ,  #the id of the job
+                    '-V' : ''               #job inherits of the whole environment
+                    }
+        if queue:
+            options[ '-q' ] = queue
+        sge_opts = ''
+
+        for opt in options.keys():
+            sge_opts += opt + ' ' + options[opt]+' '
+                 
+        sge_cmd = os.path.join( self.sge_prefix , 'qrsh' ) + ' ' + sge_opts
+        cmd = sge_cmd + " " + commandLine
+
+        try:
+            fout = open( serviceName + ".out" , 'w' )
+            ferr = open( serviceName + ".err" , 'w' )
+        except IOError , err:
+            msg= "SGE: can't open file for standard job output: "+ str(err)
+            self._logError( dirPath , serviceName , jobKey ,
+                            admMsg = msg ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = msg )
+
+            raise MobyleError , msg
+        xmlEnv.update( self.sge_env )
+        try:
+            pipe = Popen( cmd ,
+                          shell  = True ,
+                          stdout = fout  ,
+                          stdin  = None  ,
+                          stderr = ferr  ,
+                          close_fds = True ,
+                          env       = xmlEnv
+                          )
+        except OSError, err:
+            msg= "SGE execution failed: "+ str(err)
+            self._logError( dirPath , serviceName , jobKey ,
+                           admMsg = msg ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey ,
+                                           msg
+                                           ) ,
+                            exc_info = True
+                          )
+            raise MobyleError , msg
+        adm = Admin( dirPath )
+        adm.setExecutionAlias( self.execution_config_alias  )
+        adm.setNumber( jobKey )
+        adm.commit()
+        linkName = ( "%s/%s.%s" %( self._cfg.admindir() ,
+                                   serviceName ,
+                                   jobKey
+                                   )
+                                   )
+        try:
+            os.symlink(
+                       os.path.join( dirPath , '.admin') ,
+                       linkName
+                       )
+        except OSError , err:
+            msg = "can't create symbolic link %s in ADMINDIR: %s" %( linkName , err )
+            self._logError( dirPath , serviceName , jobKey ,
+                            admMsg = msg ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey  ,
+                                           msg
+                                        )
+                            )
+            raise MobyleError , msg
+        logging_shutdown() #close all loggers 
+        pipe.wait()
+        MLogger(child = True )
+        try:
+            os.unlink( linkName )
+        except OSError , err:
+            msg = "can't remove symbolic link %s in ADMINDIR: %s" %( linkName , err )
+            self._logError( dirPath , serviceName , jobKey ,
+                            admMsg = msg ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey ,
+                                           msg
+                                            )
+                            )
+            raise MobyleError , msg
+
+        fout.close()
+        ferr.close()
+        #self._returncode = pipe.returncode
+        oldStatus = self.status_manager.getStatus( dirPath )
+
+        if oldStatus.isEnded():
+            return oldStatus
+        else:
+            if pipe.returncode == 0 :# finished
+                status = Status( code = 4 ) #finished
+            elif pipe.returncode in ( 137 , 143 , 153 ): #killed
+                ## 137 =  9 ( SIGKILL ) + 128 ( python add 128 )
+                ## 143 = 15 ( SIGTERM )
+                ## 153 = 25 ( SIGXFSZ ) + 128 (file size exceeded )
+                ## be careful if a job exit with a 137 or 153 exit status it will be labelled as killed
+                status = Status( code = 6 , message = "Your job has been cancelled" ) 
+            elif pipe.returncode > 128 :
+                status = Status( code = 6 , message = "Your job execution failed ( %s )" %pipe.returncode ) 
+                ## if return code > 128 it's a signal
+                ## the 9 , 15 25 signal are send by administrator
+                ## the other are self aborting signal see signal(7)   
+            else:
+                status = Status( code = 4 , message = "Your job finished with an unusual status code ( %s ), check your results carefully." % pipe.returncode )
+            return status
+
+
+    def getStatus( self ,jobNum ):
+        """
+        @param sge_config: the configuration of this Execution engine
+        @type sge_config: a L{SGEConfig} instance
+        @param jobNum:
+        @type jobNum:
+        @return: the status of job with number jobNum
+        @rtype: a Mobyle.Status.Status instance
+        @todo: for best performance, restrict the sge querying to one queue
+        """
+        sge_cmd = [ os.path.join( self.sge_prefix , 'qstat' ) , '-r' ]
+        
+        try:
+            pipe = Popen( sge_cmd,
+                          executable = sge_cmd[0] ,
+                          shell = False ,
+                          stdout = PIPE ,
+                          stdin = None  ,
+                          stderr= None  ,
+                          close_fds = True ,
+                          env= self.sge_env
+                          )
+        except OSError , err:
+            raise MobyleError , "can't querying sge : %s :%s "%( sge_cmd , err )
+
+        sge2mobyleStatus = { 'r' : 3 , # running
+                             't' : 3 ,
+                             'R' : 3 ,
+                             's' : 7 , #hold
+                             'S' : 7 , 
+                             'T' : 7 ,
+                             'h' : 7 ,
+                             'w' : 2 , #pending
+                             'd' : 6 , #killed 
+                             'E' : 5 , #error
+                             }
+
+        for job in pipe.stdout :
+            
+            job_sge_status = job.split()
+            try:
+                status = job_sge_status[ 4 ][ -1 ]
+                continue
+            except ( ValueError , IndexError ):
+                pass #it's not the fisrt line
+            try:
+                if not ( jobNum == job_sge_status[2] and 'Full' == job_sge_status[0] ):
+                    continue #it's not the right jobNum
+            except IndexError :
+                continue #it's not the 2nde line
+
+            pipe.stdout.close()
+            pipe.wait()
+            
+            if pipe.returncode == 0 :
+                try:
+                    return Status( code = sge2mobyleStatus[ status ]  )
+                except MobyleError , err :
+                    raise MobyleError , "unexpected sge status: " +str( status )
+            else:
+                raise MobyleError , "cannot get status " + str( pipe.returncode )
+        return Status( code = -1 ) #unknown
+
+        
+    def kill( self, job_number ):
+        """
+        @todo: kill the Job
+        """
+        sge_cmd = "%s %s 1>&2" %( os.path.join( self.sge_prefix , 'qdel' ) ,  job_number )
+        os.environ.update( self.sge_env )
+        os.system( sge_cmd )
+        
+
diff --git a/Src/Mobyle/Execution/SYS.py b/Src/Mobyle/Execution/SYS.py
new file mode 100755
index 0000000..3bd9d33
--- /dev/null
+++ b/Src/Mobyle/Execution/SYS.py
@@ -0,0 +1,194 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os 
+from subprocess import Popen
+from signal import SIG_DFL , SIGTERM , SIGKILL
+from time import sleep
+
+from logging import getLogger, shutdown as logging_shutdown
+_log = getLogger(__name__)
+from Mobyle.MobyleLogger import MLogger
+
+from Mobyle.Admin import Admin
+from Mobyle.Status import Status
+from Mobyle.Execution.ExecutionSystem import ExecutionSystem
+from Mobyle.MobyleError import MobyleError
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+class SYS(ExecutionSystem):
+    """
+    Run the commandline by a system call
+    """
+
+    def _run(self , commandLine , dirPath , serviceName , jobKey , jobState , queue , xmlEnv ):
+        """
+        Run the commandLine in a subshell (system call) and redirect the standard error and output on service.name.out and service.name.err, then restore the sys.stderr and sys.stdout
+        @return: the L{Status} of this job and a message
+        @rtype: Status object
+        """
+        fout = open( serviceName + ".out" , 'w' )
+        ferr = open( serviceName + ".err" , 'w' )
+
+        try:
+            #the new process launch by popen must be a session leader 
+            # because it launch in background and if we send a siggnal to the shell 
+            # this signal will no propagate to all child process
+            # and if we kill the process leader we kill the python 
+            # thus we execute our setsid which transform the new process as a session leader
+            # and execute the command prior to return.
+            # in these conditions we could kill the process leader and the signal is propagate to
+            # all processes belonging to this session. 
+            command_path =  os.path.join( dirPath , ".command" )
+            setsidPath = '%s/Tools/setsid' %self._cfg.mobylehome()
+            command = [ setsidPath , setsidPath , '/bin/sh' , command_path ]
+            # _log.debug("SYS env : %s\n" % str(self.xmlEnv) )
+
+            pipe = Popen( command ,
+                          shell     = False ,
+                          stdout    = fout ,
+                          stdin     = None ,
+                          stderr    = ferr ,
+                          close_fds = True ,
+                          env       = xmlEnv
+                          )
+
+        except OSError, err:
+            msg= "System execution failed: command = %s : %s" %( command , err)
+            self._logError( dirPath , serviceName , jobKey,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = msg )
+            
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey ,
+                                           msg
+                                           ) ,
+                                            exc_info = True
+                             )
+
+            raise MobyleError , msg 
+            
+        jobPid = pipe.pid
+        
+        adm = Admin( dirPath )
+        adm.setExecutionAlias( self.execution_config_alias )
+        adm.setNumber( jobPid )
+        adm.commit()
+
+        linkName = ( "%s/%s.%s" %( self._cfg.admindir() ,
+                                   serviceName ,
+                                   jobKey
+                                )
+                     )
+        try:
+            os.symlink(
+                os.path.join( dirPath , '.admin') ,
+                linkName
+                )
+        except OSError , err:
+            msg = "can't create symbolic link %s in ADMINDIR: %s" %( linkName , err )
+            self._logError( dirPath , serviceName , jobKey,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+            
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey ,
+                                           msg
+                                            )
+                             )
+            raise MobyleError , msg
+        logging_shutdown() #close all loggers  
+        pipe.wait()
+        MLogger(child = True )
+        try:
+            os.unlink( linkName )
+        except OSError , err:
+            msg = "can't remove symbolic link %s in ADMINDIR: %s" %( linkName , err )
+
+            self._logError( dirPath , serviceName , jobKey ,
+                            userMsg = "Mobyle internal server error" ,
+                            logMsg = None )
+            
+            _log.critical( "%s/%s : %s" %( serviceName ,
+                                           jobKey,
+                                           msg
+                                        )
+                             )
+
+            raise MobyleError , msg
+        fout.close()
+        ferr.close()
+        #self._returncode = pipe.returncode
+        
+        oldStatus = self.status_manager.getStatus( dirPath )
+        if oldStatus.isEnded():
+            return oldStatus
+        else:
+            if pipe.returncode == 0 :# finished
+                status = Status( code = 4 ) #finished
+            elif pipe.returncode == -15 or pipe.returncode == -9: #killed
+                status = Status( code = 6 , message = "Your job has been cancelled" ) 
+            elif pipe.returncode < 0 or pipe.returncode > 128:
+                #all the signals that we don't know where they come from
+                status = Status( code = 5 , message = "Your job execution failed ( %s )" %pipe.returncode )
+            else:
+                status = Status( code= 4 , message = "Your job finished with an unusual status code ( %s ), check your results carefully." % pipe.returncode )
+            return status
+    
+
+    def getStatus( self , pid ):
+        """
+        @param pid:
+        @type pid:
+        @return: the status of job with pid pid
+        @rtype: a Status instance
+        """
+        pid = int( pid )
+        try:
+            message = None
+            os.kill( pid , SIG_DFL )
+        except OSError , err:
+            if str( err ).find( 'No such process' ) != -1:
+                return Status( code = -1 ) #unknown
+            else:
+                raise MobyleError( str(err) ) 
+        return Status( code = 3 , message = message ) #running
+
+    def kill( self, job_pid ):
+        """
+        kill a job
+        @param job_pid: the pid of the job
+        @type job_pid: int
+        @raise MobyleError: if can't kill the job
+        @call: by L{Utils.Mkill}
+        """
+        try:
+            job_pid = int( job_pid )
+            job_pgid = os.getpgid( job_pid )
+        except ValueError, err:
+            raise MobyleError( "can't kill job  - %s" % err )
+        try:
+            os.killpg( job_pgid , SIGTERM )
+        except OSError , err:
+            raise MobyleError( "can't kill job  - %s" % err )
+        try:
+            sleep(0.2)
+            os.killpg( job_pgid , SIG_DFL )
+            try:
+                os.killpg( job_pgid , SIGKILL )
+            except OSError , err:
+                raise MobyleError , "can't kill job - %s" % err
+        except OSError , err:
+            return None
diff --git a/Src/Mobyle/Execution/SgeDRMAA.py b/Src/Mobyle/Execution/SgeDRMAA.py
new file mode 100644
index 0000000..b08e710
--- /dev/null
+++ b/Src/Mobyle/Execution/SgeDRMAA.py
@@ -0,0 +1,21 @@
+
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os 
+import logging
+_log = logging.getLogger(__name__)
+
+from Mobyle.Execution.DRMAA import DRMAA
+
+
+class SgeDRMAA(DRMAA):
+    def __init__( self, drmaa_config ):
+        os.environ[ 'SGE_ROOT'] = drmaa_config.root
+        os.environ[ 'SGE_CELL'] = drmaa_config.cell
+        super( SgeDRMAA , self ).__init__( drmaa_config )
diff --git a/Src/Mobyle/Execution/__init__.py b/Src/Mobyle/Execution/__init__.py
new file mode 100644
index 0000000..2669d75
--- /dev/null
+++ b/Src/Mobyle/Execution/__init__.py
@@ -0,0 +1,8 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
diff --git a/Src/Mobyle/IndexBase.py b/Src/Mobyle/IndexBase.py
new file mode 100644
index 0000000..d2143dc
--- /dev/null
+++ b/Src/Mobyle/IndexBase.py
@@ -0,0 +1,115 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+Mobyle.IndexBase
+Provides mechanisms to generate, cache, and load Mobyle indexes
+"""
+import os
+from logging import getLogger
+i_log = getLogger(__name__)
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+from Mobyle.Registry import registry, ProgramDef, WorkflowDef, ViewerDef, TutorialDef
+from lxml import etree
+import pickle
+
+
+parser = etree.XMLParser( no_network = False )
+
+class IndexGenerator(object):
+    def __init__(self, cls):
+        self.indexClass = cls
+        
+    def load(self, type):
+        index_path = os.path.join(_cfg.index_path(), type + self.indexClass.indexFileName)
+        try:
+            input = open(index_path, 'rb')
+            idx = pickle.load(input)
+            input.close()
+        except IOError:
+            idx = {}
+            i_log.warning("index file '%s' could not be found, set as empty. Please check documentation: %s." % (index_path,os.path.join(os.environ.get('MOBYLEHOME','$MOBYLEHOME'),'Tools','README')))
+        return idx
+    
+    def dump(self, type, dir, registry=registry):
+        output = open(os.path.join(dir, type + self.indexClass.indexFileName), 'wb')
+        idx = {}
+        for s in getattr( registry, type + 's'):
+            try:
+                doc = etree.parse(s.path)
+                doc.xinclude()
+                if (isinstance(s, ProgramDef)):
+                    idx[registry.getProgramUrl(s.name, s.server.name)] = self.indexClass.getIndexEntry(doc, s)
+                elif (isinstance(s, WorkflowDef)):
+                    idx[registry.getWorkflowUrl(s.name, s.server.name)] = self.indexClass.getIndexEntry(doc, s)                    
+                elif (isinstance(s, ViewerDef)):
+                    idx[registry.getViewerUrl(s.name)] = self.indexClass.getIndexEntry(doc, s)                    
+                elif (isinstance(s, TutorialDef)):
+                    idx[registry.getTutorialUrl(s.name)] = self.indexClass.getIndexEntry(doc, s)                    
+            except:
+                i_log.error("Error while generating %s entry for %s/%s" %\
+                            (self.indexClass.__name__, s.server.name, \
+                             s.name), exc_info=True)
+                i_log.error(s.name)
+                i_log.error(s.path)
+                i_log.error(s.url)
+
+        pickle.dump(idx, output, 2)
+        output.close()
+        
+class Index(object):
+
+    indexFileName=""
+
+    def __init__(self, type):
+        ig =IndexGenerator(self.__class__)
+        self.index = ig.load(type)
+        self.type = type
+        
+    @classmethod
+    def generate(cls, type, dir, registry):
+        ig =IndexGenerator(cls)
+        ig.dump(type, dir, registry)
+        
+    @classmethod
+    def getIndexEntry(cls, doc):
+        raise NotImplementedError
+     
+
+def _XPathQuery(node, query, returnType="valueString"):
+    """
+    _XPathQuery is a helper function to get property values from the XML
+    @param node: the node which is queried
+    @type node: Element
+    @param query: Compiled XPath Query
+    @type query: varies
+    @param returnType: the type we want returned by the function valueString|valueList|rawResult
+    @type string
+    """
+    try:
+        if returnType == "rawResult":
+            rawResult = node.xpath(query)
+            return rawResult
+        else:
+            valueList = [n for n in node.xpath(query) \
+                         if n and n.strip()!='']
+            if returnType == "valueList":
+                return valueList
+            valueString = ' '.join(valueList)
+            if returnType == "valueString":
+                return valueString
+            raise Exception("unauthorized returnType value (%s), \
+                            authorized values are rawResult|valueList|valueString"\
+                             % valueString)
+    except Exception, e:
+        i_log.error(e)
+        
\ No newline at end of file
diff --git a/Src/Mobyle/InterfacePreprocessor.py b/Src/Mobyle/InterfacePreprocessor.py
new file mode 100644
index 0000000..5c8d7c9
--- /dev/null
+++ b/Src/Mobyle/InterfacePreprocessor.py
@@ -0,0 +1,109 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+import os
+import logging
+from lxml import etree
+from Mobyle.ConfigManager import Config
+from Mobyle.Utils import indent
+
+_cfg = Config()
+log = logging.getLogger('mobyle.registry' )
+
+class InterfacePreprocessor(object):
+    """
+    InterfacePreprocessor is a component that generates the pre-computed parts of the MobylePortal interface, i.e., form and
+    job pages, workflow graphs, etc.
+    """
+    
+    def __init__(self):
+        self.load_xsl_pipe()
+        
+    def load_xsl_pipe(self):
+        """
+        Preload all necessary xsl files and define the xsl processing pipeline
+        """
+        self.XslPipe = []
+        local_categories_path = os.path.join(_cfg.mobylehome() , "Local", "categories.xml")
+        if os.path.exists(local_categories_path):
+            local_categories_url = os.path.join("file://",local_categories_path)
+            style0 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "localize.xsl"))
+            self.localize_style = etree.XSLT(style0)
+            self.XslPipe.append((self.localize_style, {'localCategoriesUrl':"'"+local_categories_url+"'"}))
+        style1bis = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "clean_import.xsl"))
+        self.clean_style = etree.XSLT(style1bis)
+        style1 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "parameters.xsl"))
+        self.param_style = etree.XSLT(style1)
+        style2 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "layout.xsl"))
+        self.layout_style = etree.XSLT(style2)
+        style3 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "annotate.xsl"))
+        self.ann_style = etree.XSLT(style3)
+        style4 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "results.xsl"))
+        self.res_style = etree.XSLT(style4)
+        style5 = etree.parse(os.path.join(_cfg.portal_path(), "xsl", "graphviz_simplify.xsl"))
+        self.gvz_style = etree.XSLT(style5)
+        self.XslPipe += [
+                (self.clean_style, {}),
+                (self.param_style, {'previewDataLimit':str(_cfg.previewDataLimit()),'programUri':''}),
+                (self.layout_style, {'type':"'form'",'programUri':''}),
+                (self.layout_style, {'type':"'viewer'",'programUri':''}),
+                (self.res_style, {'previewDataLimit':str(_cfg.previewDataLimit())}),
+                (self.ann_style, {}),
+                (self.gvz_style, {}),
+                (self.layout_style, {'type':"'job_input'"}),
+                (self.layout_style, {'type':"'job_output'"})
+                ]
+
+    
+    def process_interface(self, programpath):
+        """
+        Pre-process the xsl on program xml definitions
+        @param programpath : xml path file to process
+        @type programpath : string
+        @return: if the processing run well or not
+        @rtype: boolean
+        """        
+        doc = etree.parse(programpath)
+        if doc.getroot().tag=='workflow':
+            from Mobyle.Workflow import Parser
+            from Mobyle.WorkflowLayout import layout
+            p = Parser()
+            w = p.parse(programpath)
+            i_l_s = layout(w) # graph layout as an svg string
+            i_l_e = etree.XML(i_l_s) # graph layout as an element
+            h = w.find("head")
+            if h is None:
+                h = etree.SubElement(w,'head') # graph interface container element
+            g_i = w.find("head/interface[@type='graph']")
+            if g_i is None:
+                g_i = etree.SubElement(w.find('head'),'interface') # graph interface container element
+            g_i.clear()
+            g_i.set("type","graph")
+            g_i.append(i_l_e)
+            fileResult = open(programpath,"w")
+            fileResult.write(etree.tostring(w, pretty_print=True))
+            fileResult.close()
+            doc = etree.parse(programpath)
+
+        for style, params in self.XslPipe:
+            for p in params.keys():
+                if p == 'programUri':
+                    params[p] = "'"+programpath+"'"
+                    log.debug('programUri=%s' % programpath)
+            result = style(doc, **params)
+            doc = result
+
+        #write the result on the xml itself
+        try:
+            fileResult = open(programpath,"w")
+            indent(doc.getroot())
+            fileResult.write(str(doc))
+            fileResult.close()
+            return True
+        except IOError, ex:
+            log.error("Problem with the html generation: %s." % ex)
+            return False  
diff --git a/Src/Mobyle/Job.py b/Src/Mobyle/Job.py
new file mode 100644
index 0000000..64f9aa8
--- /dev/null
+++ b/Src/Mobyle/Job.py
@@ -0,0 +1,317 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+import os 
+import random 
+from string import ascii_uppercase
+import resource
+import time 
+import logging 
+j_log = logging.getLogger( __name__ )
+
+from Mobyle.JobState import JobState , path2url , url2path
+from Mobyle.Admin import Admin
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Mobyle.MobyleError import MobyleError , UserValueError
+from Mobyle.Utils import executionLoader
+import Local.Policy
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+class Job:
+
+    """
+    create the environment to run a job
+      - working directory creation
+    """
+    
+    def __init__(self, service=None , cfg=None , session = None , userEmail = None , workflowID = None , email_notify= 'auto' , ID = None, debug=None ):
+        """
+        @param service:
+        @type service: a {Service} instance
+        @param cfg: a config object
+        @type cfg: L{ConfigManager.Config} instance
+        @param session:
+        @type session: a L{Session} instance
+        @param userEmail: the email of the user 
+        @type userEmail: a L{Mobyle.Net.EmailAddress} instance
+        @param workflowID: the url of the workflow owner of this job
+        @type workflowID: string
+        @param email_notify: if the user must be or not notify of the results at the end of the Job.
+        the 3 authorized values for this argument are: 
+          - 'true' to notify the results to the user
+          - 'false' to Not notify the results to the user
+          - 'auto' to notify the results based on the job elapsed time and the config  EMAIL_DELAY
+        @type email_notify: string 
+        """
+        
+        if cfg:
+            self.cfg = cfg
+        else:
+            from Mobyle.ConfigManager import Config
+            self.cfg = Config()
+        self._debug = debug
+        self.runner = None
+        self.session = session
+        self.userEmail = userEmail
+        self.workflowID = workflowID
+        self.sm = StatusManager()
+        if ID:
+            self.ID = ID
+            self.work_dir = url2path( ID )
+            self.jobState = JobState( uri = ID )
+            self.key = os.path.split( self.work_dir )[ 1 ]
+        else:
+            self.service = service
+            self.work_dir = None
+            self.jobState = None
+            self.cmdLine = None
+            self.key = self._newKey()
+            self.email_notify = email_notify
+            self.makeEnv()
+            self.ID = self.getURL()
+
+    def makeEnv( self, uri = None ):
+        """
+        create the environment to run a job
+          - working directory creation
+          - fixing permission
+          - creation of the xml file index.xml
+        """
+        basedir =  self.cfg.results_path() 
+        if not os.path.exists( basedir ):
+            msg = "directory " + str( basedir ) + " does not exist"
+            j_log.error(msg)
+            raise MobyleError, msg
+        if uri is None:            
+            if self.session and self.service.getUrl().startswith(self.session.url):
+                name = 'user_defined'
+            else:
+                name = self.service.getName()
+            workdir = os.path.join( basedir , name , self.key )
+            if os.path.exists( workdir ):
+                msg = 'cannot make directory '+ str( workdir )+" : File exists"
+                j_log.error( msg )
+                raise MobyleError , msg
+        try:
+            os.makedirs( workdir , 0755 ) #create parent directory
+        except Exception , err:
+            j_log.critical( "unable to create job directory %s: %s " %( workdir , err ), exc_info = True )
+            raise MobyleError , "Internal server Error"
+
+        os.umask(0022)
+        self.work_dir = workdir
+        resource.setrlimit( resource.RLIMIT_CORE , (0 , 0) )
+        maxFileSize = self.cfg.filelimit()
+        resource.setrlimit( resource.RLIMIT_FSIZE , ( maxFileSize , maxFileSize ) )
+        self.date = time.localtime()
+        
+        #be careful the order of setting informations in state is important
+        self.jobState = JobState( uri=workdir, service=self.service )        
+
+        serviceName = self.service.getName()
+        jobID = self.getURL()
+        
+        self.jobState.setName( self.service.getUrl() )
+        self.jobState.setHost( self.cfg.root_url() )
+        self.jobState.setID( jobID )
+        self.jobState.setDate( self.date )
+        if self.userEmail is not None:
+            self.jobState.setEmail( self.userEmail )
+        sessionKey = None
+        if self.session is not None:
+            sessionKey = self.session.getKey()
+            self.jobState.setSessionKey( sessionKey )
+        if self.workflowID is not None:
+            self.jobState.setWorkflowID( self.workflowID ) 
+        self.jobState.commit()
+        self.sm.create( self.work_dir , Status( code= 0 ) ) # building
+
+        try:
+            remote_host = os.environ['REMOTE_HOST']
+        except KeyError :
+            remote_host = "UNKNOWN"
+        try:
+            ip_addr = os.environ['REMOTE_ADDR']
+        except KeyError :
+            ip_addr = 'local'
+        
+        remote =  ip_addr+"\\"+remote_host 
+
+        ## Attention I'm building the job instance
+        ## during the MobyleJob instanciation
+        ## thus the job is not already add to the session
+        ## this is done explicitly in the CGI when a value is submitted
+
+        Admin.create( self.work_dir , remote , serviceName , jobID , 
+                      userEmail = self.userEmail , 
+                      sessionID = sessionKey , 
+                      workflowID = self.workflowID )
+        
+    def _newKey(self):
+        """
+        @return: a unique key which serve to indentify a job
+        @rtype: string
+        """
+
+        #for PBS/SGE jobname, the first char of tmp dir must begin by a letter
+        letter = ascii_uppercase[ random.randrange( 0 , 26 ) ]
+        #max 15 chars for PBS jobname
+        strTime = "%.9f" % time.time()
+        strTime = strTime[-9:]
+        strPid = "%05d" % os.getpid()
+        strPid = strPid.replace( '.' , '' )[ -5 : ]
+        return letter + strPid + strTime
+
+    def setSession(self , session ):
+        self.session = session
+        sessionKey = self.session.getKey() 
+        self.jobState.setSessionKey( sessionKey )
+        self.jobState.commit()
+        adm = Admin( self.work_dir )
+        adm.setSession( sessionKey )
+        adm.commit()
+        
+    def getKey( self ):
+        """
+        @return: the unique key of this job
+        @rtype: string
+        """
+        return  self.key
+
+    def getDate( self ):
+        """
+        @return: the submission date of this job
+        @rtype: string
+        """
+        return  self.date
+    
+
+    def getDir(self):
+        """
+        @return: the absolute path to the directory where the job is executed
+        @rtype: string
+        """
+        return self.work_dir
+
+    def getCommandLine(self):
+        if self.ID:
+            return self.jobState.getCommandLine()
+        else:
+            return self.cmdLine
+
+    def getURL(self):
+        """
+        @return: the URL to the directory where the job is executed
+        @rtype: string
+        """
+        url = path2url( self.work_dir )
+        return url
+
+    def getService( self ):
+        """
+        @return: the instance of the service used for this job
+        @rtype: Service
+        """
+        return self.service
+
+    def getServiceName(self):
+        return self.service.getName()
+    
+    def getStatus(self):
+        return self.sm.getStatus( self.work_dir )
+    
+    def getEmail( self ):
+        """
+        @return: the email of the user or None if there is no email
+        @rtype: -L{Net.EmailAddress} instance
+        """
+        return self.userEmail
+
+    def isLocal(self):
+        """
+        @return: True if this job is on this server , False otherwise
+        @rtype: boolean
+        """ 
+        return self.jobState.isLocal()
+        
+    def run( self ):
+        """
+        Run the Job (instanciate a CommandRunner)
+        @call: L{MobyleJob.run <MobyleJob>}
+        """
+        from Mobyle.RunnerFather import CommandRunner
+        
+        if self.jobState is None:
+            msg = "you must make an environment for the job before running it"
+            self._logError( logMsg = msg ,
+                            userMsg = "Mobyle Internal server error"
+                            )
+            
+            raise MobyleError, msg
+
+        oldStatus = self.sm.getStatus( self.work_dir )
+        if oldStatus != Status( string="building" ):
+            msg = "try to run a Job which have %s status" % oldStatus
+            self._logError( logMsg = msg ,
+                            userMsg = "Mobyle Internal server error"
+                            )
+            
+            raise MobyleError, msg
+        self.sm.setStatus( self.work_dir , Status( code = 1 ) )#submitted
+
+        self.runner = CommandRunner( self )
+        self.cmdLine = self.runner.getCmdLine()
+        debug = self._debug or self.cfg.debug( self.service.getName() )
+
+        if debug > 0 and debug < 3:# the job will not run
+            self.sm.setStatus( self.work_dir , Status( code = 4,message="job simulation only" ) )#finished
+        else:
+            if self.userEmail is not None :
+                try:
+                    Local.Policy.allow_to_be_executed( self )
+                except UserValueError, err:
+                    self._logError( userMsg = str(err) ) #set the job in status error 
+                    raise err
+                except AttributeError:
+                    j_log.warning( "func allow_to_be_executed not found in Local.Policy: see Example.Local.Policy.py to update:  all jobs are allowed")
+                    #allow_to_be_executed is not in local policy
+                    #so all jobs are allowed to run
+                    pass
+            self.runner.run()
+        
+
+    def kill( self ) :
+        adm = Admin( self.getDir() )
+        jobNum = adm.getNumber()
+        if jobNum is None:
+            raise MobyleError( "no jobNum for the job : %s" % self.ID )
+        try:
+            execKlass = executionLoader( jobID = self.ID )
+            execKlass.kill( jobNum )            
+            self.sm.setStatus( self.work_dir , Status( code= 6 , message = "Your job has been cancelled" ) )
+        except MobyleError , err :
+            msg = "cannot kill job %s : %s" % ( self.ID , err)
+            j_log.error( msg ) 
+            raise MobyleError( msg )
+
+    def _logError( self , userMsg = None , logMsg = None ):
+
+        if userMsg :
+            self.sm.setStatus( self.work_dir , Status( code = 5 , message = userMsg ) ) #error
+
+        if logMsg :
+            j_log.error( "%s : %s : %s" %( self.service.getName() ,
+                                          self.getKey() ,
+                                          logMsg
+                                          )
+                        )
+            
diff --git a/Src/Mobyle/JobFacade.py b/Src/Mobyle/JobFacade.py
new file mode 100644
index 0000000..994a3c4
--- /dev/null
+++ b/Src/Mobyle/JobFacade.py
@@ -0,0 +1,588 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager                                                              #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+"""
+Mobyle.JobFacade
+
+This module provides an simplified and seamless access to
+ - local jobs and
+ - remote jobs
+"""
+import os
+import time #@UnresolvedImport
+import urllib #@UnresolvedImport
+import urllib2 #@UnresolvedImport
+import simplejson #@UnresolvedImport
+from logging import getLogger#@UnresolvedImport
+j_log = getLogger(__name__)
+acc_log = getLogger('Mobyle.access')
+
+from Mobyle.MobyleJob  import MobyleJob
+from  Mobyle.Parser import parseService
+from Mobyle.JobState import JobState
+from Mobyle.MobyleError import MobyleError, UserValueError, NoSpaceLeftError
+from Mobyle.Registry import registry
+from Mobyle.WorkflowJob import WorkflowJob
+from Mobyle.Workflow import Workflow, Parser
+from Mobyle.Service import Program
+from Mobyle.Utils import getStatus as utils_getStatus , killJob as utils_killJob
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Mobyle.DataProvider import DataProvider
+from JobState import ProgramJobState, WorkflowJobState
+#BEGIN #819
+from Mobyle.Service import MobyleType
+from Mobyle.Classes.Core import AbstractTextDataType
+#END #819
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+
+class JobFacade(object):
+    """
+    JobFacade is an abstract class that is an access point to a Mobyle Job.
+    """
+
+    def __init__(self, programUrl=None, workflowUrl=None, service=None, jobId=None,workflowId=None,email_notify='auto'):
+        if programUrl is not None:
+            self.programUrl = programUrl
+        elif workflowUrl is not None:
+            self.workflowUrl = workflowUrl            
+        elif service is not None:
+            self.service = service
+        if jobId:
+            self.jobId = jobId
+        self.email_notify = email_notify
+        self.workflowId = workflowId
+
+    def _getFormValuesToAddToSession(self,session, param):
+        if param['srcFileName'] and not(param['value']):
+            param['userName'] = session.getData(param['srcFileName'])['userName']
+        if param['value'] and not(param['srcFileName']):
+            try:
+                param['srcFileName'] = \
+                session.addData( param['userName'], \
+                                  param['parameter'].getType(), \
+                                  content = param['value'], \
+                                  inputModes = [param['inputMode']])
+            except NoSpaceLeftError, err:
+                raise UserValueError(param.get('parameter'), msg=err.message)
+        return param['srcFileName']
+    
+    def addJobToSession(self, session, jobInfo):
+        """
+        links the job to a session by:
+         - adding the job to the session
+         - adding the inFile data to the session
+         - getting user file name from the session
+        @param session: the user "session"
+        @type session: Mobyle.Session
+        @param jobInfo: a dictionary containing job information such as id, date, etc.
+        @type: dictionary
+        """
+        dataUsed = []
+        for paramName in self.service.getUserInputParameterByArgpos():
+            param = self.service.getParameter(paramName)
+            if param.isInfile():
+                if param.getDataType().isMultiple():
+                    inputNames = set([inputName.partition('.')[0] for inputName in self.request.keys() if inputName.startswith(paramName+'[')])
+                    valueToSet = [self._getFormValuesToAddToSession(session,self.params[inputName]) for inputName in inputNames if self.params.has_key(inputName)]
+                    dataUsed.extend(valueToSet)
+                elif self.params.has_key(paramName):
+                    valueToSet = self._getFormValuesToAddToSession(session,self.params[paramName])
+                    dataUsed.append(valueToSet)
+        userName = self.servicePID  + ' - ' + jobInfo['date']
+        session.addJob( jobInfo['id'],userName=userName,dataUsed=dataUsed)
+        jobInfo['userName'] = userName
+        return jobInfo
+
+    def _processFormParameterValue(self, param,inputName, index=0):
+        value, userName, src, srcFileName, inputMode = None, None, None, None, None
+        if param.isInfile():
+            inputMode = self.request.get(inputName+'.mode',"upload")
+            if self.request.has_key(inputName+".ref"):
+                srcFileName = self.request.get(inputName+".ref")
+                src = self.session
+                userName = self.request.get(inputName+".name")
+            elif self.request.has_key(inputName+".src"):
+                srcFileName = self.request.get(inputName+".srcFileName")
+                src = self.request.get(inputName+".src")
+                userName = self.request.get(inputName+".name")
+            else:
+                value = self.request.get(inputName)
+                userName = inputName + ".data"
+        else:
+            value = self.request.get(inputName, None) 
+        if (value is not None and value!="") or srcFileName: # if the value is not null (or blank from BMID)...
+            self.params[inputName] = {'value': value, 
+                                      'userName': userName , 
+                                      'srcFileName': srcFileName, 
+                                      'src': src, 
+                                      'inputMode':  inputMode, 
+                                      'parameter': param,
+                                      'index':index
+                                      }
+        
+    def _processForm(self):
+        """
+        process the cgi form:
+         - preprocessing infiles, parsing user and safe file names
+         - preprocessing boolean values, which have specific default values
+         - create a parameter values dictionary which is used later
+        """
+        for paramName in self.service.getUserInputParameterByArgpos():
+            param = self.service.getParameter(paramName)
+            if param.getDataType().isMultiple():
+                inputNames = sorted(set([inputName.partition('.')[0] for inputName in self.request.keys() if inputName.startswith(paramName+'[')]))
+                idx=0
+                for inputName in inputNames:
+                    self._processFormParameterValue(param,inputName,index=idx)
+                    idx = idx + 1
+            else:
+                self._processFormParameterValue(param,paramName)
+            
+    def parseService(self):
+        """
+        initializes the self.service and self.servicePID properties corresponding to the service url
+          uses self.programUrl or self.workflowUrl
+        """
+        if hasattr(self,'programUrl'):
+            self.service  = parseService(self.programUrl)
+            self.servicePID = registry.programsByUrl[self.programUrl].pid
+        elif hasattr(self,'workflowUrl'):
+            mp = Parser()
+            self.service = mp.parse(self.workflowUrl)
+            self.service.url = self.workflowUrl
+            self.servicePID = registry.workflowsByUrl[ self.workflowUrl ].pid   
+  
+    def create(self, request_fs=None, request_dict=None, session=None,simulate=False):
+        """
+        create sets up most of the class attributes TODO: check if this could be done in __init__?
+        """
+        self.request = {}
+        if request_dict:
+            self.request = request_dict
+        elif request_fs:
+            for key in request_fs.keys():
+                self.request[key] = request_fs.getvalue(key)
+        serviceDef = None
+        if self.request.get('programName',None):
+            serviceDef = registry.programsByUrl[self.request.get('programName')]
+        elif self.request.get('workflowUrl',None):
+            serviceDef = registry.workflowsByUrl[ self.request.get( 'workflowUrl')]
+        simulate = simulate or self.request.get('mobyle:simulate', False)=='true'
+        if not(simulate) and not(session) and (serviceDef.server.name=='local' and not(serviceDef.isExported()))\
+            and not(self.workflowId and registry.isJobLocal(self.workflowId)):
+            # test if service is exported is needed unless it is only a simulation
+            raise MobyleError("unauthorized access to service %s@%s, which is restricted to local use." % (serviceDef.name,serviceDef.server.name))
+        self.email = self.request.get('email', None)
+        if not(self.email) and session:
+            self.email = session.getEmail()
+        self.parseService()
+        if hasattr(self.service,'pid'):
+            self.servicePID = self.service.pid
+        if (not(simulate) and not( self.email ) and not( _cfg.opt_email( self.servicePID ) ) ):
+            # test if email is needed unless it is only a simulation
+            return {"emailNeeded":"true"}
+        if (not(simulate) and session and not(session.isActivated())):
+            # test if session is active is needed unless it is only a simulation
+            return {"activationNeeded":"true"}                
+        self.params = {}
+        self.session = session
+        self._processForm()
+        resp = self.submit(session,simulate)
+        if resp.has_key('id'):
+            self.jobId = resp['id']
+            resp['pid'] = str(registry.getJobPID(resp['id']))
+        # log the job in the access log
+        if not(resp.has_key('errormsg')):
+            msg = "%s %s %s %s %s" % (self.servicePID, # service PID
+                                              resp.get('pid','').replace(self.servicePID+'.',''), #job key
+                                              str(self.email), # user email
+                                              os.environ.get('REMOTE_HOST',os.environ.get('REMOTE_ADDR','local')), # ip or host
+                                              os.environ.get('HTTP_X_MOBYLEPORTALNAME','unknown') # client portal
+                                              )
+            acc_log.info( msg )
+        return resp
+    
+    def getOutputParameterValues(self, name):
+        js = JobState(self.jobId)
+        outputRefs = js.getOutput(name)
+        values = []
+        if outputRefs:
+            for outputRef in outputRefs:
+                fh = js.open(outputRef[0])
+                value = ''.join( fh.readlines() )
+                values.append({'value':value,'ref':self.jobId+'/'+outputRef[0],'size':outputRef[1],'fmt':outputRef[2]})
+        return values
+
+    def getOutputParameterRefs(self, name):
+        js = JobState(self.jobId)
+        outputRefs = js.getOutput(name)
+        values = []
+        if outputRefs:
+            for outputRef in outputRefs:
+                values.append({'src':self.jobId,'srcFileName':outputRef[0],'size':outputRef[1],'fmt':outputRef[2]})
+        return values
+
+
+    def getFromService(cls, programUrl=None, workflowUrl=None, service=None,workflowId=None):
+        """
+        create a new JobFacade from the service URL.
+        @param programUrl: service URL
+        @type session: string
+        @return: the appropriate job facade
+        @rtype: JobFacade
+        """
+        j=None
+        if programUrl:
+            if registry.programsByUrl.has_key(programUrl):
+                if registry.programsByUrl[programUrl].server.name=='local':
+                    j = LocalJobFacade(programUrl=programUrl, workflowId=workflowId)
+                else:
+                    j = RemoteJobFacade(programUrl=programUrl, workflowId=workflowId)
+            else:
+                raise MobyleError(msg="Service for url %s cannot be found" % programUrl)
+        elif workflowUrl:
+            if registry.workflowsByUrl.has_key(workflowUrl):
+                if registry.workflowsByUrl[workflowUrl].server.name=='local':
+                    j = LocalJobFacade(workflowUrl=workflowUrl, workflowId=workflowId)
+                else:
+                    j = RemoteJobFacade(workflowUrl=workflowUrl, workflowId=workflowId)
+            else:
+                raise MobyleError(msg="Service for url %s cannot be found" % workflowUrl)
+        elif service is not None:
+            j = LocalJobFacade(service=service, workflowId=workflowId)
+        return j
+    getFromService = classmethod(getFromService)
+      
+    def getFromJobId(cls, jobId):
+        """
+        create a JobFacade to access an existing job.
+        @param jobId: the job identifier
+        @type jobId: string
+        @return: the appropriate job facade
+        @rtype: JobFacade
+        """
+        jobState = JobState(jobId)
+        jfargs = {'jobId': jobState.getID(),'programUrl':None,'workflowUrl':None}
+        if jobState.isWorkflow():
+            jfargs['workflowUrl'] = jobState.getName()
+        else:
+            jfargs['programUrl']= jobState.getName()
+        # this id is identical to the one in parameter, 
+        # except it has been normalized (may have removed
+        # trailing index.xml from the id)
+        if jobState.isLocal():
+            return(LocalJobFacade(**jfargs))
+        else:
+            return(RemoteJobFacade(**jfargs))
+    getFromJobId = classmethod(getFromJobId)
+
+class RemoteJobFacade(JobFacade):
+    """
+    RemoteJobFacade is a class that is an access point to a Mobyle Job on a remote server.
+    """
+    
+    def __init__(self, programUrl=None, workflowUrl=None, service=None, jobId=None ,workflowId=None ):
+        JobFacade.__init__(self,programUrl=programUrl, workflowUrl= workflowUrl, service=service, jobId=jobId ,workflowId = workflowId )
+        if hasattr(self,'programUrl'):
+            self.server = registry.programsByUrl[self.programUrl].server
+        else:
+            self.server = registry.workflowsByUrl[self.workflowUrl].server
+        self.endpoint = self.server.url
+
+    def submit(self, session=None, simulate=False):
+        """
+        submits the job on the remote server
+        @param session: the session used to load infile values
+        @type session: Mobyle.Session
+        @return: job information as a dictionary
+        @rtype: dictionary
+        """
+        endpointUrl = self.endpoint + "/job_submit.py"
+        #values = self.request
+        for param in [param for param in self.params.values() \
+                      if (param.has_key('parameter') \
+                          and param['parameter'].isInfile())]:
+#            if param['srcFileName'] and not(param['value']):
+#                param['value'] = session.getContentData(param['srcFileName'], \
+#                                                        forceFull = True)[1]
+            if param['srcFileName'] and not(param['value']):
+                param['src'] = DataProvider.get(param['src'])
+                param['value'] = urllib.urlopen('%s/%s' % (param['src'].getDir(), param['srcFileName'])).read()
+                #param['value'] = urllib.urlopen(param['src']+'/'+param['srcFileName']).read()
+        requestDict = {}
+        if hasattr(self, 'programUrl'):
+            requestDict['programName'] = self.programUrl
+        else:
+            requestDict['workflowUrl'] = self.workflowUrl            
+        if self.email:
+            requestDict['email'] = self.email
+        if simulate:
+            requestDict['mobyle:simulate'] = 'true'
+        requestDict['workflowId'] = self.workflowId            
+        for name, param in self.params.items():
+            requestDict[name] = param['value']
+        try:
+            response = self._remoteRequest(endpointUrl, requestDict)
+        except:
+            return {"errormsg":"A communication error happened during the \
+                    submission of your job to the remote Portal"}
+        return response
+  
+  
+    def getStatus(self):
+        """
+        gets the job status on the remote server
+        @return: job status information as a dictionary
+        @rtype: dictionary
+        """
+        endpointUrl = self.endpoint + "/job_status.py"
+        requestDict = {}
+        requestDict['jobId'] = self.jobId
+        try:
+            response = self._remoteRequest(endpointUrl, requestDict)
+        except:
+            return {"errormsg":"A communication error happened while \
+                    asking job status to the remote Portal"}
+        status = Status(string=response['status'], message=response['msg'])
+        return status
+
+    def getSubJobs(self):
+        """
+        gets the list of subjobs (along with their status)
+        @return: subjobs list of dictionaries
+        @rtype: dictionary
+        """
+        endpointUrl = self.endpoint + "/job_subjobs.py"
+        requestDict = {}
+        requestDict['jobId'] = self.jobId
+        response = self._remoteRequest(endpointUrl, requestDict)
+        rv= []
+        if response:
+            for job_entry in response:
+                if job_entry.has_key('jobSortDate'):
+                    jobDate = time.strptime(job_entry['jobSortDate'],"%Y%m%d%H%M%S")
+                if job_entry.has_key('status'):
+                    jobstatus = Status(string=job_entry['status'], message=job_entry.get('status_message'))
+                jobID = job_entry['url']
+                jobPID = self.server.name + '.' + job_entry['jobID']
+                serviceName = job_entry['programName']
+                rv.append({'jobID':jobID,
+                                'jobPID':jobPID,
+                                'userName':jobID,
+                                'programName':serviceName,
+                                'date':jobDate,
+                                'status':jobstatus,
+                                'owner':registry.getJobPID(self.jobId)
+                                })
+        return rv
+
+    def killJob(self):
+        endpointUrl = self.endpoint + "/job_kill.py"
+        requestDict = {}
+        requestDict['jobId'] = self.jobId
+        try:
+            response = self._remoteRequest(endpointUrl, requestDict)
+        except:
+            return {"errormsg":"A communication error happened while \
+                    asking job status to the remote Portal"}
+        return response
+    
+    
+    def _remoteRequest(self, url, requestDict):
+        """
+        encodes and executes the proper request on the remote server, with 
+        proper error logging
+        @param url: the target url
+        @type url: string
+        @param requestDict: the request parameters
+        @type requestDict: dictionary
+        @return: response as a dictionary
+        @rtype: dictionary
+        """
+        data = urllib.urlencode(requestDict)
+        headers = { 'User-Agent' : 'Mobyle-1.0',
+                    'X-MobylePortalName' : _cfg.portal_name() }
+        req = urllib2.Request(url, data,headers)
+        try:
+            handle = urllib2.urlopen(req)
+        except (urllib2.HTTPError), e:
+            j_log.error("Error during remote job communication for remote service %s \
+             to %s, HTTP response code = %s" % (self.programUrl, url, e.code))
+            return None
+        except (urllib2.URLError), e:
+            j_log.error("Error during remote job communication for remote service %s \
+             to %s, URL problem = %s" % (self.programUrl, url, e.reason))
+            return None
+        try:
+            str = handle.read()
+            jsonMap = simplejson.loads(str)
+        except ValueError, e:
+            j_log.error("Error during remote job communication for remote service %s \
+             to %s, bad response format: %s" % (self.programUrl, url, str(e)))
+            return None
+        return jsonMap
+        
+
+class LocalJobFacade(JobFacade):
+    """
+    LocalJobFacade is a class that is an access point to a Mobyle Job on the local server.
+    """
+
+
+    def submit(self, session=None, simulate=False):
+        """
+        submits the job on the local server
+        @param session: the session used to load infile values
+        @type session: Mobyle.Session
+        @return: job information as a dictionary
+        @rtype: dictionary
+        """
+        try:
+            if isinstance(self.service, Program):
+                self.job = MobyleJob( service    = self.service , 
+                                      email      = self.email,
+                                      session    = session,
+                                      workflowID = self.workflowId,
+                                      debug = (None,1)[simulate],
+                                      email_notify=self.email_notify)
+            elif isinstance(self.service, Workflow):
+                self.job = WorkflowJob( workflow   = self.service,
+                                        email      = self.email,
+                                        session    = session,
+                                        workflowID = self.workflowId,
+                                        #debug = (None,1)[simulate],
+                                        email_notify=self.email_notify)
+            else:
+                raise NotImplementedError("No Job can be instanciated for a %s service" % str(self.service.__class__))
+            for paramName in self.service.getUserInputParameterByArgpos():
+                param = self.service.getParameter(paramName)
+                try:
+                    if param.getDataType().isMultiple():
+                        inputNames = set([inputName.partition('.')[0] for inputName in self.request.keys() if inputName.startswith(paramName+'[')])
+                        valueToSet = [self._getFormValueToSet(self.params[inputName]) for inputName in inputNames if self.params.has_key(inputName)]
+                    else:
+                        if self.params.has_key(paramName):
+                            valueToSet = self._getFormValueToSet(self.params[paramName])
+                        else:
+                            continue
+                    self.job.setValue(paramName, valueToSet)
+                except MobyleError , err:
+                    if param.promptHas_lang( "en"):
+                        userMsg = "Error while setting value for parameter: %s " % param.getPrompt( lang="en")
+                    else:
+                        userMsg = "Error while setting value for parameter: %s " % param.getName()
+                    sm = StatusManager ()
+                    sm.setStatus( self.job.getDir() , Status( code = 5 , message = userMsg ) )
+                    raise err
+            # run Job         
+            self.job.run()
+            return {
+                    'id': str(self.job.getJobid()),
+                    'date':str(time.strftime( "%x  %X", self.job.getDate())),
+                    'status': str( self.job.getStatus() ),
+                    }
+        except UserValueError, e:
+            jobId = None
+            if hasattr(e, 'message') and e.message:
+                msg = {'errormsg':str(e.message)}
+            else:
+                msg = {'errormsg':str(e)}
+            if hasattr(self, 'job') and self.job:
+                jobId = self.job.getJobid()
+                msg['id'] = str(jobId)
+            if hasattr(e, 'param') and e.param:
+                msg["errorparam"] = e.param.getName()
+            if jobId:
+                pid_str = str(registry.getJobPID(jobId))
+            else:
+                pid_str = "none"
+            j_log.error("user error in job %s (email %s): %s"% (pid_str, getattr(self,"email","anonymous"),str(e)))
+            return msg
+        except MobyleError, e:
+            j_log.error(e, exc_info = True)
+            if hasattr(e, 'message') and e.message:
+                msg = {'errormsg':str(e.message)}
+            else:
+                msg = {'errormsg':str(e)}
+            if hasattr(self, 'job') and self.job:
+                jobId = self.job.getJobid()
+                msg['id'] = str(jobId)
+            if hasattr(e, 'param') and e.param:
+                msg["errorparam"] = e.param.getName()
+            return msg
+  
+    def _getFormValueToSet(self,param):
+        if param['parameter'].isInfile():
+            if param['src']:
+                param['src'] = DataProvider.get(param['src'])
+                return (param['userName'], None, param['src'], param['srcFileName'],param['index'])
+            else:
+                return (param['userName'], param['value'], None, None,param['index'])
+        else:
+            return param['value']
+
+        
+    def getStatus(self):
+        """
+        gets the job status on the local server
+        @return: job status information as a dictionary
+        @rtype: dictionary
+        """
+        return utils_getStatus( self.jobId )
+    
+    def getSubJobs(self):
+        """
+        gets the list of subjobs (along with their status)
+        @return: subjobs list of dictionaries
+        @rtype: list
+        """
+        js = JobState(self.jobId)
+        if hasattr(js, 'getSubJobs'):
+            subjobs = js.getSubJobs()
+            subsubjobs = []
+            for s in subjobs:
+                s['jobPID'] = registry.getJobPID(self.jobId) + '::'+ registry.getJobPID(s['jobID'])
+                child_jf = JobFacade.getFromJobId(s['jobID'])
+                s['status'] = child_jf.getStatus()
+                for ss in child_jf.getSubJobs():
+                    ss['jobPID'] = s['jobPID'] + '::' + registry.getJobPID(ss['jobID'])
+                    schild_jf = JobFacade.getFromJobId(ss['jobID'])
+                    ss['status'] = schild_jf.getStatus() 
+                    subsubjobs.append(ss)                   
+            subjobs.extend(subsubjobs)
+            return subjobs
+        else:
+            return []
+    
+    def getExecutionDetails(self):
+        js = JobState(self.jobId)
+        execution_details = {}
+        if isinstance(js.state,ProgramJobState):
+            execution_details['cmd']=js.getCommandLine()
+            # TODO it should be specified if the contents
+            # of the paramfiles have to be sent back as well
+            execution_details['paramfiles']=js.getParamfiles()
+            # TODO the environment vars are not stored except in the .forChild.dump
+            # we can do something about it if necessary, otherwise accessing them
+            # is too much work...
+            #execution_details['env']=self.job._job.runner._xmlEnv
+            st = utils_getStatus( self.jobId )
+            execution_details['status']={'code':str(st),'message':st.message}
+            if st.isOnError():
+                execution_details['errormsg']=st.message
+        return execution_details
+
+        
+    def killJob(self):
+        """
+        
+        """
+        utils_killJob( self.jobId )
diff --git a/Src/Mobyle/JobState.py b/Src/Mobyle/JobState.py
new file mode 100644
index 0000000..7562aa6
--- /dev/null
+++ b/Src/Mobyle/JobState.py
@@ -0,0 +1,1149 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+"""
+manage the information in the index.xml
+"""
+import os
+from lxml import etree
+
+from time import localtime, strftime , strptime
+import urllib2 , urlparse
+import re
+import types
+import copy
+
+from logging import getLogger
+js_log = getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError , URLError , HTTPError , JobError
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+from Mobyle.Utils import indent as utils_indent
+from Mobyle.Service import Program
+from Mobyle.Workflow import Workflow
+
+
+_extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+"""
+G{classtree _abstractState , JobState , ProgramJobState , WorkflowJobState }
+"""
+
+
+
+def path2url( Dir ):
+    """
+    translate a directory absolute path in mobyle tree into an url in Mobyle 
+    @param Dir: the directory to translate this dir should be in Mobyle tree. Doesn't check if the path exist
+    @type Dir: string
+    @return: an url http corresponding to the directory dir
+    @rtype: string
+    @raise MobyleError: -L{MobyleError}
+    """
+    tmpdir = os.path.normpath( _cfg.results_path() ) #remove the ending slash
+    begin = Dir.find(tmpdir )
+    if begin != -1:
+        end = begin + len( tmpdir )
+        return "%s/%s" %( _cfg.results_url() , Dir[end:].lstrip( os.path.sep ) )         
+    else:
+        msg = str( Dir ) + " is not Mobyle directory compliant "
+        js_log.error( msg )
+        raise MobyleError , msg
+    
+
+def url2path( url ):
+    jobsUrl = _cfg.results_url()
+    if not jobsUrl in url:
+        msg =  "url2path: " + str( url ) + " is not a local Mobyle url"
+        js_log.warning( msg )
+        raise MobyleError, msg
+    
+    match = re.search( jobsUrl , url )
+    begin , end = match.span()
+    extrapath = url[ end : ].lstrip( '/' )
+    return os.path.normpath( os.path.join( _cfg.results_path() , extrapath ))
+    
+    
+def normUri( uri ):
+    """
+    normalize the uri
+      - if the uri is a distant url , return the url of the directory containing the index.xml
+      - if the uri is an local url , return the absolute path
+      - if the uri is a local path , return the absolute path
+    @param uri:
+    @type uri: string
+    @return: a normalize uri
+    @rtype: string
+    @raise: MobyleError if the uri is not a path or uri protocol is not http or file
+    """
+    protocol ,host, path, _ , _ , _ = urlparse.urlparse( uri )
+    
+    if protocol == 'file' or protocol == '':
+        path = os.path.abspath( os.path.join( host , path ) )        
+    elif protocol == 'http':
+        root_url =  _cfg.root_url()[7:] #remove the protocol http://
+        if host == root_url:
+            try:
+                path = url2path( uri )
+            except MobyleError:
+                path = uri
+        else:            
+            path = uri
+    else:
+        raise MobyleError, "Mobyle doesn't support this protocol: " + str( protocol )
+
+    if path[-10:] == "index.xml":
+        path = path[ : -10 ]
+    return path.rstrip( '/' )
+
+
+def getContentFile( uri ):
+    """
+    @param uri: the url or a path (absolute or relative) to a file
+    @type uri: string
+    @return: the content of the file designing by uri as a string
+    @rtype: string
+    @raise: MobyleError , if the protocol is not supported
+    """
+    protocol , host , path , _,_,_ = urlparse.urlparse( uri )
+    if protocol == '' or protocol == 'file':
+        if not os.path.isabs( path ):
+            if protocol == 'file':
+                path = os.path.abspath( os.path.join( host,path ))
+            else:
+                path = os.path.abspath( path )
+        try:
+            fh = open( path , 'r' )
+        except IOError, err:
+            msg = "getContentFile : " + str( err )
+            #js_log.error( msg ) # normal la premiere fois ne plus logger
+            raise MobyleError ,err
+        
+    elif protocol == 'http':
+        #urlopen manage HTTP redirect
+        try:
+            fh = urllib2.urlopen( uri )
+        except urllib2.HTTPError, err: #a subclass of URLError
+            msg = "unable to get %s : %s" %( uri , err )
+            js_log.error( msg )     
+            raise HTTPError( err )
+        except urllib2.URLError, err:
+            msg = "unable to get %s : %s" %( uri , err )
+            js_log.error( msg )
+            raise URLError( err )
+    else:
+        raise MobyleError, "Mobyle doesn't support " + protocol + " protocol"
+    content = ''
+    l = fh.readline()
+    while l:
+        content = content + l
+        l = fh.readline()
+    fh.close()
+    return content
+
+
+
+class _abstractState( object ):
+    
+    def __init__(self, uri = None , domTree = None):
+        if uri:
+            if uri[-10:] == "index.xml":
+                uri = uri[:-10]
+            self.uri =  uri.lstrip( os.path.sep ) #the uri of the directory
+            self._MyUri = normUri( uri )
+            protocol , host , path, a,b,c = urlparse.urlparse( self._MyUri )
+
+            if protocol == 'http':
+                self._islocal = False
+                indexUri = self._MyUri + "/index.xml"
+            else:
+                if self._MyUri.find( _cfg.results_path() ) == -1:
+                    msg = "the "+ str( self._MyUri ) + " is not a Mobyle directory "
+                    js_log.error( msg )
+                    raise MobyleError, msg
+
+                if not os.path.exists( self._MyUri ):
+                    msg = "no such file or directory : "+ str( self._MyUri )
+                    js_log.error( msg )
+                    raise MobyleError, msg
+
+                if not os.path.isdir( self._MyUri ):
+                    msg = self._MyUri + " is not a directory"
+                    js_log.error( msg )
+                    raise MobyleError , msg
+
+                indexUri = os.path.join( self._MyUri , "index.xml" )
+                self._islocal= True
+            self.indexName = indexUri
+
+            try:
+                self._parse()
+            except IOError , err:
+                if self._islocal:
+                    self._createDocument()
+                else:
+                    msg = "can't create index file "+str( err )
+                    js_log.error( msg )
+                    raise MobyleError , msg
+        elif domTree is not None:
+            self.doc = domTree
+            self.root = self.doc.getroot()
+        else:
+            msg = "you should provide either an uri or a dom tree"
+            js_log.error( msg )
+            raise MobyleError , msg
+    
+
+    def _parse(self):
+        """
+        parse a xml string or a file containing a xml tree
+        """
+        parser = etree.XMLParser( no_network = False )
+        self.doc = etree.parse( self._MyUri + "/index.xml" , parser)
+        self.root = self.doc.getroot()
+        
+
+    def update(self):
+        """
+        update the document the file index.xml
+        """
+        self._parse()
+
+
+    def _updateNode(self, node , nodeName , content ):
+        """
+        replace the content of the text_node child of a node by content. if the node nodeName doesn't exist, it make it
+        @param nodeName: the name of the Node to update
+        @type nodeName: String
+        @param content: the content of the text-node child of nodeName
+        @type content: String
+        """
+        nodes = node.xpath( "./" + nodeName )
+        if nodes:
+            node2update = nodes[0]
+            node2update.text = content
+        else:  
+            newNode = etree.Element( nodeName )
+            newNode.text = str( content )
+            node.append( newNode )
+        
+
+    def _addTextNode( self, node , nodeName , content , attr = None):
+        """ 
+        add a text node named nodeName with content to the node node
+        @param node: the node on which the new node will append 
+        @type node: node element
+        @param nodeName: the name of the new node
+        @type nodeName: string
+        @param content: the content of the new node
+        @type content: string
+        """
+        if attr:
+            newNode = etree.Element( nodeName  ,  dict( zip( attr.keys() , map( str , attr.values() ) )))
+        else:
+            newNode = etree.Element( nodeName )
+        try:
+            newNode.text = str( content )
+        except UnicodeEncodeError , err :
+            js_log.error( nodeName + " : "+ str( err )  )
+            raise err
+        except Exception , err:
+            js_log.error( "node: %s , content: %s : %s"%( nodeName, content , err ) , exc_info = True )
+            raise MobyleError , err
+        node.append( newNode )
+
+
+    def commit( self ):
+        """
+        write into the file index.xml in the working directory 
+        """
+        if self._MyUri.find("http://") != -1:
+            raise MobyleError, "can't modify a distant xml"
+        else:
+            try:
+                tmpFile = open( "%s.%d" %(self.indexName , os.getpid() )  , 'w' )
+                utils_indent( self.doc.getroot() )
+                tmpFile.write( etree.tostring( self.doc , xml_declaration=True , encoding='UTF-8' )) 
+                tmpFile.close()
+                os.rename( tmpFile.name , self.indexName )
+            except IOError, err:
+                js_log.error( "IOError during write index.xml on disk")
+                raise MobyleError ,err
+
+    def isLocal(self):
+        return self._islocal
+     
+    def _createDocument( self ):
+        self.root = etree.Element( "jobState" )
+        self.doc = etree.ElementTree( self.root )
+        self.root.addprevious(etree.ProcessingInstruction('xml-stylesheet','href="/portal/xsl/job.xsl" type="text/xsl"'))
+
+    def getOutputs( self ):
+        """
+        @return: a list of tuples or None
+                  -each tuple is composed with 2 elements a parameterName and a list of files
+                  -each file is a tuple with the file name and the size  
+        @rtype: [ ( string parameterName , [ (string fileName , long size ) , ...) , ... ]
+        """
+        self._parse( )
+        outputNodes = self.root.findall( "./data/output" )
+        res = []
+        
+        for outputNode in outputNodes:
+            parameterName = outputNode.find( 'parameter/name' ).text                 
+            files =[]
+            fileNodes = outputNode.findall( "./file" )
+            for fileNode in fileNodes:
+                filename = fileNode.text
+                file_attr = fileNode.attrib
+                size = file_attr[ "size" ]
+                files.append( ( str( filename ) , long( size ) ) )
+            res.append( ( parameterName , files ) )
+        if res:
+            return res
+        else:
+            return None
+  
+            
+    def getOutput( self , parameterName ):
+        """
+        @param parameterName: the name of a parameter which produce result
+        @type parameterName: string
+        @return: a list containing the results filename , size and format . if there isn't any result for this parameter, return None
+        @rtype:  [ ( string filename , long size , string fmt or None ) , ... ]
+        """
+        self._parse( )
+        fileNodes = self.root.xpath( "./data/output[ parameter/name = '" + parameterName + "' ]/file" )
+        files =[]
+        for fileNode in fileNodes:
+            filename =  fileNode.text 
+            size = long( fileNode.get( 'size' ) )
+            fmt = fileNode.get( "fmt" , None ) 
+            if fmt :
+                fmt = str( fmt )
+            files.append( ( filename , size , fmt ) )
+        if files:
+            return files
+        else:
+            return None
+    
+    def getInput(self, parameterName):
+        self._parse( )
+        fileNodes = self.root.xpath( "./data/input[ parameter/name = '" + parameterName + "' ]/file" )
+        files =[] 
+        
+        def xml_to_tuple( node ):
+            fileName= str( node.text)
+            size = long( node.get( "size" ) )
+            fmt = node.get( "fmt" , None ) 
+            if fmt :
+                fmt = str( fmt )
+            return ( fileName , size , fmt )
+        
+        for fileNode in fileNodes:
+            rawNode = fileNode.find( 'raw')
+            files.append( xml_to_tuple( rawNode ) )
+            convNode = fileNode.find( 'formattedFile' )
+            if convNode is not None :
+                files.append( xml_to_tuple( convNode ) )
+        return files
+           
+           
+    def getInputFiles( self ):
+        """
+        @return: a list of tuples or None
+                  -each tuple is composed with 2 elements, a parameter and a list of files
+                  -each file is a tuple of 3 elements with the file name , the size and the format if it's known or None 
+        @rtype: [ ( parameter , [ (str fileName , int size, str format or None ) , ...) , ... ]
+        """
+        self._parse( )
+        inputNodes = self.root.findall( "./data/input")
+        res = []
+        
+        def xml_to_tuple( node ):
+            fileName= str( node.text)
+            size = long( node.get( "size" ) )
+            fmt = node.get( "fmt" , None ) 
+            if fmt :
+                fmt = str( fmt )
+            return ( fileName , size , fmt )
+            
+        for inputNode in inputNodes:
+            parameterNode = inputNode.find( 'parameter' )
+            parameterName = parameterNode.find( 'name').text
+            fileNodes = inputNode.findall( "./file[raw]" )
+            if fileNodes:
+                files =[]
+                for fileNode in fileNodes:
+                    rawFile = fileNode.find( 'raw')
+                    files.append( xml_to_tuple( rawFile ) )
+                    convFile = fileNode.find( 'formattedFile' )
+                    if convFile is not None :
+                        files.append( xml_to_tuple( convFile ) )
+                res.append( ( parameterName , files ) )
+            else:
+                # handle Mobyle1.5->Mobyle 1.0.7 compatibility
+                # in the parsing of <file> tags which have changed
+                # with the multiple inputs options. 
+                fileNodes = inputNode.findall( "./file" )
+                if fileNodes:
+                    files =[]
+                    formattedFileNodes = inputNode.findall( "./formattedFile" )
+                    fileNodes += formattedFileNodes
+    
+                    for fileNode in fileNodes:
+                        fileName= str( fileNode.text )
+                        size = long( fileNode.get( "size" ) )
+                        fmt = fileNode.get( "fmt" , None ) 
+                        if fmt :
+                            fmt = str( fmt )
+                        files.append( ( fileName , size , fmt ) )
+                    res.append( ( parameterName , files ) )
+                
+        if res:
+            return res
+        else:
+            return None
+        
+           
+            
+    def getDir( self ):
+        """
+        @return: the absolute path to the directory where the job is executed
+        @rtype: string
+        @raise: MobyleError when the jobID is not local
+        """
+        if  self._islocal :
+            return self._MyUri
+        else:
+            raise MobyleError, "it's not a local job :" + self._MyUri
+    
+    def getID( self ):
+        """
+        @return: the id of the job 
+        @rtype: string
+        """
+        try:
+            self._parse()
+            return  self.root.find( "./id" ).text  
+        except AttributeError:
+            msg = "the element \"id\", doesn't exist"
+            js_log.error( msg )
+            raise MobyleError , msg
+    
+    def setID( self , ID ):
+        """
+        set the node id or if this node doesn't exist make it
+        @param id: the job identifier (the url corresponding to the working directory). 
+        @type id: String:
+        """
+        self._updateNode( self.root , 'id' , ID )
+
+        
+    def getSessionKey( self ):
+        """
+        @return: the key of the session
+        @rtype: string
+        """
+        self._parse()
+        sk = self.root.find( "./sessionKey")
+        if sk is None:
+            return None 
+        else:
+            return sk.text
+
+    def setSessionKey( self , sessionkey ):
+        """
+        Set the sessionkey of this workflow to sessionkey
+        @param sessionkey: the sessionkey of this workflow
+        @type sessionkey: string
+        """
+        self._updateNode( self.root , 'sessionKey' , sessionkey )
+        
+    def getName( self ):
+        """
+        @return: the url corresponding to the service definition used for this job
+        @rtype: string
+        """
+        self._parse()
+        name = self.root.find("./name" )
+        if name is not None:
+            return name.text
+        else:
+            msg = "the element: \"name\" doesn't exist"
+            js_log.error( msg )
+            raise MobyleError , msg
+
+
+    def setName( self , name):
+        """
+        update the node name or if this node doesn't exist make it
+        @param name: the url of the service definition used for this job. 
+        @type name: String:
+        """
+        self._updateNode( self.root , 'name' , name )
+            
+
+    def getDate( self):
+        """
+        @return: the date of the job the date format is: "%x %X"
+        @rtype: string
+        """
+        self._parse()
+        date = self.root.find("./date")
+        if date is not None:
+            return date.text
+        else:
+            msg = "the element: \"date\" doesn't exist"
+            js_log.error( msg )
+            raise MobyleError , msg
+
+
+    def setDate( self, date= None):
+        """
+        update the node date or if this node doesn't exist make it
+        @param date: the date. 
+        @type date: String:
+        """
+        if date is None:            
+            date = strftime( "%x  %X", localtime() )
+
+        if type( date ) == types.StringType :
+            date = strptime(  date , "%x  %X")
+        else: # I assume the date is a <type 'time.struct_time'>
+            date = strftime( "%x  %X", date )
+
+        self._updateNode( self.root , 'date' , date )
+        
+        
+    def getEmail( self):
+        """
+        @return: the email of the user
+        @rtype: string
+        """
+        self._parse()
+        email = self.root.find("./email")
+        if email is not None:
+            return email.text
+        else:
+            return None
+        
+    
+    def setEmail( self, email ):
+        """
+        set the node email or if this node doesn't exist, make it
+        @param email: the email user for this job. 
+        @type email: String:
+        """
+        self._updateNode( self.root , 'email' , str( email ) )
+
+    def getOutputFile( self, fileName ):
+        """
+        @param fileName:
+        @type fileName: String
+        @return: the content of a output file as a string
+        @rtype: string
+        """
+        return getContentFile( self._MyUri + "/" + fileName )
+
+
+    def open( self, fileName ):
+        """
+        return an file object if the file is local or a file like object if the file is distant
+        we could apply the same method on this object: read(), readline(), readlines(), close(). (unlike file the file like object doesn't implement an iterator).
+        @param fileName: the name of the file (given by getResults).
+        @type fileName: string
+        @return: a file or file like object
+        """
+        if self._islocal :
+            try:
+                fh = open( os.path.join( self._MyUri, fileName ), 'r' )
+            except IOError, err:
+                raise MobyleError ,err
+        else:
+            try:
+                fh = urllib2.urlopen( self._MyUri +'/'+ fileName )
+            except urllib2.HTTPError,err:
+                raise MobyleError ,err
+        return fh
+
+    def getHost( self):
+        """
+        @return: the host of the job
+        @rtype: string
+        """
+        self._parse()
+        host = self.root.find("./host")
+        if host is not None:
+            return host.text
+        else:
+            return None
+
+    def setHost( self, host ):
+        """
+        update the node host or if this node doesn't exist make it
+        @param host: the host of the job. 
+        @type host: String:
+        """
+        self._updateNode( self.root , 'host' , host  )
+
+    def addInputDataFile( self , paramName , File , fmtProgram = None  , formattedFile = None ):
+        """
+        if the node result exist add new nodes for files, otherwise create a new node results and add nodes for files
+        @param paramName: the parameter name to update or create
+        @type paramName: string
+        @param files: a tuple of fileName , size , the data format
+        @type files: list of tuple ( string fileName , int , string format or None )
+        @param fmtProgram: the name of the program used to reformat the data
+        @type fmtProgram:  string
+        @param formattedFile: the file after reformatting
+        @type formattedFile: a tuple of ( string fileName , long size , string data format )
+        """
+        #js_log.debug( "setInputDataFile pas d'input parameter avec le nom %s " %paramName)
+        dataNode = self.root.find( './data' )
+        if dataNode is None:
+            #js_log.debug( "pas de noeud data" )
+            dataNode = etree.Element( 'data' )
+            self.root.append( dataNode )
+       
+        try:
+            inputNode = self.root.xpath( './data/input[parameter/name = "'+ paramName + '" ]')[0]
+        except IndexError :
+            inputNode = self._createInOutNode( 'input', paramName )
+            #js_log.debug( "1 nouvel inputNode a ete cree" )
+            dataNode.append( inputNode )
+        fileNode = etree.Element( 'file' )
+        inputNode.append( fileNode )    
+        fileName , size , fmt = File
+        attr = {}
+        attr[ 'size' ] = str( size ) 
+        if fmt :
+            attr[ 'fmt' ] = fmt
+        self._addTextNode( fileNode , 'raw' , os.path.basename( fileName ) , attr )    
+        if fmtProgram :
+            self._addTextNode( fileNode , 'fmtProgram' , fmtProgram )
+        if formattedFile :
+            formattedFileName , formattedSize , formattedFmt = formattedFile
+            attr = {}
+            attr[ 'size' ] =  str( formattedSize )
+            if formattedFmt :
+                attr[ 'fmt' ] = formattedFmt
+            self._addTextNode( fileNode , 'formattedFile' , os.path.basename( formattedFileName ) , attr  )
+                
+                
+    def renameInputDataFile(self , paramName , oldName, newName ):
+        """
+        Change the name of an input file
+        @param paramName: the parameter name 
+        @type paramName: string
+        @param newName: the new value of the input file name
+        @type newName: string
+        """
+        try:
+            inputNode = self.root.xpath( './data/input[parameter/name = "'+ paramName + '" ]')[0]
+        except IndexError :
+            raise MobyleError, "try to rename an unkown Input File parameter: %s" %paramName
+        
+        fileNodes = inputNode.findall( 'file' )
+        for fileNode in fileNodes:
+            rawNode = fileNode.find( 'raw' )
+            if rawNode.text == oldName:
+                rawNode.set( 'origName' , rawNode.text )
+                rawNode.text = newName
+            formattedNode = fileNode.find( 'formattedFile' )
+            if formattedNode is not None:
+                if formattedNode.text == oldName:
+                    formattedNode.set( 'origName' , formattedNode.text )
+                    formattedNode.text = newName
+        
+        
+    def setInputDataValue( self , paramName , value  ):
+        """
+        if the node result exist add new nodes for files, otherwise create a new node results and add nodes for files
+        @param paramName: the parameter name to update or create
+        @type paramName: string
+        @param prompt: the prompt L{Parameter} of this parameter
+        @type prompt: ( string prompt , string lang )
+        @param paramType: the parameter Type 
+        @type paramType: a MobyleType instance
+        @param files: a fileName or a sequence of fileName
+        @type files: a String or a sequence of Strings
+        """
+        try:
+            inputNode = self.root.xpath( './data/input/parameter[ name = "'+ paramName + '" ]')[0]
+            raise MobyleError , "this input data already exist " + paramName
+        except IndexError :
+            dataNode = self.root.find( './data' )
+            if dataNode is None:
+                dataNode = etree.Element(  'data' )
+                self.root.append( dataNode )
+            inputNode = self._createInOutNode( 'input', paramName )
+            
+            firstoutputNode = self.root.find( './data/output')
+            if firstoutputNode is not None:
+                firstoutputNode.addprevious( inputNode )
+            else :
+                dataNode.append(inputNode)
+            try:
+                self._addTextNode( inputNode , 'value' , str( value ) )
+            except Exception, err:
+                js_log.error( "cannot add '%s' for parameter %s : %s" %( value , paramName , err ) )
+                from Mobyle.StatusManager import StatusManager
+                from Mobyle.Status import Status
+                if self._islocal:
+                    sm = StatusManager()
+                    dirPath = self._MyUri
+                    sm.setStatus( dirPath , Status( code = 5 , message = 'Mobyle Internal Error' ) )
+                raise MobyleError( 'Mobyle Internal Error' ) 
+    
+    
+    def setOutputDataFile( self , paramName , files , isstdout = False ):
+        """
+        if the node result exist add new nodes for files, otherwise create a new node results and add nodes for files
+        @param paramName: the parameter name to update or create
+        @type paramName: string
+        @param files: a list where each element are composed of 3 item ( fileName , size , format )
+        @type files: [ ( string , int , string or None ) , ... ] 
+        """
+        try:
+            outputNode = self.root.xpath( './data/output/parameter[ name = '+ paramName + ' ]')[0]
+            raise MobyleError , "this output data already exist " + paramName
+        except IndexError :
+            dataNode = self.root.find( './data' )
+            if dataNode is None:
+                dataNode = etree.Element( 'data' )
+                self.root.append( dataNode )
+            
+            if isstdout:
+                outputNode = self._createInOutNode( 'output', paramName , paramAttrs = {'isstdout' : '1' } )
+            else:
+                outputNode = self._createInOutNode( 'output', paramName  )
+            dataNode.append( outputNode )
+        for File in files:
+            filename , size , fmt = File
+            attr = {}
+            attr[ 'size' ] = str( size )
+            if fmt:
+                attr [ 'fmt' ] = fmt
+            self._addTextNode( outputNode , 'file' , os.path.basename( filename ) , attr = attr )
+              
+                    
+    def _createInOutNode( self , io , paramName , paramAttrs = {} ):
+        if io != 'input' and io != 'output' :
+            raise MobyleError , "io could take only 'input' or 'output' as value"
+        inOutputNode = etree.Element( io )
+        parameterNode = self._createParameter(  paramName , attrs = paramAttrs)
+        inOutputNode.append( parameterNode )
+        return inOutputNode
+ 
+    def _createParameter( self , paramName , attrs = {}):       
+        newParameterNode = etree.Element( "parameter" )
+        if attrs :
+            attributes = newParameterNode.attrib
+            attributes.update( str( attrs ) )
+        self._addTextNode( newParameterNode , 'name' , paramName )
+        return newParameterNode
+
+    def delInputData( self , paramName ):
+        try:
+            inputDataNode = self.root.xpath( "./data/input[ parameter/name = '" + paramName + "' ]")[0]
+        except IndexError:
+            raise MobyleError, "there is no data with parameter named:" + str( paramName )            
+        else:
+            dataNode = self.root.find( "./data")
+            dataNode.remove( inputDataNode )
+
+    def getArgs( self ):
+        """
+        @return: 
+        @rtype:
+        """
+        inputDataNodes = self.root.findall( './data/input' )
+        args = {}
+        
+        for inputDataNode in inputDataNodes:
+            """
+            toutes les valeurs meme les noms de fichiers
+            nom : value
+            """
+            value = inputDataNode.find( './value' )
+            if value is not None :
+                value = value.text
+            else:
+                value = inputDataNode.find( './formattedFile')
+                if value is not None:
+                    value = value.text
+                else:
+                    value = inputDataNode.find( './file')
+                    if value is not None:
+                        value = value.text
+                    else:
+                        raise MobyleError , "this input has not value nor file"
+            name = inputDataNode.find( './parameter/name').text
+            args[ name ] = value
+        return args     
+               
+    
+    def getPrompt( self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: string
+        @return: the prompt of this parameter
+        @rtype: string
+        """
+        try:
+            prompt = self.root.xpath( './data/*/parameter[ name = "' + paramName + '"]/prompt/text()' )[0]
+            return prompt
+        except IndexError:
+            return None
+    
+    
+
+class JobState( object ):
+    """
+    the JobState Object manage the informations in index.xml file
+    G{}
+    """
+    _refs = {}
+    
+    def __new__( cls , uri = None , service = None ):
+
+        state = None
+        if uri is None:
+            uri = os.getcwd()
+        if uri[-9:] == "index.xml":
+            uri = uri[:-9]
+        uri = uri.rstrip( '/' )
+        MyUri = normUri( uri ) 
+        protocol , _ , _ , _ , _ , _ = urlparse.urlparse( MyUri )
+        if protocol == 'http':
+            islocal = False
+            indexUri = MyUri + "/index.xml"
+        else:
+            results_path = _cfg.results_path() 
+            if MyUri.find( results_path ) == -1:
+                msg = "the "+ str( MyUri ) + " is not a Mobyle jobs directory "
+                js_log.error( msg )
+                from errno import EINVAL
+                raise JobError( EINVAL , "not a Mobyle jobs directory" , MyUri )
+            if not os.path.exists( MyUri ):
+                import sys
+                msg = "call by %s : no such directory : %s" %( os.path.basename( sys.argv[0] ) , MyUri )
+                js_log.error( msg )
+                from errno import ENOENT
+                raise JobError( ENOENT , "No such directory" , MyUri )
+            if not os.path.isdir( MyUri ):
+                msg = MyUri + " is not a directory"
+                js_log.error( msg )
+                from errno import ENOTDIR
+                raise JobError( ENOTDIR , "not a directory" , MyUri )
+            indexUri = os.path.join( MyUri , "index.xml" )
+            islocal = True
+        try:
+            return cls._refs[ MyUri ]
+        except KeyError:
+            self = super( JobState , cls ).__new__( cls )
+            self.state = state
+            self.uri = uri        #uri given by the user
+            self._MyUri = MyUri   #path if uri = local url if uri is distant
+            self._islocal = islocal
+            if self._islocal and not os.path.exists( indexUri ):
+                if service is not None:
+                    self.createState( service )
+                else:
+                    raise MobyleError , "%s does not exists" %indexUri
+       
+            else:
+                try:
+                    parser = etree.XMLParser( no_network = False )
+                    doc = etree.parse( indexUri , parser )
+                except Exception ,err:
+                    raise MobyleError , "problem in parsing %s : %s" %( indexUri , err )
+                root = doc.getroot()
+                if len(root.xpath('workflow'))>0:
+                    self.state = WorkflowJobState( domTree = doc )
+                else:
+                    self.state = ProgramJobState( domTree = doc )
+
+            self.state.uri = self.uri
+            self.state._MyUri = self._MyUri
+            self.state.indexName = indexUri  
+            self.state._islocal = self._islocal
+
+            cls._refs[ MyUri ] = self
+            return self
+        
+
+    def __getattr__( self , name ):
+        if self.state :
+            return getattr( self.state , name )
+        else:
+            msg = "Jobstate instance is empty. you must create a State before"
+            raise MobyleError , msg
+
+
+    def createState( self , service ):
+        if self.state:
+            msg = "cannot create a JobState, Jobstate already exists"
+            raise MobyleError , msg
+        else:
+            if isinstance(service, Program):
+                if self._islocal:
+                    self.state = ProgramJobState( uri = self._MyUri )
+                    if hasattr(service, 'simulation_path'):
+                        self.state.setSimulationDefinition(service)
+                    else:
+                        self.state.setDefinition( service.getUrl())
+                else:
+                    msg ="can't create a State on a distant Mobyle Server"
+                    raise MobyleError , msg
+            elif isinstance(service, Workflow):
+                if self._islocal:
+                    self.state = WorkflowJobState( uri = self._MyUri )
+                    self.state.setDefinition(service)
+                else:
+                    msg ="can't create a State on a distant Mobyle Server"
+                    raise MobyleError , msg
+            else:
+                raise MobyleError 
+
+    def getWorkflowID( self ):
+        """
+        @return: the url of the worklow owner of this job.
+        @rtype: string
+        """
+        self._parse()
+        wf = self.root.find( "./workflowID" )
+        if wf is None:
+            return None
+        else:
+            return wf.text
+
+
+    def setWorkflowID( self , worklowID ):
+        """
+        update the node worklowID or if this node doesn't exist, make it
+        @param worklowID: the url of the worklow owner of this job. 
+        @type worklowID: String:
+        """
+        self._updateNode( self.root , 'workflowID' , worklowID )
+##############################################
+#                                            #
+#               ProgramJobState              #
+#                                            #
+##############################################
+
+
+class ProgramJobState( _abstractState ):
+    """
+    ProgramJobState Object manages the information in index.xml file for a program job
+    """
+
+    def isWorkflow(self):
+        return False
+    
+    def setDefinition( self , program_url ):
+        """Copy the program definition in the program job for future reference"""
+        from Mobyle.Registry import registry
+        #                    /data/services/servers/SERVER/programs/PROGRAM_NAME.xml  
+        match = re.search(  "/data/services/servers/(.*)/.*/(.*)\.xml" , program_url )
+        server_name = match.group(1)
+        service_name = match.group(2)
+        try:
+            program_path = registry.getProgramPath( service_name , server_name )
+        except KeyError:
+            raise MobyleError( "registry have no service %s for server %s" %(service_name , server_name )) 
+        program_def  = etree.parse( program_path )
+        program_node = program_def.getroot()
+        #if a node 'program already exists , remove it bfore to add the new one
+        nodes = self.root.xpath( 'program' )
+        if nodes:
+            p = nodes[0].getparent()
+            for i in nodes:
+                p.remove(i)  
+        self.root.insert(0, program_node )
+
+    def setSimulationDefinition( self , service ):
+        """Copy the program definition in the program job for future reference"""
+        program_def  = etree.parse( service.simulation_path )
+        program_node = program_def.getroot()
+        #if a node 'program already exists , remove it bfore to add the new one
+        nodes = self.root.xpath( 'program' )
+        if nodes:
+            p = nodes[0].getparent()
+            for i in nodes:
+                p.remove(i)  
+        self.root.insert(0, program_node )
+        
+    
+    def getDefinition(self):
+        """Copy the program definition in the program job for future reference"""
+        nodes = self.root.xpath( 'program' )
+        if nodes:
+            return nodes[0]
+        else:
+            return None
+    
+    def getCommandLine( self ):
+        """
+        @return: the Command line
+        @rtype: string
+        """
+        self._parse()
+        cl = self.root.find( "./commandLine" )
+        if cl is None:
+            return None
+        else:
+            return cl.text
+
+
+    def setCommandLine( self , command ):
+        """
+        update the node command or if this node doesn't exist, make it
+        @param command: the command of the job. 
+        @type command: String:
+        """
+        self._updateNode( self.root , 'commandLine' , command )
+
+    def getStdout( self ):
+        """
+        we assume that the standart output was redirect in programName.out
+        @return: the content of the job stdout as a string
+        @rtype: string
+        @raise MobyleError: if the job is not finished a L{MobyleError} is raised
+        """
+        try:
+            outName = self.root.xpath( './data/output/parameter[@isstdout=1]/name/text()' )[0]
+        except IndexError:
+            outName = os.path.join( self._MyUri , os.path.basename(self.getName())[:-4] + ".out" )
+        return getContentFile( outName )
+    
+
+    def getStderr( self ):
+        """
+        @return: the content of the job stderr as a string
+        @rtype: string
+        @raise MobyleError: if the job is not finished a L{MobyleError} is raised
+        """
+        try:
+            errname = os.path.join( self._MyUri , os.path.basename(self.getName())[:-4] + ".err" )
+        except KeyError:
+            return None
+        return getContentFile( errname )
+
+    def getParamfiles(self):
+        """
+        @return: a list containing the parameter files description. if there isn't any result return None
+        @rtype: list of tuple ( string filename , long size )
+        """
+        self._parse( )
+        results = []
+        for paramf in self.root.findall( "./paramFiles/file" ):
+            filename = paramf.text
+            size = paramf.get( "size" )
+            results.append( ( str( filename ) , long( size ) ) )
+        return results
+
+
+    def setParamfiles( self , files ):
+        """
+        if the node result exist add new nodes for files, otherwise create a new node results and add nodes for files
+        param files: a list of fileName , size of file
+        type files: [ ( String fileName , Long size ) , ... ]
+        """
+        paramfiles_node = self.root.findall( './paramFiles' )
+        lastChild = self.root.findall( './*' )[-1]
+        
+        if paramfiles_node:
+            paramfiles_node  = paramfiles_node[0]
+        else:
+            paramfiles_node = etree.Element( 'paramFiles' )
+            if lastChild.tag == 'commandLine':
+                lastChild.addprevious( paramfiles_node )
+            else:
+                self.root.append( paramfiles_node )
+        for File in files:
+            fileName , size = File
+            attr = {}
+            attr[ 'size' ] = size
+            self._addTextNode( paramfiles_node , 'file', fileName , attr )
+
+    def getStatus(self):
+        raise NotImplementedError , "ProgramState does not manage status anymore use StatusManager instead"
+
+
+    def setStatus(self):
+        raise NotImplementedError , "ProgramState does not manage status anymore use StatusManager instead"
+
+
+
+class WorkflowJobState( _abstractState ):
+    """
+    WorkflowJobState Object manages the information in index.xml file for a workflow job
+    """
+
+    def isWorkflow(self):
+        return True
+        
+    def setDefinition(self, workflow):
+        """Copy the workflow definition in the workflow job for future reference"""
+        nodes = self.root.xpath( 'workflow' )
+        if nodes:
+            p = nodes[0].getparent()
+            for i in nodes:
+                p.remove(i)        
+        self.root.append(copy.deepcopy(workflow))
+    
+    def getDefinition(self):
+        """Copy the workflow definition in the workflow job for future reference"""
+        nodes = self.root.xpath( 'workflow' )
+        return nodes[0]
+        
+    def setTaskJob(self, task, jobId):
+        """Set the job that runs a specific task"""
+        nodes = self.root.xpath( 'jobLink[@taskRef="%s"]' % task.id)
+        if nodes:
+            p = nodes[0].getparent()
+            for i in nodes:
+                p.remove(i)      
+        jobLinkElNode = etree.Element("jobLink")
+        jobLinkElNode.set("taskRef",task.id)
+        jobLinkElNode.set("jobId",jobId)
+        self.root.append( jobLinkElNode )
+        
+    def getTaskJob(self, task):
+        """Get the job that runs a specific task"""
+        res = self.root.xpath( 'jobLink[@taskRef="%s"]/@jobId' % task.id)
+        if len(res)>0:
+            return res[0]
+
+    def getSubJobs(self):
+        """Get all the corresponding subjobs information"""
+        subjobs = []
+        res = self.root.xpath( 'jobLink')
+        for entry in res:
+            jobID = entry.xpath('@jobId')[0]
+            taskID = entry.xpath('@taskRef')[0]
+            task = self.root.xpath('workflow/flow/task[@id="%s"]' % taskID)[0]
+            serviceName = task.xpath('@service')[0]
+            try:
+                job = JobState(jobID)
+                subjobs.append({'jobID':jobID,
+                                'userName':jobID,
+                                'programName':serviceName,
+                                'date':strptime( job.getDate() , "%x  %X"),
+                                'owner':self.getID()
+                                })
+            except MobyleError, me:
+                # this happens if the job index.xml file cannot be retrieved, especially if it is a removed subtask (e.g., remote)
+                pass
+            
+        return subjobs
diff --git a/Src/Mobyle/MobyleError.py b/Src/Mobyle/MobyleError.py
new file mode 100644
index 0000000..b2195c6
--- /dev/null
+++ b/Src/Mobyle/MobyleError.py
@@ -0,0 +1,125 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+from exceptions import Exception
+
+class MobyleError(Exception):
+
+    def __init__(self, msg = None):
+        self._message = str( msg )
+        
+    def _get_message(self):
+        return self._message
+    #workaround to ensure Mobyle compatibility
+    #with either python 2.5 and python 2.6
+    #as self.message is deprecated in python 2.6
+    message = property( fget = _get_message )
+    
+    def __str__(self, *args, **kwargs):
+        return self.message
+
+class ServiceError(MobyleError):
+    pass
+
+class ParameterError(ServiceError):
+    pass
+
+class ConfigError(MobyleError):
+    pass
+
+class ParserError( MobyleError ):
+    pass
+
+class UnDefAttrError(ParameterError):
+    """
+    Raised  when attempt to apply a method on a parameter attribute which is not exist  
+    """
+    pass
+ 
+class UserValueError(MobyleError):
+    """
+    Raised when the user do a mistake. ex a wrong value for a parameter or forget to specify a mandatory parameter...
+    """
+    def __init__(self, parameter = None , msg = None):
+        self._param = parameter
+        self._message = msg
+    
+    def _get_param(self):
+        return self._param
+    param = property( fget = _get_param )
+    
+    def __str__(self):
+        
+        #lang = Mobyle.ConfigManager.Config().lang()
+        #problem d'import cyclique
+        # TOFIX
+        lang = 'en'
+        
+        if self._param:
+            if self._param.promptHas_lang( lang ):
+                try:
+                    err_msg = self._param.getPrompt( lang ) + " = " + self._message
+                except:
+                    err_msg = "%s : invalid value" % self._param.getPrompt( lang )
+            else:
+                if self._param.promptLangs():
+                    try:
+                        err_msg =self._param.getPrompt( self._param.promptLangs()[0] ) + " = " + self._message
+                    except:
+                        err_msg = "%s : invalid value" % self._param.getPrompt( self._param.promptLangs()[0] )
+                else:
+                    try:
+                        err_msg = self._param.getName() + self._message
+                    except:
+                        err_msg = "%s : invalid value" %self._param.getName() 
+        else:
+            err_msg = self._message
+        return err_msg
+
+
+class UnSupportedFormatError( MobyleError ):
+    pass
+
+class EvaluationError( MobyleError ):
+    pass
+
+class NetError( MobyleError ):
+    pass
+
+class HTTPError( NetError ):
+    def __init__(self, *args):
+        self.code = args[0].getCode()
+
+class URLError( NetError ):
+    pass
+
+class JobError( MobyleError ):
+    def __init__(self, *args):
+        self.errno = args[0]
+        self.strerror = args[1]
+        self.jobID = args[2]
+        
+    def __str__(self):
+        return "[ Errno %d] Cannot open Job %s : %s "%( self.errno , 
+                                                        self.jobID ,
+                                                        self.strerror ,
+                                                        )
+class SessionError( MobyleError ):
+    pass
+
+class EmailError( MobyleError ):
+    pass
+
+class TooBigError( EmailError ):
+    pass
+
+class AuthenticationError( SessionError ):
+    pass
+    
+class NoSpaceLeftError( SessionError ):
+    pass
\ No newline at end of file
diff --git a/Src/Mobyle/MobyleJob.py b/Src/Mobyle/MobyleJob.py
new file mode 100644
index 0000000..f85da9e
--- /dev/null
+++ b/Src/Mobyle/MobyleJob.py
@@ -0,0 +1,1063 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+
+import os
+import logging 
+import types
+import glob
+
+from Mobyle.MobyleLogger import MLogger
+MLogger()
+
+from Mobyle.Net import EmailAddress , checkHost
+from Mobyle.MobyleError import MobyleError , UserValueError , UnDefAttrError , ServiceError ,UnSupportedFormatError , EvaluationError
+from Mobyle.Job import Job
+from Mobyle.Evaluation import Evaluation
+from Mobyle.JobState import JobState , url2path as jobState_url2path
+from Mobyle.Utils import getStatus as utils_getStatus , safeFileName , makeService
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Mobyle.Admin import Admin
+
+_extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+     
+
+class MobyleJob:
+    """this class is the interface with the cgis to submit a job"""
+    
+    def __init__(self, progUrl= None , email = None , service = None, params = None, evaluator = None , ID = None , session = None , cfg = None , workflowID = None , email_notify= 'auto', debug=None):
+        """
+        We could instanciate a MobyleJob whith a progName or directly with a Service instance , with params or a Evaluation instance.
+        if we instanciate a MobyleJob with a progName mobyleJob will instanciate a service corresponding to this name .
+        you could provide the user parameters in a dictionary params which will used to fill an Evalution instance or provide directly the Evaluation instance.
+        
+        @param progUrl: the url of a xml file
+        @type progUrl: String
+        @param email: the user Email address where the results will be sent
+        @type email: string or L{EmailAddress}
+        @param service: if you want to run a service which is already instanciate
+        @type service: a L{Service} instance
+        @param evaluator: an Evaluation instance
+        @type evaluator: L{Evaluation} 
+        @param ID: the url of job, if an id is specified 
+        @type ID: string
+        @param cfg: a config object
+        @type cfg: L{ConfigManager.Config} instance
+        @param workflowID: the url of the workflow ownre of this job
+        @type workflowID: string
+        @param email_notify: if the user must be or not notify of the results at the end of the Job.
+        the 3 authorized values for this argument are: 
+          - 'true' to notify the results to the user
+          - 'false' to Not notify the results to the user
+          - 'auto' to notify the results based on the job elapsed time and the config  EMAIL_DELAY
+        @type email_notify: string 
+        @raise L{MobyleError}: if the id is not a valid url a MobyleError is raised
+        @todo: estce que passer un evaluateur a MobyleJob a toujours un sens avec la nouvelle architecture (creation de fichiers ... l'evaluateur a un nom de fichier comme valeur comment recuperer le contenu)
+        """
+        self.m_log = logging.getLogger( __name__ )
+        if cfg is None:
+            from Mobyle.ConfigManager import Config
+            self.cfg = Config()
+        else:
+            self.cfg = cfg
+            
+        if ID:
+            self.jobState = JobState( ID )
+            self._job = None
+            self._hasRun = True
+        else:
+            self.build_log = logging.getLogger('Mobyle.builder')
+            
+            if progUrl:
+                jobName = os.path.basename( progUrl )[:-4]
+            elif service:
+                jobName = service.getName()
+            else:
+                raise MobyleError, "you must provide either a service or a service name"
+
+            ## check the service availablity
+            serviceID = 'local.'+jobName
+            if self.cfg.isDisabled( serviceID  ):
+                if self.cfg.isDisabled():
+                    raise MobyleError, "Sorry, the server is not available for now."
+                else:
+                    raise MobyleError, "Sorry, the program " + jobName +" is  not available for now."
+            try:
+                remoteHost = os.environ[ 'REMOTE_HOST' ]
+            except KeyError:
+                remoteHost = None
+            try:
+                ip = os.environ[ 'REMOTE_ADDR' ]
+            except KeyError :
+                ip = 'local'
+            self._remote =( ip , remoteHost )
+            if  self._remote[1]:
+                remoteLog = self._remote[1]  #the remote host    
+            else:
+                remoteLog  = self._remote[0] #the remote addr
+            if session :
+                email = session.getEmail()
+                if email:
+                    self.userEmail = EmailAddress( session.getEmail() )
+                    resp = self.userEmail.checkBlackList()
+                    if not resp.status:
+                        raise UserValueError( msg = resp.user_message )
+                else:
+                    self.userEmail = None
+            elif email :
+                if  isinstance( email , types.StringTypes ):
+                    self.userEmail = EmailAddress( email )
+                else:
+                    self.userEmail = email
+                resp = self.userEmail.check()
+                if not resp.status:
+                    msg = "%s %s %s %s" %( jobName ,
+                                           unicode( str( self.userEmail ).decode('ascii', 'replace')).encode( 'ascii', 'replace' ) ,
+                                           remoteLog ,
+                                           "FORBIDDEN "+ resp.message
+                                           )
+                    self.m_log.info( msg )
+                    raise UserValueError( msg = resp.user_message )                 
+            else:
+                self.userEmail = None
+            email_notify = email_notify.lower()        
+            resp =  checkHost()
+            if not resp.status:
+                msg = "%s %s %s %s %s" %( jobName,
+                                          unicode( str( email ).decode('ascii', 'replace')).encode( 'ascii', 'replace' ) ,
+                                          remoteLog ,
+                                          "FORBIDDEN ",
+                                           resp.message
+                                          )
+                
+                self.m_log.warning( msg )
+                raise UserValueError( msg = resp.user_message )
+
+            ## building the MobyleJob
+            self.progUrl = progUrl
+            self._service = service
+            self.params = params # a garder??
+            self._evaluator = evaluator
+            self.session = session
+            self._job = None             #an instance of Mobyle.Job
+            self.jobState = None       #an instance of JobState
+            self._adm = None
+            self._hasRun = False
+            
+            if params is None:
+                if service is None:
+                    if progUrl is None:
+                        ### TODO il n'y a pas encore de job de cree donc logger cette erreur ?
+                        raise MobyleError, "you must provide either a service or a service url"
+                    else:
+                        if evaluator is None or not evaluator.isFill():
+                            #self._service = self._makeService( progUrl )
+                            try:
+                                self._service = makeService( progUrl )
+                            except MobyleError , err :
+                                
+                                self._logError( userMsg = "Mobyle internal server error" )
+                                raise MobyleError( err )
+                            self._service = makeService( progUrl )
+                            self._evaluator = self._service.getEvaluator()
+                        else:
+                            if evaluator.isFill():
+                                try:
+                                    self._service = makeService( progUrl )
+                                except MobyleError , err :
+                                    self._logError( userMsg = "Mobyle internal server error")
+                                    raise MobyleError( err )
+
+                                self._service.setEvaluator( evaluator )
+                else: #service is not None
+                    if self._evaluator is None or not self._evaluator.isFill():
+                        self._evaluator = self._service.getEvaluator()
+                    else:
+                        self._service.setEvaluator( evaluator )
+                self._debug = debug or self.cfg.debug( self._service.getName() )
+                self._job = Job( self._service ,
+                                            self.cfg ,
+                                            userEmail = self.userEmail ,
+                                            session = self.session ,
+                                            workflowID = workflowID ,
+                                            email_notify = email_notify,
+                                            debug=debug
+                                            )
+            else: #params is not None
+                if service is None:
+                    if progUrl is None:
+                        raise MobyleError,"you must provide either a service a service url"
+                    else:
+                        self._service = makeService( progUrl )
+                        self._evaluator = self._service.getEvaluator()
+                else: #service is not None
+                    self._evaluator = Evaluation()
+                    self._service.setEvaluator( self._evaluator )
+                self._debug = self.cfg.debug( self._service.getName() )
+                self._job = Job( self._service ,
+                                            userEmail = self.userEmail ,
+                                            session = self.session ,
+                                            workflowID = workflowID ,
+                                            email_notify = email_notify
+                                            )
+                
+            ############ in all cases except ID ###################
+
+            if self._debug > 1:
+                self.build_log.debug( "MobyleJob : params= " + str( params ) )
+            self.jobState = self._job.jobState
+            self._adm = Admin( self._job.getDir() )
+
+
+    ###############################################################
+    #
+    #                
+    #
+    ###############################################################
+
+    def setSession(self , session ):
+        self.session = session
+        if self._job is not None:
+            self._job.setSession( session )
+    
+    
+    def _toFileAndConvert(self, parameter , value):
+        paramName = parameter.getName()
+        if len( value ) != 5 : 
+            msg = "Problem with the infile : " + paramName
+            self._logError( logMsg = msg ,
+                            userMsg = "Internal Mobyle Server Error"
+                          )
+            raise UserValueError( parameter = parameter , msg = msg )
+        else:    
+            
+            destFileName , data  , src , srcFileName, idx = value
+            if not destFileName and not data and not src:
+                data  = None
+                src = None
+            elif destFileName and not ( data or src ):
+                msg = "parameter %s: is empty. please check your data" % paramName
+                self._logError( logMsg = msg ,
+                                userMsg = msg
+                                )
+                raise UserValueError( parameter =  parameter , msg = msg )
+            elif data and src:
+                msg = "parameter %s: you cannot specify data and source at the same time" % paramName
+                self._logError( logMsg = msg ,
+                                userMsg = msg
+                                )
+                raise UserValueError( parameter =  parameter , msg = msg )
+
+            elif not srcFileName and not data and src:
+                msg = "parameter %s: if you specify a src, you must specify a src file name" % paramName
+                self._logError( logMsg = msg ,
+                                userMsg = msg
+                                )
+                raise UserValueError( parameter =  parameter , msg = msg )
+               
+            elif not srcFileName and data and not src:
+                srcFileName = paramName + ".data"      
+                                
+            # I put systematically the value in index.xml even if its the
+            # same as the previous value because the value is a filename 
+            # and 2 files with the same name could have different contents
+            
+            ##fileName = basename
+            acceptedMobyleType = parameter.getType()
+            dataType = acceptedMobyleType.getDataType()
+            paramName = parameter.getName()
+            try:
+                fileName, sizeIn = dataType.toFile( data, self._job , destFileName, src , srcFileName )
+            except MobyleError, err:
+                msg = "%s/%s : %s : %s"%( self._service.getName(), self.getJobid() , paramName, err)
+                self.m_log.error( msg )
+                raise MobyleError( msg )
+            detectedMobyleType = acceptedMobyleType.detect( ( self._job, fileName ) )
+            acceptedFormats = acceptedMobyleType.getAcceptedFormats()
+            if detectedMobyleType.dataFormat is None :
+                self.jobState.addInputDataFile( paramName, ( fileName  ,  sizeIn ,  'not detected' ) )
+                self.jobState.commit()
+                if not acceptedFormats:
+                    return fileName     
+                elif ( set( [ f for f , r in acceptedFormats] )- set( dataType.supportedFormat() ) ):
+                    # format accepted by the parameter  -  format supported by the conevrters
+                    # case of a not detected format but the parameter accept some formats not supported by converter
+                    self.m_log.warning("%s/%s : %s : a data format which has not been detected has been accepted in job" %( self._service.getName(), self.getJobid() , paramName ))
+                    return fileName 
+                else:
+                    # case of a not detected format and the all format accepetd by the parameter are supported by the converters 
+                    msg = "Your %s format cannot be detected: please submit your data in one of the following formats: %s"%( dataType.getName() , dataType.supportedFormat() )
+                    sm = StatusManager()
+                    sm.setStatus( self.getDir() , Status( code= 5 , message= msg ))
+                    raise UserValueError( parameter = parameter ,
+                                          msg = msg )
+            else: #the format of the data has been detected
+                outFileName = None
+                if not acceptedFormats:#if there isn't any accepted format => all formats are available
+                    self.jobState.addInputDataFile( paramName ,
+                                                            ( fileName  ,  sizeIn ,  detectedMobyleType.dataFormat ) ,
+                                                            fmtProgram = detectedMobyleType.format_program
+                                                            )
+                    self.jobState.commit()
+                    return fileName 
+                    if self._debug > 1:
+                            self.build_log.debug("self._service.setValue( %s , %s )" %( paramName , fileName ) )
+                else:# some accepted format are specified
+                    for out_format , force_reformat in acceptedFormats:
+                        if detectedMobyleType.dataFormat == out_format:
+                            if force_reformat:
+                                continue
+                            else:
+                                self.jobState.addInputDataFile( paramName ,
+                                                                ( fileName  ,  sizeIn ,  detectedMobyleType.dataFormat ) ,
+                                                                fmtProgram = detectedMobyleType.format_program
+                                                                )
+                                self.jobState.commit()
+                                outFileName = fileName
+                                break
+                        else:
+                            continue
+                    if outFileName is None:#the data must be converted
+                        try:
+                            outFileName , convertedMobyleType = detectedMobyleType.convert( ( self._job, fileName ) , acceptedMobyleType )
+                            sizeOut = os.path.getsize( os.path.join( self._job.getDir(),outFileName ) )
+                            self.jobState.addInputDataFile( paramName ,
+                                                            ( fileName  ,  sizeIn ,  detectedMobyleType.dataFormat ) ,
+                                                            fmtProgram = convertedMobyleType.format_program ,
+                                                            formattedFile = ( outFileName , sizeOut , convertedMobyleType.dataFormat )
+                                                            )
+                            self.jobState.commit()
+                        except UnSupportedFormatError ,err :
+                            self.m_log.error( "UnSupportedFormatError:%s"%err ,exc_info=True)
+                            msg = str( err )
+                            sm = StatusManager()
+                            sm.setStatus( self.getDir() , Status( code= 5 , message= msg ))
+                            raise UserValueError( parameter = parameter , msg = msg )
+                        except Exception, err:
+                            self.m_log.error( str(err), exc_info= True)
+                   
+                return outFileName
+
+            
+                        
+                        
+                        
+                        
+    def setValue( self , paramName , value ):
+        """
+        set the value for one parameter. the value is cast in the right type. if the value cannot be cast a UserValueError is raised
+        @param paramName: the name of the parameter
+        @type paramName: string
+        @param value : the value of the parameter
+        @type value:
+           - for simple type: any
+           - for the infile: a tuple ( destFileName , content , src , srcFileName )
+             - the name of the file
+             - the data the content of the file , string or None 
+             - the src where the file could be retrieve, MobyleJob , Job or Session instance or None
+        data is specified when data is new on the server.
+        src is specified when the data already exists on the server.
+        you can't specify data and src in the same time
+        @raise: MobyleError if the paramName doesn't match with any parameter name
+        """
+        if self._hasRun:
+            msg = "MobyleJob.setValue this job has already ran"
+            self._logError( logMsg = msg ,
+                            userMsg = msg
+                            )
+            raise MobyleError , msg
+        
+        if self._debug > 1:
+            self.build_log.debug( "\n--------------------- MobyleJob set user value for " + paramName + "--------------------" )        
+        if paramName in  self._service.getAllParameterNameByArgpos():
+            parameter = self._service.getParameter( paramName )
+            acceptedMobyleType = parameter.getType()
+            dataType = acceptedMobyleType.getDataType()
+            if value == '' :
+                value = None
+            if self._service.isInfile( paramName ):
+                
+                ###############################################################
+                if dataType.isMultiple():
+                    fileNames = []
+                    value.sort(key=lambda item:item[4])
+                    for item in value:
+                        #destFileName , data  , src , srcFileName, idx = item
+                        #destFileName file name in the destination folder
+                        #data file contents (if src is None)
+                        #src: folder where the source file can be found (if data is None)
+                        #srcFileName: file name in the src folder (if data is None)
+                        #idx: index in the ordered list of values
+                        #self.m_log.debug( "destFileName=%s ,data=%s  , src=%s , srcFileName=%s"% (destFileName , data  , src , srcFileName))
+                        fileName = self._toFileAndConvert( parameter , item )
+                        fileNames.append( fileName )
+                    parameter.setValue( fileNames )
+                    if self._debug > 1:
+                        self.build_log.debug("self._service.setValue( %s , %s )" %( paramName , fileNames ) )
+                else:#parameter is represents One file
+                    fileName = self._toFileAndConvert( parameter , value )
+                    parameter.setValue( fileName )
+                   
+            else : #the parameter is not an infile
+                oldValue = parameter.getValue()
+                try:
+                    detectedMobyleType = dataType.detect( value )
+                    converted_value , convertedMobyleType = detectedMobyleType.convert( value , acceptedMobyleType )
+                    parameter.setValue( converted_value )
+                    if self._debug > 1:
+                        try:
+                            self.build_log.debug( "self._service.setValue( %s , %s )" %( paramName , converted_value ) )
+                        except:
+                            self.build_log.debug( "!!!!!!!! problem during reporting log !!!!!!!!!!!!" )
+                except UserValueError , err:
+                        msg =  str( err )
+                        self._logError( logMsg = msg ,userMsg = msg)
+                        raise UserValueError(parameter=parameter, msg=msg)
+                    
+                newValue = parameter.getValue( )
+                if newValue != oldValue :
+                    rawVdef =  parameter.getVdef()
+                    if rawVdef is None:
+                        vdef = None
+                    else:
+                        vdef , vdefMT = detectedMobyleType.convert( rawVdef , acceptedMobyleType )
+                        if parameter.getType().getDataType().getName() == 'MultipleChoice':
+                            sep = parameter.getSeparator()
+                            vdef = sep.join( vdef)
+                    if newValue == vdef :
+                        if oldValue is not None and oldValue != vdef:
+                            self.jobState.delInputData( paramName )
+                            self.jobState.commit()
+                    else:
+                        self.jobState.setInputDataValue( paramName , newValue )
+                        self.jobState.commit()
+        else:
+            msg = "the parameter named : %s doesn't exist in %s" %( paramName ,
+                                                                    self._service.getName()
+                                                                    )
+            self._logError( logMsg = msg ,
+                            userMsg = msg
+                            )
+            raise UserValueError( msg = msg )
+
+
+
+    def _validateParameters(self):
+        """
+        verifie que le parametre est valide cad si c'est un choice qu'il appartient bien a la liste si c'est une sequence passe dans squizz ...
+        @raise UserValueError: throws UserValueError raised by checkUser
+        """
+        if self._debug > 1:
+            self.build_log.debug("""\n
+              \t################################################
+              \t#                                              #
+              \t#              validation beginning            #
+              \t#                                              #
+              \t################################################
+              \n""")
+                
+        for paramName in self._service.getAllParameterNameByArgpos():
+            ###################################################
+            #                                                 #
+            #         check if parameter have a value         #
+            #                                                 #
+            ###################################################
+            
+            value = self._service.getValue(paramName)
+            if self._debug > 1:
+                self.build_log.debug("------------- " + paramName + " -------------")
+                self.build_log.debug("value= %s : type= %s"%( value , type(value) ) )
+                self.build_log.debug("service.precondHas_proglang( %s , 'python' ) = %s " %(
+                    paramName ,
+                    self._service.precondHas_proglang( paramName , 'python' )
+                    )
+                            )
+                    
+            if self._service.precondHas_proglang( paramName , 'python' ):
+                preconds = self._service.getPreconds( paramName , proglang='python' )
+                allPrecondTrue = True
+                for precond in preconds:
+                    try:
+                        evaluatedPrecond = self._evaluator.eval( precond )
+                    except EvaluationError, err:
+                        self.build_log.error("ERROR during precond evaluation: %s : %s"%( precond, err ) )
+                        msg = "ERROR during %s %s precond evaluation: %s : %s" % ( self._service.getName(),
+                                                                                  paramName ,
+                                                                                  precond ,
+                                                                                  err
+                                                                                 )
+                        self._logError( logMsg = msg ,
+                                        userMsg = "Internal Server Error"
+                                        )
+                        raise MobyleError( "Internal Server Error" )
+                    
+                    if self._debug > 1:
+                        self.build_log.debug("precond= " + precond )
+                        self.build_log.debug("eval precond= " + str( evaluatedPrecond ))
+                        
+                    if not evaluatedPrecond :
+                        allPrecondTrue = False
+                        break
+                
+                if not allPrecondTrue :
+                    if self._debug > 1:
+                        self.build_log.debug("next parameter")
+                    continue #next parameter
+            else:
+                if self._debug > 1:
+                    if self._service.precondHas_proglang( paramName, 'perl' ) :
+                        self.build_log.debug( "################ WARNING ###################" )
+                        self.build_log.debug( "had a precond code in Perl but not in Python" )
+                        self.build_log.debug( "############################################" )
+            if value is None :
+                if self._debug > 1:
+                    self.build_log.debug( " value is None " )
+                if self._service.ismandatory( paramName ):
+                    msg = "The mandatory Parameter : %s, must be specified " % paramName
+                    self._logError( logMsg = msg ,
+                                    userMsg = msg
+                                    )
+
+                    raise  UserValueError(parameter = self._service.getParameter( paramName ) ,
+                                          msg = "This parameter is mandatory" )
+            
+            if self._debug > 1:
+                self.build_log.debug( "call service.validate( " + paramName + " )" )
+            try:    
+                self._service.validate( paramName )
+            except UserValueError , err:
+                msg = str(err)
+                self.build_log.debug( msg )
+                self._logError( logMsg = msg ,
+                                userMsg = msg
+                                )
+                raise UserValueError ,err
+            
+            except MobyleError , err:
+                msg = str(err)
+                self.build_log.debug( msg )
+                self._logError( logMsg = msg ,
+                                userMsg = "Mobyle Internal Server Error"
+                                )                
+                raise MobyleError , err
+            ####################################################
+            #                                                  #
+            #  check if the Parameter have a secure paramfile  #
+            #                                                  #
+            ####################################################
+
+            try:
+                if self._debug > 1:
+                    self.build_log.debug( "check if the Parameter have a secure paramfile" )
+
+                paramfile = self._service.getParamfile( paramName )
+                try:
+                    safeParamfile = safeFileName( paramfile )
+                except UserValueError , err :
+                    msg = str( err )
+                    self._logError( logMsg = msg ,
+                                    userMsg = msg
+                                    )
+                    raise UserValueError( parameter= self._service.getParameter( paramName ), msg = msg )
+                
+                if not safeParamfile or safeParamfile != paramfile :
+                    msg = "The Parameter:%s, have an unsecure paramfile value: %s " %(  paramName ,
+                                                                                        paramfile
+                                                                                        )
+                    if self._debug < 2 :
+                        self._logError( logMsg = None ,
+                                        userMsg = "Mobyle Internal Server Error"
+                                        )
+                        
+                        self.m_log.critical( "%s/%s : %s" %( self._service.getName(),
+                                                          self._job.getKey() ,
+                                                          msg
+                                                          )
+                                        )
+                    else:
+                        self._logError( logMsg =  msg ,
+                                        userMsg = "Mobyle Internal Server Error"
+                                        )
+
+
+                    raise MobyleError , "Mobyle Internal Server Error"
+                else:
+                    if self._debug > 1:
+                        self.build_log.debug( "paramfile = %s ...........OK" % paramfile )
+            except UnDefAttrError :
+                if self._debug > 1:
+                    self.build_log.debug("no paramfile")
+                pass
+        
+        
+    def _collisions(self):
+        """
+        check if there is no potential collisions between the infiles and the output masks.
+        if ther is a risk of collision the input file is renamed
+        """
+        paramsOut = self._service.getAllOutParameter(  )
+        evaluator = self._service.getEvaluator()
+        allMasks = []
+        potCollisions =[]
+        for paramName in paramsOut :
+            if self._service.precondHas_proglang( paramName , 'python' ):
+                    preconds = self._service.getPreconds( paramName , proglang='python' )
+                    allPrecondTrue = True
+                    for precond in preconds:
+                        if not evaluator.eval( precond ) :
+                            allPrecondTrue = False
+                            break
+                    if not allPrecondTrue :
+                        continue #next parameter
+            unixMasks = self._service.getFilenames( paramName , proglang = 'python' )
+            service_name = self._service.getName()
+            #unixMasks += ["%s.out"%service_name , "%s.err"%service_name ]
+            allMasks += unixMasks 
+            workdir = self.getDir()
+            for mask in unixMasks:
+                mask = os.path.join( workdir , mask )
+                Files = glob.glob( mask )
+                if Files:
+                    self.m_log.warning( 'Potential Collision: %s input files match the \"%s\" output parameter mask %s' %( Files ,
+                                                                                                                           paramName,
+                                                                                                                           mask
+                                                                                                                           ) )
+                    potCollisions += [ os.path.basename( f ) for f in  Files ]
+        infiles = self._service.getAllInFileParameter() 
+        for infileName in potCollisions:
+            for parameter in infiles:
+                oldNames = self.jobState.getInput( parameter.getName())
+                oldNames = [ fileName for fileName , size , fmt in oldNames ]
+                
+                for oldName in oldNames:
+                    if oldName == infileName:
+                        newName = "mob_%s_collision" %( infileName )
+                        parameter.renameFile(  workdir , oldName, newName  )
+                        values = parameter.getValue()
+                        sep = parameter.getSeparator()
+                        if sep is not None:
+                            values = values.split( sep )
+                            if oldName in values: #the collision can be between input before conversion and output so the oldname is not in evaluator (seq.out -> seq.fasta) 
+                                oldName_i = values.index( oldName )
+                                values[ oldName_i ] = newName
+                                parameter.setvalue( values )
+                        else:
+                            if oldName == values:
+                                 parameter.setValue( newName )
+                        self.jobState.renameInputDataFile( parameter.getName() , oldName, newName )
+                        self.jobState.commit()
+                        self.m_log.warning( '%s/%s : Potential Collision : %s file was renamed into %s' %( self._service.getName(),
+                                                                                                          self._job.getKey() ,
+                                                                                                          oldName ,
+                                                                                                          newName )
+                        )
+            
+        for mask in allMasks :
+            mask = os.path.join( workdir , mask )
+            collideFiles = glob.glob( mask )
+            if collideFiles:
+                msg= '%s/%s : Potential Collision: %s input files output mask %s' %( self._service.getName(),
+                                                                                     self._job.getKey() ,
+                                                                                     collideFiles ,
+                                                                                     mask
+                                                                                   ) 
+                self._logError( userMsg = "Mobyle Internal Server Error", 
+                                logMsg  = msg
+                                )
+                raise MobyleError , msg
+
+    def run(self):
+        """
+        run a job
+        @raise MobyleError: L{MobyleError} if the job dosen't exist. or if session is not activated
+        """
+        if self._job is not None:
+            
+            if not self.cfg.isAuthorized( self._service.getName() , self._remote[0] ):
+                msg = "Sorry, you are not allowed to run this service."
+                logMsg = " %s try to access restricted service"%self._remote[0]
+                self._logError( userMsg = msg , 
+                                logMsg = logMsg,
+                                )                
+                raise MobyleError( msg )
+            
+            if self.session and not self.session.isActivated():
+                self._logError( userMsg = "session is not activated : run aborted" , 
+                                logMsg = "session \"%s\" is not activated : run aborted" % self.session.getKey() 
+                                )
+                raise MobyleError( "session is not activated" )
+            
+            # for security reason 
+            # I remove some unsuful variable form environment
+            # to avoid to propagate them in all cluster nodes
+            # waiting a better model 
+            
+            for envVar in [ 'CONTENT_LENGTH'      ,
+                            'CONTENT_TYPE'        ,
+                            'GATEWAY_INTERFACE'   ,
+                            'HTTP_ACCEPT'         ,
+                            'HTTP_ACCEPT_ENCODING',
+                            'HTTP_ACCEPT_LANGUAGE',
+                            'HTTP_CACHE_CONTROL'  ,
+                            'HTTP_ACCEPT_CHARSET' ,
+                            'HTTP_COOKIE'         ,
+                            'HTTP_CONNECTION'     ,
+                            'HTTP_KEEP_ALIVE'     ,
+                            'HTTP_PRAGMA'         ,
+                            'HTTP_REFERER'        ,
+                            'HTTP_USER_AGENT'     ,
+                            'HTTP_X_PROTOTYPE_VERSION',
+                            'HTTP_X_REQUESTED_WITH',
+                            'QUERY_STRING'        ,
+                            'SERVER_ADMIN'        ,
+                            'REQUEST_METHOD'      ,   
+                            'REQUEST_URI'         ,
+                            'SCRIPT_FILENAME'     ,
+                            'SCRIPT_NAME'         ,
+                            #'SERVER_NAME'         ,
+                            #'SERVER_PORT'         ,
+                            'SERVER_PROTOCOL'     ,
+                            'SERVER_SIGNATURE'    ,
+                            'SERVER_SOFTWARE'     ,
+
+]:
+                try:
+                    del( os.environ[ envVar ])
+                except KeyError:
+                    pass
+                    
+            self._validateParameters()
+                    
+            #do the controls specified in the Xml file
+            if self._debug > 1:
+                self.build_log.debug("""\n
+                \t################################################
+                \t#                                              #
+                \t#           xml controls beginning             #
+                \t#                                              #
+                \t################################################
+                \n""")
+            
+            for paramName in self._service.getAllParameterNameByArgpos():
+             
+                hasCtrl = self._service.has_ctrl( paramName )
+                hasScale = self._service.hasScale( paramName , proglang = 'python' )
+             
+                if hasCtrl or hasScale:
+
+                    if self._debug > 1:
+                        self.build_log.debug("------------- " + paramName + " -------------")
+                        self.build_log.debug("service.precondHas_proglang( %s , 'python' ) = %s " %(
+                            paramName ,
+                            self._service.precondHas_proglang( paramName , 'python' )
+                            ))
+                        
+                    if self._service.precondHas_proglang( paramName , 'python' ):
+                        preconds = self._service.getPreconds( paramName , proglang='python' )
+                        allPrecondTrue = True
+                       
+                        for precond in preconds:
+                            evaluatedPrecond = self._evaluator.eval( precond )
+                            
+                            if self._debug > 1:
+                                self.build_log.debug("precond= " + precond )
+                                self.build_log.debug("eval precond= " + str( evaluatedPrecond ))
+                            if not evaluatedPrecond :
+                                allPrecondTrue = False
+                                break
+                        if not allPrecondTrue :
+                            if self._debug > 1:
+                                self.build_log.debug("next parameter")
+                            continue #next parameter
+                    if hasCtrl:
+                        if self._service.getValue( paramName ) is not None:
+                            try:
+                                self._service.doCtrls( paramName )
+                            except ServiceError , err:
+                                # when the parameterName doesn't exist 
+                                # or an error occured during the python code evaluation ( see parameter.doCrtls() )
+                                msg = str( err )
+                                self._logError( logMsg = msg , userMsg = "Internal Mobyle server Error" )
+                                raise MobyleError , msg 
+                            except UserValueError , err:
+                                msg = str( err )
+                                self._logError( logMsg = msg , userMsg = msg )
+                                raise err
+                        else:
+                            if self._debug > 1:
+                                self.build_log.debug("undefined value: next parameter")
+                            continue
+                    if hasScale:
+                        if self._debug > 1:
+                            self.build_log.debug( paramName + " has scale= True" )
+                        try:
+                            isInScale = self._service.isInScale( paramName , proglang = 'python')
+                            if self._debug > 1:
+                                self.build_log.debug( "isInScale= " + str( isInScale ) )
+                            if isInScale:
+                                continue
+                            else:
+                                smin ,smax , incr = self._service.getScale( paramName , proglang = 'python') 
+                                msg = "%s value: %s is not in scale ( %s , %s )" %(
+                                    paramName ,
+                                    self._service.getValue( paramName ) ,
+                                    smin ,
+                                    smax )
+
+                                self._logError( logMsg = msg , userMsg = msg )
+                                parameter = self._service.getParameter( paramName )
+                                raise UserValueError( parameter = parameter , msg = msg )
+                            
+                        except ValueError , err :
+                            msg = paramName + " value is None. Thus it is not in scale"
+                            self._logError(logMsg = msg , userMsg = msg )
+                            raise MobyleError , msg
+                    else:
+                        if self._debug > 1:
+                            self.build_log.debug("has scale= False" )
+
+                            continue
+            self._collisions()
+                                             
+            self._hasRun = True
+            self._job.run()  
+            return self._job.getURL()
+        
+        else:
+            if self._hasRun:
+                msg = "MobyleJob.run this job has already ran"
+            else:
+                msg = "MobyleJob.run: can't run the job (%s). It doesn't exist" % self._job.getURL() 
+
+            self._logError( logMsg = msg , userMsg = msg )
+
+            raise MobyleError , msg 
+        
+
+
+      
+    
+    ####################################################################
+    #
+    #                    get job info
+    #
+    ####################################################################
+
+    def getDir(self):
+        """
+        @return: the directory absolute path where the job is executed
+        @rtype: string
+        @call: cgiJob.printResult()
+        """
+        if self._job:
+            return self._job.getDir()
+        elif self.jobState:
+            url = self.jobState.getID()
+            return jobState_url2path( url )
+        else:
+            return None
+
+    def getDate(self):
+        """
+        @return: the submission date of this job
+        @rtype: string
+        @call: cgiJob.submit()
+        """
+        if self._job:
+            return self._job.getDate()
+        else:
+            return None     
+
+    def getJobid(self):
+        """
+        @return: url where the job is executed
+        @rtype: string
+        """
+        if self.jobState:
+            return self.jobState.getID()
+        else:
+            return None
+        
+    def getOutputs(self):
+        """
+        @return: a dictionary where the keys are the parameter name and the values a list of files which are produced by this parameter.
+        @rtype: dict
+
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getOutputs()
+            else:
+                msg = "try to get results but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None
+
+    def getOutput(self , paramName ):
+        """
+        @return: the list of results files names. if there is no files for this parameter return None 
+        @rtype: list of tuples ( string filename , long size , string fmt or None )
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getOutput( paramName )
+            else:
+                msg = "try to get result but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None
+
+    def getOutputFile( self, fileName ):
+        """
+        @param fileName:
+        @type fileName: String
+        @return: the content of a output file as a string
+        @rtype: string
+        @raise exception: 
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getOutputFile( fileName )
+            else:
+                msg = "try to get result but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None  
+        
+        
+    def isLocal( self ):
+        """
+        
+        """
+        if self.jobState:
+                return self.jobState.isLocal()
+
+    def getStatus(self):
+        """
+        @return: the status of the job
+        @rtype: L{Mobyle.Status.Status} instance 
+        """
+        if self.jobState:
+            return utils_getStatus( self.jobState.getID() )
+        else:
+            return  None 
+
+    def isFinished(self):
+        """
+        @return: True if the job is finished, False Otherwise
+        @rtype: boolean
+        """
+        if self.jobState:
+            sm = StatusManager()
+            status = sm.getStatus( self.jobState.getDir() )
+            return status.isEnded()
+        else:
+            return None
+
+    def getCommandLine(self):
+        """
+        @return: the Command line
+        @rtype: string
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getCommandLine()
+            else:
+                msg = "try to get the command but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None
+
+    def getDataPrompt(self , paramName ):
+        """
+        @return: the prompt for an In/Output parameter
+        @rtype: string
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getPrompt( paramName )
+            else:
+                msg = "try to get the command but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None
+    
+    
+    def getStdout(self):
+        """
+        @return: the content of the job stdout as a string
+        @rtype: string
+        @raise MobyleError: if the job is not finished raise a L{MobyleError}
+        """
+        if self.jobState:
+            if self._hasRun:
+                return self.jobState.getStdout()
+            else:
+                msg = "try to get the job stdout, but the job had not run"
+                raise MobyleError , msg
+        else:
+            return None
+    
+
+    def getStderr(self):
+        """
+        @return: the content of the job stderr as a string
+        @rtype: string
+        @raise MobyleError: if the job is not finished raise a L{MobyleError}
+        """
+        if self.jobState:
+            return self.jobState.getStderr()
+        else:
+            return None
+
+    
+    def open(self , fileName ):
+        """
+        return an file object if the file is local or a file like object if the file is distant
+        we could apply the same method on this object: read(), readline(), readlines(), close(). (unlike file the file like object doesn't implement an iterator).
+        @param fileName: the name of the file (given by getResults).
+        @type fileName: string
+        @return: a file or file like object, or None if the job isn't ran.
+        """
+        if self.jobState:
+            return self.jobState.open( fileName )
+        else:
+            return None
+
+
+
+    def getArgs(self):
+        """
+        read the parameters in the .index.xml file and return a dictionary containig these parameters
+        @return: the parameters of a job
+        @rtype: dictionary
+        """
+        if self.jobState:
+            return self.jobState.getArgs()
+        else:
+            return None
+
+        
+    def _logError( self , userMsg = None , logMsg = None ):
+        
+        if userMsg:
+            sm = StatusManager ()
+            sm.setStatus( self.getDir() , Status( code = 5 , message = userMsg ) )
+
+        if  logMsg :
+            self.m_log.error( "%s/%s : %s" %( self._service.getName() ,
+                                           self._job.getKey() ,
+                                           logMsg
+                                           )
+                         )
+
+
+        
+
diff --git a/Src/Mobyle/MobyleLogger.py b/Src/Mobyle/MobyleLogger.py
new file mode 100644
index 0000000..f6b9d88
--- /dev/null
+++ b/Src/Mobyle/MobyleLogger.py
@@ -0,0 +1,232 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+import logging.handlers
+import os.path
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+#===============================================================================
+# memo logging level
+#
+# CRITICAL      50
+# ERROR         40
+# WARNING       30
+# INFO          20
+# DEBUG         10
+# NOTSET         0
+#===============================================================================
+
+
+class MLogger( object ):
+
+    ## if we don't use the singleton pattern
+    ## only one instance of logger is created
+    ## but there is as much handler instance  as we call
+    ## the creation procedure thus the messages are replicated
+
+    _ref = None
+
+    def __new__( cls , child = False):
+
+        if cls._ref is None:
+            cls._ref = super( MLogger , cls ).__new__( cls )
+   
+            ########################
+            #                      #
+            #       Formatter      # 
+            #                      #
+            ########################
+
+            defaultFormatter = logging.Formatter(
+                '%(name)-10s : %(levelname)-8s : %(filename)-10s: L %(lineno)d : %(asctime)s : %(message)s' ,
+                '%a, %d %b %Y %H:%M:%S'
+                )
+
+            accountFormatter = logging.Formatter(
+                '%(asctime)s  %(message)s' ,
+                '%a, %d %b %Y %H:%M:%S')
+
+            accessFormatter = logging.Formatter(
+                '%(asctime)s %(message)s',
+                 '%a, %d %b %Y %H:%M:%S' , 
+                )
+
+            builderFormatter = logging.Formatter( '%(message)s' )
+
+            sessionFormatter = logging.Formatter(
+                 'L %(lineno)d : %(asctime)s : pid = %(process)d  : %(message)s' ,
+                '%a, %d %b %Y %H:%M:%S'
+                )                                
+            
+            ########################
+            #                      #
+            #        Handler       #
+            #                      #
+            ########################
+
+            logdir = _cfg.log_dir()
+            try:
+
+                defaultHandler = logging.FileHandler( os.path.join( logdir , 'error_log') , 'a' )
+            except IOError:
+                defaultHandler = logging.FileHandler( '/dev/null' , 'a' )
+            
+            #defaultHandler.setLevel(logging.WARNING)
+            defaultHandler.setLevel( logging.DEBUG )
+            defaultHandler.setFormatter( defaultFormatter )
+            try:
+                accountHandler = logging.FileHandler( os.path.join( logdir , 'account_log') , 'a' )
+            except IOError:
+                accountHandler = logging.FileHandler( '/dev/null' , 'a' )
+            #accountHandler.setLevel()
+            accountHandler.setFormatter( accountFormatter )
+            buildLog = os.path.join( logdir , 'build_log')
+            if not child :
+                try:          
+                    builderHandler = logging.FileHandler( buildLog , 'a' )
+                except IOError:
+                    builderHandler = logging.FileHandler( '/dev/null' , 'a' )
+                builderHandler.setLevel( logging.NOTSET )
+                builderHandler.setFormatter( builderFormatter )
+            try:
+                accessHandler = logging.FileHandler( os.path.join( logdir , 'access_log')  , 'a' )
+            except IOError:
+                accessHandler = logging.FileHandler( '/dev/null' , 'a' )
+            accessHandler.setLevel( logging.INFO )
+            accessHandler.setFormatter( accessFormatter )
+
+
+            mailHandler = logging.handlers.SMTPHandler( _cfg.mailhost(),
+                                                        _cfg.sender() ,
+                                                        _cfg.maintainer()  ,
+                                                        '[ %s ] Mobyle problem' % _cfg.root_url()
+                                                        )
+            mailHandler.setLevel(logging.CRITICAL)
+
+            try:
+                infoSessionHandler = logging.FileHandler( os.path.join( logdir , 'error_log' ) , 'a' )
+            except IOError:
+                infoSessionHandler = logging.FileHandler( '/dev/null' , 'a' )
+            infoSessionHandler.setLevel(logging.WARNING)
+            infoSessionHandler.setFormatter( defaultFormatter )            
+            try:
+                session_log_level = _cfg.session_debug()
+                if session_log_level is not None :
+                    sessionHandler = logging.FileHandler( os.path.join( logdir , 'session_log') , 'a' )
+                else:
+                    sessionHandler = logging.FileHandler( '/dev/null' , 'a' )                                  
+            except IOError:
+                sessionHandler = logging.FileHandler( '/dev/null' , 'a' )
+            
+            sessionHandler.setLevel( session_log_level if  session_log_level is not None else logging.NOTSET )
+            sessionHandler.setFormatter( sessionFormatter )
+            
+            
+            if _cfg.status_debug():
+                
+                try:
+                    statusDebugHandler = logging.FileHandler( os.path.join( logdir , 'status_log' ) , 'a' )
+                    statusDebugHandler.setLevel( logging.NOTSET )
+                    statusDebugHandler.setFormatter( sessionFormatter )  
+                except IOError:
+                    statusDebugHandler = logging.FileHandler( '/dev/null' , 'a' )
+            try:
+                statusHandler = logging.FileHandler( os.path.join( logdir , 'error_log' ) , 'a' )
+                statusHandler.setLevel( logging.WARNING )
+                statusHandler.setFormatter( defaultFormatter )
+            except IOError:
+                statusHandler = logging.FileHandler( '/dev/null' , 'a' )
+            
+            
+
+            #########################
+            #                       #
+            #        Logger         #
+            #                       #
+            #########################
+            
+            root = logging.getLogger()
+            root.setLevel( logging.DEBUG )
+
+            mobyle = logging.getLogger('Mobyle')
+            mobyle.setLevel( logging.NOTSET )
+            mobyle.propagate = False
+            mobyle.addHandler( defaultHandler  )
+            mobyle.addHandler( mailHandler )
+            
+            if not child: #can be used Father
+                
+                cgi = logging.getLogger('mobyle.cgi')
+                cgi.propagate = False
+                cgi.addHandler( defaultHandler )
+                cgi.addHandler( mailHandler )
+    
+                tc = logging.getLogger('simpleTAL.TemplateCompiler')
+                tc.propagate = False
+                tc.setLevel( logging.ERROR )
+                tc.addHandler( defaultHandler )
+                tc.addHandler( mailHandler )
+    
+                ctc = logging.getLogger('simpleTALES.Context')
+                ctc.propagate = False
+                ctc.setLevel( logging.ERROR )
+                ctc.addHandler( defaultHandler )
+                ctc.addHandler( mailHandler )
+    
+                htc = logging.getLogger('simpleTAL.HTMLTemplateCompiler')
+                htc.propagate = False
+                htc.setLevel( logging.ERROR )
+                htc.addHandler( defaultHandler )
+                htc.addHandler( mailHandler )
+    
+                xtc = logging.getLogger('simpleTAL.XMLTemplateCompiler')
+                xtc.propagate = False
+                xtc.setLevel( logging.ERROR )
+                xtc.addHandler( defaultHandler )
+                xtc.addHandler( mailHandler )
+                
+                parser = logging.getLogger('mobyle.servicestree')
+                parser.propagate = False
+                parser.addHandler( defaultHandler )
+                parser.addHandler( mailHandler )
+                
+                ##########################################################3
+                
+                access = logging.getLogger('Mobyle.access')
+                access.propagate = False
+                access.addHandler( accessHandler )
+    
+                builder = logging.getLogger('Mobyle.builder')
+                builder.propagate = False
+                builder.addHandler( builderHandler )
+                
+
+                
+            else: #can be used in Child
+                account = logging.getLogger('Mobyle.account')
+                account.propagate = False
+                account.addHandler( accountHandler )
+                
+
+            #can be used in Father and Child
+            session = logging.getLogger('Mobyle.Session')
+            session.propagate = False
+            session.addHandler( infoSessionHandler )
+            session.addHandler( sessionHandler )
+            session.addHandler( mailHandler )
+            
+            status = logging.getLogger('Mobyle.StatusManager')
+            status.propagate = False
+            status.addHandler( statusHandler )
+            if _cfg.status_debug():
+                status.addHandler( statusDebugHandler )
+            
+
+        return cls._ref
diff --git a/Src/Mobyle/Net.py b/Src/Mobyle/Net.py
new file mode 100644
index 0000000..e0e7d6f
--- /dev/null
+++ b/Src/Mobyle/Net.py
@@ -0,0 +1,402 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+
+import smtplib
+import mimetypes 
+from email import Encoders
+from email.MIMEBase import MIMEBase
+from email.MIMEMultipart import MIMEMultipart
+from email.MIMEImage import MIMEImage
+from email.MIMEText import MIMEText
+import re
+import os 
+import types
+
+from Mobyle.MobyleError import MobyleError , UserValueError , EmailError , TooBigError
+
+import Local.black_list
+import Local.Policy
+import Local.mailTemplate
+
+from logging import getLogger
+n_log = getLogger( __name__ )
+
+from Mobyle.ConfigManager import Config
+cfg = Config()
+
+
+def checkHost( ):
+
+    try:
+        userIP = os.environ[ 'REMOTE_ADDR' ]
+    except KeyError:
+        #MobyleJob is executed from commandline
+        return True
+
+    #rewriting the blacklist in a regexp
+    pattern = '|'.join( Local.black_list.host )
+    pattern = pattern.replace('.' , '\.' )
+    pattern = pattern.replace('*' , '.*')
+    pattern = "^(%s)$" % pattern
+    auto = re.compile( pattern )
+
+    method= 'host'
+    if auto.search( userIP ):
+        try:
+            userMsg = Local.Policy.emailUserMessage( method )
+        except:
+            userMsg = "you are not allowed to run on this server for now"
+        return NetResponse( False, 'IP address is in black list', userMsg, method )
+    else:
+        return NetResponse( True , '' , '' , method )
+
+
+class EmailAddress:
+        
+    INVALID = 0
+    VALID = 1
+    CONTINUE = 2
+    
+    def __init__( self , addr ):
+        """
+        @param addr: the emails addresses
+        @type addr: string or list of strings
+        """
+        if not addr:
+            raise MobyleError , 'addr must not be empty'
+        if isinstance( addr , types.StringTypes ):
+            self.addr = [ addr.strip() ]
+        elif isinstance( addr , ( types.ListType , types.TupleType ) ):
+            self.addr = addr 
+        else:
+            raise MobyleError , " addr must be a string or a list of strings : "+ str( addr )
+        
+        self._methods = [ self._checkSyntax ,
+                          self._checkBlackList ,
+                          self._checkLocalRules ]      
+        
+        if cfg.dnsResolver():
+            self._methods.append( self._checkDns )
+        
+        self._message = ''        
+    
+    def __str__(self):
+        return ','.join( self.addr )
+    
+    def getAddr(self):
+        return [ addr for addr in self.addr ]
+    
+    
+    def check( self ):
+        """
+        check if the addresses are valid:
+         right syntax, 
+         not in black list, 
+         according to the local rules 
+         and the domain have a mx fields)
+        @return: True if the addresses pass the controls, False otherwise
+        @rtype: boolean  
+        """
+        self._message = ''
+        for oneAddr in self.addr:
+            for method in self._methods:
+                rep = method( oneAddr )
+                if rep.status == self.CONTINUE :
+                    continue
+                elif rep.status == self.VALID or rep.status == self.INVALID:
+                    return rep
+                else:
+                    raise MobyleError , method.__name__+ " return an invalid response :"+ str(rep)
+        return NetResponse( self.VALID , '' , '' , '' )
+
+
+    def getMessage( self ):
+        return self._message
+            
+    def _checkSyntax( self , addr ):
+        method =  'syntax'
+        email_pat = re.compile( "^[a-z0-9\-\.+_]+\@([a-z0-9\-]+\.)+([a-z]){2,4}$" , re.IGNORECASE )
+        match = re.match( email_pat , addr )
+
+        if match is None:
+            try:
+                userMsg = Local.Policy.emailUserMessage( method )
+            except:
+                userMsg = "you are not allowed to run on this server for now"
+            return NetResponse(self.INVALID , "invalid syntax for email address" , userMsg,  method )
+        else:
+            return NetResponse(self.CONTINUE , '' , '' ,  method )
+
+    def checkBlackList( self ):
+        for oneAddr in self.addr:
+            rep = self._checkBlackList( oneAddr )
+            if rep.status == self.CONTINUE :
+                continue
+            elif rep.status == self.VALID or rep.status == self.INVALID:
+                return rep
+        return NetResponse( self.VALID , '' , '' , '' )
+        
+    def _checkBlackList( self , addr ):
+        pattern = '|'.join( Local.black_list.users )
+        pattern = pattern.replace('.' , '\.' )
+        pattern = pattern.replace('*' , '.*')
+        pattern = "^(%s)$" % pattern
+        auto = re.compile( pattern )
+        method = 'blackList'
+        if auto.search( addr ):
+            try:
+                userMsg = Local.Policy.emailUserMessage( method )
+            except:
+                userMsg = "you are not allowed to run on this server for now"
+            return NetResponse(self.INVALID , "email is in black_list" , userMsg , method )
+        else:
+            return NetResponse(self.CONTINUE , '' , '' , method )
+
+
+    def _checkLocalRules( self, addr  ):
+        #tester si le module existe ? existe toujours meme si vide ?
+        rep = Local.Policy.emailCheck( email = addr )
+        message = ''
+        userMsg = ''
+        method = 'LocalRules'
+        if rep == self.INVALID:
+            message = "email is rejected by our local policy"
+            try:
+                userMsg = Local.Policy.emailUserMessage( method )
+            except:
+                userMsg = "you are not allowed to run on this server for now"
+        return NetResponse( rep , message , userMsg , method)
+
+
+    def _checkDns( self , addr ):
+        import dns.resolver
+        method = 'dns'
+        try:
+            userMsg = Local.Policy.emailUserMessage( method )
+        except:
+            userMsg = "you are not allowed to run on this server for now"
+        user , domainName  = addr.split('@')
+        try:
+            answers = dns.resolver.query( domainName , 'MX')
+        except dns.resolver.NXDOMAIN , err :
+            self._message = "unknown name domain"
+            return NetResponse( self.INVALID , "unknown name domain" , userMsg , method )
+        except dns.resolver.NoAnswer ,err :
+            try:
+                answers = dns.resolver.query( domainName , 'A')
+            except:
+                return NetResponse( self.INVALID , "no mail server" , userMsg , method )
+            return NetResponse( self.CONTINUE , '' , '' , method )
+        except (dns.name.EmptyLabel, dns.resolver.NoNameservers):
+            return NetResponse( self.INVALID , "no domain name server" , userMsg , method )
+        except dns.exception.Timeout:
+            return NetResponse( self.CONTINUE, "dns timeout" , userMsg , method )
+        except Exception, err:
+            msg = "unexpected error in  Email._checkDns : "+ str( err )
+            n_log.critical( msg, exc_info = True )
+            return NetResponse( self.INVALID , msg , userMsg , method )
+        return NetResponse( self.CONTINUE , '' , '' , method )
+            
+class NetResponse:
+    
+    def __init__(self, status , message , user_message , method ):
+        """ """
+        self.status = status
+        """Internal Mobyle Message"""
+        self.message = message
+        """User Message"""
+        self.user_message = user_message
+        """the name of the method"""
+        self.method = method
+    
+
+        
+class Email: 
+    
+    def __init__( self , To , cc = None): 
+        """
+        @param To: the recipients addresses
+        @type To: EmailAddress instance
+        @param cc: the emails adresses in copy of this mail
+        @type cc: EmailAddress instance
+        """   
+        self.To = To
+        self.cc = cc
+        self.mailhost = cfg.mailhost()
+        self.headers = None
+        self.body = None
+        
+    def getBody(self):
+        return self.body
+    
+    def getHeaders(self):
+        return self.headers
+    
+    def send( self , templateName , dict ,  files= None  ):
+        """
+        send an email to the Email.To recipients, using the template to build email body.
+        @param template: the template of the mail. see Local/mailTemplate.py
+        @type template: string 
+        @param dict: the dictionnary used to expend the template
+        @type dict: dictionnary
+        @param files: the list of file names to attach to this email
+        @type files: list of strings 
+        """
+        try:
+            template = getattr( Local.mailTemplate , templateName )
+        except AttributeError ,err:
+            msg = "error during template %s loading: err" %( templateName ,err )
+            n_log.critical( msg )
+            raise MobyleError , err 
+        try:
+            mail = template % dict
+        except ( TypeError , KeyError ) , err:
+            msg = "error during template %s expanding: %s. This mail sending is aborted" %( templateName , err )
+            n_log.critical( msg )
+            raise MobyleError , msg 
+        if not mail and not files:
+            errMsg = "no msg and no files for template %s send email aborted" % templateName
+            n_log.warning( errMsg )
+            return None
+        
+        mailAttr , msg = self._parse( mail )
+        if files:
+            emailBody = MIMEMultipart()
+        else:
+            emailBody = MIMEText( msg , 'plain' , 'utf-8')
+        
+        mailAttr[ 'To' ] = str( self.To )
+        recipients = self.To.getAddr()
+        if self.cc:
+            recipients.extend( self.cc.getAddr() )
+            emailBody[ 'Cc' ] = str( self.cc )
+            
+        for attr in mailAttr.keys():
+            if attr == 'Reply-To':
+                emailBody[ attr ] = ', '.join( mailAttr[ attr ] )
+            elif attr == 'Cc' or attr == 'Bcc' :
+                recipients.extend( mailAttr[ attr ] )
+                emailBody[ attr ] = ', '.join( mailAttr[ attr ] )
+            else:
+                emailBody[ attr ] =  mailAttr[ attr ] 
+        try:
+            try:
+                s = smtplib.SMTP( self.mailhost, timeout= 30 )
+            except TypeError:
+                # timeout was added in python 2.6
+                s = smtplib.SMTP( self.mailhost )
+        except Exception, err:
+            #except smtplib.SMTPException , err:
+            n_log.error( "can't connect to mailhost \"%s\"( check Local.Config.Config.py ): %s" %( self.mailhost , err ) )
+            raise EmailError , err
+        
+        if files:
+            emailBody.preamble = 'You will not see this in a MIME-aware mail reader.\n'
+            # To guarantee the message ends with a newline
+            emailBody.epilogue = ''            
+            if msg :
+                msg = MIMEText( msg  , 'plain' , 'utf-8' )
+                emailBody.attach( msg )
+
+            for filename in files:
+                if not os.path.isfile( filename ):
+                    continue
+    
+                # Guess the content type based on the file's extension.  Encoding
+                # will be ignored, although we should check for simple things like
+                # gzip'd or compressed files.
+    
+                ctype, encoding = mimetypes.guess_type( filename )
+                if ctype is None or encoding is not None:
+                    # No guess could be made, or the file is encoded (compressed), so
+                    # use a generic bag-of-bits type.
+                    ctype = 'application/octet-stream'
+    
+                maintype, subtype = ctype.split( '/' , 1 )
+                if maintype == 'text':
+                    fp = open( filename , 'r')
+                    # Note: we should handle calculating the charset
+                    msg = MIMEText( fp.read() , _subtype = subtype )
+                    fp.close()
+                elif maintype == 'image':
+                    fp = open( filename , 'rb' )
+                    msg = MIMEImage( fp.read() , _subtype = subtype )
+                    fp.close()
+                else:
+                    if filename == 'index.xml':
+                        fp = self._indexPostProcess()
+                    else:
+                        fp = open( filename , 'rb' )
+                        
+                    msg = MIMEBase( maintype , subtype )
+                    msg.set_payload( fp.read() )
+                    fp.close()
+                    # Encode the payload using Base64
+                    Encoders.encode_base64( msg )
+    
+                # Set the filename parameter
+                msg.add_header( 'Content-Disposition' , 'attachment', filename = os.path.basename( filename) )
+                emailBody.attach( msg )
+        
+        try:
+            s.sendmail( mailAttr[ 'From' ] , recipients , emailBody.as_string() )
+            s.quit()
+        except smtplib.SMTPSenderRefused, err:
+            if err.smtp_code == 552 :
+                raise TooBigError , str( err )
+            else:
+                raise EmailError , err
+        except smtplib.SMTPException , err:
+            n_log.error( "can't send email : %s" % err )
+            raise EmailError , err
+                
+        except Exception , err:    
+            #except smtplib.SMTPException , err:
+            n_log.error( "can't send email : %s" % err , exc_info = True)
+            n_log.error( str( recipients ) )
+            raise EmailError , err                
+            
+    def _parse(self , mail ):
+        headers = {}
+        mail = mail.split( '\n' )
+        iterator = iter( mail )
+        begin = False
+
+        for line in iterator:
+            if not (begin or line):
+                begin = True
+                continue
+
+            splitedLine = line.split(':')
+            fields = splitedLine[0].strip()
+            value = ':'.join( splitedLine[1:] )
+            if fields == 'From':
+                value = value.split( ',' )
+                try:
+                    value = value[0].strip()
+                except IndexError :
+                    raise MobyleError, '"From:" field cannot be empty '
+                if not value:
+                    raise MobyleError, '"From:" field cannot be empty '
+            elif fields in ( 'Reply-To' , 'Cc' , 'Bcc' ) :
+                value = value.split( ',' )
+                value = [ val.strip() for val in value if val.strip() ]
+                if not value:
+                    continue
+            elif not fields:
+                body = '\n'.join( iterator )
+                break
+            else:
+                value = value.strip()
+                if not value:
+                    continue
+            headers[ fields ] = value
+        self.headers = headers
+        self.body = body
+        return   headers , body
diff --git a/Src/Mobyle/Parser.py b/Src/Mobyle/Parser.py
new file mode 100644
index 0000000..66ac176
--- /dev/null
+++ b/Src/Mobyle/Parser.py
@@ -0,0 +1,553 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+ Tools to parse and build services for Mobyle
+
+"""
+import os.path
+from lxml import etree
+from logging import getLogger
+b_log = getLogger( 'Mobyle.builder' )
+p_log = getLogger( __name__ )
+
+import Mobyle.Service
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.Evaluation import Evaluation
+
+from Mobyle.MobyleError import MobyleError , ParserError
+from Mobyle.ConfigManager import Config
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+def parseService( serviceUrl  , debug = 0 ) :
+    """
+    @param serviceUrl: the url of a Mobyle Service definition
+    @type serviceUrl: string
+    @return: a service
+    @rtype: service instance
+    """
+    try:
+        servicePath = serviceUrl
+    except KeyError:
+        raise MobyleError , "the service %s doesn't exist" % serviceUrl
+    
+    parser = etree.XMLParser( no_network = False )
+    doc = etree.parse( servicePath , parser )
+    root = doc.getroot()
+    if root.tag == "program":
+        service = parseProgram( root , debug = debug )
+    elif root.tag == "workflow":
+        service = parseWorkflow( root )
+    else:
+        raise ParserError , "a service must be a program or a workflow"
+    service.header.setUrl( serviceUrl )   
+    return service
+
+    
+    
+def parseWorkflow( workflowNode ):
+        raise NotImplementedError, "parsePipeline is not yet implemented todo"
+
+    
+    
+    
+def parseProgram( programNode , debug = 0 ):
+    ######################################
+    #
+    #  validation 
+    #
+    ######################################
+    evaluator = Evaluation() 
+    dataTypeFactory = DataTypeFactory()
+    program = Mobyle.Service.Program( evaluator )
+    headNode = programNode.find( "./head" )
+    header = parseHeader( program , headNode , context = program )
+    program.addheader( header )
+    allParameterNodes = programNode.findall( './parameters/parameter[name]')
+    for parameterNode in allParameterNodes:
+        try:
+            program.addParameter( parseParameter( parameterNode , 
+                                                  evaluator = evaluator, 
+                                                  dataTypeFactory = dataTypeFactory,
+                                                  context = program ) )
+        except MobyleError , err :
+            raise ParserError , "%s : %s : %s" %( program.getName() ,
+                                                  parameterNode.find( "./name" ).text ,
+                                                  err
+                                                  )
+    allParagraphNodes = programNode.findall( './parameters/paragraph')
+    for paragraphNode in allParagraphNodes:
+        program.addParagraph( parseParagraph( paragraphNode , 
+                                              evaluator = evaluator, 
+                                              dataTypeFactory = dataTypeFactory, 
+                                              context = program )  )
+    if debug > 1:
+        b_log.debug("""
+        \t##########################################
+        \t#                                        #
+        \t#              vdefs filling             #
+        \t#                                        #
+        \t##########################################
+        """)
+    #service.resetAllParam() #tous les parametres sont dans l'evaluateur
+    for paramName in program.getAllParameterNameByArgpos() :
+        #all parameters must be in evaluation space
+        #in some output parameters there is a format element eg to rename the output file
+        # see outfile parameter in protdist.xml ( this renaming is mandatory to avoid conflict 
+        # when 2 phylip job are piped
+        try:
+            program.reset( paramName )
+        except MobyleError , err :
+            msg = "%s.%s : invalid vdef : %s" %( program.getName(),
+                                             paramName ,
+                                             err
+                                             )
+            if debug < 2 :
+                p_log.critical( msg )
+            else:
+                p_log.error( msg )
+            raise MobyleError , msg
+    return program
+    
+
+def parseHeader( service ,  headNode , context = None ):
+    """
+    fill the head of the service with the element from the headNode
+    @param service: the service
+    @type service: a Program instance or WorkflowDef instance
+    @param headNode: a node representing a header
+    @type headNode: a dom object 
+    """
+    
+
+    def parse( package_or_program , pack_or_prg_node):
+        name = pack_or_prg_node.find( "./name" ).text
+        package_or_program.setName( name )
+        try:
+            version = pack_or_prg_node.find( "./version" ).text
+            package_or_program.setVersion( version )
+        except AttributeError:
+            pass
+        title = pack_or_prg_node.find( "./doc/title" ).text
+        package_or_program.setTitle( title )
+        doclinks = pack_or_prg_node.findall( "./doc/doclink" )
+        for doclink in doclinks:
+            package_or_program.addDoclink( doclink.text )
+        categoryNodes = pack_or_prg_node.findall( "./category" )
+        categories = []
+        for categoryNode in categoryNodes:
+            categories.append( categoryNode.text )
+        if categories:
+            package_or_program.addCategories( categories )
+        try:
+            commandNode = pack_or_prg_node.find( "command" )
+            command = commandNode.text
+            type = commandNode.get( "type" )
+            path = commandNode.get( "path" )
+            if type and path:
+                package_or_program.setCommand( command , type = type , path = path )
+            elif type:
+                package_or_program.setCommand( command , type = type )
+            elif path:
+                package_or_program.setCommand( command , path = path )
+            else:
+                package_or_program.setCommand( command )
+        except AttributeError:
+            pass
+        envNodes = pack_or_prg_node.findall( "env" )
+        for envNode in envNodes:
+            try:
+                envName = envNode.get( 'name')
+                envValue = envNode.text
+                package_or_program.addEnv( envName , envValue )
+            except IndexError:
+                raise ParserError , "invalid env element in head"
+        
+    pack_node = headNode.find('package')
+    package = None
+    if pack_node is not None:
+        package = Mobyle.Service.Package()
+        parse( package, pack_node )
+    prog_head_node = headNode
+    program_header =  Mobyle.Service.ProgramHeader()
+    parse( program_header , prog_head_node)
+    return Mobyle.Service.Header(package , program_header)          
+
+
+def parseParagraph( paragraphNode , evaluator  = None , dataTypeFactory = None , context = None):
+    paragraph = Mobyle.Service.Paragraph( evaluator )
+    if dataTypeFactory is None:
+        dataTypeFactory = DataTypeFactory()
+    name = paragraphNode.find( "name" ).text
+    paragraph.setName( name )
+    for promptNode in paragraphNode.findall( './prompt' ): # plusieurs prompts dans des lang differents
+        promptLang = promptNode.get( 'lang' )
+        if promptLang is None:
+            prompt = promptNode.text
+            prompt = prompt.strip()
+            if prompt:
+                paragraph.addPrompt( prompt )
+            else:
+                continue #the element prompt is empty
+        else:
+            prompt = promptNode.text
+            prompt = prompt.strip()
+            if prompt:
+                paragraph.addPrompt( prompt , lang = promptLang )
+            else:
+                continue #the element prompt is empty
+    precondNodes = paragraphNode.findall( './precond/code' )
+    for precondNode in precondNodes :
+        precond = precondNode.text
+        proglang = precondNode.get( 'proglang' )
+        if proglang :
+            paragraph.addPrecond( precond , proglang = proglang )
+        else:
+            paragraph.addPrecond( precond )
+    try:
+        argpos = paragraphNode.find( './argpos' ).text
+        paragraph.setArgpos( int( argpos ) )
+    except AttributeError:
+        pass
+    except ValueError:
+        raise ParserError , "Argpos must be an integer"
+    
+    format = paragraphNode.find( './format' ) 
+    if format :
+        for codeNode in format.find( './code'):
+            proglang = codeNode.get( 'proglang' )
+            code = codeNode.text
+            paragraph.addFormat( code , proglang)
+    
+    ##################################
+    #
+    #   descente recursive dans l'arbre des paragraphes et paramettres
+    #
+    ##################################
+    paragraphChildNodes = paragraphNode.findall( './parameters/paragraph')
+    for paragraphChildNode in paragraphChildNodes:
+        paragraphChild = parseParagraph( paragraphChildNode , 
+                                         evaluator = evaluator , 
+                                         dataTypeFactory = dataTypeFactory ,
+                                         context = context )
+        paragraph.addParagraph( paragraphChild )
+    parameterChildNodes = paragraphNode.findall( './parameters/parameter')
+    for parameterChildNode in parameterChildNodes:
+        try:
+            parameterChild = parseParameter( parameterChildNode , 
+                                             evaluator = evaluator, 
+                                             dataTypeFactory = dataTypeFactory,
+                                             context = context )
+        except MobyleError , err :
+            raise ParserError , "error while parsing parameter %s : %s" %( parameterChildNode.find( "./name").text ,
+                                                  err
+                                                  )
+        paragraph.addParameter( parameterChild )
+    return paragraph 
+
+    
+
+def parseParameter( parameterNode , evaluator = None , dataTypeFactory = None , context = None):
+    attrs = parameterNode.attrib
+    try: 
+        out = attrs.get( 'isout' , None ) in ["1","true"] or  attrs.get( 'isstdout' , None ) in ["1","true"]
+        typeNode = parameterNode.find( "./type")
+        mobyleType = parseType(  typeNode , 
+                                 dataTypeFactory = dataTypeFactory , 
+                                 out = out ,
+                                 context = context
+                                )
+    except ( ParserError , MobyleError ), err :
+        raise ParserError , "error while parsing parameter %s : %s" %( parameterNode.find( "./name").text ,
+                                              err
+                                              )
+
+    parameter = Mobyle.Service.Parameter( mobyleType )
+    
+    #############################
+    #                           #
+    #   parsing des attributs   #
+    #                           #
+    #############################
+    
+    if 'ismandatory' in attrs and  attrs[ 'ismandatory' ] in ["1","true"]:
+        parameter.setMandatory( True )
+    if 'ismaininput' in attrs and  attrs[ 'ismaininput' ] in ["1","true"]:
+        parameter.setMaininput( True )
+    if 'iscommand' in attrs and  attrs[ 'iscommand' ] in ["1","true"]:
+        parameter.setCommand( True )
+    if 'ishidden' in attrs and  attrs[ 'ishidden' ] in ["1","true"]:
+        parameter.setHidden( True )
+    if 'issimple' in attrs and  attrs[ 'issimple' ] in ["1","true"]:  
+        parameter.setSimple( True )
+    if 'isout' in attrs and  attrs[ 'isout' ] in ["1","true"]:
+        parameter.setOut( True )
+    if 'isstdout' in attrs and  attrs[ 'isstdout' ] in ["1","true"]: 
+        parameter.setStdout( True )
+        parameter.setOut( True )
+    if 'bioMoby' in attrs and  attrs[ 'bioMoby' ] == "1":    
+        parameter.setBioMoby( attrs[ 'bioMoby' ] )
+    try:
+        name = parameterNode.find( "name" ).text
+        parameter.setName( name )
+    except AttributeError:
+        raise ParserError , "parameter has no tag \"name\""
+    
+    for promptNode in parameterNode.findall( './prompt' ): # plusieurs prompts dans des lang differents
+        promptLang = promptNode.get( 'lang' )
+        if promptLang is None:
+            parameter.addPrompt( promptNode.text )
+        else:
+            parameter.addPrompt( promptNode.text , lang = promptLang )
+    format = parameterNode.find( './format' ) 
+    if format is not None:
+        for codeNode in format.findall( './code'):
+            proglang = codeNode.get( 'proglang' )
+            code = codeNode.text
+            if code is None:
+                code = "" 
+                pname = codeNode.find( "/program/head/name" ).text
+                p_log.warning( "find empty element code in %s.%s parameter. The code value is set to \"\" " %( pname , name )  )
+            parameter.addFormat( code , proglang)
+    
+    vdefs = [] # #dans clustalW hgapresidue est un MultipleChoice et la vdef est une liste de valeurs
+    for vdefNode in parameterNode.findall( './vdef/value' ):
+        vdefs.append( vdefNode.text )
+    if vdefs:
+        parameter.setVdef( vdefs )
+    #the vdef can't be code anymore the service and apidoc must be updated
+    #getVdef could return always list 
+    try:
+        argpos = parameterNode.find( './argpos' ).text
+        parameter.setArgpos( int( argpos ) )
+    except AttributeError:
+        pass
+    except ValueError:
+        raise ParserError , "Argpos must be an integer"
+ 
+    vlist = parameterNode.find( './vlist' )
+    if vlist is not None:
+        elems  = vlist.findall('./velem')
+        for elem in elems:
+            try:
+                label = elem.find('./label' ).text
+            except AttributeError:
+                label = "" 
+            try:  
+                value = elem.find('./value' ).text
+            except AttributeError:
+                value = ""
+            undef = elem.get( 'undef' )
+            if bool( undef ):
+                parameter.setListUndefValue( value )
+            else:    
+                parameter.addElemInVlist(  label , value ) 
+        
+    flist = parameterNode.find( './flist' )      
+    if flist is not None:
+        elems  = flist.findall('./felem')
+        for elem in elems:
+            try:
+                label = elem.find( './label' ).text 
+            except AttributeError:
+                label = ""
+            try:
+                value = elem.find( './value' ).text
+            except AttributeError:
+                value = ""
+            codes = {}
+            for codeNode in elem.findall( './code' ):
+                code = codeNode.text
+                if code is None:
+                    code = "" 
+                    pname = codeNode.find( "/program/head/name" ).text
+                    p_log.warning( "find empty felem code in %s.%s parameter. The code value is set to \"\" " %( pname , name )  )
+                proglang = codeNode.get( 'proglang' )
+                if proglang is None:
+                    pname = codeNode.find( "/program/head/name").text
+                    msg = "find felem code in %s.%s parameter without proglang"%( pname , name ) 
+                    p_log.critical(msg)
+                    raise ParserError , msg
+                codes [ proglang ] = code
+            undef = elem.get( 'undef' )
+            if bool( undef ):
+                parameter.setListUndefValue( value )
+            else:                   
+                parameter.addElemInFlist( value, label , codes )
+            
+    ctrls = parameterNode.findall( './ctrl' )
+    for ctrl in ctrls:
+        messages = []
+        codes = []
+        messageNodes = ctrl.findall( './message/text' )
+        for messageNode in messageNodes:
+            message = toText( messageNode)
+            messages.append( message )
+        for codeNode in ctrl.findall( './code'):
+            code = codeNode.text
+            proglang = codeNode.get( 'proglang' )
+            codes.append( ( code , proglang ) )
+        parameter.addCtrl( ( messages , codes ) ) 
+   
+    precondNodes = parameterNode.findall( './precond/code' )
+    for precondNode in precondNodes :
+        precond = precondNode.text.strip() 
+        if precond == '':
+            pname = codeNode.find( "/program/head/name" ).text
+            msg = "WARNING: %s/%s: the code of precond is empty" %( pname , name ) 
+            b_log.warning( msg ) 
+            p_log.warning( msg )
+            continue          
+        proglang = precondNode.get( 'proglang' )
+        if proglang is not None:
+            parameter.addPrecond( precond , proglang = proglang )
+        else:
+            parameter.addPrecond( precond )
+    paramfile= parameterNode.find( './paramfile' )
+    if paramfile is not None:
+        paramfile = paramfile.text
+        parameter.setParamfile( paramfile )
+    filenamesNode = parameterNode.find( './filenames' )
+    if filenamesNode is not None:
+        for codeNode in filenamesNode.findall( './code' ):
+            parameter.setFilenames( codeNode.text , codeNode.get( 'proglang' ) )
+   
+    scaleNode = parameterNode.find( './scale')
+    if scaleNode is not None:
+        minNode = scaleNode.find('./min' )
+        maxNode = scaleNode.find('./max' )
+        if minNode is None or maxNode is None:
+            raise MobyleError, "parameter named:\"%s\" have a malformed element scale" % parameter.getName()
+        try:
+            inc = scaleNode.find('./inc' ).text
+        except AttributeError:
+            inc = None
+        try:
+            max = maxNode.find( './value' ).text
+            maxProglang = None
+            min = minNode.find( './value' ).text
+            minProgLang = None
+            parameter.setScale( min , max , inc = inc )
+        except AttributeError:
+            try:
+                minCodes = {}
+                maxCodes = {}
+                maxCodeNodes = maxNode.findall( './code' )
+                minCodeNodes = minNode.findall( './code' )
+                for codeNode in maxCodeNodes:
+                    maxProglang = codeNode.get( 'proglang' )                    
+                    maxCode = codeNode.text
+                    maxCodes[ maxProglang ] = maxCode
+                for codeNode in minCodeNodes:
+                    minProglang = codeNode.get( 'proglang' )                    
+                    minCode = codeNode.text
+                    minCodes[ minProgLang ] = minCode
+                for minProglang in minCodes.keys():
+                    parameter.setScale( minCodes[ minProglang ] , maxCodes[ minProglang ] , inc = inc , proglang = minProglang )
+            except ( AttributeError , KeyError ) :
+                raise ParserError, "parameter named:\"%s\" have a malformed element scale" % parameter.getName()
+    separator = parameterNode.find( './separator' )
+    if separator is not None:
+        if separator.text is None:
+            parameter.setSeparator( '' )
+        else:
+            parameter.setSeparator( separator.text )
+    return parameter
+
+
+def parseType( typeNode , dataTypeFactory = None , out = False , context = None):
+    if not dataTypeFactory:
+        dataTypeFactory = DataTypeFactory()
+    try:
+        bioTypes = typeNode.findall( "./biotype" )
+        bioTypes = [ bt.text for bt in bioTypes ]
+    except IndexError :
+        bioTypes = None
+    try:    
+        card = typeNode.find( "./type/card" ).text
+        try:
+            min , max = card.split(",")
+        except ValueError:
+            if len( card ) == 2:
+                min = max = card 
+            else:
+                raise ParserError , "invalid card: %s .the card element must be a string of 2 integer or \"n\" separate by a comma : "% card 
+        try:
+            min = int( min )
+        except ValueError :
+            if min != "n":
+                raise ParserError , "invalid card: %s .the card element must be a string of 2 integer or \"n\" separate by a comma : "% card 
+        try:
+            max = int( max )
+        except ValueError :
+            if max != "n":
+                raise ParserError , "invalid card: %s .the card element must be a string of 2 integer or \"n\" separate by a comma : "% card 
+        card = ( min , max )
+    except AttributeError :
+        card = ( 1 , 1 )
+    try:
+        superKlass = typeNode.find( "./datatype/superclass" ).text
+    except AttributeError:
+        superKlass = None
+    try:
+        klass = typeNode.find( "./datatype/class" ).text
+    except AttributeError:
+        if superKlass is None :
+            raise ParserError , typeNode.find( "./name" ).text + " must have either a \"class\" element either a \"class\" and \"superclass\" element"
+        else:
+            raise ParserError , typeNode.find( "./name" ).text +" if the \"superclass\" is specified the the \"class\" element must be also specified"
+    try:
+        if superKlass:
+            dataType = dataTypeFactory.newDataType( superKlass , xmlName = klass )
+        else:
+            dataType = dataTypeFactory.newDataType( klass )
+    except MobyleError , err :
+        raise ParserError , err
+    mobyleType = Mobyle.Service.MobyleType( dataType , 
+                                            bioTypes = bioTypes , 
+                                            card = card )
+    
+    formatNodes = typeNode.findall( "dataFormat" )
+    if not out and context and isinstance( context , Mobyle.Service.Program ):
+        acceptedformats = []
+        for formatNode in formatNodes :
+            format = formatNode.text
+            force = formatNode.get( "force" , False ) == '1'
+            acceptedformats.append( (formatNode.text , force ) )
+        mobyleType.setAcceptedFormats( acceptedformats )
+                   
+    elif formatNodes:
+        dataFormat = formatNodes[0]
+        #le format_prg
+        mobyleType.setDataFormat( dataFormat.text )
+    return  mobyleType  
+
+
+
+def toText( textNode ):
+    try:
+        content = textNode.text.strip()
+    except AttributeError:
+        content = None
+    lang = textNode.get( 'lang' )
+    if lang:
+        return ( content , None , lang , None )
+    
+    href = textNode.get( 'href' )
+    proglang = textNode.get( 'proglang' )        
+    if href is not None:
+        return ( content , None , None , href )
+    elif proglang is not None:
+        return ( content , proglang , None , None )
+    else:
+        return ( content , None , 'en' , None )
+
+    
diff --git a/Src/Mobyle/Registry.py b/Src/Mobyle/Registry.py
new file mode 100644
index 0000000..1b3352b
--- /dev/null
+++ b/Src/Mobyle/Registry.py
@@ -0,0 +1,631 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+Registry.py
+
+This module holds:
+- The Registry class that describes the list of installed/imported services 
+  and their servers
+- The DefNode class, an abstract class that describes an element (node or
+  leaf) of the services tree
+- The ProgramDef class that describes a service (leaf in the services tree)
+- The CategoryDef class that describes a category (node in the services tree)
+- The ServerDef class that describes a server (node in the services tree)
+"""
+import os
+from hashlib import md5
+from glob import glob
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+from logging import getLogger
+r_log = getLogger( __name__ )
+
+
+class DefNode(object):
+    """
+    DefNode is the class that provides the tree structure to the 
+    server/categories/program hierarchy
+    """
+
+    def __init__(self):
+        self.name = None
+        self.children = []
+        self.parents = []
+              
+    def getDescendants(self):
+        """
+        Returns the descendants of the node recursively
+        @return: the list of descendant nodes
+        @rtype: list
+        """
+        r = []
+        r.extend(self.children)
+        for p in self.children:
+            r.extend(p.getDescendants())
+        return r
+
+    def getAncestors(self):
+        """
+        Returns the ancestors of the node recursively
+        @return: the list of ancestor nodes
+        @rtype: list
+        """
+        r = []
+        r.extend(self.parents)
+        for p in self.parents:
+            r.extend(p.getAncestors())
+        return r
+
+    def addDescendant(self, subpath):
+        """
+        Adds a descendant to the node, by recursively adding the descendants from the provided subpath if they do not exist
+        @param subpath: the subpath, a list of descendant objects 
+                        (e.g.: [CategoryDef, CategoryDef, ProgramDef])
+        @type subpath: string
+        """
+        if len(subpath)==0:
+            return
+        else:
+            c = [c for c in self.children if (c.name == subpath[0].name and (not(hasattr(c,'server')) or c.server == subpath[0].server))]
+            if not(c):
+                c = subpath[0]
+                self.children.append(c)
+                c.parents.append(self)
+            else:
+                c = c[0]
+            c.addDescendant(subpath[1:])
+            
+    def __cmp__(self, other):
+        """
+        Comparison operator that allows to classify the tree structure alphabetically
+        based on node names
+        @param other: the other node to which self is compared
+        @type other: DefNode
+        @return: the comparison result
+        @rtype: boolean
+        """
+        return cmp(self.name.lower(), other.name.lower())
+    
+    def getChildCategories(self):
+        """
+        Returns child categories, i.e. child nodes which happen to be CategoryDef instances
+        @return: a list of category nodes
+        @rtype: list
+        """
+        l = [c for c in self.children if not(isinstance(c, ServiceDef))]
+        l.sort()
+        return l
+
+    def getChildServices(self):
+        """
+        Returns child programs, i.e. child nodes which happen to be ServiceDef instances
+        @return: a list of program nodes
+        @rtype: list
+        """
+        l = [c for c in self.children if isinstance(c, ServiceDef)]
+        l.sort()
+        return l
+
+class Registry(DefNode):
+    
+    #__shared_state = {}
+    
+    def __init__( self ):
+        #self.__dict__ = self.__shared_state
+        DefNode.__init__(self)
+        self.programs = []
+        self.workflows = []
+        self.viewers = []
+        self.tutorials = []
+        self.servers = []
+        self.serversByName = {}
+        self.serversByUrl = {}
+        self.programsByUrl = {}
+        self.workflowsByUrl={}
+        self.viewersByUrl = {}
+        self.tutorialsByUrl= {}
+        self.servers_path=_cfg.servers_path()
+    
+    def load( self):
+        """
+        
+        """
+        serverProperties = self._getServerProperties()
+        
+        for name, properties in serverProperties.items():
+            server = ServerDef( name = name,
+                                url = properties[ 'url' ] ,
+                                help = properties[ 'help' ],
+                                repository = properties[ 'repository' ],
+                                jobsBase = properties[ 'jobsBase' ] ,
+                               )
+            self.addServer(server)
+            for name in properties[ 'programs' ]:
+                try:                        
+                    url = self.getProgramUrl( name, server.name )
+                    path = self._computeProgramPath( name, server.name)
+                    program = ProgramDef( name = name,
+                                          url = url,
+                                          path = path,
+                                          server = server
+                                        )
+                    # service disabled
+                    if _cfg.isDisabled( portalID = "%s.%s" %( server.name , program.name ) ): 
+                        program.disabled = True
+                    # service with restricted access
+                    if not( _cfg.isAuthorized( url )):
+                        program.authorized = False
+                    self.addProgram( program )
+                except IndexError, e:
+                    r_log.error("Error while loading %s: %s" % (name, e))
+            
+            for name in properties[ 'workflows' ]:
+                try:                        
+                    url = self.getWorkflowUrl( name, server.name)
+                    path = self._computeWorkflowPath( name, server.name)
+                    wf = WorkflowDef( name = name,
+                                          url = url,
+                                          path = path,
+                                          server = server
+                                        )
+                    # service disabled
+                    if _cfg.isDisabled( portalID = "%s.%s" %( server.name , wf.name ) ): 
+                        wf.disabled = True
+                    # service with restricted access
+                    if not( _cfg.isAuthorized( url )):
+                        wf.authorized = False
+                    self.addWorkflow( wf )
+                except IndexError, e:
+                    r_log.error("Error while loading %s: %s" % (name, e))
+            
+            
+            if server.name == 'local':
+                for name in properties['viewers']:
+                    
+                    try:                        
+                        url = self.getViewerUrl( name )
+                        path = self.getViewerPath( name )
+                        viewer = ViewerDef( name= name,
+                                       url= url,
+                                       path=path,
+                                       server=server
+                                              )
+                        self.addViewer(viewer)
+                    except IndexError, e:
+                        r_log.error("Error while loading %s: %s" % (name, e))
+                        
+                for name in properties[ 'tutorials' ]:
+                    try:                        
+                        url = self.getTutorialUrl(name)
+                        path = self._computeTutorialPath(name)
+                        tt = TutorialDef( name = name,
+                                              url = url,
+                                              path = path,
+                                              server = server
+                                            )
+                        # service disabled
+                        if _cfg.isDisabled( portalID = "%s.%s" %( server.name, name ) ): 
+                            tt.disabled = True
+                        # service with restricted access
+                        if not( _cfg.isAuthorized( url )):
+                            tt.authorized = False
+                        self.addTutorial( tt )
+                    except IndexError, e:
+                        r_log.error("Error while loading %s: %s" % (name, e))
+                        
+
+    def addServer(self, server):
+        self.servers.append( server )
+        self.serversByName[ server.name ] = server
+        self.serversByUrl[ server.url ] = server
+
+    def addProgram( self, program ):
+        self.programs.append( program )
+        self.programsByUrl[ program.url ] = program
+        self.serversByName[ program.server.name ].programs.append( program )
+        self.serversByName[ program.server.name ].programsByName[ program.name ] = program
+    
+    def addWorkflow(self, workflow ):
+        self.workflows.append( workflow )
+        self.workflowsByUrl[ workflow.url ] = workflow
+        self.serversByName[ workflow.server.name ].workflows.append( workflow )
+        self.serversByName[ workflow.server.name ].workflowsByName[ workflow.name ] = workflow
+    
+    def addViewer(self, viewer ):
+        self.viewers.append( viewer )
+        self.viewersByUrl[ viewer.url ] = viewer
+        self.serversByName[ viewer.server.name ].viewers.append( viewer )
+        self.serversByName[ viewer.server.name ].viewersByName[ viewer.name ] = viewer
+    
+    def addTutorial(self, tutorial ):
+        self.tutorials.append( tutorial )
+        self.tutorialsByUrl[ tutorial.url ] = tutorial
+        self.serversByName[ tutorial.server.name ].tutorials.append( tutorial )
+        self.serversByName[ tutorial.server.name ].tutorialsByName[ tutorial.name ] = tutorial
+    
+    def has_service(self , service ):
+        """
+        @param service: the service to test the existance.
+        @type service: a ServiceDef instance
+        @return: True if the service exists in this registry, False otherwise.
+        @rtype: boolean.
+        """
+        if isinstance( service , ProgramDef ):
+            try:
+                self.serversByName[ service.server.name ].programsByName[ service.name ]
+                return True
+            except KeyError:
+                return False
+        elif isinstance( service , WorkflowDef ):
+            try:
+                self.serversByName[ service.server.name ].workflowsByName[ service.name ]
+                return True
+            except KeyError:
+                return False
+        elif isinstance( service , ViewerDef ):
+            try:
+                self.serversByName[ service.server.name ].viewersByName[ service.name ]
+                return True
+            except KeyError :
+                return False
+        elif isinstance( service , TutorialDef ):
+            try:
+                self.serversByName[ service.server.name ].tutorialsByName[ service.name ]
+                return True
+            except KeyError :
+                return False
+
+    def pruneService(self, service):
+        """
+        remove a Service Definition from the registry
+        @param service: the Service to remove
+        @type service: a ServiceDef instance
+        """
+        if isinstance( service , ProgramDef ):
+            if self.has_service(service):
+                self.programs.remove(service)
+                del self.programsByUrl[service.url]
+                self.serversByName[service.server.name].programs.remove(service)
+                del self.serversByName[service.server.name].\
+                    programsByName[service.name]
+#                if (len(service.server.services)==0):
+#                    del self.serversByName[service.server.name]
+#                    self.servers.remove(service.server)
+        elif isinstance( service , WorkflowDef ):
+            if self.has_service(service):
+                self.workflows.remove(service)
+                del self.workflowsByUrl[service.url]
+                self.serversByName[service.server.name].workflows.remove(service)
+                del self.serversByName[service.server.name].\
+                    workflowsByName[service.name]
+#                if (len(service.server.services)==0):
+#                    del self.serversByName[service.server.name]
+#                    self.servers.remove(service.server)
+        elif isinstance( service , ViewerDef ):
+            if self.has_service(service):
+                self.viewers.remove(service)
+                del self.viewersByUrl[service.url]
+                self.serversByName[service.server.name].viewers.remove(service)
+                del self.serversByName[service.server.name].\
+                    viewersByName[service.name]
+#                if (len(service.server.services)==0):
+#                    del self.serversByName[service.server.name]
+#                    self.servers.remove(service.server)
+
+        elif isinstance( service , TutorialDef ):
+            if self.has_service(service):
+                self.tutorials.remove(service)
+                del self.tutorialsByUrl[service.url]
+                self.serversByName[service.server.name].tutorials.remove(service)
+                del self.serversByName[service.server.name].\
+                    tutorialsByName[service.name]
+#                if (len(service.server.services)==0):
+#                    del self.serversByName[service.server.name]
+#                    self.servers.remove(service.server)
+
+
+    def _getServerProperties(self):
+        """
+        @return: a dict of all deployed servers associated with their respective properties
+        @rtype: { server_name :{ 'url': string , 
+                                 'help': string, 
+                                 'repository':string , 
+                                 'jobBase': string ,
+                                 'programs': []  ,
+                                 'workflows' : [] ,
+                                 'viewers': [] #available only for local server ,
+                                 'tutorials': [] #available only for local server }
+        """
+        imported_portals = _cfg.portals()
+        properties = {}
+        all_server_dir_path = glob( os.path.join(self.servers_path , '*' ) )
+        for server_dir_path in all_server_dir_path:
+            if not os.path.isdir(server_dir_path):
+                continue
+            else:
+                server_name = os.path.basename( server_dir_path )
+                properties[ server_name ] = {}
+                deployed_programs_path = glob( os.path.join( server_dir_path , ProgramDef.directory_basename  ,'*.xml') )
+                deployed_workflows_path = glob( os.path.join( server_dir_path , WorkflowDef.directory_basename ,'*.xml') )
+                properties[ server_name ][ 'programs' ] = [ os.path.basename( p )[:-4] for p in deployed_programs_path ]
+                properties[ server_name ][ 'workflows' ] = [ os.path.basename( p )[:-4] for p in deployed_workflows_path ]
+                if server_name == 'local':
+                    properties[ server_name ][ 'url' ]        = _cfg.cgi_url()
+                    properties[ server_name ][ 'help' ]       = _cfg.mailHelp()
+                    properties[ server_name ][ 'repository' ] = _cfg.repository_url()
+                    deployed_viewers_path = glob(os.path.join( server_dir_path , ViewerDef.directory_basename ,"*.xml") )
+                    properties[ server_name ][ 'viewers' ]    = [ os.path.basename( p )[:-4] for p in deployed_viewers_path ] 
+                    deployed_tutorials_path = glob(os.path.join( server_dir_path , TutorialDef.directory_basename ,"*.xml") )
+                    properties[ server_name ][ 'tutorials' ]    = [ os.path.basename( p )[:-4] for p in deployed_tutorials_path ]
+                    properties[ server_name ][ 'jobsBase' ]   = _cfg.results_url()
+                elif server_name in imported_portals:
+                    properties[ server_name ][ 'url' ]        = imported_portals[ server_name ][ 'url' ]
+                    properties[ server_name ][ 'help' ]       = imported_portals[ server_name ][ 'help' ]
+                    properties[ server_name ][ 'repository' ] = imported_portals[ server_name ][ 'repository' ]
+                    properties[ server_name ][ 'repository' ] = imported_portals[ server_name ][ 'repository' ]
+                    properties[ server_name ][ 'jobsBase' ]   = "%s/jobs" %properties[ server_name ][ 'repository' ]
+                else:
+                    r_log.warning("the server '%s' is deployed but not appear in the configuration (skip in registry)" % server_name)
+                    del properties[ server_name ]
+        return properties
+
+    def getProgramUrl(self , name , server='local'):
+        #the server.repository  point to services_path 
+        return "%s/services/servers/local/%s/%s.xml" %( self.serversByName[server].repository , 
+                                 ProgramDef.directory_basename ,
+                                 name )
+    
+    def getWorkflowUrl(self , name , server='local'):
+        return "%s/services/servers/local/%s/%s.xml" %( self.serversByName[server].repository ,
+                                 WorkflowDef.directory_basename ,
+                                 name )
+
+    def getViewerUrl(self, name ):
+        return "%s/services/servers/local/%s/%s.xml" %( self.serversByName[ 'local' ].repository,
+                                 ViewerDef.directory_basename , 
+                                 name )
+        
+    def getTutorialUrl(self, name ):
+        return "%s/services/servers/local/%s/%s.xml" %( self.serversByName[ 'local' ].repository,
+                                 TutorialDef.directory_basename , 
+                                 name )        
+    def getJobPID(self, url):
+        """
+        @param url: the url of 
+        @type url: string
+        @return: the portal identifier for this job
+        @rtype: string
+        """
+        server = self.getServerByJobId(url)
+        jobPID = url.replace(server.jobsBase,'').lstrip('/').replace('/','.')
+        if server.name != 'local':
+            jobPID = server.name + '.' + jobPID
+        return jobPID
+
+    def isJobLocal(self, url):
+        """
+        @param url: the url of 
+        @type url: string
+        @return: true if the job is local, false otherwise
+        @rtype: boolean
+        """
+        return self.getJobPID(url).find('.')>-1 or self.getJobPID(url).startswith('local.')
+
+    def getJobURL(self, pid):
+        # if the PID is composite (i.e. it's a workflow task), only take the last part into account. 
+        pid = pid.split('::').pop() 
+        l = pid.split('.')
+        if (len(l)==2):
+            server_name = 'local'
+        else:
+            server_name = l[0]
+            l = l[1:]
+        return self.serversByName[server_name].jobsBase + '/' + '/'.join(l)
+
+    def getViewerPath(self, name):
+        return os.path.join( self.servers_path , 'local', ViewerDef.directory_basename  , name + '.xml')
+    
+    def getTutorialPath(self, name):
+        return os.path.join( self.servers_path , 'local', TutorialDef.directory_basename  , name + '.xml')
+    
+    def getProgramPath(self, name, server_name ='local'):
+        if server_name in self.serversByName:
+            if name in self.serversByName[ server_name ].programsByName:
+                return self.serversByName[ server_name ].programsByName[ name ].path
+            else:
+                raise KeyError , "unknown service %s for server %s " %( name , server_name )
+        else:
+            raise KeyError , "unknown server %s" %server_name
+            
+    def _computeProgramPath( self , name , server_name= 'local' ):  
+        return os.path.join( self.servers_path , server_name, ProgramDef.directory_basename , name + '.xml')
+      
+    def _computeWorkflowPath( self , name , server_name= 'local' ):  
+        return os.path.join( self.servers_path , server_name, WorkflowDef.directory_basename , name + '.xml') 
+
+    def _computeTutorialPath( self , name , server_name= 'local' ):  
+        return os.path.join( self.servers_path , server_name, TutorialDef.directory_basename , name + '.xml') 
+      
+    def getServerByJobId(self,jobId):
+        for server in self.servers:
+            if jobId.startswith(server.jobsBase):
+                return server
+
+
+class ServiceDef(DefNode):
+    """
+    ServiceDef is the superclass that provides the service information to the registry
+    """
+    
+    directory_basename = None
+    
+    def __init__(self, url, name=None, path=None, server=None):
+        """
+        @param url: the url of the definition of the program on the server where it is executed
+        @type url: String
+        @param name: the name of the program 
+        @type name: String
+        @param path: the os path to the local version of the file (local file definition or local cache of distant service)
+        @type path: String
+        @param server: the execution server definition
+        @type server: ServerDef
+        """
+        DefNode.__init__(self)
+        self.url = url
+        self.name = name
+        self.path = path
+        self.server = server
+        self.categories = []
+        
+        if (self.server.name == 'local'):
+            """portal id"""
+            self.pid = self.name
+        else:
+            self.pid = '%s.%s' % (self.server.name, self.name)
+        self.disabled = False
+        self.authorized = True
+
+    def isExported(self):
+        return self.server.name == 'local' and self.name in _cfg.exported_services()
+    
+    def __eq__(self , other ):
+        return self.server.name == other.server.name and self.name == other.name 
+        
+class ProgramDef(ServiceDef):
+    """
+    ProgramDef is the class that provides the service information to the registry
+    """
+    type = "program"
+    directory_basename = "programs"  
+
+class WorkflowDef(ServiceDef):
+    """
+    WorkflowDef is the class that provides the workflow information to the registry
+    """
+    type = "workflow"    
+    directory_basename = "workflows"  
+    
+    
+    
+class ViewerDef(ServiceDef):
+    """
+    ViewerDef is the class that provides the viewer information to the registry
+    """
+    type = "viewer"
+    directory_basename = "viewers"        
+   
+class TutorialDef(ServiceDef):
+    """
+    TutorialDef is the class that provides the tutorial information to the registry
+    """
+    type = "tutorial"
+    directory_basename = "tutorials"        
+   
+
+
+class ServerDef(DefNode):
+    """
+    ServerDef is the class that provides the server information to the registry
+    """
+
+    def __init__(self, name, url, help, repository, jobsBase  ):
+        """    
+        @param name: the short name, as displayed for instance in the services tree menu
+        @type name: String
+        @param url: the url of the server (cgi-bin base url for now)
+        @type url: String
+        @param help: the help email contact
+        @type help: String
+        @param repository: the url wher to find programs, workflows, etc...
+        @type repository: String
+        @param jobsBase: the url of the directory that contains all of the server's jobs
+        @type jobsBase: String
+        """
+        DefNode.__init__(self)
+        self.name = name
+        self.url = url
+        self.help = help
+        self.repository = repository
+        """top-level categories"""
+        self.categories = [] # top-level categories        
+        """the url to revieve a job"""
+        self.jobsBase = jobsBase
+        """ """
+        self.programs = [] 
+        """ """
+        self.programsByName = {}
+        """ """
+        self.workflows = [] 
+        """ """
+        self.workflowsByName = {}
+        self.viewers = []
+        self.viewersByName = {}
+        self.tutorials = []
+        self.tutorialsByName = {}
+        self.path = os.path.join( _cfg.services_path() , self.name )
+
+    def __cmp__(self, other):
+        """
+        Comparison operator that allows to classify the servers alphabetically
+        based on node names, except local server which is first
+        @param other: the other node to which self is compared
+        @type other: DefNode
+        @return: the comparison result
+        @rtype: boolean
+        """
+        if self.name=='local':
+            return -1
+        else:
+            return cmp(self.name.lower(), other.name.lower())
+
+class CategoryDef(DefNode):
+    """
+    CategoryDef is the class that provides the category information to the registry
+    """
+
+    def __init__ (self, name, parentCategory=None, server=None):
+        """    
+        @param name: the name of the category
+        @type name: String
+        @param parentCategory: the parent category (if any)
+        @type parentCategory: CategoryDef
+        @param server: the server to which the Category belongs
+        @type server: ServerDef
+        """
+        DefNode.__init__(self)
+        self.name = name
+        self.parentCategory = parentCategory
+        self.server = server
+        self.services = [] # top-level services
+        self.categories = [] # top-level categories
+
+class ServiceTypeDef(CategoryDef):
+    def __cmp__(self, other):
+        """
+        Comparison operator that allows to classify the service types
+        with tutorials at the end
+        @param other: the other node to which self is compared
+        @type other: DefNode
+        @return: the comparison result
+        @rtype: boolean
+        """
+        if other.name=='Tutorials':
+            return -1
+        elif self.name=='Tutorials':
+            return 1
+        else:
+            return cmp(self.name.lower(), other.name.lower())
+
+        
+registry = Registry()
+registry.load()
+
diff --git a/Src/Mobyle/RunnerChild.py b/Src/Mobyle/RunnerChild.py
new file mode 100755
index 0000000..05eff5a
--- /dev/null
+++ b/Src/Mobyle/RunnerChild.py
@@ -0,0 +1,245 @@
+#! /usr/bin/env python
+
+
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+"""
+Classes executing the command and managing the results  
+"""
+import os 
+import sys
+import glob
+import time
+import cPickle
+import atexit
+
+#"the environment variable MOBYLEHOME must be defined in the environment"
+#append Mobyle Home to the search modules path
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append(  os.path.join( MOBYLEHOME , 'Src' )  )
+
+
+from Mobyle.MobyleLogger import MLogger
+MLogger( child = True )
+
+from logging import getLogger
+rc_log = getLogger( 'Mobyle.RunnerChild' )
+
+from Mobyle.JobState import JobState
+from Mobyle.Utils import executionLoader , zipFiles , emailResults
+from Mobyle.Net import EmailAddress
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Mobyle.Admin import Admin
+from Mobyle.MobyleError import MobyleError
+from Mobyle.ConfigManager import Config
+from Mobyle.Registry import registry
+_cfg = Config()
+
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+class AsynchronJob:
+    """
+    is instantiated in child process instantiate the object corresponding
+    to the execution manager defined in Config, and after the completion of the job
+    manage the results
+    """
+
+    
+    def __init__(self, commandLine, dirPath, serviceName, resultsMask , userEmail = None, email_notify = 'auto' , jobState = None , xmlEnv = None):
+        """
+        @param commandLine: the command to be executed
+        @type commandLine: String
+        @param dirPath: the absolute path to directory where the job will be executed (normaly we are already in)
+        @type dirPath: String
+        @param serviceName: the name of the service
+        @type serviceName: string
+        @param resultsMask: the unix mask to retrieve the results of this job
+        @type resultsMask: a dictionary { paramName : [ string prompt , ( string class , string or None superclass ) , string mask ] }
+        @param userEmail: the user email adress
+        @type userEmail: string
+        @param email_notify: if the user must be or not notify of the results at the end of the Job.
+        the 3 authorized values for this argument are: 
+          - 'true' to notify the results to the user
+          - 'false' to Not notify the results to the user
+          - 'auto' to notify the results based on the job elapsed time and the config  EMAIL_DELAY
+        @type email_notify: string 
+        @param jobState: the jobState link to this job
+        @type jobState: a L{JobState} instance
+        @param xmlEnv: the environement variable need by the program
+        @type xmlEnv: dictionnary
+        @call: by the main of this module which is call by L{AsynchronRunner}
+        """
+        self._command = commandLine
+        self._dirPath = dirPath
+        self.serviceName = serviceName
+        self.father_pid = os.getppid()
+        self.father_done = False
+        if jobState is None:
+            self.jobState = JobState( self._dirPath )
+        else:
+            self.jobState = jobState
+        self.userEmail = userEmail
+        self.email_notify =  email_notify 
+        
+        if self._dirPath[-1] == '/':
+            self._dirPath = self._dirPath[:-1]
+
+        self.jobKey = os.path.split( self._dirPath )[ 1 ]
+        
+        atexit.register( self.childExit , "------------------- %s : %s -------------------" %( serviceName , self.jobKey ) )
+        
+        t0 = time.time()
+        ############################################
+        self._run( serviceName , xmlEnv )
+        #############################################
+        t1 = time.time()
+        
+        self.results = {}
+        for paramName in resultsMask.keys():
+            resultsFiles = []
+            #type is a tuple ( klass , superKlass )
+            masks = resultsMask[ paramName ]
+            for mask in masks :
+                for File in  glob.glob( mask ):
+                    size = os.path.getsize( File )
+                    if size != 0:
+                        resultsFiles.append(  ( str( File ) , size , None ) ) #we have not information about the output format 
+            if resultsFiles: 
+                self.results[ paramName ] = resultsFiles  #a list of tuple (string file name , int size ,  string format or None )
+                self.jobState.setOutputDataFile( paramName , resultsFiles )
+
+        self.jobState.commit()
+        try:
+            zipFileName = self.zipResults()
+        except Exception :
+            msg = "an error occured during the zipping results :\n\n"
+            rc_log.critical( "%s/%s : %s" %( self.serviceName , self.jobKey , msg ) , exc_info = True)
+            zipFileName = None
+        if self.userEmail:
+            if self.email_notify == 'auto':
+                # we test email_delay() to see if it is >= to 0, 
+                # as it seems that sometimes it is not >0.
+                if ( t1 - t0 ) >= _cfg.email_delay():
+                    emailResults(_cfg,
+                                       self.userEmail,
+                                       registry ,
+                                       self.jobState.getID(),
+                                       self._dirPath ,
+                                       self.serviceName ,
+                                       self.jobKey ,
+                                      FileName = zipFileName )
+            elif self.email_notify == 'true':
+                emailResults( _cfg,
+                                   self.userEmail,
+                                   registry ,
+                                   self.jobState.getID(),
+                                   self._dirPath ,
+                                   self.serviceName ,
+                                   self.jobKey ,  
+                                   FileName = zipFileName )
+            else:
+                pass    
+
+    def childExit(self , message ):
+        print >> sys.stderr , message
+        #rc_log.log( 12 , "runnerChild %d ending, send a SIGCHLD to %d" %( os.getpid() , self.father_pid ) )
+
+    def _run(self , serviceName, xmlEnv ):
+        dispatcher = _cfg.getDispatcher()
+        
+        execution_config = dispatcher.getExecutionConfig( self.jobState )
+        try:
+            exec_engine = executionLoader( execution_config =  execution_config )
+        except MobyleError ,err :
+            msg = "unknown execution system : %s" %err
+            rc_log.critical("%s : %s" %( serviceName ,
+                                         msg
+                                         ), exc_info = True 
+            )
+            sm = StatusManager()
+            sm.setStatus( self._dirPath , Status( code = 5 , message = 'Mobyle internal server error' ) )
+            raise MobyleError, msg
+        except Exception , err:
+            rc_log.error( str(err ), exc_info=True) 
+            raise err
+        
+        exec_engine.run( self._command , self._dirPath , serviceName ,  self.jobState , xmlEnv )
+        
+    
+    def zipResults(self ):    
+        files2zip = []
+
+        for Files in self.results.values():
+            for File in Files:
+                files2zip.append( ( File[0] , os.path.basename( File[0]) ) ) #File is tuple (string file name , int size , string format or None )
+        
+        xsl_path = os.path.join( _cfg.portal_path() , "xsl" ,)
+        jobXslPath = os.path.join( xsl_path , "job.xsl" ) 
+        files2zip.append( ( jobXslPath , "job.xsl" ) )
+                
+        identXslPath = os.path.join( xsl_path , "ident.xsl" )
+        files2zip.append( ( identXslPath , "ident.xsl" ) )
+
+        cssPath = os.path.join( _cfg.portal_path() , "css",  "mobyle.css" ) 
+        files2zip.append( ( cssPath , "mobyle.css" ) )
+
+        
+        paramsfiles = self.jobState.getParamfiles()
+        if paramsfiles: 
+            for paramsfile in paramsfiles:
+                files2zip.append( ( os.path.join( self._dirPath , paramsfile[0] ) , paramsfile[0] ) )
+        
+        inputFiles = self.jobState.getInputFiles() #inputFiles = [ ( parameterName , [ (fileName , format or None ) , ...) , ... ]
+        if inputFiles is not None:
+            for files in inputFiles:
+                for item in files[1]: #item = ( filename , size , fmt ) 
+                    files2zip.append( ( os.path.join( self._dirPath , item[0] ) , item[0] ) )
+        
+        files2zip.append( ( os.path.join( self._dirPath , "index.xml") , "index.xml" ) )
+        zipFileName = "%s_%s.zip" %(self.serviceName , self.jobKey )    
+        zip_filename = zipFiles( zipFileName , files2zip )
+        return  zip_filename    
+
+if __name__ == '__main__':
+    try:
+        fh = open(".forChild.dump", "r")
+        fromFather = cPickle.load( fh )
+        fh.close() 
+    except Exception, err :
+        rc_log.critical( str( err ) )
+        raise MobyleError( err )
+
+    try:
+        os.chdir( fromFather[ 'dirPath' ] )
+    except OSError, err:
+        msg = fromFather[ 'serviceName' ] + ":" + str( err )
+        rc_log.critical( msg )
+        raise MobyleError ,msg
+
+    userEmail = fromFather[ 'email']
+    if userEmail is not None:
+        userEmail = EmailAddress( userEmail  )
+        
+    child = AsynchronJob( fromFather[ 'commandLine' ] , # string the unix command line
+                          fromFather[ 'dirPath' ] ,     # absolute path of the working directory
+                          fromFather[ 'serviceName' ] , # string 
+                          fromFather[ 'resultsMask'] ,  # 
+                          userEmail = userEmail  ,      # Net.EmailAddress to
+                          xmlEnv = fromFather[ 'xmlEnv' ] , #a dict
+                          email_notify = fromFather[ 'email_notify' ] #'true' , 'false' or 'auto'
+                          )
diff --git a/Src/Mobyle/RunnerFather.py b/Src/Mobyle/RunnerFather.py
new file mode 100644
index 0000000..41255ce
--- /dev/null
+++ b/Src/Mobyle/RunnerFather.py
@@ -0,0 +1,352 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+"""
+Run the command , do the choice of a synchronous vs. asynchronous process.
+in the case of asynchronous process, manage the creation of the child process and the synchronization with it.
+"""
+
+
+import os
+import sys
+import time
+import cPickle
+
+from logging import getLogger , shutdown as logging_shutdown
+rf_log = getLogger( __name__ )
+
+from Mobyle.CommandBuilder  import CommandBuilder
+from Mobyle.JobState  import JobState
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Mobyle.Admin import Admin
+from Mobyle.MobyleError import MobyleError
+
+__extra_epydoc_fields__ = [ ('call', 'Calledby','Called by') ]
+
+
+
+class CommandRunner:
+    """
+
+    """
+
+    def __init__( self, job , jobState = None  ):
+        """
+        instanciate a L{CommandBuilder} and use it to build the CommandLine, then run it
+        @param service:
+        @type job: a L{Job} instance
+        @param jobState:
+        @type jobState:
+        @call: l{Job.run}
+        @todo: implementation pour le warper de cgi ou WS a faire
+        """
+        self.job = job
+        self.job_dir = self.job.getDir()
+        self._service = self.job.getService()
+        service_name = self._service.getName()
+        if jobState is None:
+            self._jobState = JobState( self.job_dir )
+        else:
+            self._jobState = jobState
+        
+        commandBuilder = CommandBuilder()
+        
+        method = self._service.getCommand()[1].upper()
+
+        if method == '' or method == 'LOCAL':
+
+            try:
+                cmd = commandBuilder.buildLocalCommand( self._service )
+                self._commandLine = cmd[ 'cmd' ]
+                self._xmlEnv = cmd[ 'env' ]
+                paramfiles = cmd[ 'paramfiles' ]
+            except Exception ,err :
+                msg = "an error occured during the command line building: " 
+                self._logError( userMsg = "Mobyle Internal Server Error",
+                                logMsg = None #the error log is filled by the rf_log.critical
+                                )
+                #this error is already log in build.log
+                msg = "%s/%s : %s" %( service_name ,
+                                                   self.job.getKey() , 
+                                                   msg , 
+                                                   )
+                
+                if self.job.cfg.debug( service_name ) == 0:
+                    rf_log.critical( msg  , exc_info = True) # send an email
+                else:
+                    rf_log.error( msg , exc_info = True) 
+                 
+                raise MobyleError , "Mobyle Internal Server Error"
+            js_paramfiles = []
+            if paramfiles :
+                for paramfile_name , string_handle  in paramfiles.items():
+                    paramfile_handle = open( os.path.join(  self.job_dir , paramfile_name ) , 'w' )
+                    content = string_handle.getvalue()
+                    paramfile_handle.write( content )
+                    paramfile_handle.close()
+                    js_paramfiles.append( ( os.path.basename( paramfile_name ) , len( content ) ) )
+                self._jobState.setParamfiles( js_paramfiles )
+                self._jobState.commit()
+                
+        elif method == "GET" or method == "POST" or method == "POSTM":
+            raise NotImplementedError ,"cgi wrapping is not yet implemented"
+        elif method == "SOAP" or method == "XML-RPC":
+            raise NotImplementedError ,"cgi wrapping is not yet implemented"
+        else:
+            raise MobyleError, "unknown method of command : "+str( method )
+        
+        try:
+            commandFile = open( os.path.join( self.job_dir , ".command" ), 'w' )
+            
+            # if a module commande fail
+            # the job exit with a return -15 (in mobyle 255 -15 =241)
+            # which is considered by Mobyle as a MobyleError
+            module = self.job.cfg.module( service_name )
+            if module is not None:
+                commandFile.write( "source %s || exit -15 \n" % module[0] )
+                commandFile.write( module[1] % {'PACK_NAME': self._service.getPackageName(),
+                                                'PACK_VERS': self._service.getPackageVersion(),
+                                                'PROG_NAME': self._service.getName(),
+                                                'PROG_VERS': self._service.getVersion(),
+                } )
+                commandFile.write(" || exit -15 \n")
+            commandFile.write( self._commandLine + "\n" )
+            commandFile.close()
+        except IOError , err:
+            msg = "an error occured during the .command file creation: " + str( err )
+            self._logError( userMsg= "Mobyle Internal Server error" ,
+                            logMsg = msg
+                            )
+            raise MobyleError , err
+
+        self._jobState.setCommandLine( self._commandLine )
+        self._jobState.commit()
+
+
+    def run(self):
+        """
+        instanciate  an L{AsynchronRunner} in function of the attribute synchron.
+        """
+        return AsynchronRunner( self._commandLine , self.job_dir , self.job , jobState = self._jobState , xmlEnv = self._xmlEnv )
+
+    def getCmdLine(self):
+        return self._commandLine
+
+
+    def _logError( self , userMsg = None , logMsg = None ):
+        
+        if userMsg :
+            sm = StatusManager()
+            sm.setStatus( self.job_dir , Status( code = 5 , message = userMsg ) )
+
+        if logMsg :
+            rf_log.error( "%s/%s : %s" %( self._service.getName() ,
+                                          self.job.getKey() ,
+                                          logMsg
+                                          )
+                          )
+
+
+
+class AsynchronRunner:
+    """
+    manage the child process creation
+    manage the synchronisation with his child
+    """
+    
+
+    def __init__( self, commandLine , work_dir , job , jobState = None , cfg = None , xmlEnv = None ):
+        """
+        the father process
+        @param commandLine: the command to be executed
+        @type commandLine: String
+        @param work_dir: the absolute path to directory where the job will be executed
+        @type work_dir: String
+        @param service:
+        @type service: A L{Service} instance
+        @param jobState:
+        @type jobState: a L{JobState} instance
+        @type cfg: L{ConfigManager.Config} instance
+        @call: L{CommandRunner.run}
+        """
+        if cfg :
+            self._cfg = cfg
+        else:
+            from Mobyle.ConfigManager import Config
+            self._cfg = Config()
+
+        self._command = commandLine
+        self._xmlEnv = xmlEnv
+        self.work_dir = work_dir
+        self._job = job
+        self._service = self._job.getService()
+        self._child_pid = 0
+        #self.child_done = False
+
+        if jobState is None:
+            self.jobState = JobState( self.work_dir )
+        else:
+            self.jobState = jobState
+        try:
+            self._child_pid = os.fork()
+        except Exception , err:
+            msg = "Can't fork : " + str( err )
+            self._logError( logMsg = msg ,
+                            userMsg = "Mobyle Internal server error"
+                            )
+
+            raise MobyleError , msg
+        
+        
+        if self._child_pid > 0 : ####### FATHER #######
+            pass    
+        elif self._child_pid == 0 : ####### CHILD #######
+            os.setsid()
+            devnull = os.open( "/dev/null" , os.O_RDWR )
+            os.dup2( devnull , sys.stdin.fileno() )
+
+            try:    
+                os.chdir( self.work_dir )
+            except OSError, err:
+                msg = "unable to change directory to:" + str( err )
+                self._logError( logMsg = None ,
+                                userMsg = "Mobyle Internal server error"
+                                )
+                rf_log.critical( msg )
+                raise MobyleError , "Internal server Error"
+
+            serviceName = self._service.getName()
+            
+            try:
+                #logfile = os.open(  os.path.join( self._cfg.log_dir(), 'debug' ) , os.O_CREAT | os.O_WRONLY | os.O_TRUNC )    
+                logfile = os.open( os.path.join( self._cfg.log_dir(), 'child_log' ) , os.O_APPEND | os.O_WRONLY | os.O_CREAT , 0664 )                
+                            
+                os.dup2( logfile , sys.stdout.fileno() )
+                os.dup2( logfile , sys.stderr.fileno() )
+                os.close( logfile )
+
+                
+            except ( IOError , OSError ) , err :
+                rf_log.critical( "error in redirecting stderr or stdout to debug : ", exc_info = True ) 
+                
+                self._logError( logMsg = None ,
+                                userMsg = "Mobyle Internal server error"
+                                )
+                
+                try :
+                    os.dup2( devnull , sys.stdout.fileno() )
+                    os.dup2( devnull , sys.stderr.fileno() )
+                except (IOError , OSError ) , err:
+                    rf_log.critical( "error in redirecting stderr or stdout to /dev/null : " , exc_info = True ) 
+                    
+                
+            childName = os.path.join( self._cfg.mobylehome() ,  
+                                      'Src' ,
+                                      'Mobyle' ,
+                                      'RunnerChild.py'
+                                      )
+
+
+            email = self._job.getEmail()
+            
+            if email is None:
+                email_to = None
+            else:
+                email_to = str( email )
+
+            try:
+                paramsOut = self._service.getAllOutParameter(  )
+                results_mask ={}
+                evaluator = self._service.getEvaluator()
+                stdout = False
+                for paramName in paramsOut :
+                    if self._service.precondHas_proglang( paramName , 'python' ):
+                        preconds = self._service.getPreconds( paramName , proglang='python' )
+                        allPrecondTrue = True
+                        for precond in preconds:
+                            if not evaluator.eval( precond ) :
+                                allPrecondTrue = False
+                                break
+                        if not allPrecondTrue :
+                            continue #next parameter
+                    if self._service.isstdout( paramName ):
+                        stdout = True
+
+                    unixMasks = self._service.getFilenames( paramName , proglang = 'python' )
+                    results_mask [ paramName ] = unixMasks 
+                serviceName = self._service.getName()
+                
+                if not stdout :
+                    results_mask [ 'stdout' ] =  [ serviceName + '.out' ]
+                results_mask [ 'stderr' ] = [ serviceName + '.err']
+            
+            except MobyleError, err:
+                msg = "AsynchronRunner.__init__ : " + str( err )
+                self._logError( logMsg = msg ,
+                                userMsg = "Mobyle Internal server error"
+                                )
+                raise MobyleError , msg
+                
+            forChild = { 'serviceName' : self._service.getName() ,
+                         'email'       : email_to ,
+                         'dirPath'     : self.work_dir,
+                         'commandLine' : self._command ,
+                         'resultsMask' : results_mask ,
+                         'xmlEnv'      : self._xmlEnv ,
+                         'email_notify': self._job.email_notify
+                         }
+            try:
+                fh = open( os.path.join( self.work_dir , ".forChild.dump") ,'w' )
+                cPickle.dump( forChild , fh )
+                fh.close()
+            except IOError , err:
+                msg = "error during dumping forChild.dump of job %s" %self.work_dir
+                self._logError( logMsg = msg ,
+                                userMsg = "Mobyle Internal server error"
+                                )
+            
+            cmd = [ childName ]
+            
+            logging_shutdown() #close all loggers
+            try:
+                os.execv( cmd[0] , cmd )
+            except Exception, err:
+                msg = "AsynchronRunner : CRITICAL : L 420 : %s : %s/%s : __init__: exec child caught an error: %s" %( time.strftime( '%a, %d %b %Y %H:%M:%S' , time.localtime() ) ,
+                                                                                                           self._service.getName() ,
+                                                                                                           self._job.getKey() ,
+                                                                                                           err
+                                                                                                           )
+                try:
+                    mylog = open( os.path.join( self._cfg.log_dir() , 'error_log' ) , 'a' )
+                except:
+                    mylog = open( '/dev/null' )
+                mylog.write( msg +'\n')
+                mylog.close()
+                self._logError( logMsg = None ,
+                                userMsg = "Mobyle Internal server error"
+                                )
+                raise MobyleError, msg
+
+
+    def _logError( self , userMsg = None , logMsg = None ):
+
+        if userMsg :
+            sm = StatusManager()
+            sm.setStatus( self.work_dir , Status( code = 5 , message =  userMsg ) )
+            
+
+        if logMsg :
+            rf_log.error( "%s/%s : %s" %( self._service.getName() ,
+                                          self._job.getKey() ,
+                                          logMsg
+                                          )
+                        )
+    
+
+
diff --git a/Src/Mobyle/SearchIndex.py b/Src/Mobyle/SearchIndex.py
new file mode 100644
index 0000000..f1c4832
--- /dev/null
+++ b/Src/Mobyle/SearchIndex.py
@@ -0,0 +1,137 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+Mobyle.Index
+
+This module manages the list of available programs to:
+ - search them (using search fields)
+ - classify them (building the categories tree)
+ - cache this information (on disk as a JSON file)
+"""
+from Mobyle.Registry import *
+import re
+
+from logging import getLogger
+r_log = getLogger(__name__)
+
+from Mobyle import IndexBase
+
+queries = {
+           'head': '/*/head',
+           'package': 'package',
+           'name': 'name//text()',
+           'title': 'doc/title/text()', 
+           'description': 'doc/description/text/text()',
+           'categories': 'category/text()',
+           'comment': 'doc/comment/text/text()',
+           'authors': 'doc/authors/text()',
+           'references': 'doc/reference/text()',
+           'parameter': './/parameter[name]',
+           'parameter_name': 'name/text()',
+           'parameter_prompt': 'prompt/text()',
+           'parameter_comment': 'comment/text/text()',
+           'parameter_type': 'type',
+           'parameter_biotype': 'biotype/text()',
+           'parameter_datatype': 'datatype',
+           'parameter_class': 'class/text()',
+           'parameter_superclass': 'superclass/text()',
+           'paragraph': '//paragraph',
+           'paragraph_name': 'name/text()',
+           'paragraph_prompt': 'prompt/text()',
+           'paragraph_comment': 'comment/text/text()'
+          }
+
+class SearchIndex(IndexBase.Index):
+
+    indexFileName = 'search.dat'
+
+    def filterRegistry(self, keywordList):
+        keywordList = [re.escape(k) for k in keywordList] # escape special re characters...
+        keywordsRe = re.compile('(%s)' % '|'.join(keywordList), re.I)
+        servicesList = getattr( registry, self.type + 's')[:]
+        for s in servicesList:
+            s.searchMatches = []
+            if self.index.has_key(s.url):
+                for field, value in self.index[s.url].items():
+                    if isinstance(value, basestring):
+                        self._searchFieldString(field, value, keywordsRe, s)
+                    if isinstance(value, list):
+                        for valueItem in value:
+                            self._searchFieldString(field, valueItem, keywordsRe, s)
+            if len(s.searchMatches) == 0:
+                registry.pruneService(s)
+
+    def _searchFieldString(self, fieldName, fieldValue, rx, program):
+        if len(rx.findall(fieldValue))>0:
+            program.searchMatches.append((fieldName,rx.sub('<b>\\1</b>',fieldValue)))
+        
+    @classmethod
+    def getIndexEntry(cls, doc, index):
+        """
+        Return an search index entry value
+        @return: the index entry: value
+        @rtype: object
+        """
+        head = IndexBase._XPathQuery(doc, queries['head'], 'rawResult')[0]
+        fields = {}
+        fields['name'] =         IndexBase._XPathQuery(head, queries['name'])
+        fields['title'] =        IndexBase._XPathQuery(head, queries['title'])
+        fields['description'] =  IndexBase._XPathQuery(head, queries['description'])
+        fields['categories'] =   IndexBase._XPathQuery(head, \
+                                              queries['categories'], \
+                                              'valueList')
+        fields['comment'] =      IndexBase._XPathQuery(head, queries['comment'])
+        fields['authors'] =      IndexBase._XPathQuery(head, queries['authors'])
+        fields['references'] =   IndexBase._XPathQuery(head, queries['references'])
+        package = IndexBase._XPathQuery(head, queries['package'], 'rawResult')
+        if package:
+            package = package[0]
+            fields['package name'] =         IndexBase._XPathQuery(package, queries['name'])
+            fields['package title'] =        IndexBase._XPathQuery(package, queries['title'])
+            fields['package description'] =  IndexBase._XPathQuery(package, queries['description'])
+            fields['package categories'] =   IndexBase._XPathQuery(package, \
+                                                  queries['categories'], \
+                                                  'valueList')
+            fields['package comment'] =      IndexBase._XPathQuery(package, queries['comment'])
+            fields['package authors'] =      IndexBase._XPathQuery(package, queries['authors'])
+            fields['package references'] =   IndexBase._XPathQuery(package, queries['references'])            
+        fields['parameter name'] = []
+        fields['parameter prompt'] = []
+        fields['parameter comment'] = []
+        fields['parameter bioTypes'] = []
+        fields['parameter class'] = []
+        fields['parameter superclass'] = []
+        pars = IndexBase._XPathQuery(doc, queries['parameter'], 'rawResult')
+        for p in pars:
+            fields['parameter name'].append(IndexBase._XPathQuery(p, queries['parameter_name']))
+            fields['parameter prompt'].append(IndexBase._XPathQuery(p, queries['parameter_prompt']))
+            fields['parameter comment'].append(IndexBase._XPathQuery(p, queries['parameter_comment']))
+            parType = IndexBase._XPathQuery(p, \
+                                  queries['parameter_type'], 
+                                  'rawResult')[0]
+            fields['parameter bioTypes'].append(IndexBase._XPathQuery(parType, \
+                                         queries['parameter_biotype']))
+            parDataType = IndexBase._XPathQuery(parType, \
+                                      queries['parameter_datatype'],\
+                                      'rawResult')[0]
+            fields['parameter class'].append(IndexBase._XPathQuery(parDataType, \
+                                      queries['parameter_class']))
+            fields['parameter superclass'].append(IndexBase._XPathQuery(parDataType, \
+                                      queries['parameter_superclass']))
+        fields['paragraphs'] = []
+        pars = IndexBase._XPathQuery(doc, queries['paragraph'], 'rawResult')
+        fields['paragraph name'] = []
+        fields['paragraph prompt'] = []
+        fields['paragraph comment'] = []
+        for p in pars:
+            fields['paragraph name'].append(IndexBase._XPathQuery(p, queries['paragraph_name']))
+            fields['paragraph prompt'].append(IndexBase._XPathQuery(p, queries['paragraph_prompt']))
+            fields['paragraph comment'].append(IndexBase._XPathQuery(p, queries['paragraph_comment']))
+        return fields
diff --git a/Src/Mobyle/Service.py b/Src/Mobyle/Service.py
new file mode 100644
index 0000000..7677b03
--- /dev/null
+++ b/Src/Mobyle/Service.py
@@ -0,0 +1,4548 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+from logging import getLogger
+b_log = getLogger('Mobyle.builder')
+s_log = getLogger( __name__ )
+
+import types
+import os
+import copy
+
+from Mobyle.MobyleError import MobyleError , ServiceError , UnDefAttrError , ParameterError , UserValueError
+from Mobyle.Evaluation  import Evaluation
+from Mobyle.ConfigManager  import Config
+_cfg = Config()
+__extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+
+
+
+
+###############################################################
+#                                                             #
+#                      Class Program                          #
+#                                                             #
+###############################################################
+
+
+class Program(object):
+    """
+    a Program object is the Python representation of the xml program definition
+    and had methods to manage the data
+    """
+    _paramError = " this program doesn't content parameter named : "
+    
+    def __init__(self, evaluator = None , ):
+        """
+        this object is the python representation of program describe in xml
+        @call: indirectly in L{MobyleJob._makeProgram}
+        @call: indirectly in L{RunnerChild} main
+        """
+        self._debug = None #set to cfg.debug( ProgramName ) by setName()
+        
+        if evaluator is None:
+            self._evaluator = Evaluation()
+        else:
+            self._evaluator = evaluator
+            
+        self.cfg = Config()
+        self.header = None
+        self._parameters = Parameters()
+        
+    def _getAllParameter(self):
+        """
+        @return: all  L{Parameter}'s instance in a list
+        @rtype: list of Parameter
+        @call: L{getAllParameterNameByArgpos}
+        @call: L{getCommandParameterName}
+        """
+        return self._parameters.getAllParameter()
+
+    def getAllParameterByArgpos(self):
+        """
+        @return: a list of all  L{parameter's <Parameter>}  sorted by their argpos.
+        @rtype: list of L{parameter's <Parameter>}
+        """
+        paramList = self._getAllParameter()
+        paramList.sort(self.cmpPara)
+        return paramList
+            
+    def getAllParameterNameByArgpos(self):
+        """
+        @return: a list of all  L{parameter's <Parameter>} name sorted by their argpos.
+        @rtype: list of string
+        @call: L{CGIJob.form2Dict} L{MobyleJob.__init__} L{MobyleJob._fillEvaluator} L{MobyleJob._validateParameters}
+        @call: L{getAllHiddenParameter}, L{getAllSimpleParameter}, L{getAllMandatoryParameter}
+        """
+        paramList = self._getAllParameter()
+        paramList.sort(self.cmpPara)
+        return [ p.getName() for p in paramList ] 
+
+    def getUserInputParameterByArgpos( self ):
+        paramList = self._getAllParameter()
+        paramList.sort( self.cmpPara )
+        result = []
+        for param in paramList:
+            if not param.isout() and not param.isstdout() and not param.ishidden():
+                result.append( param.getName() )
+        return result
+
+    def getUserOutputParameters( self ):
+        paramList = self._getAllParameter()
+        result = []
+        for param in paramList:
+            if param.isout() or param.isstdout():
+                result.append( param.getName() )
+        return result
+
+    def getAllParagraphNameByArgpos(self):
+        """
+        @return: a list of all  L{Paragraph}'s name sorted by their argpos.
+        @rtype: list of string
+        """
+        paragList = self._getAllParagraph()
+        paragList.sort(self.cmpPara)
+        result = []
+        for parag in paragList:
+            result.append( parag.getName() )
+        return result
+
+    def cmpPara(self ,para1 , para2 ):
+        """
+        compare two  L{parameter <Parameter>} or  L{paragraph <Paragraph>} according to their argpos.
+        @rtype: int  
+        @return:
+          - negative number if para1 > para2
+          - 0 if para1 and para2 are equal
+          - positive number otherwise
+        @call: L{getAllParameterNameByArgpos}, L{getAllParagraphNameByOrder}
+        """ 
+        argpos1 = para1.getArgpos()
+        argpos2 = para2.getArgpos()    
+        return argpos1 - argpos2
+
+    def getDebug(self):
+        return self.cfg.debug( self.header.getName() )
+    
+    def getAllHiddenParameter(self):
+        """
+        @return: a list of all hidden parameter's name sorted by their argpos.
+        @rtype: list of strings.
+        """
+        result = []
+        for paramName in self.getAllParameterNameByArgpos():
+            if self.ishidden(paramName):
+                result.append( paramName )
+        return result
+    
+
+    def getAllSimpleParameter(self):
+        """
+        @return: A list of all simple parameter's name sorted by their argpos.
+        @rtype: list of strings.
+        """
+        result = []
+        for paramName in self.getAllParameterNameByArgpos():
+            if self.issimple(paramName):
+                result.append( paramName )
+        return result
+
+
+    def getAllMandatoryParameter(self):
+        """
+        @return: A list of all mandatory parameter's name sorted by their argpos.
+        @rtype: list of strings.
+        """
+        result = []
+        for paramName in self.getAllParameterNameByArgpos():
+            if self.ismandatory(paramName):
+                result.append( paramName )
+        return result
+
+
+    def getAllOutParameter(self):
+        """
+        @return: A list of all out parameter's name sorted by their argpos.
+        @rtype: list of strings.
+        """
+        result = []
+        for paramName in self.getAllParameterNameByArgpos():
+            if self.isout( paramName ) or self.isstdout( paramName ):
+                result.append( paramName )
+        return result
+
+   
+    def getAllOutFileParameter(self):
+        """
+        @return: A list of all out parameter
+        @rtype: list of Parameter instances.
+        """
+        result = []
+        for param in self._getAllParameter():
+            if param.isOutfile() :
+                result.append( param )
+        return result
+
+    def getAllInFileParameter(self):
+        """
+        @return: A list of all in parameter.
+        @rtype: list of Parameter instances.
+        """
+        result = []
+        for param in self._getAllParameter():
+            if param.isInfile() :
+                result.append( param )
+        return result
+
+        
+    def getCommandParameterName(self):
+        """
+        @return: The name of the first parameter with attribute iscommand= True.
+         ( it should be have only one parameter with iscommand = True per Program).
+          If doesn't find any parameter with iscommand = True, return None.
+        @rtype: a not empty string or None.
+        @call:  L{CommandBuilder.buildLocalCommand<CommandBuilder>}
+        """
+        for param in self._getAllParameter():
+            if param.iscommand():
+                return param.getName()
+        return None
+
+    def addheader(self , header ):
+        CommandParameterName = self.getCommandParameterName()
+        if CommandParameterName :
+            msg = "try to add a header with a command but \"%s\" parameter is already used as command" % CommandParameterName
+            s_log.error( msg )
+            raise ServiceError , msg
+        self.header = header
+        self.debug = self.cfg.debug( header.getName() ) 
+    
+    def addParameter(self, parameter):
+        """
+        Add a  L{parameter <Parameter>} instance or a sequence of L{parameter <Parameter>} instances at the Program top level.
+        @param parameter: the parameter to add
+        @type parameter: a -L{Parameter} instance
+        """
+        if type(parameter) == type([]) or type(parameter) == type( () ):
+            for param in parameter:
+                self._parameters.addParameter(param)
+                param.setFather(self)
+                param.setEvaluator(self._evaluator)
+        else:
+            self._parameters.addParameter(parameter)
+            parameter.setFather(self)
+            parameter.setEvaluator(self._evaluator)
+            
+    def getParameter(self, paramName):
+        """
+        @return: the  L{Parameter} instance which have the name paramName. if no parmeter found return None
+        @rtype: a  L{Parameter} instance or None
+        @call: by all Sevice methods which are delegated to parameter
+        """
+        return self._parameters.getParameter( paramName )
+       
+    def _getAllParagraph(self):
+        """
+        @return: all  L{Paragraph} instances from all levels
+        @rtype: a list of  L{Paragraph} instances
+        @call: L{getAllParagraphNameByOrder}
+        """
+        return self._parameters.getAllParagraph()
+
+
+    def _getParagraph(self, paragName):
+        """
+        @param paragName: the name of the paragraph
+        @type paragName: String
+        @return: the  L{Paragraph} instance which have the name paragName. If no pragraph with name paragName was found, return None.
+        @rtype: a   L{Paragraph} instance
+        @call: L{getInfo}
+        """
+        return self._parameters.getParagraph( paragName)
+
+
+    def addParagraph(self, paragraph):
+        """
+        Add a  L{Paragraph} or a sequence of Paragraph instance at the Program top level
+        @param paragraph: the paragraph to add
+        @type paragraph:  L{Paragraph} instance.
+        """
+        if type(paragraph) == type([]) or type(paragraph) == type( () ):
+            for parag in paragraph:
+                self._parameters.addParagraph(parag)
+                parag.setFather(self)
+                parag.setEvaluator(self._evaluator)
+        else:
+            self._parameters.addParagraph(paragraph)
+            paragraph.setFather(self)
+            paragraph.setEvaluator( self._evaluator)
+
+
+    def getPath(self, name):
+        """
+        @param name: a name of the  L{Paragraph} or  L{Parameter}
+        @type name: String
+        @return: a string corresponding to the path of the paragraph or parameter name. each name is separated by a /
+        @rtype: string
+        @call: L{Parameters.getPath} , L{Program.getPath}
+        """
+        return self._parameters.getPath(self, name)
+
+    def _getPara(self, name):
+        """
+        @param name: the name of a Parameter or a Paragraph
+        @type name: String
+        @return: an instance of the  L{Parameter} or the  L{Paragraph} which have the name name. this  method is used by methods which could call either with a parameter or a paragraph as argument eg get/setArgpos()
+        @rtype: an object of L{Para} type. Para is an abstract class thus in fact a  L{Parameter} or the  L{Paragraph} instance.
+        @call: by all Program methods delegated to the Para L{Para.getPrompt} , L{Para.promptLangs}, L{Para.promptHas_lang}, L{Para.setPrompt}, L{Para.hasFormat}, L{Para.getFormat}, L{Para.formatHas_proglang}, L{Para.formatProglangs}, L{Para.setFormat}, L{Para.getPrecond}, L{Para.precondHas_proglang}, L{Para.precondProglangs}. 
+        """
+        result = self.getParameter(name)
+        if result :
+            return result
+        else:
+            return self._getParagraph(name)
+
+        
+    def addInfo(self, content, proglang = None , lang = None , href = False):
+        """
+        Set an info on this Program
+        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+        @param content: an info on the Program or a href toward an info
+        @type content: String
+        @param proglang: the programming language of the info code
+        @type proglang: String
+        @param lang: is the symbol of a language in  iso639-1 (ex:english= 'en',french= 'fr') 
+        @type lang: String
+        @param href: True if the content is a href, False otherwise
+        @type href: Boolean
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "an info couldn't be a text and a code or href in the same time"
+        if proglang:
+            self._parameters.addInfo( content , proglang = proglang)
+        elif lang:
+            self._parameters.addInfo( content , lang = lang)
+        elif href:
+            self._parameters.addInfo( content )
+        else :
+            raise ServiceError, "invalid argument for info : " + str(proglang) + str(lang) + str(href)
+        
+        
+
+    def getInfo(self, paragraphName=None, proglang =None ,lang= None, href = False):
+        """
+        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+        @param paragraphName: the name of a paragraph, if paragraphName = None the method return the info of the Program. if paragraphName couldn't be found a ServiceError will be raised
+        @type paragraphName: String
+        @param proglang: The programming language of the info code
+        @type proglang: String
+        @param lang: is the symbol of a language in iso639-1 (ex: 'en', 'fr') for a text info
+        @type lang: String
+        @param href: True if the info is a href, False otherwise
+        @type href: Boolean
+        @return: the info corresponding to the specified lang, proglang or href.
+        @rtype: string.
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "an info couldn't be a text and a code or a href in the same time "
+        if paragraphName:
+            paragraph = self._getParagraph(paragraphName)
+            if paragraph:
+                if proglang:
+                    return paragraph.getInfo( proglang = proglang)
+                elif lang:
+                    return paragraph.getInfo( lang = lang)
+                elif href:
+                    return paragraph.getInfo( )
+                else :
+                    raise ServiceError, "invalid argument for info : " +str(lang)+" , "+str(proglang)+" , "+str(href)
+            else:
+                raise ServiceError, "the paragraph with name "+str(paragraphName)+" doesn't exist"
+        else:
+            if proglang:
+                return self._parameters.getInfo( proglang = proglang)
+            elif lang:
+                return self._parameters.getInfo( lang = lang)
+            elif href:
+                return self._parameters.getInfo( )
+            else :
+                raise ServiceError, "invalid argument for info : " +str(lang)+" , "+str(proglang)+" , "+str(href)
+
+               
+    def infoProglangs(self):
+        """
+        @return: the list of programming languages used by the info codes.
+        @rtype: list of string.
+        """
+        return self._parameters.infoProglangs()
+
+    def infoHas_lang(self, lang='en'):
+        """
+        @param lang: the symbol of a language in iso639-1 (ex: 'en', 'fr') 
+        @type lang: String
+        @return: True if the info has a text written in lang. False otherwise.
+        @rtype: boolean.
+        """
+        return self._parameters.infoHas_lang( lang= lang)
+        
+
+    def infoLangs(self):
+        """
+        @return: the list of languages used by the info texts
+        @rtype: a list of string.
+        """
+        return self._parameters.infoLangs()
+         
+
+    def infoHas_href(self):
+        """
+        @return: True if the info has a 'href', False otherwise.
+        @rtype: boolean.
+        """
+        return self._parameters.infoHas_href() 
+        
+    ##############################################
+    #                                            #
+    #     delegated methods to the header        #
+    #                                            #
+    ##############################################
+    def getUrl(self):
+        """
+        @return: the url of the definition of this Program
+        @rtype: string
+        """
+        return self.header.getUrl()
+    
+    def getPackageTitle(self):
+        """
+        @return: a String representing the Package title
+        @rtype: string
+        """ 
+        return self.header.getPackageTitle()
+    
+    def getTitle(self):
+        """
+        @return: a String representing the  L{Program} title
+        @rtype: string
+        """ 
+        return self.header.getTitle()
+
+    
+    def getPackageName(self):
+        """
+        @return: a String resprenting the name of the Package.
+        @rtype: string
+        """
+        return self.header.getPackageName()
+        
+    def getName(self):
+        """
+        @return: a String resprenting the name of the L{Program}.
+        @rtype: string
+        """
+        return self.header.getName()
+              
+    def getPackageVersion(self):
+        """
+        @return: a String representing the version of the Package
+        @rtype: string
+        """
+        return self.header.getPackageVersion()
+
+    def getVersion(self):
+        """
+        @return: a String representing the version of the program
+        @rtype: string
+        """
+        return self.header.getVersion()
+
+    def getPackageDoclinks(self):
+        """
+        @return: a list of Strings. Each String represent a documentation link
+        """
+        return self.header.getPackageDoclinks()
+
+    def getDoclinks(self):
+        """
+        @return: a list of Strings. Each String represent a documentation link
+        """
+        return self.header.getDoclinks()
+    
+    def getPackageCategories(self):
+        """
+        @return: the list of String, each string representing
+        a category in which the Package is classified.
+        @rtype: a list of String
+        """
+        return self.header.getPackageCategories()
+
+    def getCategories(self):
+        """
+        @return: the list of String, each string representing
+        a category in which the Program is classified.
+        @rtype: a list of String
+        """
+        return self.header.getCategories()    
+    
+#    def getHelp(self, lang = None, proglang =None , href = False):
+#        """
+#        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+#        @param proglang: The programming language of the help code
+#        @type proglang: String
+#        @param lang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text 
+#        @type lang: String
+#        @param href: True if the content is a href false otherwise
+#        @type href: boolean
+#        @return: the help corresponding to the specified lang or proglang or href
+#        @rtype: string.
+#        """
+#        self.header.getHelp(lang = lang, proglang =proglang , href = href )
+#
+#               
+#    def helpHas_proglang(self, proglang = 'python'):
+#        """
+#        @param proglang: the programming language that encode the help
+#        @type proglang: String
+#        @return: True if a code written in proglang is used for the help,
+#        False otherwise.
+#        @rtype: boolean.
+#        """
+#        return self.header.helpHas_proglang( proglang = proglang)
+#
+#    def helpProglangs(self):
+#        """
+#        @return: the list of proglangs used for the code help.
+#        @rtype: list of string.
+#        """
+#        return self.header.helpProglangs()
+#
+#    def helpHas_lang(self, lang='en'):
+#        """
+#        @return: True if the help has a text written in lang. False otherwise
+#        @rtype: boolean.
+#        """
+#        return self.header.helpHas_lang( lang= lang)
+#        
+#
+#    def helpLangs(self):
+#        """
+#        @return: the list of langs used for the help texts.
+#        @rtype: a list of string
+#        """
+#        return self.header.helpLangs()
+#         
+#
+#    def helpHas_href(self):
+#        """
+#        @return: True if the help has a 'href', False otherwise.
+#        @rtype: boolean
+#        """
+#        return self.header.helpHas_href()
+#
+#        
+#    def helpHrefs(self):
+#        """
+#        @return: The list of href for the help.
+#        @rtype: list of string 
+#        """
+#        return self.header.helpHrefs()
+
+        
+    def getCommand(self):
+        """
+        @return: (name, type , path)
+          1. for local program:
+              - name is the name of the program
+              - type is 'local' (by default)
+              - path is the path where is the program (by default the $PATH variable) 
+          2. for cgi:
+              - name is the name of the cgi
+              - type is the method to call the cgi ( GET | POST | POSTM )
+              - path is the url where is the script 'http://www.myDomain.org'
+          3. for web Program:
+              - name is the name of the method
+              - type is the protocol to call the ws (soap | xml-rpc | ... )
+              - path is the url of the wsdl
+        @rtype: tuple of 3 string (name, type, path).
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.header.getCommand()
+
+    def getEnv(self, varName):
+        """
+        @param varName: the name of the environment variable.
+        @type varName: String
+        @return: the value of the environment variable,  if there is no environment variable return None.
+        @rtype: string or None
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.header.getEnv(varName ) 
+
+
+    def envHas_var(self, var):
+        """
+        @param var: the name of the environment variable
+        @type var: String
+        @return: True the variable var is specified, False otherwise.
+        @rtype: boolean.
+        """
+        return self.header.envHas_var(var)
+    
+    def envVars(self):
+        """
+        @return: the names of all environment variables.
+        @rtype: string
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.header.envVars()
+    
+            
+
+
+
+    ##############################################
+    #                                            #
+    #     delegated methods to the parameter     #
+    #                                            #
+    ##############################################
+
+    
+    def setValue(self, paramName, value):
+        """
+        Set the current value for this parameter.        
+        @param paramName: a parameter name
+        @type paramName: String
+        @param value: the value to set for this parameter
+        @type value: any
+        @raise  ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: L{MobyleJob}._fillEvaluator()
+        """
+        param = self.getParameter( paramName )
+        if param :
+            param.setValue( value )
+        else:
+            raise ServiceError ,self._paramError + str( paramName )
+
+        
+    def getValue(self, paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: The current value of the parameter. if the value is not defined return None
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}, L{doCtrls}
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getValue()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def setValueAsVdef( self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: 
+        """
+        param = self.getParameter( paramName )
+        if param :
+            param.setValueAsVdef()
+        else:
+            raise ServiceError ,self._paramError + str( paramName )
+
+    def reset( self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: 
+        """
+        param = self.getParameter( paramName )
+        if param :
+            return param.reset()
+        else:
+            raise ServiceError ,self._paramError + str( paramName )
+
+    def resetAllParam( self ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: 
+        """
+        params ={}
+        for param in self._getAllParameter():
+            params[ param.getName() ] = param.reset()
+        return params
+
+
+    def validate(self, paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if the value is valid. otherwise a UserValueError is raised.
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @raise UserValueError: a  L{UserValueError} is raised if the value can't be validate.
+        @call: L{MobyleJob}
+        """
+        param = self.getParameter( paramName)
+        if param :
+            param.validate()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def convert(self, paramName , value , acceptedMobyleType ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @param value: the value to convert
+        @type value: any
+        @return: the value cast in the right type
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @raise :
+        @call: L{MobyleJob , CommnadBuilder}
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.convert( value , acceptedMobyleType )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+        
+    def getType(self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: a mobyle Type of this parameter.
+        @rtype: L{MobyleType} instance
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.  
+        """        
+        param = self.getParameter( paramName)
+        if param :
+            return param.getType()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+ 
+    
+    def getDataType(self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: the data type of this parameter.
+        @rtype: ( string class , string superclass | None )
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.  
+        """        
+        param = self.getParameter( paramName)
+        if param :
+            return param.getDataType()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+    
+    def getBioTypes( self , paramName):
+        param = self.getParameter( paramName)
+        if param :
+            return param.getBioTypes()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+  
+    def getAcceptedDataFormats( self , paramName):
+        param = self.getParameter( paramName)
+        if param :
+            return param.getAcceptedDataFormats()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+    
+    def forceReformating( self ,  paramName):
+        param = self.getParameter( paramName)
+        if param :
+            return param.forceReformating()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+    
+    def getDataFormat( self , paramName):
+        param = self.getParameter( paramName)
+        if param :
+            return param.getDataFormat()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def hasVdef(self,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if the parameter has a defined vdef, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            if param.vdefKeys():
+                return True
+            else:
+                return False
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+        
+    def getVdef( self , paramName ):
+        """
+        If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: the default value for the parameter.it could be a code or a list of value
+        @rtype: String or list (it's depends of the parameter type).
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getVdef()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def ismandatory(self, paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter must be specify by the user, False otherwise.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: by  L{Job.__init__()}
+        """
+        param = self.getParameter( paramName )
+        if param :
+            return param.ismandatory()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+                                                                      
+
+    def ishidden(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter is hidden in html interface, False otherwise.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.ishidden()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    
+    def iscommand(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter is the command, False otherwise.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @call: by L{CommandBuilder.buildLocalCommand()<CommandBuilder>}
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.iscommand()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+                
+    
+    def issimple(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter appear in the simple interface,
+        False otherwise
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.issimple()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def isout(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter is an output of the program, False otherwise.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.isout() 
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def isstdout( self ,paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter is the standard output of the program, False otherwise.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.isstdout()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def isInfile(self , paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if this parameter should be transform in file (the parameter type is a Sequence or Text and isout is false).
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.isInfile()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+
+    def isOutfile(self , paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if the value of this parameter is a file(s) name (the parameter type is a Sequence or Text and isout is True).
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.isOutfile()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def ismaininput(self , paramName ):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: True if the value of this parameter is a main input data.
+        @rtype: boolean
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.ismaininput()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+         
+    
+    def getMainInputs( self ): 
+        """
+        @return: the list of main input parameter names
+        @rtype: list of strings
+        """
+        params = self._getAllParameter()
+        mainInputs = [ param.getName() for param in params ]
+        return mainInputs
+  
+
+    def getFormfield(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: how the parameter will appear in the interface. ex in web interface formfield could be : checkbox, select ...
+        @rtype: string
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        param =self.getParameter( paramName)
+        if param :
+            return param.getFormfield()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+        
+    
+    def getBioMoby(self ,paramName):
+        """
+        @param paramName: a parameter name
+        @type paramName: String
+        @return: the name of the corresponding object in the BioMoby-S ontology.
+        @raise ServiceError: If the paramName doesn't match with any parameter
+        name a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getBioMoby()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getPrompt(self , name, lang= None ):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param lang: the language of the prompt
+        @type lang: should be String according to MNtoken iso639-1 (english='en' , french= 'fr ...)
+        @return: the Prompt of the paragraph or the parameter. if there is no prompt for this parameter return None.
+        @rtype: string or None
+        @call: L{MobyleError.UserValueError}
+        @raise ServiceError: If the paramName doesn't match with any parameter  name a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.getPrompt( lang )
+        else :
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: " + str(name)
+
+
+    def promptLangs(self, name):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @return: the list of language in which the prompt is written.
+        @rtype: list of strings
+        @call: L{Job}
+        @call: L{MobyleError.UserValueError<UserValueError>}
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.promptLangs()
+        else :
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: " + str(name)
+
+
+    def promptHas_lang(self, name, lang):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param lang: the language in which is written the prompt
+        @type lang: String
+        @return: True if the prompt is written in lang, False otherwise.
+        @rtype: boolean.
+        @call: L{Job}
+        @call: L{MobyleError.UserValueError<UserValueError>}
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.promptHas_lang( lang )
+        else :
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: " + str(name)
+        
+        
+    def setPrompt(self, name, prompt , lang='en'):
+        """
+        set the Prompt for the paragraph or the parameter
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param prompt: the paragraph or parameter prompt 
+        @type prompt: String
+        @param lang: the language of the prompt
+        @type lang: should be String according to MNtoken iso639-1 (english='en' ,french= 'fr ...).
+        @raise ServiceError: If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        """
+        ## n'est pas appele le parser appel directement les fonctions du paragraph ou du parameter
+        para =self._getPara(name)
+        if para :
+            para.setPrompt( prompt, lang)
+        else:
+            raise ServiceError , "this program does not containt neither parameter nor paragraph named: " + str(name)
+
+
+
+    def hasFormat(self, paramName):
+        """
+        @return: True if the  L{Parameter} has a format, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para =self._getPara(paramName)
+        if para :
+            para.hasFormat( )
+        else:
+            raise ServiceError , "this program does not containt neither parameter nor paragraph named: " + str(paramName)
+
+
+    def getFormat(self ,name, proglang):
+        """
+        If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param proglang: the programming language in which the code is written
+        @type proglang: string
+        @return: the format
+        @rtype: string
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.getFormat(proglang)
+        else:
+            return None
+
+    def formatHas_proglang(self, name, proglang='python'):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param proglang: the name of a programming language.
+        @type proglang: string.
+        @return: True if a format written in proglang exist, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.formatHas_proglang( proglang )
+        else :
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: " + str(name)
+       
+
+
+    def formatProglangs(self, name):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @return: A list containing the proglang used to encode the format.
+        @rtype: list of strings.
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.formatProglangs()
+        else :
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: " + str(name)
+       
+
+    def setFormat(self, name, format , proglang="python"):
+        """
+        set the Format for the paragraph or the parameter according to the name 
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param format: the paragraph or parameter format 
+        @type format: String
+        @param proglang:
+        @type proglang: String
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para =self._getPara(name)
+        if para :
+            para.setFormat( format, proglang)
+        else:
+            raise ServiceError , "this program does not containt neither parameter nor paragraph named: "+str(name)
+
+  
+    def getPreconds(self , name, proglang='python'):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param proglang:
+        @type proglang: String
+        @return: a list of string representing the preconds of the parameter or paragraph and 
+            all preconds of parents paragraph in reverse order.
+            if no precond could be found return []
+        @rtype: list of strings .
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @call: by L{Mobyle.CommandBuilder.buildLocalCommand}
+        """
+        para = self._getPara(name)
+        if para :
+            return para.getPreconds(proglang)
+        else:
+            return ServiceError , "this program does not containt neither parameter nor paragraph named: "+str(name)
+
+
+    def precondHas_proglang(self, name, proglang):
+        """
+        @param name: a parameter or paragraph name
+        @type name: String
+        @param proglang: the programming language
+        @type proglang: String
+        @return: True if the precond is encoded in this programming language, False othewise
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.precondHas_proglang(proglang)
+        else:
+            return ServiceError , "this program does not containt neither parameter nor paragraph named: "+str(name)
+        
+        
+
+    def precondProglangs(self , name):
+        """
+        If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.        
+        @param name: a parameter or paragraph name
+        @type name: String
+        @return: a list of programming laguage in which the precond is encoded.
+        @rtype: list of strings.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para :
+            return para.precondProglangs()
+        else:
+            return ServiceError , "this program does not containt neither parameter nor paragraph named: "+str(name)
+
+ 
+
+    def getVlist(self ,paramName,label):
+        """
+        @param paramName: the name of the parameter 
+        @type paramName: A String
+        @param label: a label of the vlist
+        @type label: a String
+        @return: the String representing value associated to this label.
+        @rtype: string.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getVlist(label)
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def vlistLabels(self,paramName):
+        """
+        @param paramName: the name of the parameter 
+        @type paramName: String
+        @return: a list of String representing all the labels .
+        @rtype: a list of strings.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.        
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.vlistLabels()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def vlistHas_label(self, paramName,label):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String
+        @return: True if the parameter with name paramName has a label == label
+        False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.vlistHas_label(label)
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def hasVlist(self, paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String
+        @return: Return True if the parameter has a vlist, False othewise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.        
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.hasVlist()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def hasFlist(self, paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String
+        @return: True if the Parameter has a flist, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.        
+        """       
+        param = self.getParameter( paramName)
+        if param :
+            return param.hasFlist()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+        
+    def flistValues(self, paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String.
+        @return: a list of all values of the vlist
+        @rtype: list of string.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.        
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.flistValues()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+
+    def flistHas_value(self,paramName, value):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String
+        @param value: the value to link the code to the label vlist
+        @type value: integer
+        @return: True if the the flist has a value == value, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @raise UnDefAttrError: if the parameter hasn't flist a UnDefAttrError is raised
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.flistHas_value( value )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+                   
+
+    def flistProglangs(self,paramName, value):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String        
+        @param value:
+        @type value:
+        @return: the list of proglang available for a given value.
+        @rtype: a list of strings.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @raise UnDefAttrError: if the parameter hasn't flist a L{UnDefAttrError} is raised
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.flistProglangs( value )
+        else:
+            raise ServiceError, self._paramError + str( paramName )
+
+
+
+    def flistHas_proglang(self,paramName , value , proglang):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String        
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: Boolean, True if the flist has the value and a code written in proglang associated with, False otherwise.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @raise UnDefAttrError: if the parameter hasn't flist a UnDefAttrError is rais
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.flistHas_proglang( value , proglang )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+        
+    def getFlistCode(self ,paramName, value, proglang):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: a String
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: the code associated with the value and written in proglang. if there isn't this value or this proglang an Error is raised.
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        @raise  L{ ServiceError}: if there isn't this value or this proglang an  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getFlistCode(value, proglang)
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+
+    def getArgpos(self ,name):
+        """
+        If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        @param name: a parameter or paragraph name
+        @type name: String
+        @return: an int representing the argpos of a parameter or a paragraph
+          if argpos isn't defined, return the argpos of the upper paragraph and so on.
+          if no argpos could be found return 1
+        @rtype: integer.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para:
+            return para.getArgpos()
+        else:
+            raise ServiceError , self._paramError + str( name )
+        
+    def setArgpos(self, name,value):
+        """
+        set the argpos to value for the paragraph or the parameter
+
+        @param name: A parameter or paragraph name.
+        @type name: String.
+        @param value: The argpos.
+        @type value: Integer.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        para = self._getPara(name)
+        if para:
+            para.setArgpos(value)
+        else:
+            raise ServiceError, "this program does not containt neither parameter nor paragraph named: "+str(name)
+
+    def has_ctrl(self,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String         
+        @return: True if the parameter has  L{Ctrl}, False otherwise.
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.has_ctrl()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+        
+
+    def getCtrls(self ,paramName):
+        """
+        If the paramName doesn't match with any parameter name a  L{ServiceError} is raised.
+        @param paramName: the name of a parameter
+        @type paramName: String
+        @return: a list of  L{Ctrl} instances for the parameter.
+        @rtype: [ L{Ctrl} instances,...]
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getCtrls()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+    def doCtrls(self , paramName):
+        """
+        do the controls specific to the parameter paramName
+        @return: if the control are Ok return True other wise a UserValueError is raised.
+        @rtype ???: a determiner doit retourner false ou lever une erreur ???? 
+        @raise ServiceError: If the name neither match with a parameter name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.doCtrls()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+       
+    def hasParamfile(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName:  String
+        @return: True if the parameter should be specified in a file
+        instead the command line, False otherwise
+        @rtype: boolean.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.hasParamfile()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getParamfile(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName:  String
+        @return: the name of the parameter file. if the parameter must be
+        specified in a file instead on the command line.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @raise UnDefAttrErrorError: If the parameter haven't a paramfile an  L{UnDefAttrErrorError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getParamfile()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getAllFileNames(self, proglang = 'python'):
+        """
+        @param proglang: the programming language used to defined the filenames
+        @type proglang: string
+        @return: The unix mask ( *.dnd ) which permit to retrieve the results files.
+        @rtype: list of strings. If there isn't any fileName return an empty list
+        """
+        outParameters = self.getAllOutParameter()
+        result=[]
+        for parameter in outParameters:
+            try:
+                filenames = self.getFilenames(parameter, proglang = 'python')
+                for  filename in filenames:
+                    if filename  not in result:
+                        result.append( filename )
+            except UnDefAttrError:
+                continue
+        return result
+
+
+    
+    def getFilenames(self ,paramName, proglang = 'python'):
+        """
+        @param paramName: the name of the parameter
+        @type paraName: String
+        @param proglang: the programming language used to defined the filenames
+        @type proglang: string
+        @return: The unix mask ( *.dnd ) which permit to retrieve the results files.
+        @rtype: string.
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        @call: RunnerFather to fill the 4Child.dump structure 
+        @call: Core in validate method of all "file" parameter
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getFilenames( proglang )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def hasScale( self , paramName , proglang = 'python' ):
+        """
+        @return: True if the param has a scale with code in proglang or with value, False otherwise. 
+        @rtype: boolean
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.hasScale( proglang= proglang )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+        
+
+
+    def isInScale( self , paramName , proglang = 'python'):
+        """
+        @return: True if the value is in range(min, max), false otherwise
+        @rtype: boolean
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.isInScale( proglang= proglang )
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+        
+    def getScale(self , paramName , proglang= None):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String
+        @param proglang: the programming language if the scale is defined by codes. if proglang = None, it mean that the scale is defined by values .
+        @type proglang: String
+        @return: a tuple (min,max,inc)
+          -min is either a value if proglang =None or a code if proglang is specified
+          -max is either a value if proglang =None or a code if proglang is specified
+          -inc is a value
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getScale(proglang= proglang)
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getSeparator(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String
+        @return: the string used to separate the differents values of a mutipleChoice vlist.
+        @rtype: string
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getSeparator()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getWidth(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String
+        @return: the width of the widget.
+        @rtype: integer
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getWidth()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+    def getHeight(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String
+        @return: The height of the widget.
+        @rtype: integer
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getHeight()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+        
+    def getExemple(self ,paramName):
+        """
+        @param paramName: the name of the parameter
+        @type paramName: String
+        @return: A typical value for this parameter
+        @rtype: string
+        @raise ServiceError: If the name neither match with a parameter
+        name nor a paragraph name, a  L{ServiceError} is raised.
+        """
+        param = self.getParameter( paramName)
+        if param :
+            return param.getExemple()
+        else:
+            raise ServiceError , self._paramError + str( paramName )
+
+
+
+    def getEvaluator(self):
+        """
+        @return: a reference toward the  L{Evaluation} instance link to this program.
+        @rtype:  L{Evaluation object}
+        """
+        return self._evaluator
+
+
+    def setEvaluator(self, evaluator):
+        """
+        """
+        self._evaluator = evaluator
+        paras = self._parameters.getAllParagraph()
+        paras += self._parameters.getAllParameter()
+        for para in paras:
+            para.setEvaluator( evaluator)
+            
+    def isProgram(self):
+        """
+        @return: True
+        """
+        return True
+
+    def getFather(self):
+        """
+        @return: None. A program have no father
+        it's used as stopping condition when a child want to retrieve the program
+        """
+        return None    
+
+
+    
+#############################################################
+#                                                           #
+#                Class Parameters                           #
+#                                                           #
+#############################################################
+
+class Parameters(object):
+    """the parameters objects are mainly used to managed the
+    parameter and paragraph"""
+    
+
+    def __init__(self):
+        self._paragraphList= []
+        self._parameterList= []
+        self._info = Text()
+        
+    def addParagraph(self, paragraph):
+        """
+        add a paragraph instance into this parameters
+        @param paragraph: the pargraph to add
+        @type paragraph: a Paragraph object
+        """
+        self._paragraphList.append( paragraph )
+
+    def addParameter(self, parameter):
+        """
+        add a parameter instance into this parameters
+        @param parameter: the parameter to add
+        @type parameter: a Parameter object
+        """
+        self._parameterList.append( parameter )
+
+
+
+    def getParagraph(self, paragraphName):
+        """
+        @param paragraphName: the name of a paragraph to retrieve
+        @type paragraphName: String
+        @return: the instance of the  L{Paragraph} which has the name paragraphName. if no pragraph is found, return None.
+        @rtype:  L{Paragraph} object or None
+        """
+        result = None
+        for paragraph in self._paragraphList:
+            if paragraph.getName() == paragraphName:
+                return paragraph
+            else:
+                result = paragraph.getParagraph(paragraphName)
+                if result:
+                    return result
+        return None
+
+
+
+    def getParameter(self, parameterName):
+        """
+        @param parameterName: the name of a parameter to retrieve
+        @type parameterName: String
+        @return: the instance of the  L{Parameter} which has the name parameterName. if no parameter is found, return None.
+        @rtype:  L{Parameter} object or None
+        """
+        for parameter in self._parameterList:
+            if parameter.getName() == parameterName :
+                return parameter
+        result = None
+        for paragraph in self._paragraphList:
+            result = paragraph.getParameter(parameterName)
+            if result:
+                return result
+        return None
+
+    def getAllParagraph(self):
+        """
+        @return: all paragraphs instances in a list.
+        @rtype: list of L{paragraphs <Paragraph>} object.
+        """
+        allParagraph = self._paragraphList[:]
+        for paragraph in self._paragraphList:
+            allParagraph += paragraph.getAllParagraph()
+        return allParagraph
+
+    
+    def getAllParameter(self):
+        """
+        @return: all L{parameter <Parameter>} instances in a flat list.
+        @rtype: list of parameter object.
+        """
+        allParameter = self._parameterList[:]
+        for paragraph in self._paragraphList:
+            allParameter += paragraph.getAllParameter()
+        return allParameter
+        
+
+    #getPath ne devrait pas etre appelee directement mais par
+    #l'intermedaire de getPath du program ou du paragraph
+
+    def getPath(self,parent, name):
+        """
+        @param parent: the parent of the paragraph or the program
+        @type parent: a  L{Paragraph} instance or a  L{Program} instance
+        @param name: the name of the paragraph or parameter
+        @type name: String
+        @return:  the path each name is separated by '/'
+        @rtype: string
+        """
+        for para in self._thisLevel():
+            if para.getName() == name :
+                return parent.getName()+"/"+ name
+        result = None
+        for paragraph in self._paragraphList:
+            result = paragraph.getPath(name)
+            if result:
+                return parent.getName()+"/"+ result
+        return None
+
+
+
+
+    def addInfo(self, content, proglang = None , lang = None , href = False):
+        """
+        Set an help on this program
+        @param content: an help on the program or a href toward an help
+        @type content: String
+        @param proglang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text or 'href' for the link
+        @type proglang: String
+        @param href: True if the content is a href false otherwise
+        @type href: boolean
+        @raise ServiceError: if more than one argument among proglang, lang, href is specified a L{ServiceError} is raised
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "an info couldn't be a text and a code or href at the sametime"
+        if proglang:
+            self._info.addCode( content , proglang= proglang)
+        elif lang:
+            self._info.addText( content , lang = lang)
+        elif href:
+            self._info.addHref( content)
+        else :
+            raise ServiceError, "invalid argument for info : " + proglang + lang + href
+        
+
+        
+    def getInfo(self, proglang =None, lang = None , href = False):
+        """
+        @param lang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text or 'href' for the link
+        @type lang: String
+        @param proglang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text or 'href' for the link
+        @type proglang: String
+        @param href: True if the content is a href false otherwise
+        @type href: Boolean
+        @return: the info corresponding to the specified lang or proglang or href.
+        @rtype: string
+        @raise ServiceError: if more than one argument among proglang, lang, href is be specified a L{ServiceError} is raised
+        @raise MobyleError: a  L{MobyleError} is propagated  if there isn't any code written in proglang or text in lang or if href is true and it's not a href
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "the info couldn't be a text and a code or a href at the same time"
+        if proglang:
+            return self._info.getCode( proglang)
+        elif lang:
+            return self._info.getText( lang)
+        elif href:
+            return self._info.hrefs( )
+        else :
+            raise ServiceError, "invalid argument for info : " +str(lang)+" , "+str(proglang)+" , "+str(href)
+
+               
+    def infoHas_proglang(self, proglang = 'python'):
+        """
+        @return: True if the info is encoded in the programming language proglang, False otherwise.
+        @rtype: boolean.
+        """
+        return self._info.has_code( proglang)
+
+    def infoProglangs(self):
+        """
+        @return: the list of programming languages used for the info codes
+        @rtype: list of string.
+        """
+        return self._info.proglangs()
+
+    def infoHas_lang(self, lang='en'):
+        """
+        @return: True if the info has a text written in lang. False otherwise.
+        @rtype: boolean.
+        """
+        return self._info.has_lang(lang)
+        
+
+    def infoLangs(self):
+        """
+        @return: the list of langs used for the info texts.
+        @rtype: list of strings
+        """
+        return self._info.langs()
+         
+
+    def infoHas_href(self):
+        """
+        @return: True if the info has a 'href', False otherwise.
+        @rtype: boolean
+        """
+        return self._info.has_href() 
+    
+
+    def _thisLevel(self):
+        """
+        @return: all paragraph and parameter of this level in a list
+        @rtype: list of L{Para} instances
+        """
+        return self._parameterList[:]+ self._paragraphList[:]
+        
+       
+#    def isProgram(self):
+#        return False
+        
+############################################################
+#                                                          #
+#                  Class header                            #
+#                                                          #
+############################################################
+class Package(object):
+    
+    def __init__(self):
+        self.name = None
+        self.version = None
+        self.doclinks = []
+        self.categories = []
+    
+    def setTitle(self, value):
+        """
+        set the title of the Program
+        @param value: the Program title
+        @type value: String
+        """
+        try:
+            value + "a"
+            self.title = value
+        except TypeError:
+            raise ServiceError , "the title must be a String"
+        
+    def setName(self, value):
+        """
+        set the name of the L{Program} to value
+        @param value: the L{Program} name
+        @type value: String
+        """
+        try:
+            value + "a"
+            self.name = value
+        except TypeError:
+            raise ServiceError , "the name must be a String"
+        
+                
+    def setVersion(self, value):
+        """
+        set the version of the program
+        @param value: the version of the program
+        @type value: String
+        """
+        self.version = value
+
+    
+    def addDoclink(self, values):
+        """
+        Add a link toward a documentation
+        @param values:
+        @type values: a list or a tuple of Strings
+        """
+        if type(values) == type([]) or type(values) == type( () ):
+            self.doclinks += values
+        else:
+            self.doclinks.append( values )
+                        
+ 
+    def addCategories(self, values):
+        """
+        add a category or sequence of categories in categories
+        @param values: a category or a sequence of categories.
+        @type values: String or Strings sequence.
+        """
+        #if values is a list or a tuple of categories
+        if type(values) == type([]) or type(values) == type( () ):
+            self.categories += values
+        else:
+            self.categories.append(values)
+            
+            
+                
+class ProgramHeader(Package):
+    
+    def __init__(self):
+        super(ProgramHeader, self).__init__()
+        self.command    = ('' , '' , '') #will be a tuple like (name, type, path)
+        self.env        = {}
+        
+        
+    def setUrl(self , url ):
+        """
+        @param url: the url of this Program definition
+        @type url: string
+        """
+        self.url = url
+        
+    def setCommand(self, name, type='local', path=None):
+        """
+        set the name, the  type and the path of the command
+        @param name:
+            1. for local program: is the name of the program
+            2. for cgi: is the name of the cgi
+            3. for web Program: is the name of the method
+        @type name: String
+        @param type:
+            1. for local program: is 'local' (by default)
+            2. for cgi: is the method to call the cgi ( GET | POST | POSTM )
+            3. for web Program: is the protocol to call the ws (soap | xml-rpc | ... )
+        @type type: String
+        @param path:
+            1. for local program: is the path where is the program (by default the $PATH variable)
+            2. for cgi: is the url where is the script 'http://www.myDomain.org'
+            3. for web Program: is the url of the wsdl
+        @type path: String
+        """
+        self.command = ( name, type, path )
+
+        
+
+    def addEnv(self, var, value):
+        """
+        add an variable environment need to run the programm
+        @param var: the name of the variable
+        @type var: String
+        @param value: the value of the environment variable
+        @type value: String
+        """
+        self.env[ var ] = value
+
+
+    def getEnv(self, varName):
+        """
+        @param varName: the name of the environment variable.
+        @type varName: String
+        @return: the value of the environment variable,  if there is no environment variable return None.
+        @rtype: string or None
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        if self.env:
+            try:
+                return self.env[ varName ]
+            except KeyError:
+                return None
+        else:
+            return None
+
+
+    def envHas_var(self, var):
+        """
+        @param var: the name of the environment variable
+        @type var: String
+        @return: True the variable var is specified, False otherwise.
+        @rtype: boolean.
+        """
+        return var in self.env
+    
+    
+    def envVars(self):
+        """
+        @return: the names of all environment variables.
+        @rtype: string
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.env.keys()
+
+
+
+class Header(object):
+    def __init__(self , package , program_header ):
+        self.package_header = package
+        self.prog_header = program_header
+        
+    def setUrl(self, serviceUrl):
+        """
+        to keep backward compliance 
+        """
+        self.prog_header.setUrl(serviceUrl)
+        
+    def getUrl(self):
+        """
+        @return: the url of the definition of this Program
+        @rtype: string
+        """
+        return self.prog_header.url
+
+        
+    def getPackageTitle(self):
+        """
+        @return: a String representing the Package title
+        @rtype: string
+        """ 
+        try:
+            return self.package_header.title
+        except AttributeError:
+            return self.prog_header.title
+    
+    def getTitle(self):
+        """
+        @return: a String representing the  L{Program} title
+        @rtype: string
+        """ 
+        return self.prog_header.title
+
+    
+    def getPackageName(self):
+        """
+        @return: a String resprenting the name of the package.
+        @rtype: string
+        """
+        try:
+            return self.package_header.name
+        except AttributeError:
+            return self.prog_header.name
+        
+    def getName(self):
+        """
+        @return: a String resprenting the name of the L{Program}.
+        @rtype: string
+        """
+        return self.prog_header.name
+        
+             
+    def getPackageVersion(self):
+        """
+        @return: a String representing the version of the Package
+        @rtype: string
+        """
+        try:
+            return self.package_header.version
+        except AttributeError:
+            return self.prog_header.version
+    
+    def getVersion(self):
+        """
+        @return: a String representing the version of the program
+        @rtype: string
+        """
+        try:
+            return self.prog_header.version
+        except AttributeError:
+            return self.package_header.version
+        
+    def getPackageDoclinks(self):
+        """
+        @return: a list of Strings. Each String represent a documentation link
+        """
+        try:
+            return self.package_header.doclinks
+        except AttributeError:
+            return self.prog_header.doclinks
+
+    def getDoclinks(self):
+        """
+        @return: a list of Strings. Each String represent a documentation link
+        """
+        return self.prog_header.doclinks
+
+
+    def getPackageCategories(self):
+        """
+        @return: the list of String, each string representing
+        a category in which the Package is classified.
+        @rtype: a list of String
+        """
+        try:
+            return self.package_header.doclinks
+        except AttributeError:
+            return self.prog_header.categories
+            
+    def getCategories(self):
+        """
+        @return: the list of String, each string representing
+        a category in which the Program is classified.
+        @rtype: a list of String
+        """
+        return self.prog_header.categories
+
+
+    def getCommand(self):
+        """
+        @return: (name, type , path)
+          1. for local program:
+              - name is the name of the program
+              - type is 'local' (by default)
+              - path is the path where is the program (by default the $PATH variable) 
+          2. for cgi:
+              - name is the name of the cgi
+              - type is the method to call the cgi ( GET | POST | POSTM )
+              - path is the url where is the script 'http://www.myDomain.org'
+          3. for web Program:
+              - name is the name of the method
+              - type is the protocol to call the ws (soap | xml-rpc | ... )
+              - path is the url of the wsdl
+        @rtype: tuple of 3 string (name, type, path).
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.prog_header.command
+
+
+
+    def getEnv(self, varName):
+        """
+        @param varName: the name of the environment variable.
+        @type varName: String
+        @return: the value of the environment variable,  if there is no environment variable return None.
+        @rtype: string or None
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.prog_header.getEnv(varName)
+
+
+    def envHas_var(self, var):
+        """
+        @param var: the name of the environment variable
+        @type var: String
+        @return: True the variable var is specified, False otherwise.
+        @rtype: boolean.
+        """
+        return self.prog_header.envHas_var(var)
+    
+    
+    def envVars(self):
+        """
+        @return: the names of all environment variables.
+        @rtype: string
+        @call: L{CommandBuilder.BuildLocalCommand<CommandBuilder>}
+        """
+        return self.prog_header.envVars()    
+
+
+
+#    def addHelp(self, content, proglang = None , lang = None , href = False):
+#        """
+#        Set an help on this Program. Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+#        @param content: an help on the Program or a href toward an help
+#        @type content: String
+#        @param proglang: The programming language of the help code
+#        @type proglang: String
+#        @param lang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text 
+#        @type lang: String
+#        @param href: True if the content is a href false otherwise
+#        @type href: boolean
+#        """
+#
+#        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+#            raise ServiceError, "an help couldn't be a text and a code or href at the same time"
+#        if proglang:
+#            self._help.addCode( content , proglang)
+#        elif lang:
+#            self._help.addText( content , lang)
+#        elif href:
+#            self._help.addHref( content)
+#        else :
+#            raise ServiceError, "invalid argument for help : " + proglang + lang + href
+#        
+#        
+#
+#    def getHelp(self, lang = None, proglang =None , href = False):
+#        """
+#        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+#        @param proglang: The programming language of the help code
+#        @type proglang: String
+#        @param lang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text 
+#        @type lang: String
+#        @param href: True if the content is a href false otherwise
+#        @type href: boolean
+#        @return: the help corresponding to the specified lang or proglang or href
+#        @rtype: string.
+#        """
+#
+#        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+#            raise ServiceError, "an help couldn't be a text and a code or href in the same time "
+#        if proglang:
+#            return self._help.getCode( proglang)
+#        elif lang:
+#            return self._help.getText( lang)
+#        elif href:
+#            return self._help.hrefs( )
+#        else :
+#            raise ServiceError, "invalid argument for help : " +str(lang)+" , "+str(proglang)+" , "+str(href)
+#
+#
+#               
+#    def helpHas_proglang(self, proglang = 'python'):
+#        """
+#        @param proglang: the programming language that encode the help
+#        @type proglang: String
+#        @return: True if a code written in proglang is used for the help,
+#        False otherwise.
+#        @rtype: boolean.
+#        """
+#        return self._help.has_proglang( proglang)
+#
+#    def helpProglangs(self):
+#        """
+#        @return: the list of proglangs used for the code help.
+#        @rtype: list of string.
+#        """
+#        return self._help.proglangs()
+#
+#    def helpHas_lang(self, lang='en'):
+#        """
+#        @return: True if the help has a text written in lang. False otherwise
+#        @rtype: boolean.
+#        """
+#        return self._help.has_lang(lang)
+#        
+#
+#    def helpLangs(self):
+#        """
+#        @return: the list of langs used for the help texts.
+#        @rtype: a list of string
+#        """
+#        return self._help.langs()
+#         
+#
+#    def helpHas_href(self):
+#        """
+#        @return: True if the help has a 'href', False otherwise.
+#        @rtype: boolean
+#        """
+#        return self._help.has_href() 
+#
+#        
+#    def helpHrefs(self):
+#        """
+#        @return: The list of href for the help.
+#        @rtype: list of string 
+#        """
+#        return self._help.hrefs()
+
+        
+
+
+
+
+############################################################
+#                                                          #
+#                 abstract Class Para                      #
+#                                                          #
+############################################################
+
+
+class Para(object):
+    """
+    This class is abstract thus it should never be instanciate.
+    The class Para is used to group the attributes and 
+    shared methods by parameters and paragraph together.
+    """
+    def __init__(self, evaluator ):
+        self._debug = None
+        self._father = None
+        self._evaluator = evaluator
+        self._name = None
+        self._argpos = None
+        self._prompt = {}
+        self._precond = {}
+        self._format = {}
+
+    def __str__(self):
+        if self._name:
+            return self._name
+        else:
+            return repr( self )
+
+    def setName(self, value):
+        """
+        set the name of the paragraph or parameter to value.
+        Two paragraphs or parameter can't have the same name
+        @param value: the name of the paragraph or parameter
+        @type value: String
+        """
+        self._name = value
+
+        
+    def getName(self):
+        """
+        @return: The name of this para(graph or meter)
+        @rtype: string
+        """
+        return self._name
+
+    
+    def getArgpos(self ):
+        """
+        @return: the argpos for the paragraph or parameter
+         - if argpos isn't defined, return the argpos of the upper paragraph and so on.
+         - if no argpos could be found, return 1
+        @rtype: number 
+        """
+        if self._argpos != None :
+            return self._argpos
+        else:
+            if self._father.isProgram():
+                return 1 
+            else:
+                return self._father.getArgpos()
+
+    
+    
+    def getDebug(self ):
+        """
+        @return: the debug level
+        @rtype: int
+        """
+        if self._debug is None :
+            debug = self._father.getDebug()
+            if debug is None:
+                debug = 0
+            self._debug = debug
+        return self._debug
+           
+    
+    def setArgpos(self, value):
+        """
+        set the argpos to value for this paragraph or parameter
+        @param value: the argpos value
+        @type value: Integer
+        """
+        try:
+            value + 3
+            self._argpos = value
+        except TypeError:
+            raise ServiceError , "argpos must be a number"
+
+
+    def addPrompt(self, value, lang="en"):
+        """
+        add a prompt to this parameter or paragraph
+        @param value: the prompt to add
+        @type value: String
+        @param lang: the laguage encoding the prompt: it should be iso639-1 compliant
+        @type lang: String
+        """
+        self._prompt[ lang ] = value
+
+
+    def getPrompt(self, lang = None):
+        """
+        @param lang: the laguage encoding the prompt: it should be iso639-1 compliant
+        @type lang: String
+        @return: The prompt. if there is no prompt , return None
+        @rtype: string
+        """
+        if not lang:
+            lang =_cfg.lang()
+        try:
+            return self._prompt[ lang ]
+        except KeyError:
+            return None
+
+    def promptLangs(self):
+        """
+        @return: the list of language in which the prompt is written.
+        @rtype: string
+        """
+        return self._prompt.keys()
+
+    def promptHas_lang(self,lang):
+        """
+        @param lang: the language in which is written the prompt
+        @type lang: String
+        @return: True if the prompt is written in lang, False otherwise.
+        @rtype: boolean.
+        """
+        return self._prompt.has_key( lang )
+        
+
+    def getPreconds(self , proglang= 'python' ):
+        """
+        @param proglang: the programming language which encode the precond
+        @type proglang: String
+        @return: the list of precond in reverse order. If no precond could be found return [].
+        @rtype: list of string
+        """
+        preconds = []
+                 
+        father = self.getFather()
+        if not father.isProgram():
+            prec = father.getPreconds( proglang = proglang )
+            preconds += prec
+        if self._precond:
+            pc = self._precond[proglang]
+            if pc:
+                preconds += pc 
+        return preconds
+                
+                
+    def addPrecond(self, precond, proglang='python'):
+        """
+        add a precond for this paragraph or parameter. Be carful if a precond with the same prolang already exist it will be replace by this one
+        @param precond: the precond to add
+        @type precond: String
+        @param proglang: the encoding language for the precond
+        @type proglang: String
+        """
+        precond = precond.strip() 
+        if self._precond.has_key( proglang ):
+            self._precond[ proglang ].append( precond )
+        else:
+            self._precond[ proglang ]= [ precond ]
+            
+    def precondHas_proglang(self, proglang):
+        """
+        @param proglang: the programming language
+        @type proglang: String
+        @return: True if the precond is encoded in this programming language, False othewise.
+        @rtype: boolean.
+        """
+        if self._precond:
+            return self._precond.has_key(proglang)
+        else:
+            if self._father.isProgram():
+                return None
+            else:
+                return self._father.precondHas_proglang( proglang )
+
+    def precondProglangs(self):
+        """
+        @return: a list of programming laguage in which the precond is encoded.
+        @rtype: list of string.
+        """
+        if self._precond:
+            return self._precond.keys()
+        else:
+            if self._father.isProgram():
+                return None
+            else:
+                return self._father.precondProglangs()
+
+
+    def hasFormat(self):
+        """
+        @return: True if the  L{Parameter} has a format, False otherwise
+        @rtype: boolean
+        """
+        if self._format:
+            return True
+        else:
+            return False
+
+
+    def getFormat(self , proglang):
+        """
+        @param proglang: the programming language which encode the format
+        @type proglang: String.
+        @return: the format
+        @rtype: string
+        """
+        try:
+            return self._format[proglang]
+        except KeyError:
+            raise ServiceError, "no format for this proglang: "+proglang
+
+
+    def formatProglangs(self):
+        """
+        @return: a list of programming languages.
+        @rtype: string
+        """
+        return self._format.keys()
+
+
+    def formatHas_proglang(self, proglang='python'):
+        """
+        @param proglang: the name of a programming language.
+        @type proglang: string.
+        @return: True if a format written in proglang exist, False otherwise.
+        @rtype: boolean
+        """
+        return self._format.has_key(proglang)
+
+     
+    def addFormat(self, format, proglang):
+        """
+        add a format to this parameter or paragraph
+        @param format: the format to add
+        @type format: String
+        @param proglang: the programming language that encode the format
+        @type proglang: String
+        """
+        self._format[proglang] = format.strip()
+        
+
+    def isProgram(self):
+        """
+        @return: False.
+        @rtype boolean
+        """
+        return False
+
+    def setFather(self,father):
+        """
+        set the father of this paragraph or the paragraph or program at the upper level
+        @param father: is a reference to the instance of the paragraph or program at the upper level
+        @type father: a  L{Program} or  L{Paragraph} instance"""
+        self._father = father
+        
+
+    def getFather(self):
+        """
+        @return: a reference toward the instance of the paragraph or program at the upper level
+        @rtype: a L{Paragraph} or a L{Program} object
+        """
+        return self._father    
+    
+    
+    def getProgram(self):
+        program = self.getFather()
+        while program is not None and not program.isProgram():
+            program = program.getFather()
+        return program
+    
+    def setEvaluator(self, evaluator):
+        """
+        set the evaluator of this paragraph. it's a reference toward the program's evaluator.
+        @param evaluator: is a reference to the instance of the paragraph or program at the upper level
+        @type evaluator: a  L{Evaluation} instance
+        """
+        self._evaluator = evaluator
+
+       
+    def getEvaluator(self):
+        """
+        @return: a reference toward the instance of the  L{Evaluation} instance of the program.
+        @rtype:  L{Evaluation} object.
+        """
+        return self._evaluator    
+    
+   
+############################################################
+#                                                          #
+#                  Class Paragraph                         #
+#                                                          #
+############################################################
+
+
+class Paragraph(Para):
+
+    def __init__( self , evaluator ):
+        Para.__init__(self , evaluator )
+        self._layout = []
+        self._parameters = Parameters()
+        
+            
+    def getParameters(self):
+        """
+        @return: the -L{Parameters} instance 
+        @rtype: Parameters object
+        """
+        return self._parameters
+    
+    def getParagraph(self, paragraphName):
+        """
+        @param paragraphName: the name of the paragraph to retrieve
+        @type paragraphName: String
+        @return: the instance of  L{Paragraph} in this paragraph or
+        in lower paragraph and which have the name parameterName.
+        @rtype: Paragraph object or None.
+        """
+        return self._parameters.getParagraph(paragraphName)
+
+    def addParameter(self, parameter):
+        """
+        add a parameter to this paragraph. The added parameter
+        will be at a lower level
+        @param parameter: the parameter to add
+        @type parameter: A Parameter Instance
+        """
+        self._parameters.addParameter(parameter)
+        parameter.setFather(self)
+        parameter.setEvaluator(self._evaluator)
+
+            
+    def addParagraph(self, paragraph):
+        """
+        add a paragraph to this paragraph the added paragraph
+        will be at a lower level
+        @param paragraph: the paragraph to add
+        @type paragraph: A Paragraph instance
+        """
+        self._parameters.addParagraph(paragraph)
+        paragraph.setFather(self)
+        paragraph.setEvaluator(self._evaluator)
+                   
+        
+    def getParameter(self, parameterName):
+        """
+        @param parameterName: the name of the parameter to retrieve
+        @type parameterName: String
+        @return: the instance of  L{Parameter} in this paragraph
+        or in lower paragraph and which have the name parameterName
+        @rtype:  L{Parameter} object or None
+        """
+        return self._parameters.getParameter(parameterName)
+      
+
+
+    def getAllParagraph(self):
+        """
+        @return: all  L{Paragraph} instances in this paragraph
+        @rtype: list of  L{Paragraph} instances
+        """
+        return self._parameters.getAllParagraph()
+        
+    def getAllParameter(self):
+        """
+        @return: all  L{Parameter} instances in this paragraph.
+        @rtype: list of  L{Parameter} instances
+        """
+        return self._parameters.getAllParameter()
+        
+
+    def getPath(self,name):
+        """
+        @param name: the name of a parameter or paragraph
+        @type name: String
+        @return: the path of this paragraph
+        it have the form    program/paragraph1/paragraph2/...[parameter]
+        @rtype: string
+        """
+        return self._parameters.getPath(self,name)
+
+    
+    def addInfo(self, content, proglang = None , lang = None , href = False):
+        """
+        Set an info on this paragraph
+        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{PargraphError} is raised.
+        @param content: an info on the program or a href toward an info
+        @type content: String
+        @param proglang: the programming language of the info code
+        @type proglang: String
+        @param lang: is the symbol of a language in  iso639-1 (ex:english= 'en',french= 'fr') 
+        @type lang: String
+        @param href: True if the content is a href, False otherwise
+        @type href: Boolean
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "an info couldn't be a text and a code or href in the same time"
+        if proglang:
+            self._parameters.addInfo( content , proglang = proglang)
+        elif lang:
+            self._parameters.addInfo( content , lang = lang)
+        elif href:
+            self._parameters.addInfo( content )
+        else :
+            raise ServiceError, "invalid argument for info : " + proglang + lang + href
+        
+        
+
+    def getInfo(self, proglang =None ,lang= None, href = False):
+        """
+        Only one of the arguments proglang, lang or href must be specified. If more than one arguments among these are specified, a  L{ServiceError} is raised.
+        @param proglang: The programming language of the info code
+        @type proglang: String
+        @param lang: is the symbol of a language in iso639-1 (ex: 'en', 'fr') for a text info
+        @type lang: String
+        @param href: True if the info is a href, False otherwise
+        @type href: Boolean
+        @return: the info corresponding to the specified lang, proglang or href
+        @rtype: string
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise ServiceError, "an info couldn't be a text and a code or a href in the same time "
+        if proglang:
+            return self._parameters.getInfo( proglang = proglang)
+        elif lang:
+            return self._parameters.getInfo( lang = lang)
+        elif href:
+            return self._parameters.getInfo( )
+        else :
+            raise ServiceError, "invalid argument for info : " +str(lang)+" , "+str(proglang)+" , "+str(href)
+
+               
+    def infoProglangs(self):
+        """
+        @return: the list of programming languages used by the info codes.
+        @rtype: string.
+        """
+        return self._parameters.infoProglangs()
+
+    def infoHas_lang(self, lang='en'):
+        """
+        @param lang: the symbol of a language in iso639-1 (ex: 'en', 'fr') 
+        @type lang: String
+        @return: True if the info has a text written in lang, False otherwise.
+        @rtype: boolean
+        """
+        return self._parameters.infoHas_lang( lang= lang)
+        
+
+    def infoLangs(self):
+        """
+        @return: the list of languages used by the info texts.
+        @rtype: list of string.
+        """
+        return self._parameters.infoLangs()
+         
+
+    def infoHas_href(self):
+        """
+        @return: True if the info has a 'href', False otherwise.
+        @rtype: boolean
+        """
+        return self._parameters.infoHas_href() 
+        
+
+
+
+        
+############################################################
+#                                                          #
+#                  Class Parameter                         #
+#                                                          #
+############################################################
+        
+class Parameter( Para ):
+    """
+    Parameter is an abstract class which contains all the attributes
+    and methods common to all parameter subclass
+    while the class in core.py are the subclass and provide the specific
+    methods 
+    """
+    def __init__(self, mobyleType , evaluator = None , name = None , value=None ):
+        self._mobyleType = mobyleType
+        if evaluator is None:
+            evaluator =Evaluation()
+        self.cfg = Config()
+        Para.__init__(self, evaluator )
+        self._name = name
+        self._ismandatory = False
+        self._ishidden = False
+        self._iscommand = False
+        self._isstdout = False
+        self._issimple = False # encore d'actualite ??
+        self._formfield = ""
+        self._isout = False
+        self._ismaininput = False
+        self._width = None
+        self._height = None
+        self._separator = None
+        self._filenames = {} #the keys are programming language, the values are list of strings
+        self._paramfile = ""
+        self._scalemin = {}           #the keys is 'value' or a proglang
+        self._scalemax = {}           #the values will be a value or a code
+        self._scaleinc = None 
+        self._vlist = {}    #????????????????????
+        self._flist = {}    #with value as key
+        self._undefValue = None
+        self._ctrls = []    # a ctrl list
+        self._vdef = None  
+        if value is not None:
+            if self._name is not None:
+                self.setValue( value )
+            else:
+                raise MobyleError , "if value is specified name must be specified"
+   
+    def clone(self , dt ):
+        """does not make realy a clone rather a copy without father cfg and with a new datatype
+        """
+        newMT = self._mobyleType.clone( dt )
+        newEvaluator = Evaluation( dict = self.getEvaluator()._2dict_() )
+        newParam = Parameter( newMT , evaluator = newEvaluator )
+        for attr in self.__dict__.keys() :
+            if attr not in [ "__builtins__" , "_evaluator" , "_mobyleType" , "cfg" , "_father"]:
+                setattr(newParam , attr, copy.deepcopy( getattr( self , attr )) )
+        return newParam      
+           
+    def ancestors( self ):
+        #1 exclude the parameter self.__class__.__name__ itself
+        #-3 exclude "Parameter" , "Para" , and "object"
+        return [ k.__name__ for k in self.__class__.mro()][1:-3]
+
+    def getValue( self ):
+        """
+        @return: the current value for this parameter.
+        If value is not defined return None
+        @rtype: any
+        @call: Program.getValue
+        """
+        evaluator = self.getEvaluator()
+        return evaluator.getVar( self.getName() )
+    
+    
+        
+    def setValue( self , value ) :
+        """
+        set the current value for this parameter and put it in the evaluation pace.
+        if the value doesn't match with the parameter's class a UserValueError is
+         thrown
+        the SequenceParameter can throws an UnsupportedFormatError if the output
+         format is nos supported or a MobyleError if something goe's wrong during
+         the conversion
+        @param value: the value  of this parameter
+        @type value: any builtin classes (in core.py) or an instance of a class defined by user in ../Classes
+        @call: Program.setValue
+        """
+        if self.ishidden():
+            raise MobyleError , "the parameter %s is hidden, it's value cannot be changed" % self.getName()
+        elif self.isout():
+            raise MobyleError , "the parameter %s is out, it's value cannot be changed" % self.getName()
+        else:
+            if value is not None:
+                if self._separator is not None:
+                    #if separator is not None the Datatype is a MultipleDataType.
+                    #the argument value is a list
+                    #the value in the evaluator must be the final string
+                    value = self._separator.join( value )
+                self.getEvaluator().setVar( self.getName() ,  value  )
+                
+            else:
+                self.getEvaluator().setVar( self.getName() , None )
+                
+
+    def setValueAsVdef( self ):
+        """
+        get the vdef and setValue with it
+        """
+        if self.isInfile():
+            raise MobyleError , "an infile parameter can't have vdef"
+        vdef = self.getVdef()
+        if vdef is None :
+            self.getEvaluator().setVar( self.getName() , None )
+        else:
+            convertedVdef , mt = self.convert( vdef )
+            self.getEvaluator().setVar( self.getName() , convertedVdef )
+
+    def renameFile( self,  workdir , oldName , newName ):
+        """
+        rename a parameter File, rename the file on the filesystem and chage the value in Evaluator
+        @param workdir: the absolute path to the job directory (where to find the file
+        @type workdir: string
+        @param newName:the file name to rename in the workdir
+        @type newName: string
+        """
+        if self.isInfile():
+            try:
+                os.rename( os.path.join( workdir , oldName ) , os.path.join( workdir , newName ) )
+            except IOError , err:
+                raise MobyleError , 'Cannot rename the file corresponding to %s parameter :%s'%(  self.getName(),
+                                                                                             err)
+        else:
+            s_log.error( 'the parameter %s is not an Infile. Cannot rename it\'s file' % self.getName() )
+            raise MobyleError , 'Internal Mobyle Error'
+
+    def reset( self ):
+        vdef = self.getVdef()
+        if vdef is not None:
+            convertedVdef , mt = self.convert( vdef , self.getType() )
+            if self.getDataType().getName() == 'MultipleChoice':
+                sep = self.getSeparator()
+                convertedVdef = sep.join( convertedVdef )
+            self.getEvaluator().setVar( self.getName() , convertedVdef )
+            return convertedVdef
+        else:
+            self.getEvaluator().setVar( self.getName() , None )
+            return None
+    
+    def getVdef( self ):
+        """
+        return: the vdef
+        @rtype: string or list of string
+        """
+        return self._vdef
+
+
+    def setVdef(self, value ):
+        """
+        set the vdef for this parameter. the vdef is either a value(s) or a code(s).if vdef is a value this one is converted
+        @param value: the vdef .
+        @type value :is a value, a code or a list of value or a list of code
+         - a value = val1
+         - a list of value = [val1 , val2, ...] 
+        @raise ServiceError: if try to set a vdef and a vdef already exists raise a  L{ServiceError}.
+        """
+        if self._vdef:
+            raise ServiceError, "a vdef is already specify"
+        else:
+            value_type = type( value )
+            if value_type == types.ListType:
+                if len( value ) == 1 :
+                    self._vdef = value[0]
+                else:
+                    self._vdef = value
+            else:
+                self._vdef = value[0]
+
+
+    def convert( self , value , acceptedMobyleType  ):
+        if self._paramfile:
+            return self.getDataType().convert( value , acceptedMobyleType , detectedMobyleType = self , paramFile = True )
+        else:
+            return self.getDataType().convert( value , acceptedMobyleType , detectedMobyleType = self )
+        
+    def validate(self):
+        return self.getDataType().validate( self )
+        
+    def getType( self ):
+        return self._mobyleType
+    
+    def getDataType( self ) :
+        return self._mobyleType.getDataType()
+    
+    def setDataType(self, dt):
+        self._mobyleType.dataType= dt
+        
+    def getBioTypes( self ):
+        return self._mobyleType.getBioTypes()
+   
+    def getAcceptedDataFormats( self ):
+        return self._mobyleType.getAcceptedFormats()
+    
+    def forceReformating( self ):
+        return self._mobyleType.forceReformating()
+    
+    def getDataFormat( self ):
+        return self._mobyleType.dataFormat
+    
+    def setDataFormat( self , format ):
+        self._dataFormat = format
+    
+    def ismaininput(self):
+        """
+        @return: True if the parameter is mandatory, False otherwise.
+        @rtype: boolean
+        @call: called by L{Program.ismandatory}
+        """
+        return self._ismaininput        
+         
+    def setMaininput(self, value):
+        """
+        set if this parameter is mandatory or not
+        @param value: Boolean
+        """
+        self._ismaininput = value
+ 
+             
+    def ismandatory(self):
+        """
+        @return: True if the parameter is mandatory, False otherwise.
+        @rtype: boolean
+        @call: called by L{Program.ismandatory}
+        """
+        return self._ismandatory        
+         
+    def setMandatory(self, value):
+        """
+        set if this parameter is mandatory or not
+        @param value: Boolean
+        """
+        self._ismandatory = value
+     
+    def ishidden(self ):
+        """
+        @return: True if this parameter is hidden, False otherwise.
+        @rtype: boolean
+        @call: called by L{Program.ishidden}
+        """
+        return self._ishidden
+         
+         
+    def setHidden(self, value):
+        """
+        set if this parameter is appear in the web interface or not
+        @param value: Boolean
+        """
+        self._ishidden = value
+        
+    def getArgpos(self):
+        """
+        """
+        if self.iscommand() and not self._argpos :
+            return 0
+        else:
+            return super( Parameter , self ).getArgpos()
+        
+    def iscommand(self ):
+        """
+        @return: True if this parameter is the command, False otherwise.
+        @rtype: boolean
+        @call:  called by L{Program.iscommand}
+        """
+        return self._iscommand       
+
+    def setCommand(self, value):
+        """
+        @param value:
+        @type value:
+        """
+        program = self.getProgram()
+        if program:
+            command = program.getCommand()[0]
+            if command:
+                msg = "try to set the parmeter \"%s\" as command but the program as already a command" % self.getName()
+                s_log.error( msg )
+                raise ServiceError , msg
+            commandParameterName = program.getCommandParameterName()
+            if commandParameterName:
+                msg = "try to set parameter \"%s\" as command but the parameter \"%s\" is already set as command" %( self.getName() ,
+                                                                                                                      commandParameterName()
+                                                                                                                    )
+                s_log.error( msg )
+                raise ServiceError , msg
+        self._iscommand = value
+     
+
+    def issimple(self ):
+        """
+        @return: True if this parameter is simple, False otherwise
+        @rtype: boolean
+        @call:  called by L{Program.issimple}
+        """
+        return self._issimple
+
+    def setSimple(self, simple):
+        """
+        set if the parameter appear in the simple web interface or not
+        @param simple:
+        @type simple: Boolean
+        """
+        self._issimple = simple
+            
+    def isout(self ):
+        """
+        @return: True if this parameter is produce by the program (an output), False otherwise.
+        @rtype: boolean
+        @call:  called by L{Program.isout()}
+        """
+        return self._isout or self._isstdout
+        
+
+    def setOut( self , out ):
+        """
+        defined if the parameter is an output of the program or not
+        @param out:
+        @type out: boolean
+        """
+        self._isout = out
+        if out :
+            self._ishidden = True
+            
+    def isstdout( self ):
+        """
+        @return: True if this parameter is produce by the program (an output), False otherwise.
+        @rtype: boolean
+        @call:  called by L{Program.isstdout()}
+        """
+        return self._isstdout
+        
+
+    def setStdout( self , stdout ):
+        """
+        defined if the parameter is an output of the program or not
+        @param stdout:
+        @type stdout: boolean
+        """
+        self._isstdout = stdout
+        self.setOut( stdout )
+
+    def isInfile(self ):
+        """
+        @return True if parameter is an input file, False otherwise.
+        @rtype: boolean
+        @call:  called by L{Program.isInfile()}
+        """
+        dt = self.getDataType()
+        if dt.isFile():  
+            return not self.isout()
+        else:
+            return False
+    
+    def isMultiple(self ):
+        """
+        @return: @return True if parameter is a Multiple File parameter, False otherwise. 
+        @rtype: boolean
+        @call:  called by L{Program.isInfile()}
+        """
+        return self.getDataType().isMultiple()  
+        
+    def isOutfile(self ):
+        """
+        be careful this method is overriden in the parameter which could be
+        Outfile ( TextParameter , BinaryParameter ,...)
+        @return: False 
+        @rtype: boolean
+        @call:  called by L{Program.isOutfile()}
+        """
+        dt = self.getDataType()
+        if dt.isFile():  
+            return  self.isout()
+        else:
+            return False
+
+
+    def head( self , data ):
+        """
+        param data: 
+        type data:
+        return: return the head of the data 
+        rtype:
+        """
+        return self.getDataType().head( data )
+
+    def cleanData( self , data ):
+        """
+        param data: 
+        type data:
+        return: clean data prior to write it on disk
+        rtype:
+        """
+        return self.getDataType().cleanData( data )
+
+
+    
+    def getFormfield(self ):
+        """
+        @return: the formfield of the parameter if it exist.
+        @rtype: string or None
+        """
+        if self._formfield == "":
+            return None
+        else:        
+            return self._formfield
+
+       
+    def setFormfield(self , formfield):
+        """
+        set the formfield of this parameter
+        @param formfield: the formfield
+        @type a String representing the formfield
+        """
+        self._formfield = formfield
+        
+
+    def getBioMoby(self ):
+        """
+        @return: the BioMoby Class corresponding to this parameter
+        @rtype:  string or None
+        """
+        return self._mobyleType.getBioMoby()
+
+
+
+    def addElemInVlist (self, label, value):
+        """
+        add the couple  label,value in a vlist the labels are the keys to retrieve the values
+        @param label: the label
+        @type label: String
+        @param value: the value
+        @type value: String
+        @raise ValueError: a ValueError if the vlist has already a label label.
+        """
+        if  self.vlistHas_label( label ):
+            raise ValueError , "this label : %s already exist" %label
+        else:
+            if label:
+                label = str( label )
+            else:
+                label = ''
+            if value:
+                value = str( value )
+            else:
+                value = ''
+                
+            self._vlist[ label ] = value 
+            if not hasattr( self._mobyleType , '_vlist' ):
+                self._mobyleType._vlist = {}
+            self._mobyleType._vlist[ label ] = value
+
+    def delElemInVlist (self,label):
+        """
+        retrieve the value associated to the label and delete the couple label, value
+        @param label: the label
+        @type label: String
+        @raise UnDefAttrError: a L{UnDefAttrError} if the parameter hasn't any vlist.
+        @raise ValueError: a ValueError if the vlist hasn't a label label.
+        """
+        if not self._vlist:
+            raise UnDefAttrError, "no vlist for this Parameter"
+        try:
+            if not label:
+                label = ''
+            del self._vlist[label]
+            del self._mobyleType._vlist[ label ]
+        except KeyError:
+            raise  ValueError, " no label %s in this vlist" %label
+
+            
+    def getVlist(self ,label):
+        """
+        @param label: a label in the vlist
+        @type label: String
+        @return: return the value associated to this label in the vlist
+        @rtype: string
+        @raise UnDefAttrError: if this parameter haven't vlist raise an L{UnDefAttrError}
+        @raise ValueError: if the label doesn't match with any labels in vlist raise a ValueError
+        @todo:  revoir le type des erreurs levees
+        """
+        if not self._vlist:
+            raise UnDefAttrError , "this parameter haven't vlist"
+        try:
+            return self._vlist[label]
+        except KeyError:
+            raise ValueError , " no label " + str(label)+" in this vlist"
+
+    def vlistLabels(self):
+        """
+        @return: a list of labels in the vlist
+        @rtype: list of string
+        """
+        return self._vlist.keys()
+        
+
+    def hasVlist(self):
+        """
+        @return: True if the parameter has a vlist, False otherwise.
+        @rtype: boolean
+        """
+        if self._vlist :
+            return True
+        else :
+            return False
+        
+    def vlistHas_label (self,label):
+        """
+        @param label: the label to search in vlist
+        @type label: String
+        @return: True if label match with a label in vlist, false otherwise.
+        @rtype:  boolean
+        """
+        return self._vlist.has_key( label )
+    
+
+    def getVlistValues(self):
+        """
+        @return: a list of all values (not converted) in vlist.
+        @rtype: list of string
+        @call:  by L{ChoiceParameter} and L{MultipleChoiceParameter}.validate 
+        """
+        return self._vlist.values()
+
+
+    def addElemInFlist(self , value , label , codes):
+        """
+        @param value: 
+        @type value: string
+        @param label:
+        @type label: string
+        @param codes:
+        @type codes: dictionary
+        """
+        #print "parameter :" , self.getName()
+        #print "addElemInFlist value =", value ," label = ", label ," codes = ",codes
+        self._flist[ value ] = ( label , codes )
+        if not hasattr( self._mobyleType , '_flist' ):
+            self._mobyleType._flist ={}
+        else:
+            self._mobyleType._flist[ label ] = value
+    
+    def getFlistValues(self):
+        """
+        @return: the keys of a flist.
+        @rtype: list of strings
+        @call: by L{Program}.flistValues
+        """
+        return  self._flist.keys()
+
+
+    def flistHas_value(self, value):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @return: True if the flist has a value == value, False otherwise.
+        @rtype: boolean
+        @raise UnDefAttrError: if the parameter hasn't flist a  L{UnDefAttrError}
+        is raised
+        ( called by Parameter.flistProglangs )
+        """
+        return self._flist.has_key(value)
+                           
+    def flistLabels(self):
+        """
+        @return: a list of labels in the flist
+        @rtype: list of string
+        """
+        return self._flist.keys()
+ 
+    def flistProglangs(self, value = None):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @return: the list of proglang available for a given value.
+        @raise UnDefAttrError: if the parameter hasn't flist a  L{UnDefAttrError} is raised
+        """
+        proglangs = []
+        if value is None :
+            values = self._flist.keys()
+        else:
+            if self._flist.flistHas_value( value ):
+                values = [ value ]
+            else:
+                raise MobyleError , "this parameter has no value:" +str( value )+" in it's flist"
+        for value in values :
+            newValues  = self._flist[ value ][1].keys()
+            for newValue in newValues:
+                if newValue not in proglangs:
+                    proglangs.append( newValue )
+        return proglangs
+        
+
+    def flistHas_proglang(self,value,proglang):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: Boolean, True if the flist has the value and a code written in proglang associated with, False otherwise.
+        @rtype: boolean
+        """
+        
+        if self.flistHas_value(value):
+            return self._flist[value][1].has_key( proglang )
+        else:
+            return False
+            #raise ValueError, self.getName()+" : the value " + str(value) + " doesn't exist for this flist"
+    
+    def getFlistCode(self , value , proglang):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: the code associated with the value and written in proglang.
+        @rtype: string
+        @raise ParamaterError: if there isn't this value or this proglang an  L{ParamaterError} is raised
+        """
+        try:
+            return self._flist[value][1][proglang]
+        except KeyError:
+            raise ParameterError, "%s : the proglang %s is not defined for the flist " %( self.getName(),
+                                                                                              proglang
+                                                                                              )
+
+    def hasFlist(self):
+        """
+        @return: True if the Parameter has a flist, False otherwise.
+        @rtype: boolean
+        """
+        if self._flist:
+            return True
+        else:
+            return False
+
+    def getListUndefValue( self ):
+        """
+        @return:
+        @rtype: string
+        """
+        return self._undefValue
+
+    def setListUndefValue(self , value ):
+        """
+        @param value:
+        @type value: string
+        """
+        self._undefValue = value
+        self._mobyleType._undefValue = value
+        
+    def has_ctrl(self):
+        """
+        @return: True if the parameter has  L{Ctrl}, False otherwise.
+        @rtype:boolean
+        """
+        if self._ctrls:
+            return True
+        else :
+            return False
+
+    def getCtrls(self):
+        """
+        @return: the list of Ctrl instances.
+        @rtype: list of Ctrl objects.
+        @raise UnDefAttrError: if the parameter haven't a Ctrl,  raise an  L{UnDefAttrError}
+        """
+        if self._ctrls:
+            return self._ctrls
+        else:
+            raise UnDefAttrError,"no ctrl for this parameter"
+
+
+        
+    def addCtrl(self, ctrl):
+        """
+        @param ctrl: is a tuple made with a list of messages and a list of codes
+        @type ctrl: ([(String content , String proglang , String lang , Boolean href)],[(String proglang, String code)])
+        """
+        myCtrl = Ctrl()
+        
+        for message in  ctrl[0]:
+            if ( (message[1] and message[2]) or (message[1] and message[3]) or (message[2] and message[3]) ):
+                raise ServiceError
+
+            if message[1]:
+                myCtrl.addMessage(message[0] ,proglang =message[1] )
+            elif message[2]:
+                myCtrl.addMessage(message[0] ,lang = message[2])
+            elif message[3]:
+                myCtrl.addMessage(message[0] ,href = message[3] )
+                                
+        for code in ctrl[1]:
+            myCtrl.addCode(code[0] , code[1])
+
+        self._ctrls.append( myCtrl )
+
+    def hasParamfile(self):
+        """
+        @return: True if the parameter should be specified in a file instead
+        the command line, False otehwise
+        @rtype: boolean
+        """
+        if self._paramfile:
+            return True
+        else:
+            return False
+
+
+    def getParamfile(self):
+        """
+        @return: the name of the file parameter when they must be specipfied
+        in a file instead on command line.
+        @rtype: string.
+        @raise  UnDefAttrError: if the parameter haven't a paramfile an L{UnDefAttrError} is raised.
+        """
+        if self._paramfile:
+            return self._paramfile
+        else:
+            raise UnDefAttrError,"no paramfile for this parameter"
+        
+    def setParamfile(self, fileName):
+        """
+        set the paramfile
+        @param fileName: the name of the paramfile
+        @type fileName: String
+        """
+        #on pourrait des maintenant eliminer les espaces du nom de fichier
+        fileName.replace(' ','')
+        self._paramfile = fileName
+
+        
+    def getFilenames(self, proglang = 'python' ):
+        """
+        @param proglang: the programming language used to defined the filenames
+        @type proglang: string
+
+        @return: the UNIX MASK which permit to retrieve the results files.
+        @rtype:  a list of string
+        @raise UnDefAttrError:  if the parameter haven't a filenames , raise an  L{UnDefAttrError}
+        @call: L{Program.getFileNames}( parameterName )
+        @todo:  si ca doit etre evaluer alors il faut considerer ca come du code car la syntaxe va etre differente en perl ,et en python donc je propose de modifier la dtd <!ELEMENT filenames (code)+> et en python maVariable+".*.aln" chaque mask etant separer par un espace
+        """
+        if not self.isout():
+            raise MobyleError, self.getName() + " : only out parameter could have filenames"
+
+        if self._filenames:
+            try:
+                filenames= self._filenames[ proglang ]
+            except KeyError:
+                program = self.getProgram()
+                if program:
+                    programName = program.getName()
+                else:
+                    programName = "without_program"               
+                                
+                s_log.warning( "%s.%s have filenames in %s proglangs but not in python" %( programName , self.getName() , self._filenames.keys() ) )
+                return []
+
+            #each mask could contain a variable, thus they must be evaluated in evaluator
+            result = []
+            for mask in filenames:
+                try:
+                    evaluatedMask = self.getEvaluator().eval( mask )
+                    if evaluatedMask is None:
+                        # a mask could be a variable therefore it could be None !! 
+                        continue 
+                    result.append( evaluatedMask )
+                except Exception ,err :
+                    raise MobyleError, "error in filenames code evaluation ( " + mask + " ) : " + str( err )
+            return result
+        else:
+            raise UnDefAttrError,"no filenames for this parameter"
+
+
+         
+    def setFilenames(self , fileNames , proglang = 'python' ):
+        """
+        @param fileNames: a unix mask to retrieve the results files (ex: *.aln,*.dnd,...)
+        @type fileNames: String
+        @param proglang: the programming language used to defined the filenames
+        @type proglang: string
+        """
+        if self._filenames.has_key( proglang ):
+            self._filenames[ proglang ].append( fileNames)
+        else:
+            self._filenames[proglang] = [ fileNames ]
+        
+
+
+    def hasScale( self , proglang = 'python' ):
+        """
+        @retrun: True if the param has a scale with code in proglang or with value, False otherwise.
+        @rtype: boolean
+        """
+        try:
+            _ = self._scalemin[ proglang ]
+        except KeyError:
+            try:
+                _ = self._scalemin['value']
+            except KeyError:
+                return False
+        try:
+            _ = self._scalemin[ proglang ]
+        except KeyError:
+            try:
+                _ = self._scalemin[ 'value' ]
+            except KeyError:
+                return False
+        return True
+
+    
+
+    def isInScale( self , proglang = 'python' ):
+        """
+        @return: True if the value is in range(min, max), false otherwise
+        @rtype: boolean
+        """
+        evaluator = self.getEvaluator()
+        try:
+            smin = self._scalemin[ proglang ]
+        except KeyError:
+            try:
+                smin = self._scalemin[ 'value' ]
+            except KeyError:
+                raise MobyleError,  "no value nor code in %s for this scale min" % proglang 
+        try:
+            smin = evaluator.eval( smin )
+        except Exception , err :
+            s_log.error( "error during scale min evaluation : %s. Check %s.%s definition" %( err ,
+                                                                                             self.getProgram().getName() , 
+                                                                                             self.getName()))
+            raise MobyleError( "Internal Server Error" ) 
+        try:
+            smax = self._scalemax[ proglang ]
+        except KeyError:
+            try:
+                smax = self._scalemax[ 'value' ]
+            except KeyError:
+                raise MobyleError ,"no value nor code in %s for this scale max" % proglang 
+        try:
+            smax = evaluator.eval( smax )
+        except Exception , err :
+            s_log.error( "error during scale max evaluation : %s. Check %s.%s definition" %( err,
+                                                                                             self.getProgram().getName() , 
+                                                                                             self.getName()))
+            raise MobyleError( "Internal Server Error" ) 
+        value = self.getValue()
+        if value is not None:
+            
+            if value >= smin and value <= smax :
+                return True
+            else:
+                return False
+        else:
+            raise ValueError , "undefined value" 
+        
+
+
+
+    def getScale(self , proglang = 'python' ):
+        """
+        @param proglang: the programming language 
+        @type proglang: String
+
+        @return: a tuple ( min , max , inc )
+          - min  
+          - max 
+          - incr is a value
+        @rtype: tuple ( , ,  )
+        @raise 
+        """
+        try:
+            smin = self._scalemin[ proglang ]
+            evaluator = self.getEvaluator()
+            min = evaluator.eval( smin )
+        except KeyError:
+            try:
+                smin = self._scalemin[ 'value' ]
+            except KeyError:
+                raise MobyleError , "no value nor code in %s for this scale min" % proglang 
+        try:
+            smax = self._scalemin[ proglang ]
+            evaluator = self.getEvaluator()
+            smax = evaluator.eval( smax )
+        except KeyError:
+            try:
+                smax = self._scalemax[ 'value' ]
+            except KeyError:
+                raise MobyleError , "no value nor code in %s for this scale max" % proglang
+        return ( smin , smax , self._scaleinc )
+
+    
+
+    def setScale(self, min , max , inc = None , proglang = None):
+        """
+        @param min: specify the minimum of the scale
+        @type min: could either a value or String representing a code
+        @param max: specify the maximum of the scale
+        @type max: could either a value or String representing a code
+        @param inc: specify the incrementation of the scale
+        @type inc: a number
+        @parameter proglang: if min, max are code, proglang should be specified and it's the programming language.
+        @type proglang: a String
+        @todo: faire des tests sur inc le convertir?
+        """
+        if proglang:
+            self._scalemin[ proglang ] = min
+            self._scalemax[ proglang ] = max
+        else:
+            self._scalemin[ 'value' ] = min
+            self._scalemax[ 'value' ] = max
+        if inc:
+            self._scaleinc= inc
+        
+        
+    def getSeparator( self ):
+        """
+        @return: a character used to split the values of a MultipleChoice,( default value : '')
+        @rtype: string
+        """
+        return self._separator
+        
+
+        
+    def setSeparator( self , separator ):
+        """
+        @param separator: a character used to split the values of a MultipleChoice
+        @type separator: String
+        """
+        self._separator = separator
+
+
+    def getWidth( self ):
+        """
+        @return: an Integer, representing the width of the widget.
+        @rtype: number
+        @raise UnDefAttrError: if the parameter haven't a width , raise an  L{UnDefAttrError}
+        """
+        if self._width:
+            return self._width
+        else:
+            raise UnDefAttrError,"no width for this parameter"
+
+        
+    def setWidth( self , width ):
+        """
+        @param width: the width of the widget in pixels,en pourcentage ???
+        @type width: number int float
+        """
+        try:
+            width + 3
+            self._width = width
+        except TypeError:
+            raise ServiceError , "width must be a number"
+
+
+    def getHeight(self ):
+        """
+        @return: return the height of the widget in pixels, en pourcentage????
+        @rtype: number
+        @raise UnDefAttrError: if the parameter haven't a height , raise an  L{UnDefAttrError}
+        """
+        if self._height:
+            return self._height
+        else:
+            raise UnDefAttrError,"no height for this parameter"
+
+    
+
+    def setHeight(self, height):
+        """
+        @param height: the height of the widget in pixels,en pourcentage ???
+        @type:number int float??
+        """
+        try:
+            height + 3
+            self._height = height 
+        except TypeError:
+            raise  ValueError, "height must be a number"
+
+
+    def setExemple(self, value):
+        """
+        set an exemple value
+        @param value: a typical value for this parameter
+        @type value: string
+        """
+        self._exemple = value
+        
+    def getExemple(self ):
+        """
+        @return: a typical value for this parameter.
+        @rtype: string
+        @raise UnDefAttrError: if the parameter haven't an exemple value, raise an L{UnDefAttrError} 
+        """
+        if self._exemple:
+            return self._exemple
+        else:
+            raise UnDefAttrError,"no exemple for this parameter"
+
+
+    def doCtrls(self):
+        """
+        do the controls specific to this parameter        
+        @raise  L{UserValueError}: if a control failed a  L{UserValueError} is raised, otherwise return True
+        @todo: gerer les message de type href, discuter du proto de la fonction doit retourner False ou lever une erreur??
+        @todo: gerer la langue dans la fonction d'erreur
+        """
+        if self.has_ctrl():
+            evaluator = self.getEvaluator()
+            myName = self.getName()
+            for ctrl in self.getCtrls():
+                if ctrl.has_proglang( 'python' ):
+                    rawVdef = self.getVdef()
+                    if rawVdef is None:
+                        evaluator.setVar( 'vdef' , None )
+                        convertedVdef = None
+                    else:
+                        convertedVdef  , mt = self.convert( rawVdef , self.getType() )
+                        evaluator.setVar( 'vdef' , convertedVdef )
+                    if evaluator.getVar( myName ) is not None:
+                        value = evaluator.getVar( myName )
+                        evaluator.setVar( 'value' , value )
+                    else:
+                        value = convertedVdef
+                        evaluator.setVar( 'value' , convertedVdef )
+                    try:
+                        code = ctrl.getCode( proglang= 'python' )
+                        eval_result = evaluator.eval( code )
+                        if self.getDebug() > 1 :
+                            b_log.debug( "convertedVdef = " + str(convertedVdef) + "  value = " + str( value) ) 
+                            b_log.debug( "eval( " + code + " ) = " + str( eval_result ) )
+                    except Exception ,err:
+                        msg = self.getName() + ": error during evaluation of \"" + code +"\" : " + str( err )  
+                        s_log.error( msg )
+                        raise ServiceError , msg
+                    if eval_result :
+                        continue
+                    else:
+                        messType = ctrl.whatIsIt()
+                        LANG = _cfg.lang()
+                        if messType == 'text':
+                            if ctrl.messageHas_lang( LANG ):
+                                msg = myName +" : "+ ctrl.getMessage( LANG )
+                            else:
+                                if ctrl.messageLangs():
+                                    msg =  myName +" :"+ ctrl.getMessage( ctrl.messageLangs()[0] )
+                                else:
+                                    msg = "this value"+ str(self.getValue())+" is not allowed for parameter named " + myName
+                        elif messType == 'code':
+                            if ctrl.messageHas_proglang('python'):
+                                msg= myName + " : " + evaluator.eval( ctrl.getMessage( proglang='python' ) )
+                            else:
+                                msg = "this value" + str( self.getValue() ) + " is not allowed for parameter named " + myName
+                        elif messType == 'href':
+                            #### TODO ####
+                            raise NotImplementedError, "href are not implemented in message Ctrls : todo"
+                        else:
+                            msg = "this value" + str( self.getValue() ) + " is not allowed for parameter named " + myName
+                        #s_log.error( self.getName() + " xml ctrl failed : " + msg )
+                        raise UserValueError( parameter = self,  msg = msg )
+                else:
+                    if self.getDebug() > 1:
+                        if ctrl.has_proglang( 'perl' ): 
+                            b_log.debug( "############## WARNING #####################" )
+                            b_log.debug( "had a control code in Perl but not in Python" )
+                            b_log.debug( "############################################" )
+        return True
+
+    def _isNumber(self, value):
+        """
+        test if the value is a number.
+        @param value: any value to test
+        @type value: any
+        @return: True if value is a number, False othewise
+        @rtype:  boolean
+        """
+        try:
+            value + 3
+            return True
+        except TypeError:
+            return False
+
+
+########################
+#                      #
+#      class text      #
+#                      #
+########################
+
+class Text(object):
+    """
+    is used to handle a (text)+ in the mobyle dtd.
+    a text in dtd could be either a
+     - text
+     - code
+     - or a href
+    and have the attributes
+     - lang associated with the text
+     - proglang associated with code
+     - href for href
+    """
+    
+    def __init__(self):
+        self._lang = {}
+        self._code = {}
+        self._href = []
+
+    def has_lang(self, lang ):
+        """
+        @param lang: the language in which the text is written
+        @type lang: a String should be conformed to the iso630-1 code
+        (ex 'en', 'fr').
+        @return: True if this Text have a code in this programming language,
+        False otherwise
+        @rtype: boolean
+        """
+        if self._lang:
+            return self._lang.has_key( lang )
+        else:
+            return False
+
+
+    def langs(self):
+        """
+        @return: the languages of this Text.
+        @rtype: list of strings
+        """
+        return self._lang.keys()
+
+    def addText(self, text, lang ='en'):
+        """
+        Set a text for this Text
+        @param text: the text
+        @type text: String
+        @param lang: the language in which the text is written
+        @type lang: a String should be conformed tothe iso630-1 code
+        (ex 'en', 'fr')
+        """
+        self._lang[ lang ] = text
+
+    def getText(self, lang='en'):
+        """
+        get the text associatde with the lang for this Text
+        @param lang: the language of the text
+        @type lang:the language in which the text is written
+        @type lang: a String should be conformed tothe iso630-1 code
+        (ex 'en', 'fr')
+        @return: the text
+        @rtype: string
+        @raise MobyleError: a{MobyleError} is raised if the text has no lang = 'lang'
+        """
+        try:
+            return  self._lang[lang]
+        except KeyError:
+            raise MobyleError, "no lang = "+str(lang)+" in this Text"
+    
+
+
+    def has_proglang(self, proglang):
+        """
+        @param proglang: the programming language ('python', 'perl' ...) 
+        @type proglang: string
+        @return: True if this Text have a code int his programming language,
+        False otherwise.
+        @rtype: boolean
+        """
+        return proglang in self._code
+
+    def proglangs(self):
+        """
+        @return: a list of string encoding the programming languages of this Text
+        @rtype: list of strings
+        """
+        return self._code.keys()
+
+    def addCode(self, code, proglang='python'):
+        """
+        Set a code for this Text
+        @param code: the code
+        @type code: String
+        @param proglang: the programming language of the text
+        @type proglang: String
+        """
+        self._code[ proglang ]= code
+
+
+    def getCode(self,proglang = 'python'):
+        """
+        Get the code associated with the lang for this Text
+        @param proglang: the programming language of the code
+        @type proglang:the programming language in which the code is written ('perl, 'python')
+        @type proglang: a String 
+        @return: the code
+        @rtype: string
+        @raise MobyleError: a  L{MobyleError} is raised if there isn't any code written in proglang
+        """
+        try:
+            return self._code[ proglang ]
+        except KeyError:
+            raise MobyleError, "no proglang = "+ str( proglang )+ " in this Text"
+        
+    def has_code(self , proglang = 'python'):
+        if proglang in self._code:
+            return bool( self._code[ proglang ] )
+        else:
+            return False
+
+    def has_href(self):
+        """
+        @return: True if the Text has a href, False otherwise.
+        @rtype: boolean
+        """
+        if self._href:
+            return True
+        else:
+            return False
+
+    def hrefs(self):
+        """
+        @return: a list of string encoding  the href.
+        @rtype: list of string
+        """
+        return self._href
+
+    def addHref(self, href):
+        """
+        set a href for this Text
+        @param href: the href it could be a String or a set (liste or tuple) of Strings
+        @type href: String
+        """
+        if type(href) == type([]) or type(href) == type( () ):
+            self._href += href
+        else:
+            self._href.append(href)
+
+
+    def whatIsIt(self):
+        """
+        @return: 'text', 'code' if  'href' the Text is a text a code or a href or None if text is empty.
+        @rtype: string
+        @call: by L{Parameter.doCtrls() <Parameter>}
+        """
+        if self._lang:
+            return 'text'
+        elif self._code:
+            return 'code'
+        elif self._href:
+            return 'href'
+        else:
+            return None
+
+        
+    def __str__(self):
+        return str(self._lang ) + str(self._code) + str (self._href)
+      
+
+
+######################
+#                    #
+#     class Ctrl     #
+#                    #
+######################
+
+
+class Ctrl(object):
+    """
+    is used to handle a ctrl in the mobyle dtd.
+    a ctrl is a message associated with one or more code 
+    """
+
+    def __init__(self):
+        self._code = {}
+        self._message = Text()
+
+    def getMessage(self, lang = None, proglang= None, href= False):
+        """
+        @param lang: is the symbol of a lang in iso639-1 (ex: 'en', 'fr') for the text or 'href' for the link
+        @type lang: String
+        @param proglang: the programming language 
+        @type proglang: String
+        @param href: if the message is a link toward an external page
+        @type href: Boolean
+        @return: The message corresponding to the specified lang or proglang or href
+        @rtype: string
+        @raise MobyleError: if more than one parameter is specified a L{MobyleError} is raised
+        """
+        if ( (proglang and lang) or (proglang and href) or (lang and href) ):
+            raise MobyleError, "invalid argument for getMessage" 
+        if proglang:
+            return self._message.getCode( proglang)
+        elif lang:
+            return self._message.getText( lang)
+        elif href:
+            return self._message.hrefs( )
+        else :
+            raise MobyleError, "invalid argument for message : "+str(lang)+" , "+str(proglang)+" , "+str(href)
+
+           
+
+    def getCode(self,proglang = 'python'):
+        """
+        @param proglang: the programming language 
+        @type proglang: String
+        @return: The code
+        @rtype: string
+        @raise MobyleError: if there is no code written in proglang raise L{MobyleError}
+        """
+        try:
+            return self._code[proglang]
+        except KeyError:
+            raise MobyleError, "no code in "+str(proglang)+" for this control"
+        
+    def proglangs(self):
+        """
+        @return: the list of programming langage of the code associated to this Ctrl.
+        @rtype: list of string
+        """
+        return self._code.keys()
+
+
+    def has_proglang(self, proglang = 'python'):
+        """
+        @param proglang: the programming language
+        @type proglang: String
+        @return: True if False otherwise.
+        @rtype: boolean
+        """
+        return self._code.has_key(proglang)
+
+    def messageHas_lang(self, lang = 'en'):
+        """
+        @param lang: the language of the text, according to the iso639-1
+        @type lang: String
+        @return: True if lang is used to encode the message, False otherwise
+        @rtype: boolean
+        """
+        return self._message.has_lang( lang )
+
+    def messageLangs(self):
+        """
+        @return: the list of the langs used to encode the message
+        @rtype: list of string
+        """
+        return self._message.langs()
+    
+    def messageHas_proglang(self, proglang = 'python'):
+        """
+        @param proglang:
+        @type proglang: String
+        @return: True if the proglang is used to encode the message, False otherwise
+        @rtype: boolean
+        """
+        return self._message.has_proglang(proglang)
+    
+
+    def messageProglangs(self):
+        """
+        @return: the list of the programming languages to encoding the message
+        @rtype: list of string
+        """
+        return self._message.proglangs()
+
+    def messageHrefs(self):
+        """
+        @return: the list of href
+        @rtype: list of strings
+        """
+        return self._message.hrefs()
+    
+    
+    def addMessage(self, content ,lang = None, proglang= None, href= False):
+        """
+        @param content: it must be a text if lang is specified, a code if proglang is specified or a href if href is true 
+        @type content: String
+        @param lang: the language if the content is a text (2 letters code)
+        @type lang: String
+        @param proglang: the programming language if the content is a code
+        @type proglang: String
+        @param href: true if the content is a href
+        @type href: Boolean
+        """
+        if ( (lang and proglang) or (lang and href) or (proglang and href) ):
+            raise MobyleError
+        else:
+            if lang:
+                self._message.addText(content,lang=  lang)
+            elif proglang:
+                self._message.addCode(content, proglang= proglang)
+            elif href:
+                self._message.addHref(content)
+
+    def whatIsIt(self):
+        """
+        @return: 'text', 'code' or 'href' if the message is a text a
+        code or a href or None if the message is empty.
+        @rtype: string
+        """
+        return self._message.whatIsIt()
+    
+        
+    def addCode(self, code ,proglang ):
+        """
+        @param proglang:
+        @type proglang: String
+        @param code:
+        @type code: String
+        """
+        self._code[ proglang ] = code
+        
+        
+        
+class flist(object):
+    
+    def __init__( self ):
+            self._flist = {}
+    
+     
+    def addElemInFlist(self , value , label , codes):
+        """
+        @param value: 
+        @type value: string
+        @param label:
+        @type label: string
+        @param codes:
+        @type codes: dictionary
+        """
+        self._flist[ value ] = ( label , codes )
+        
+    def flistValues(self):
+        """
+        @return: the keys of a flist.
+        @rtype: list of strings
+        @call: by L{Program}.flistValues
+        """
+        return  self._flist.keys()
+
+
+    def flistHas_value(self, value):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @return: True if the flist has a value == value, False otherwise.
+        @rtype: boolean
+        @raise UnDefAttrError: if the parameter hasn't flist a  L{UnDefAttrError}
+        is raised
+        ( called by Parameter.flistProglangs )
+        """
+        return self._flist.has_key(value)
+                           
+
+    def flistProglangs(self, value):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @return: the list of proglang available for a given value.
+        @raise UnDefAttrError: if the parameter hasn't flist a  L{UnDefAttrError} is raised
+        """
+        proglangs = []
+        for value in self._flist.keys():
+            newValues  = self._flist[ value ][1].keys()
+            for newValue in newValues:
+                if newValue not in proglangs:
+                    proglangs.append( newValue )
+        return proglangs
+        
+
+    def flistHas_proglang(self,value,proglang):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: Boolean, True if the flist has the value and a code written in proglang associated with, False otherwise.
+        @rtype: boolean
+        """
+        if self.flistHas_value(value):
+            return self._flist[value][1].has_key( proglang )
+        else:
+            return False
+
+    
+    def getFlistCode(self , value , proglang):
+        """
+        @param value: the value associated to the codes
+        @type value: any
+        @param proglang: the programming language of the code
+        @type proglang: String
+        @return: the code associated with the value and written in proglang.
+        @rtype: string
+        @raise ParamaterError: if there isn't this value or this proglang an  L{ParamaterError} is raised
+        """
+        try:
+            return self._flist[value][1][proglang]
+        except KeyError:
+            raise ParameterError, "the proglang %s is not defined for the flist " % proglang
+            
+class MobyleType(object):
+    
+    def __init__( self , dataType ,
+                  bioTypes = [] , 
+                  dataFormat = None , 
+                  item_nb = None ,
+                  format_program = None ,
+                  acceptedFormats = [] , 
+                  card = ( 1 , 1 ) , 
+                  biomoby = None):
+        """
+        @param bioType: ADN Protein 
+        @type bioType: a list of strings
+        @param dataType: Sequence , Structure , ...
+        @type dataType:  a DataType instance
+        @param format: the real format of the data
+        @type format: string
+        @param acceptedFormats: the format that are allowed for this data and forceformat. 
+         forceformat implied the reformatting of the data either the format match one acceptedFormats.
+        @type acceptedFormats: [ ( string format , boolean force ) , ... ]
+        @param card: min max. min and max could be an integer or n
+        @type card: ( int , int ) or (int , string n) 
+        """
+        if type( bioTypes ) == types.StringType:
+            self.bioTypes = [ bioTypes ]
+        else:
+            self.bioTypes = bioTypes
+        self.dataType = dataType
+        if dataFormat is not None and acceptedFormats:
+            raise MobyleError( "you cannot set acceptedFormat and format for the same MobyleType object" )
+        self.acceptedFormats = acceptedFormats
+        self.dataFormat = dataFormat
+        self.item_nb = item_nb
+        self.format_program = format_program
+        if len( card ) != 2:
+            raise MobyleError( "the cardinality must be a tuple ( min , max )" )
+        self.card = card
+        self.biomoby = biomoby
+    
+    def __str__(self):
+        s = 'dataType : '+str( self.dataType ) 
+        for attr in ( 'bioTypes' , 'dataFormat' , 'item_nb' , 'format_program' ,
+                      'acceptedFormats' , 'card' ,'biomoby' ):
+            s =" %s , %s : %s " %( s , attr , getattr( self , attr ) ) 
+        return s
+    
+    def __eq__(self , other ):
+        if isinstance( self, other.__class__ ):
+            other_acceptedFormats = [ f for f in other.acceptedFormats ]
+            other_acceptedFormats.sort()
+            self_acceptedFormats = [ f for f in self.acceptedFormats ]
+            self_acceptedFormats.sort()
+            if other.bioTypes == self.bioTypes \
+                and other.dataType == self.dataType \
+                and other_acceptedFormats == self_acceptedFormats \
+                and other.dataFormat == self.dataFormat \
+                and other.card == self.card \
+                and other.biomoby == self.biomoby:
+                return True
+        return False
+        
+    def clone(self , dt ):
+        newMT = MobyleType( dt )
+        for attr in self.__dict__.keys() :
+            if attr not in [ "__builtins__" , "dataType"  ]:
+                setattr(newMT , attr , copy.deepcopy( getattr( self , attr ) ) )    
+        return  newMT
+     
+               
+    def toDom( self ):
+        from lxml import etree        
+        typeNode = etree.Element( "type" )
+        if self.bioTypes :
+            for biotype in self.bioTypes :
+                bioTypeNode = etree.Element( "biotype" )
+                bioTypeNode.text = biotype 
+                typeNode.append( bioTypeNode )
+        dataTypeNode = self.dataType.toDom()
+        typeNode.append( dataTypeNode )    
+        if self.acceptedFormats:
+            for format , force in self.acceptedFormats :
+                if force:
+                    formatNode = etree.Element( "dataFormat" , force = "1")
+                else:
+                    formatNode = etree.Element( "dataFormat" )
+                formatNode.text = format 
+                typeNode.append( formatNode )
+        elif self.dataFormat:
+            formatNode = etree.Element( "dataFormat" )
+            formatNode.text = self.dataFormat
+            typeNode.append( formatNode )
+            #ajouter le format_program et item_nb ?
+        if self.card != ( 1 , 1 ):
+            min , max = self.card
+            cardText= "%s,%s"%( min , max )
+            cardNode = etree.Element( "card" )
+            cardNode.text =  cardText 
+            typeNode.append( cardNode )
+        elif self.item_nb :
+            cardNode = etree.Element( "card" )
+            cardNode.text = str( self.item_nb )
+            typeNode.append( cardNode )
+        if self.biomoby:
+            biomobyNode = etree.Element( "biomoby" )
+            biomobyNode.text =  str( self.biomoby ) 
+            typeNode.append( biomobyNode )
+        return typeNode
+    
+    def getBioTypes( self ):
+        return self.bioTypes
+    
+    def getDataType( self ):
+        return self.dataType
+   
+    def convert(self , value , acceptedMobyleType , paramFile= False ):
+        """
+        convert the sequence contain in the file fileName in the rigth format
+        throws an UnsupportedFormatError if the output format is not supported
+        or a MobyleError if something goes wrong during the conversion.
+
+        @param value: is a tuple ( src , srcFileName) 
+          - srcfilename is the name of the file to convert in the src
+          - src must be a L{Job} instance the conversion are perform only by jobs (not session) .
+        @type value: ( L{Job} instance dest, L{Job} instance, src)
+        @return: the fileName ( basename ) of the  sequence file and the effective MobyleType associated to this 
+        value
+        @rtype: ( string fileName , MobyleType instance )
+        @raise UnSupportedFormatError: if the data cannot be converted in any suitable format
+        """
+        outFileName , converted_mt =  self.dataType.convert(value , acceptedMobyleType , detectedMobyleType = self , paramFile = paramFile)
+        if self.bioTypes and converted_mt:
+            converted_mt.bioTypes = [ b for b in self.bioTypes ]
+        return outFileName , converted_mt
+    
+    def detect( self , value ):
+        """
+        detects the format of the sequence(s) contained in fileName
+        @param value: the src object and the filename in the src of the data to detect
+        @type value: tuple ( session/Job/MobyleJob instance , string filename )
+        @return: a tuple of the detection run information:
+            - the detected format,
+            - the detected items number,
+            - program name used for the detection.
+        """
+        detected_mt = self.dataType.detect( value )
+        if self.bioTypes :
+            detected_mt.bioTypes = [ b for b in self.bioTypes ]
+        if self.dataFormat and not detected_mt.dataFormat: #I trust the format provided by Job ...
+            detected_mt.dataFormat = self.dataFormat
+        return detected_mt
+    
+    def getAcceptedFormats( self ):
+        return self.acceptedFormats
+    
+    def setAcceptedFormats( self , acceptedDataFormats ):
+        if self.dataFormat:
+            raise MobyleError("you cannot set acceptedFormat and format for the same MobyleType object" )
+        self.acceptedFormats = acceptedDataFormats
+        
+    def getDataFormat( self ):
+        return self.dataFormat
+    
+    def setDataFormat(self , format):
+        if self.acceptedFormats:
+            raise MobyleError("you cannot set acceptedFormat and format for the same MobyleType object" )
+        self.dataFormat = format
+            
+    def getItemNumber( self ):
+        return self.item_nb
+    
+    def setItemNumber( self , nb ):
+        self.item_nb = nb
+    
+    def getFormatProgram( self ):
+        return self.format_program 
+
+    def getCard( self ):
+        return self.card
+    
+    def getBioMoby( self ):
+        return self.biomoby
+    
+    def isPipeableTo( self , inputMT ):
+        """
+        @param inputMT: The "target" input parameter type
+        @type inputMT: MobyleType
+        @return: True if the data that have this type can be piped to a program input with a MobyleType defined as inputMT
+        @rtype: boolean
+        """
+        if self._isBioTypePipeable( inputMT.getBioTypes() ) and self.dataType.isPipableToDataType( inputMT.getDataType() ) and self._isCardPipeable( inputMT.getCard() ):
+            return True
+        else:
+            return False
+        
+        
+    def _isBioTypePipeable(self , inputBioTypes ):         
+        """
+        @param inputBioTypes: The "target" input parameter biotype
+        @type inputBioTypes: List
+        @return: True if the data that have this biotype can be piped to a program input with a biotype defined as inputBioTypes
+        @rtype: boolean
+        """
+        for bioType in inputBioTypes:
+            if bioType in self.bioTypes:
+                return True
+        return False
+    
+    def _isCardPipeable( self , inputCard ):
+        """
+        @param inputCard: The "target" input parameter cardinality
+        @type inputCard: tuple
+        @return: True if the data that have this cardinality can be piped to a program input with a cardinality defined as inputCard
+        @rtype: boolean
+        """
+        inputMin , inputMax = inputCard
+        if  inputMax == 'n':
+                return True
+        else:
+            if type( self.card[1] ) == types.IntType and inputMax < self.card[1]: 
+                return True
+        return False
+        
+        
+    def isFile(self):
+        """
+        @return: True if the datatype corespond to a file , false otherwise
+        @rtype: boolean
+        """
+        return self.dataType.isFile()
+    
+    def isMultiple(self):
+        """
+        @return: True if the datatype corespond to a Multiple input , false otherwise
+        @rtype: boolean
+        """
+        return self.dataType.isMultiple()
+    
+    
+                
+    def toFile(self , data  , dest , destFileName , src , srcFileName ):
+        if self.dataType.isFile():
+            return self.dataType.toFile( data  , dest , destFileName , src , srcFileName )
+        else:
+            raise MobyleError( "this data is not a file" )
diff --git a/Src/Mobyle/Session.py b/Src/Mobyle/Session.py
new file mode 100644
index 0000000..f7f079e
--- /dev/null
+++ b/Src/Mobyle/Session.py
@@ -0,0 +1,1186 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import sys
+import os 
+import types
+import glob
+from time import time , strptime 
+import urlparse
+from httplib import HTTPConnection
+from logging  import getLogger
+from lxml import etree
+
+from Mobyle.Utils import safeFileName , sizeFormat
+from Mobyle.JobState import JobState , path2url , url2path
+from Mobyle.MobyleError import MobyleError , UserValueError , SessionError , NoSpaceLeftError 
+from Mobyle.JobFacade import JobFacade
+from Mobyle.BMPSWorkflow import BMPSWorkflow
+from Mobyle.Registry import registry
+
+from Mobyle.Transaction import Transaction
+
+
+class Session( object ):
+    """
+    This class defines a session, that stores all the information
+    about a user that should be persistent on the server
+    @author: Bertrand Neron
+    @organization: Institut Pasteur
+    @contact: mobyle at pasteur.fr
+    """
+
+    FILENAME = '.session.xml'
+
+    def __init__( self , Dir , key , cfg ):
+        
+        self.log = getLogger( 'Mobyle.Session' )
+        self.cfg = cfg
+        """the maximum size of a session ( in bytes )"""
+        self.sessionLimit = self.cfg.sessionlimit()
+        self.key = str( key )
+        """ the user/session  key"""
+        self.Dir = Dir
+        """ the absolute path to this session directory """
+
+
+    def isLocal(self):
+        """
+        we cannot instanciate a distant session
+        """
+        return True
+   
+    def getDir( self ):
+        """
+        @return: the absolute path to this session
+        @rtype: string
+        """
+        return self.Dir
+    
+    def getKey( self ):
+        """
+        @return: the session key
+        @rtype: string
+        """
+        return  self.key
+
+    def _getTransaction( self , Type ):
+        """
+        @return: the transaction of this session
+        @rtype: a L{Transaction} object
+        @raise SessionError: if can't access to the session
+        """
+        fileName = os.path.normpath( os.path.join( self.Dir , Session.FILENAME  ) )
+        try:
+            return Transaction( fileName , Type )
+        except Exception , err:
+            msg = "can't open transaction %s : %s" % ( self.getKey() , err )
+            self.log.error( msg , exc_info= True )
+            raise SessionError , "can't open user session. Please contact <%s> for help" % self.cfg.mailHelp()
+      
+       
+    def getBaseInfo( self ):
+        """
+        @return: 3 basic informations about this session , the email , isAuthenticated , isActivated
+        @rtype: ( string email , boolean isAuthenticated , boolean isActivated )
+        """
+        transaction = self._getTransaction( Transaction.READ  )
+        response = ( transaction.getEmail() , transaction.isAuthenticated() , transaction.isActivated() )
+        transaction.commit( )
+        self.log.debug( "%f : %s : baseInfo return : %s" %( time() , self.getKey() , response ) )
+        return response
+   
+    def isActivated( self ) :
+        """
+        @return: True if the session is activated, False otherwise
+        @rtype: boolean
+        """
+        transaction = self._getTransaction( Transaction.READ  )
+        activated = transaction.isActivated()
+        transaction.commit()      
+        return activated
+    
+    def  getEmail(self):
+        """
+        @return: the user email address
+        @rtype: string
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        email = transaction.getEmail()
+        transaction.commit()
+        return email
+
+    def setEmail( self, email ):
+        """
+        set the user email in the session
+        @param email: the user email
+        @type email: a Mobyle.Net.Email instance
+        """
+        if  isinstance( email , types.StringTypes ):
+            from Mobyle.Net import EmailAddress
+            email = EmailAddress( email )
+        resp = email.check()
+        if not resp.status:
+            remoteLog = "UNKNOWN"
+            try:
+                remoteLog = os.environ[ 'REMOTE_ADDR' ]
+            except KeyError:
+                pass
+            try:
+                remoteLog = os.environ[ 'REMOTE_HOST' ]
+            except KeyError:
+                pass
+                
+            msg = "session/%s %s %s FORBIDDEN %s" % ( self.getKey() ,
+                                                      unicode( str( email ).decode('ascii', 'replace')).encode( 'ascii', 'replace' ) ,
+                                                      remoteLog ,
+                                                      resp.message
+                                                    )
+    
+            self.log.warning( msg )
+            raise UserValueError( msg = resp.user_message )
+            
+        transaction = self._getTransaction( Transaction.WRITE )
+        transaction.setEmail( str(email) )
+        transaction.commit()
+
+    ##############################
+    #
+    #   data methods
+    #
+    ###############################
+
+    def _cleanData( self , content , dataType ):
+        """
+        clean the data send by the user, the cleaning depends of the parameter ( CRLF for Text etc...)
+        and calculate it's md5. the md5 is based on the data content and the datatype
+        @return: the data after cleaning , and the md5 
+        @rtype: ( string md5 , string data content )
+        """
+        from hashlib import md5
+        content = dataType.cleanData( content )
+        newMd5 = md5()
+        newMd5.update( content )
+        #the md5 is made on the content + the name of the datatype
+        #this permit us to have the same content wit 2 datatype.
+        #eg a fasta alignment could be used as alignment or sequence        
+        newMd5.update( dataType.getName() )
+        dataID = newMd5.hexdigest()
+        return dataID , content
+    
+    
+    def addData( self , name  , mobyleType , content = None , producer = None , inputModes = [] , usedBy = [] , producedBy = []):
+        """
+        @param name: the data name
+        @type name: string
+        @param mobyleType: the type of the data
+        @type mobyleType: a L{MobyleType} instance
+        @param content: the content of the data
+        @type content: string
+        @param producer: where to find the data if content is None producer must be specify and vice & versa
+        @type producer: a L{Job} , a L{MobyleJob} , or a L{Session} object
+        @param inputModes: the source of data
+        @type inputModes: a list of string among this values : 'db' , 'paste' , 'upload' , 'result'
+        @param usedBy: jobID(s) which used this data as input
+        @type usedBy: string or sequence of strings
+        @param producedBy: jobID(s) which produced this data
+        @type producedBy: string or sequence of strings   
+        @return: the identifier of the data in this session
+        @rtype: string
+        """
+        if not mobyleType.isFile():
+            raise MobyleError , "session/%s : Session could add only MobyleType which are file : %s" % ( self.getKey() , mobyleType.getDataType().getName() )
+        #transaction = self._getTransaction( Transaction.WRITE )
+
+        if content and producer :
+            raise MobyleError , "you must specify either a content or a producer not the both"
+        elif not ( content or producer ) :
+            raise MobyleError , "you must specify either a content or a producer "
+
+        self.log.info( "%f : %s : addData name = %s, producer= %s, mobyleType=%s, usedBy=%s, producedBy=%s, inputModes=%s" %(time() ,
+                                                                                                                             self.getKey(),
+                                                                                                                            name,
+                                                                                                                            producer,
+                                                                                                                            mobyleType,
+                                                                                                                            usedBy,
+                                                                                                                            producedBy,
+                                                                                                                            inputModes
+                                                                                                                            ))
+           
+
+        safeUserName = safeFileName( name )
+        acceptedInputMode = ( 'db' , 'paste' , 'upload' , 'result' )
+        if safeUserName == Session.FILENAME :
+            #logger l'erreur dans adm et logs?
+            raise MobyleError , "permission denied"
+        if inputModes :   
+            inputModesType = type( inputModes ) 
+            if inputModesType == types.StringType:
+                inputModes = [ inputModes ]
+            for inputMode in inputModes :
+                if inputMode not in acceptedInputMode :
+                    raise MobyleError , "unknown source of data : %s" % inputMode
+        if usedBy:
+            jobType = type( usedBy )
+            if  jobType == types.StringType :
+                usedBy = [ usedBy ]
+            elif jobType ==  types.ListType or jobType == types.TupleType:
+                pass
+            else:
+                raise MobyleError
+        else:
+            usedBy = []
+            
+        if producedBy:
+            jobType = type( producedBy )
+            if  jobType == types.StringType :
+                producedBy = [ producedBy ]
+            elif jobType ==  types.ListType or jobType == types.TupleType:
+                pass
+            else:
+                raise MobyleError
+        else:
+            producedBy = []      
+       
+        # to avoid md5 differences depending on the web client or platform
+        # I clean the content prior to perform the md5
+        # be careful the cleaning must be adapted to the parameter
+        # it's not the same between text and binary parameter
+        dataType = mobyleType.getDataType()
+        if producer and isinstance( producer , Session ):
+            #producer is a Session
+            #I copy the metadata about the data from the session source
+            try:
+                data = producer.getData( name )
+            except ValueError:
+                msg = "the data %s does not exist in the session %s" % ( name , self.getKey() )
+                self.log.error( msg )
+                raise SessionError , msg
+                
+            nameInProducer = data[ 'dataName' ]
+            safeUserName   = data[ 'userName' ]
+            mobyleType     = data[ 'Type' ]
+            dataBegining   = data[ 'dataBegin' ]
+            usedBy         = data[ 'usedBy' ]
+            producedBy     = data[ 'producedBy' ]
+            inputModes     = data[ 'inputModes' ]
+            dataID  = str( nameInProducer )
+         
+            dataMask = os.path.join( self.Dir , dataID + "*" )
+         
+        else:
+            if content :
+                nameInProducer = None
+                dataID , content = self._cleanData( content , dataType )
+                dataBegining = dataType.head( content )
+            else :
+                #there is a producer and it's a jobState or MobyleJob ...
+                #I must read the file to compute the md5
+
+                #the name is Safe            
+                fh = producer.open( name )
+                content = fh.read()
+                fh.close() 
+                nameInProducer = name    
+                dataID , content = self._cleanData( content , dataType )
+                dataBegining = dataType.head( content )
+                content = None 
+                
+            userExt = os.path.splitext( safeUserName )[1]
+            dataMask = os.path.join( self.Dir , dataID + "*" )
+            dataID = str( dataID + userExt )
+
+        #for all 
+        #absDataPath = os.path.join( self.Dir , dataID )
+        files_exists = glob.glob(  dataMask  )
+      
+        if files_exists : 
+            #it's important to recalculate the name in session 
+            #because the ext must be different ( upload / copy&paste )
+            #and the name return by file_exist is an absolute name and a local name in the data structure
+            dataID = os.path.basename( files_exists[0] )
+            transaction = self._getTransaction( Transaction.READ )
+            try:
+                data  = transaction.getData( dataID )
+                transaction.commit()
+            except ValueError : #the file exist but there is no entry in session
+                transaction.rollback()
+                msg = "inconsistency error between the session object and the directory"
+                self.log.error("session/%s addDatas: %s" %( self.getKey() , msg ))
+                try:
+                    name2remove = os.path.join( self.Dir , dataID )
+                    self.log.error("session/%s : remove the data %s before to add a new one " %( self.getKey() , name2remove ) )
+                    os.unlink( name2remove )
+                except IOError , err:
+                    self.log.error("session/%s : can't remove it before to add a new one : %s" %( self.getKey() , err ) )
+                #I add the data in transaction 
+                self._createNewData( safeUserName , 
+                                     content , 
+                                     dataID , 
+                                     producer , 
+                                     nameInProducer , 
+                                     mobyleType , 
+                                     dataBegining , 
+                                     producedBy = producedBy ,
+                                     usedBy = usedBy ,
+                                     inputModes = inputModes )
+ 
+                return dataID 
+
+            # the file exist and the data has already an entry in session.xml. thus I don't create a new data 
+            # but this data is used/produced by a new job
+            # furthermore, to ensure the coherence between data and jobs I check if the data is in job
+            transaction = self._getTransaction( Transaction.WRITE )
+            if usedBy:
+                transaction.linkJobInput2Data( [ dataID ] , usedBy )
+            if producedBy:
+                transaction.linkJobOutput2Data( [ dataID ] , producedBy )
+            if inputModes:
+                transaction.addInputModes(dataID,inputModes)
+            transaction.commit()
+            return dataID 
+     
+        else: #this data doesn't exist in Session
+            # the disk writing operation or link is made by setValue.
+            # more precisely by _toFile called by convert called by setValue of this parameter
+            self._createNewData( safeUserName , 
+                                 content , 
+                                 dataID , 
+                                 producer , 
+                                 nameInProducer , 
+                                 mobyleType , 
+                                 dataBegining , 
+                                 usedBy = usedBy , 
+                                 producedBy = producedBy ,
+                                 inputModes = inputModes )
+            return dataID 
+   
+    def addWorkflow(self, workflow):
+        """
+        create a new workflow (or save a given Workflow object with a new ID)
+        @param workflow: the workflow to be saved
+        @type workflow: Workflow
+        @return: the identifier of the workflow in this session
+        @rtype: string
+        """
+        transaction = self._getTransaction( Transaction.WRITE )
+        # set id
+        ids = [value['id'] for value in transaction.getWorkflows()]
+        if ids:
+            ID = max(ids)+1
+        else:
+            ID = 1
+        workflow.id = ID
+        self.saveWorkflow(workflow)
+        transaction.addWorkflowLink(workflow.id)
+        transaction.commit()
+        return workflow.id
+
+    def getWorkflows(self):
+        """
+        get the list of workflow definitions in the session.
+        @return: workflow definition identifiers list
+        @rtype: [ {'id':string } , ... 
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        workflows = transaction.getWorkflows()
+        transaction.commit()
+        return workflows  
+
+    def getBMPSWorkflows(self):
+        """
+        get the list of workflow definitions in the session.
+        FIXME: replace with getWorkflows
+        @return: workflow definition identifiers list
+        @rtype: [ {'id':string } , ... 
+        """
+        session_workflow_path_list = glob.glob(os.path.join(self.getDir(), 'BMW', '*_mobyle.xml'))
+        session_workflow_list = {}
+        for p in session_workflow_path_list:
+            w = BMPSWorkflow.get_from_mobylexml_filepath(p, self)
+            session_workflow_list[w.pid] = {'id':w.name}
+        #session_workflow_list = dict((os.path.basename( p[:-11] ),{'id':os.path.basename( p[:-11] )}) for p in session_workflow_path_list)
+        return session_workflow_list
+
+    def saveWorkflow(self, workflow):
+        """
+        save a new workflow version
+        @param workflow: the workflow to be saved
+        @type workflow: Workflow
+        """
+        # save workflow
+        wffile = open( os.path.join(self.getDir(),'workflow%s.xml' % str(workflow.id)) , 'w' )
+        wffile.write( etree.tostring(workflow, xml_declaration=True , encoding='UTF-8', pretty_print= True )) 
+        wffile.close()
+
+    def readWorkflow(self, ID):
+        """
+        read a workflow from the session
+        @param ID: the id of the workflow to be read
+        @type ID: string
+        """
+        # save workflow
+        from Mobyle.Workflow import parser, MobyleLookup
+        parser.set_element_class_lookup(MobyleLookup())
+        return etree.parse(os.path.join(self.getDir(), 'workflow%s.xml' % str(ID)), parser).getroot()
+
+    def getWorkflowUrl(self, ID):
+        """
+        return the url of a workflow
+        @param id: the id of the workflow to be read
+        @type id: string
+        """
+        return self.url+'/workflow%s.xml' % ID
+
+    def _createNewData(self, safeUserName, content, dataID, producer, nameInProducer, mobyleType, dataBegining, usedBy = None, producedBy = None,  inputModes = None ):
+        """
+        @param safeUserName: the user name for this  data
+        @type safeUserName: string
+        @param content: the content of the data
+        @type content: string
+        @param dataID: the identifier of this data in the session
+        @type dataID: string
+        @param producer: where to find the data if content is None producer must be specify and vice & versa
+        @type producer: a L{Job} , a L{MobyleJob} , or a L{Session} object
+        @param nameInProducer: the name of this data in the producer
+        @type nameInProducer: string
+        @param mobyleType: the MobyleType corresponding to this data
+        @type mobyleType: L{MobyleType} instance
+        @param dataBegining: the beginning (50 first chars )of the data
+        @type dataBegining: string
+        @param usedBy: jobID(s) which used this data as input
+        @type usedBy: string or sequence of strings
+        @param producedBy: jobID(s) which produced this data
+        @type producedBy: string or sequence of strings   
+        @param inputModes: the source of data
+        @type inputModes: a list of string among this values : 'db' , 'paste' , 'upload' , 'result'
+        @return: the identifier of the data in this session
+        @rtype: string
+        """     
+        self.log.info( "%f : %s : _createNewData safeUserName = %s, dataID= %s, producer= %s, nameInProducer= %s, mobyleType=%s, usedBy=%s, producedBy=%s, inputModes=%s" %(time() ,
+                                                                                                                                                                            self.getKey(),
+                                                                                                                                                                            safeUserName,
+                                                                                                                                                                            dataID,
+                                                                                                                                                                            producer,
+                                                                                                                                                                            nameInProducer,
+                                                                                                                                                                            mobyleType,
+                                                                                                                                                                            usedBy,
+                                                                                                                                                                            producedBy,
+                                                                                                                                                                            inputModes
+                                                                                                                                                                            ) )
+        try:
+            #                 mobyleType.toFile( data    , dest , destFileName , src      , srcFileName     )
+            fileName , size = mobyleType.toFile( content , self , dataID       , producer , nameInProducer  )
+            detectedMobyleType = mobyleType.detect( ( self, fileName ) )
+        except MobyleError , err : 
+            self.log.error("_createNewData dans MobyleError: " , exc_info = True  )
+            try:
+                os.unlink( os.path.join( self.Dir , dataID ) )
+            except OSError :
+                pass
+            raise MobyleError , err
+        except Exception , err:
+            self.log.critical( "Exception in _createNewData: %s" % self.getKey(), exc_info = True )
+            raise err
+        dataID = fileName
+          
+        if not self._checkSpaceLeft():
+            path = os.path.join( self.Dir , dataID )
+            size = os.path.getsize( path )
+            os.unlink( path )
+            self.log.info( "%f : %s : _addData unlink %s ( %d ) because there is no space in session"%( time() ,
+                                                                                                       self.getKey(),
+                                                                                                       dataID ,
+                                                                                                       size
+                                                                                                       ))
+            self.log.error( "session/%s : the data %s ( %d ) cannot be added because the session size exceeds the storage limit ( %d )" %(
+                                                                                                                                      self.getKey() ,           
+                                                                                                                                      dataID ,
+                                                                                                                                      size ,
+                                                                                                                                      self.sessionLimit
+                                                                                                                                      ) )
+         
+            raise NoSpaceLeftError , "this data cannot be added to your bookmarks, because the resulting size exceeds the storage limit ( %s )" % sizeFormat( self.sessionLimit )
+        #dataID , userName , size , dataBegining , inputModes , format = None , producedBy = [] , usedBy = []    
+        #must be clean only the format information must be uniq
+        transaction = self._getTransaction( Transaction.WRITE )
+        #self.log.error( "self.Dir ="+str( self.Dir )+", dataID  = "+str( dataID ) ) 
+        try:
+            transaction.createData( dataID , 
+                                    safeUserName , 
+                                    os.path.getsize( os.path.join( self.Dir , dataID ) ) ,
+                                    detectedMobyleType , 
+                                    dataBegining ,
+                                    inputModes , 
+                                    usedBy = usedBy  ,
+                                    producedBy = producedBy ,
+                                    )
+        except Exception, err:
+            path = os.path.join( self.Dir , dataID )
+            os.unlink( path )
+            msg = "cannot create metadata in .session.xml for data named: %s (dataID= %s): %s"%( safeUserName,
+                                                                                                           dataID ,
+                                                                                                           err)
+            self.log.error( msg )
+            raise SessionError( msg )
+        
+        if usedBy:
+            transaction.linkJobInput2Data( [ dataID ] , usedBy )
+        if producedBy:
+            transaction.linkJobOutput2Data( [ dataID ] , producedBy )
+        transaction.commit()
+        return dataID        
+       
+
+    def _checkSpaceLeft( self ):
+        """
+        @return: True if there is space disk available for this session, False otherwise
+        @rtype: boolean
+        """
+        sessionSize = 0
+        for f in os.listdir( self.Dir ):
+            sessionSize += os.path.getsize( os.path.join( self.Dir  , f ) )
+            if sessionSize > self.sessionLimit:  
+                self.log.debug( "%f : %s : _checkSpaceLeft call by= %s size found = %d" %( time() ,
+                                                                                       self.getKey()  , 
+                                                                                       os.path.basename( sys.argv[0] ) ,
+                                                                                       sessionSize
+                                                                                       )) 
+                return False
+        return True       
+       
+
+    def removeData( self , dataID ):
+        """
+        remove the data from this session ( from the xml and the directory )
+        @param dataID: the data identifier in this session
+        @type dataID:string
+        @raise SessionError: if the dataID does not match any data in the session
+        """
+        self.log.info( "%f : %s : removeData dataID= %s" %(time() ,
+                                                           self.getKey(),
+                                                           dataID
+                                                           ) )
+        if dataID == Session.FILENAME :
+            self.log.error( "session/%s : can't remove file %s" %( self.getKey(), Session.FILENAME ) )
+            raise MobyleError, "permission denied"
+
+        fileName = os.path.join( self.Dir , dataID  )
+
+        if os.path.exists( fileName ):
+            try:
+                os.unlink( fileName )
+            except OSError , err:
+                msg = str( err )
+                self.log.critical( "session/%s : can't remove data : %s" %( self.key , msg ) )
+                raise MobyleError , msg
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            transaction.removeData( dataID )
+        except ValueError , err :
+            transaction.rollback()
+            self.log.error( "session/%s : can't remove data in %s : %s" %( self.getKey() ,
+                                                                      self.FILENAME ,
+                                                                      err 
+                                                                    ) 
+                          )
+            raise SessionError, err
+        else:
+            transaction.commit()
+       
+       
+    def renameData( self , dataID  , newUserName ):
+        """
+          @param dataID: the identifier of the data in the session ( md5 )
+          @type dataID: string
+          @param newUserNAme: the new user name of this data
+          @type newUserName: string
+          @raise SessionError: if the dataID does not match any data in this session
+        """
+        self.log.info( "%f : %s : renameData dataID= %s, newUserName= %s" %(time() ,
+                                                                            self.getKey(),
+                                                                            dataID,
+                                                                            newUserName
+                                                                            ) )
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            data = transaction.renameData( dataID , newUserName )
+        except ValueError , err :
+            transaction.rollback()
+            raise SessionError , err
+        else:
+            transaction.commit()       
+       
+       
+    def getContentData( self , dataID , forceFull = False ):
+        """
+        @param dataID: the identifier of the data in the session.
+        @type dataID: string
+        @return: the head or full content of the data
+        @rtype: tuple ( string 'HEAD'/'FULL' , string content )
+        @raise SessionError: if dataID doesn't match any data in session
+        """
+        maxSize = self.cfg.previewDataLimit()
+        filename = os.path.join( self.Dir , dataID  )
+      
+        if not os.path.exists( filename ):
+            transaction = self._getTransaction( Transaction.READ )
+            hasData = transaction.hasData( dataID )
+            transaction.commit()
+            if hasData: #the data is not in the session directory But in the session data structure
+                self.log.error( "session/%s is corrupted. The data %s is not in directory But in data structure" % ( self.getKey(),
+                                                                                                                    dataID
+                                                                                                                    )
+                                                                                                                    )
+                transaction = self._getTransaction( Transaction.WRITE )
+                transaction.removeData( dataID )
+                transaction.commit()
+                self.log.error( "session/%s remove data %s" % ( self.getKey() , dataID ))
+            else: #the data is neither in the session directory nor the session data structure 
+                raise SessionError, "%s  No such data in session: %s" % ( self.getKey() , dataID )
+      
+        fh = self.open( dataID )
+        if forceFull:
+            content = fh.read()
+            flag = 'FULL'
+        else:
+            dataSize = os.path.getsize( filename )
+            if dataSize > maxSize :
+                content = fh.read( maxSize )
+                flag = 'HEAD'
+            else:
+                content = fh.read()
+                flag = 'FULL'            
+      
+        fh.close()
+        return ( flag , content )
+  
+  
+  
+    def open( self , dataID ):
+        """
+        @param dataID: the identifier of the data in the session.
+        @type dataID: string
+        @return: a file object
+        @retype: 
+        @raise SessionError: if dataID does not match any data in session
+        """
+        self.log.debug( "%f : %s  open dataName = %s" %( time(),
+                                                      self.getKey() ,
+                                                      dataID 
+                                                      ))
+        if dataID == Session.FILENAME:
+            raise MobyleError , "permission denied"
+        try:
+            fh = open( os.path.join( self.Dir , dataID ), 'r' )
+            return fh
+        except IOError, err:
+            #verifier si la donnee est toujours dans la session
+            self.log.error( "session/%s : open : %s" %( self.getKey() , err ) )
+            raise SessionError , err  
+  
+  
+  
+    def getDataSize(self , dataID ):
+        """
+        @param dataID: the identifier of the data in the session.
+        @type dataID: string
+        @return: the size of the data in byte
+        @rtype: int
+        """
+        #I don't use the size stored in transaction.datas[ 'size' ] 
+        #to avoid to put a lock on the .session file
+        dataPath =  os.path.join( self.Dir , dataID )
+        if os.path.exists(  dataPath ):
+            return os.path.getsize( dataPath )
+        else:
+            msg = "getDataSize dataID %s does not match any data in the session %s" % (dataID , self.getKey() ) 
+            self.log.error( msg )
+            raise SessionError , msg 
+
+
+    def hasData( self , dataID ):
+        """
+        @param dataID: the identifier of this data in the session 
+        @type dataID: string
+        @return: True if this session has an entry for this data, False otherwise.
+        this method does not test the existence of the data as file in session.
+        @rtype: boolean        
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        hasData = transaction.hasData( dataID )
+        transaction.commit()
+        return hasData
+    
+    def getAllData( self ):
+        """
+        @return: the all data in the session. 
+        @rtype: list of dict
+                 [   {  'dataName'  : string ,
+                        'userName'  : string ,                
+                        'Type'      : ( string  , string )
+                        'dataBegin' : string , 
+                        'inputModes': list of strings ,
+                        'producedBy': list of strings ,
+                         'usedBy'   : list of strings 
+                    } , ... 
+                 ]   
+        @raise SessionError: 
+        """
+
+        transaction = self._getTransaction( Transaction.READ )
+        datas = transaction.getAllData()
+        transaction.commit()
+        return datas  
+
+    
+    def getData(self , dataID ):
+        """
+        @param dataID: the ID of the data in the session
+        @type dataID: string or sequence of string
+        @return: the data in the session corresponding to dataID 
+        @rtype: dict
+                  { 'dataName'  : string ,
+                    'userName'  : string ,                
+                    'type'      : MobyleType
+                    'dataBegin' : string , 
+                    'inputModes': list of strings ,
+                    'producedBy': list of strings ,
+                    'usedBy'   : list of strings 
+                } 
+        @raise SessionError: if dataID does not match any data in session     
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        try:
+            data = transaction.getData( dataID )
+            transaction.commit()
+        except ValueError:
+            transaction.rollback()
+            msg = "getData dataID %s does not match any data in the session %s" % (dataID , self.getKey() ) 
+            self.log.error(msg)
+            raise SessionError , msg
+        return data
+  
+
+    def __extractServiceName( self , jobID ):
+        """
+        @param jobID: the url of a job 
+        @type job: string
+        @return: the program name for a job
+        @rtype string
+        """
+        s = jobID.split('/')
+        if s[-1] == '' or s[-1] == 'index.xml':
+            return s[ -3  ]
+        else:
+            return s[ -2 ]
+
+
+    def getAllUniqueLabels( self):
+        """
+        set description for the jobId
+        """
+        transaction= self._getTransaction(Transaction.READ)
+        
+        labelsList = transaction.getAllUniqueLabels()
+        transaction.commit()
+        return labelsList
+
+    def getJobLabels( self, jobID ):
+        """
+        @return: the labels for job with identifier jobID
+        @rtype: list of strings , [ string label1 ,string label2 ,...] or None if this jobs has no labels. 
+        @param jobID: the job identifier (it's url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        transaction= self._getTransaction(Transaction.READ)
+        labels = transaction.getJobLabels( jobID )
+        transaction.commit()
+        return labels
+    
+    def setJobLabels( self, jobID, inputLabels):
+        """
+        set labels for job with  jobId
+        @param jobID: the job identifier (it's url) of a job
+        @type jobID: string
+        @param description: the description of this job
+        @type description: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        transaction= self._getTransaction(Transaction.WRITE)
+        transaction.setJobLabels( jobID, inputLabels)
+        transaction.commit()
+
+
+    def setJobDescription( self, jobID, description):
+        """
+        set description for the job with jobId
+        @param jobID: the job identifier (it's url) of a job
+        @type jobID: string
+        @param description: the description of this job
+        @type description: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        transaction= self._getTransaction(Transaction.WRITE)
+        transaction.setJobDescription( jobID, description )
+        transaction.commit()
+
+    def getJobDescription( self, jobID):
+        """
+        @return: the description for job with identifier jobID
+        @rtype: string or None if there is no description for this job 
+        @param jobID: the job identifier (it's url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        transaction= self._getTransaction(Transaction.READ)
+        desc = transaction.getJobDescription( jobID )
+        transaction.commit()
+        return desc
+      
+    def getJobUserName( self, jobID):
+        """
+        @return: the user name for job with identifier jobID
+        @rtype: string or None if there is no user name for this job 
+        @param jobID: the job identifier (it's url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        transaction= self._getTransaction(Transaction.READ)
+        user_name = transaction.getJobUserName( jobID )
+        transaction.commit()
+        return user_name
+      
+
+    def hasJob(self , jobID ):
+        """
+        @param jobID: the url of a job 
+        @type job: string
+        @return: True if this session has an entry for this job, False otherwise.
+        this method does not test the existence of the job as directory.
+        @rtype: boolean        
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        hasJob = transaction.hasJob( jobID )
+        transaction.commit()
+        return hasJob
+    
+
+    def getAllJobs( self ):
+        """
+        @return: the list of jobs (and update their status) 
+        @rtype: list of dictionary = [  { 'jobID'       : string ,
+                                          'userName'    : string , 
+                                          'programName' : string ,
+                                          'date'        : time.time_struct ,
+                                          'status'      : Status.Status ,
+                                          'dataUsed'    : [ md5name ] ,
+                                          'dataProduced : [ md5name ] ,
+                                        } , ...
+                                    ]
+        """
+        results = []
+        job2remove = []
+        job2updateStatus = []
+        
+        transaction = self._getTransaction( Transaction.READ )
+        jobs = transaction.getAllJobs()
+        transaction.commit()
+        for job in jobs:
+            jobExist = self.jobExists(job[ 'jobID' ])
+            if jobExist == 1: #yes
+                try:
+                    job[ 'jobPID' ] = registry.getJobPID(job['jobID'])
+                    jf = JobFacade.getFromJobId( job[ 'jobID' ] )
+                    results.append( job )
+                    job['subjobs']  = jf.getSubJobs() 
+                    if job[ 'status' ].isQueryable():
+                        newStatus = jf.getStatus()
+                        if newStatus != job[ 'status' ]:
+                            job[ 'status' ] = newStatus
+                            job2updateStatus.append( ( job[ 'jobID' ] , newStatus ) )
+                except MobyleError, me:
+                    self.log.error( "problem accessing job %s: job is removed from session %s (%s)" %( job[ 'jobID' ] , self.getKey(), me.message ) )
+                    job2remove.append(job[ 'jobID' ])
+            elif jobExist == 2 :# maybe
+                results.append(job)
+            else: #the job does not exists anymore
+                job2remove.append(job[ 'jobID' ])
+        
+        if job2remove or job2updateStatus :
+            transaction = self._getTransaction(Transaction.WRITE)
+            for jobID in job2remove:
+                try:
+                    transaction.removeJob( jobID )
+                except ValueError:
+                    continue
+            for jobID , newStatus in job2updateStatus:
+                try:
+                    transaction.updateJobStatus( jobID , newStatus )
+                except ValueError:
+                    self.log.error( "can't update status. the job %s does not exist in session %s" %( jobID , self.getKey() ) )
+                    continue
+            transaction.commit()
+        return results
+
+    def getJobBMPS(self,jobID):
+        jobs=self.getAllJobs()
+        flattened_jobs = []
+        for j in jobs:
+            flattened_jobs.append(j)
+            flattened_jobs.extend(j['subjobs'])
+        j_list = [j for j in flattened_jobs if j['jobID']==jobID]
+        return j_list.pop() if len(j_list)>0 else None
+    
+    
+    def getJob(self , jobID ):
+        """
+        @param jobID: the url of a job 
+        @type job: string
+        @return: the job corresponding to the jobID
+        @rtype: dict { 'jobID'       : string ,
+                       'userName'    : string , 
+                       'programName' : string ,
+                       'date'        : time.time_struct ,
+                       'status'      : Mobyle.Status.Status instance,
+                       'dataUsed'    : [ string md5name ] ,
+                       'dataProduced : [ string md5name ] ,
+                     }
+        @raise SessionError: if the jobID does not match any job in this session
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        try:
+            job = transaction.getJob( jobID )
+        except ValueError:
+            transaction.rollback()
+            msg = "getJob jobID %s does not match any job in the session %s" %(jobID , self.getKey() ) 
+            self.log.error(msg )
+            raise SessionError , msg
+        
+        transaction.commit()
+        jobExist = self.jobExists( jobID ) 
+        if jobExist == 1: #yes 
+            if job[ 'status' ].isQueryable():
+                #cannot be replaced by call to StatusManager.getStatus
+                #because the job may be remote
+                jf = JobFacade.getFromJobId( jobID )
+                newStatus = jf.getStatus() 
+                if newStatus != job[ 'status' ]:
+                    job[ 'status' ] = newStatus
+                    try:
+                        transaction = self._getTransaction( Transaction.WRITE )
+                        transaction.updateJobStatus(  jobID , newStatus  )
+                        transaction.commit()
+                    except ValueError:
+                        transaction.rollback()
+                        self.log.error( "can't update status. the job %s does not exist in session %s" % (jobID, self.key ))                    
+                        #inconsistance entre la transaction et mon resultat
+                        #la transaction a evolue de puis le getJob, via un acces concurrent ??
+                        return None
+            return job
+        elif jobExist == 2 :# maybe
+            return job
+        else: #the job does not exists anymore
+            try:
+                transaction = self._getTransaction( Transaction.WRITE )
+                transaction.removeJob( jobID )
+                transaction.commit()
+            except ValueError:
+                transaction.rollback()
+                msg = "getJob jobID %s does not match any job any more in the session %s" % (jobID , self.getKey() ) 
+                self.log.error(msg )
+            return None
+    
+    #OPENID 
+    def addOpenIdAuthData(self, authsession):
+        """
+        @param authsession: the session used by OpenId library
+        @type authsession: Session
+        @raise SessionError : if data cannot be saved
+        """
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            transaction.addOpenIdAuthData( authsession )
+            transaction.commit()
+        except ValueError:
+            transaction.rollback()
+            msg = "Error setting auth data"
+            self.log.error( msg )
+            raise SessionError , msg
+    #OPENID
+    def getOpenIdAuthData(self):
+        """
+        @return : Session with openid data
+        @raise SessionError : if data cannot be read
+        """
+        transaction = self._getTransaction( Transaction.READ )
+        try:
+            authsession = transaction.getOpenIdAuthData()
+        except ValueError :
+            transaction.rollback()
+            msg = "Could not get auth data"
+            self.log.error(msg )
+            raise SessionError , msg
+        transaction.commit()
+        return authsession
+        
+    
+    def renameJob( self , jobID  , newUserName ):
+        """
+        @param jobID: the name of the job in the session (url)
+        @type jobID: string
+        @param newUserNAme: the new user name of this job
+        @type newUserName: string
+        @raise SessionError: if the jobID does not match any job in this session
+        """
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            transaction.renameJob( jobID , newUserName )
+            transaction.commit()
+        except ValueError:
+            transaction.rollback()
+            msg = "There is no job with ID %s in session %s" % ( jobID , self.getKey() )
+            self.log.error( msg )
+            raise SessionError , msg
+
+   
+    
+    def addJob( self, jobID  , userName = None , dataUsed = [] , dataProduced = [] ):
+        """
+        add a job in session 
+        @param jobID: the ID (url) of a job 
+        @type jobID: string
+        @param dataUsed: the name of data used by this job ( md5Name )
+        @type dataUsed: string or sequence of strings
+        @param dataProduced: the name of data produced by this job ( md5Name )
+        @param dataProduced: string or sequence of strings
+        """
+        if isinstance( dataUsed , types.StringType ) :
+            dataUsed = [ dataUsed ]
+    
+        if isinstance( dataProduced , types.StringType ):
+            dataProduced = [ dataProduced ]
+        jf = JobFacade.getFromJobId( jobID )
+        jobState = JobState( jobID )
+        if not userName:
+            userName = jobID
+        programName = self.__extractServiceName( jobID )   
+        status = jf.getStatus()
+        date = strptime( jobState.getDate() , "%x  %X" )
+            
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            transaction.createJob( jobID , userName , programName , status , date , dataUsed ,  dataProduced )
+            if dataUsed:
+                transaction.linkJobInput2Data( dataUsed , [ jobID ])
+            if dataProduced:
+                transaction.linkJobOutput2Data( dataProduced , [ jobID ] )
+            transaction.commit()
+        except ValueError , err:
+            transaction.rollback()
+            self.log.error( str( err) )
+            raise SessionError , err
+        
+    def removeJob(self , jobID ):
+        """
+        remove job from Session (in jobs and datas ) and ONLY in session. if the job is local and running kill the job.
+        @param jobID: the url of a job
+        @type job: string
+        @raise SessionError: if the jobID does not match any job in this session
+        """
+        protocol , host , path , a, b, c = urlparse.urlparse( jobID  )
+        if protocol != "http" :
+            jobID = path2url( jobID )
+        
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            jobDict = transaction.removeJob( jobID )
+        except ValueError:
+            transaction.rollback()
+            msg = "removeJob jobID %s does not match any job in the session %s" % (jobID , self.getKey() ) 
+            self.log.error( msg )
+            raise SessionError , msg
+        else:
+            transaction.commit()
+            if jobDict[ 'status' ].isEnded():#'finished' , 'error' , 'killed' 
+                return None #it doesn't usefull to kill it 
+            
+            if self.jobExists( jobID ) == True :
+                jf = JobFacade.getFromJobId( jobID )
+                status = jf.getStatus()
+                if status.isQueryable():
+                    try:
+                        jf.killJob()
+                    except MobyleError , err:
+                        self.log.error( "session/%s removeJob can't kill job = %s : %s" % ( self.getKey() , jobID , err ) )
+     
+    def jobExists( self , jobID ): 
+        """
+        @return: return 1 if the job directory exists, 0 otherwise ( it does not check if the entry exists in session.xml ).
+        for remote job, the existence of a job could not be decided ( remote server problem , timeout ...) in this case 
+        return  2 
+        @rtype: int
+        """
+        if jobID[-10:] == '/index.xml':
+            jobID = jobID[:-10]
+            
+        server = registry.getServerByJobId(jobID)
+        
+        if server is None :
+            self.log.warning('registry does not known this server: %s' % jobID )
+            return 0
+        if server.name == 'local':
+            try:
+                jobPath = url2path( jobID )
+            except MobyleError:
+                return 0
+            return os.path.exists( jobPath )
+        else: #this jobID correspond to a remote job
+            try:
+                status , newUri = self._connect( jobID + '/index.xml' ) 
+                if status == 3:
+                    for attempt in range(10):
+                        status , newUri = self._connect( newUri )
+                        if status != 3:
+                            break
+                    if status == 3:
+                        self.log.error("we attempt 10 redirections whithout reaching the target url: %s" % jobID )
+                        return 2
+                if status == 2:
+                    return 1
+                if status == 4:
+                    return 0
+                else:
+                    self.log.error( "%s : %s" %( jobID , status ) )                    
+                    return 2      
+            except Exception, e:
+                self.log.error(e, exc_info=True)
+                return 2   
+   
+    def _connect(self , url ):
+        """
+        
+        """
+        protocol , host , path , a, b, c = urlparse.urlparse( url )
+        try:
+            cn = HTTPConnection(host)
+            cn.connect()
+            cn.sock.settimeout(10) #set the timeout to 10 sec
+            cn.request("HEAD", path)
+            resp = cn.getresponse()
+            status = resp.status / 100
+            newUri = resp.getheader('Location')
+        except Exception , e:
+            self.log.error( "job %s is unreachable : %s " %(url, e )  )
+            status = 5 #in http code 500 are for errors 
+            newUri = url
+        return status , newUri
+
+
+    
diff --git a/Src/Mobyle/SessionFactory.py b/Src/Mobyle/SessionFactory.py
new file mode 100644
index 0000000..79e25da
--- /dev/null
+++ b/Src/Mobyle/SessionFactory.py
@@ -0,0 +1,190 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os 
+from hashlib import md5
+
+from Mobyle.AnonymousSession import AnonymousSession
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.MobyleError import SessionError , AuthenticationError 
+from logging import getLogger
+
+class SessionFactory( object ):
+    """
+    This class defines a session, that stores all the information
+    about a user that should be persistent on the server
+    @organization: Institut Pasteur
+    @contact:mobyle at pasteur.fr
+    """
+    __ref = None
+
+
+    def __new__( cls , cfg ):
+        if cls.__ref is  None:
+            self = super( SessionFactory , cls ).__new__( cls )
+            self.log = getLogger( 'Mobyle.Session.SessionFactory' )
+            self.cfg = cfg
+            self.__sessions = {}
+            cls.__ref = self
+
+        return cls.__ref
+
+    #OPENID, call by openid , do not check password
+    def getOpenIdAuthenticatedSession( self , userEmailAddr , ticket_id=None ):
+        """
+        @return: an already existing openid authenticated session.
+        @param userEmailAddr: the user email
+        @type userEmailAddr: a Mobyle.Net.EmailAddress instance
+        @raise AuthenticationError: the session doesn't already exists
+        """
+        mymd5 = md5()
+        mymd5.update( str( userEmailAddr ) )
+        key = mymd5.hexdigest()
+        try:
+            session = self.__sessions[ key ]
+            return session
+        except KeyError:
+            sessionDir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key  ) )
+
+            if os.path.exists( sessionDir ):
+                session = AuthenticatedSession( self.cfg , userEmailAddr , passwd=None, ticket_id=ticket_id )
+                self.__sessions[ session.getKey() ] = session
+                return session
+            else:
+                raise AuthenticationError , "There is no user with this email"
+
+
+
+    def getAuthenticatedSession( self , userEmailAddr , passwd=None, ticket_id=None ):
+        """
+        @return: an already existing authenticated session.
+        @param userEmailAddr: the user email
+        @type userEmailAddr: a Mobyle.Net.EmailAddress instance
+        @param passwd: the session pass word 
+        @type passwd: string
+        @raise AuthenticationError: if the passwd doesn't match the session passwd
+        @raise AuthenticationError: the session doesn't already exists
+        """
+        mymd5 = md5()
+        mymd5.update( str( userEmailAddr ) )
+        key = mymd5.hexdigest()
+
+        try:
+            session = self.__sessions[ key ]
+            if session.checkPasswd( passwd ):
+                return session
+            else:
+                raise AuthenticationError , "There is no user with this email and password"
+       
+        except KeyError: 
+            sessionDir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key ) )
+          
+            if os.path.exists( sessionDir ):
+                session = AuthenticatedSession( self.cfg , userEmailAddr , passwd=passwd, ticket_id=ticket_id )
+                self.__sessions[ session.getKey() ] = session  
+                return session
+            else: 
+                raise AuthenticationError , "There is no user with this email" 
+
+
+
+    def getAnonymousSession( self , key = None ):
+        """
+        @return: an anonymous session. If key is None create a new anonymous session
+        @rtype: Session object
+        @param key: the key to identify a anonymous session
+        @type key: string
+        @raise SessionError: if key is specified and doesn't match any session.
+        """
+        anonymousSessionAllowed = self.cfg.anonymousSession()
+        if anonymousSessionAllowed== 'no':
+            self.log.error("SessionFactory/can't create anonymous session ANONYMOUS_SESSION is set to \"no\" in Local/Config/Config.py")          
+            raise SessionError , "can't create anonymous session: permission denied"
+        try:
+            session = self.__sessions[ key ]
+        except KeyError: 
+            if key :
+                sessionDir = self.__getSessionDir( key )
+                
+                if os.path.exists( sessionDir ):
+                    self.log.debug( "SessionFactory.getAnonymousSession( key= %s )/ the dir exist I 'll return this session" % key)
+                    session = AnonymousSession( self.cfg , key )
+                else: 
+                    self.log.error( "can't retrieve anonymous session, the Key: %s doesn't match with any Session" % key )
+                    raise SessionError , "wrong Key: %s" % key
+                   
+            else: #new session
+                session = AnonymousSession( self.cfg )
+                self.log.debug( "SessionFactory.getAnonymousSession( key= %s ) / a new anonymous has been created . I 'll return this session" % key)
+                
+            self.__sessions[session.getKey()] = session  
+            
+        self.log.debug( "SessionFactory.getAnonymousSession( key= %s ) I return this anonymous session :key=" + str( session.getKey() )) 
+        return session
+   
+
+    def createAuthenticatedSession( self , userEmailAddr , passwd ):
+        """
+        create an authenticated session with email as login and passwd as pass word
+        @param userEmailAddr: the user email
+        @type userEmailAddr: a Mobyle.Net.EmailAddress object
+        @param passwd: the user password
+        @type passwd: string
+        @return: a new authenticated session
+        @rtype: session instance
+        @raise AuthenticationError: if there is already a session with this email, or the email is not allowed on this server
+        """
+        authenticatedSessionAllowed = self.cfg.authenticatedSession()
+      
+        if authenticatedSessionAllowed == 'no':
+            self.log.error("can't create  session AUTHENTICATED_SESSION is set to \"no\" in Local/Config/Config.py")          
+            raise SessionError , "can't create  authenticated session: permission denied"
+
+        mymd5 = md5()
+        mymd5.update( str( userEmailAddr ) )
+        key = mymd5.hexdigest()
+
+        if self.__sessions.has_key( key ) : 
+            msg = "Try to create a new Session with email %s, the %s Session already exist" % ( userEmailAddr , key)
+            self.log.error( msg )        
+            raise AuthenticationError , "user with the email you specify already exist" 
+       
+        else:  
+            sessionDir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key ) )
+          
+            if os.path.exists( sessionDir ):
+                msg = "Try to create a new Session with email %s, the %s Session already exist" % ( userEmailAddr , key)
+                self.log.error( msg )
+                raise AuthenticationError , "user with the email you specify already exist" 
+                
+            session = AuthenticatedSession( self.cfg , userEmailAddr , passwd )
+            self.__sessions[ session.getKey() ] = session   
+            return session
+  
+
+    def removeSession( self , key ):
+        sessionDir = self.__getSessionDir(  key )
+       
+        for File in os.listdir( sessionDir ):
+            os.unlink( os.path.join( sessionDir , File ) )
+        os.rmdir( sessionDir )
+        del self.__sessions[ key ]
+
+
+    def __getSessionDir( self , key ) :
+ 
+        if  len ( key ) == 32  :
+            #a md5 value is always encode in 32 char
+            return os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key  ) )
+        else:
+            ##the anonymous key have 15 char length
+            return os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AnonymousSession.DIRNAME , key ) )
+ 
+ 
+    
+
diff --git a/Src/Mobyle/Status.py b/Src/Mobyle/Status.py
new file mode 100644
index 0000000..994d116
--- /dev/null
+++ b/Src/Mobyle/Status.py
@@ -0,0 +1,101 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+from logging import getLogger
+s_log = getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError
+
+
+class Status:
+
+    _CODE_2_STATUS = { 
+                     -1 : "unknown"   , # the status cannot be determined
+                      0 : "building"  , # the job directory has been created
+                      1 : "submitted" , # the job.run method has been called
+                      2 : "pending"   , # the job has been submitted to the batch manager but wait for a slot 
+                      3 : "running"   , # the job is running in the batch manager
+                      4 : "finished"  , # the job is finished without error from Mobyle
+                      5 : "error"     , # the job has failed due to a MobyleError 
+                      6 : "killed"    , # the job has been removed by the user, or killed by the admin
+                      7 : "hold"      , # the job is hold by the batch manager
+                      }
+    
+    def __init__(self , code = None , string = None , message = ''):
+        """
+        @param code: the code of the status 
+        @type code: integer 
+        @param string: the code of the status representing by a string
+        @type string: string
+        @param message: the message associated to the status
+        @type message: string
+        """
+        if code is None and not string :
+            raise MobyleError , "status code or string must be specified"
+        elif code is not None and string :
+            raise MobyleError, "status code or string must be specified, NOT both"
+        elif code is not None :
+            if code in self._CODE_2_STATUS.keys():
+                self.code = code
+            else:
+                raise MobyleError , "invalid status code : " + str( code )
+        elif string:
+            if string in  self._CODE_2_STATUS.values():
+                iterator = self._CODE_2_STATUS.iteritems()
+                for code , status in iterator:
+                    if status == string:
+                        self.code = code
+            else:
+                raise MobyleError , "invalid status : " + str( string ) 
+        
+        if message:
+            try:
+                str( message )
+            except Exception , err:
+                s_log.error( "Status received an non valid message: %s : %s"%( message, err ) , exc_info = True )
+                raise MobyleError , err
+            self.message = message
+        else:
+            self.message = ''
+            
+        
+    def __eq__(self , other):
+        return self.code == other.code and self.message == other.message
+    
+    def __ne__(self , other ):
+        return self.code != other.code or self.message != other.message
+    
+    def __str__(self):
+        return self._CODE_2_STATUS[ self.code ]
+    
+    def isEnded(self):
+        """
+         4 : "finished"  , # the job is finished without error from Mobyle
+         5 : "error"     , # the job has failed due to a MobyleError 
+         6 : "killed"    , # the job has been removed by the user, or killed by the admin
+        """
+        return self.code in ( 4 , 5 , 6 )
+
+    def isOnError(self):
+        """
+         5 : "error"     , # the job has failed due to a MobyleError 
+         6 : "killed"    , # the job has been removed by the user, or killed by the admin
+        """
+        return self.code in ( 5 , 6 )
+        
+    def isQueryable(self):
+        """
+        1 : "submitted" , # the job.run method has been called
+        2 : "pending"   , # the job has been submitted to the batch manager but wait for a slot 
+        3 : "running"   , # the job is running in the batch manager
+        7 : "hold"      , # the job is hold by the batch manager
+        """
+        return self.code in( 1 , 2 , 3 , 7 )
+    
+    def isKnown(self):
+        return self.code != -1
diff --git a/Src/Mobyle/StatusManager.py b/Src/Mobyle/StatusManager.py
new file mode 100644
index 0000000..cf56f6c
--- /dev/null
+++ b/Src/Mobyle/StatusManager.py
@@ -0,0 +1,298 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import errno
+from lxml import etree
+import fcntl
+
+from time import sleep , time
+
+from Mobyle.Status import Status
+from Mobyle.MobyleError import MobyleError
+#from Mobyle.Utils import parse_xml_file
+
+import logging
+_log = logging.getLogger( __name__ )
+
+import sys
+import os.path
+
+
+class StatusManager(object):
+    """
+    This class is the main access to the job status
+    """
+
+    WRITE  = fcntl.LOCK_EX
+    READ   = fcntl.LOCK_SH
+    file_name = 'mobyle_status.xml'
+
+    @classmethod
+    def create( cls , job_path  , status ):
+        fileName = os.path.join( job_path , cls.file_name )
+        root = etree.Element( "status" )
+        value_node  = etree.Element( "value" )
+        root.append( value_node )
+        message_node = etree.Element( "message" )
+        root.append( message_node )
+        value_node.text = str( status )
+        message_node.text = status.message
+        try:
+            File = open(  fileName  , 'w' )
+            File.write( etree.tostring( root , encoding='UTF-8' , pretty_print = True))
+        except IOError , err:
+            msg = "cannot create status %s : %s" % ( fileName , err )
+            _log.error( msg )
+            raise MobyleError( msg )
+        finally:
+            File.close()
+
+
+
+    def _lock( self , File , lockType ):
+        """
+        try to acquire a lock of lockType on self.__File
+        @raise IOError: when it could not acquire a lock
+        """
+        IGotALock = False
+        _log.debug( "%f : %s : _lock Type= %s ( call by= %s )"  %( time() ,
+                                                                        File.name,
+                                                                        ( 'UNKNOWN LOCK', 'READ' , 'WRITE' )[ lockType ] ,
+                                                                        os.path.basename( sys.argv[0] ) ,
+                                                                        ))
+        for attempt in range( 5 ):
+            try:
+                fcntl.lockf( File , lockType | fcntl.LOCK_NB )
+                IGotALock  = True
+                _log.debug( "%f : %s : _lock IGotALock = True" %(time() , File.name ))
+                break
+            except IOError , err:
+                _log.debug( "%f : %s : _lock IGotALock = False" %(time() , File.name))
+                sleep( 0.2 )
+
+        if not IGotALock :
+            _log.error( "%s : %s" %( File.name , err ) )
+            _log.debug( "%f : %s : _lock Type= %s ( call by= %s )"  %( time() ,
+                                                                            File.name ,
+                                                                            ( 'UNKNOWN LOCK', 'READ' , 'WRITE' )[ lockType ] ,
+                                                                            os.path.basename( sys.argv[0] )
+                                                                            ))
+
+            raise IOError , err
+
+
+    def setStatus(self, job_path , status ):
+        fileName = os.path.normpath( os.path.join( job_path , self.file_name ) )
+        try:
+            File = open( fileName , 'r+' )
+        except IOError , err:
+            if err.errno == errno.ENOENT :
+                try:
+                    return self._fall_back_setStatus( job_path , status )
+                except Exception , err:
+                    _log.error( "%s : cannot open either index.xml nor mobyle_status.xml: %s" % ( job_path , err ))
+                    return Status( code= -1 )
+            else:
+                _log.error( "cannot open %s : %s" % ( fileName , err ))
+                return Status( code= -1 )
+        self._lock( File , self.WRITE ) #try to acquire a lock
+        #If I do not got a lock a IOError is raised
+        try:
+            parser = etree.XMLParser( no_network = False )
+            #doc = parse_xml_file(File , parser)
+            doc = etree.parse(File , parser)
+            root = doc.getroot()
+        except Exception , err:
+            msg = "error in parsing status %s : %s" % ( File.name , err )
+            _log.error( msg )
+            _log.debug("%f : %s : setStatus UNLOCK " %( time() , fileName ) )
+            fcntl.lockf( File , fcntl.LOCK_UN  )
+            File.close()
+            raise MobyleError( msg )
+        #################
+        old_status = self.getStatus( job_path )
+        _log.debug( "old_status= %s" %old_status )
+        if old_status.isEnded():
+            _log.warning( "%f : %s : try to update job status from %s to %s ( call by %s )"%( time() ,
+                                                                                              fileName ,
+                                                                                              old_status ,
+                                                                                              status ,
+                                                                                              os.path.basename( sys.argv[0] )
+                                                                                              ) )
+            try:
+                _log.debug("%f : %s : setStatus UNLOCK " %( time() , fileName ) )
+                fcntl.lockf( File , fcntl.LOCK_UN  )
+            except Exception , err :
+                _log.error( "%f : %s : setStatus cannot UNLOCK : %s" %( time() , fileName , err ) )
+            finally:
+                File.close()
+            return None
+        try:
+            value_node = root.find( "value" )
+            value_node.text = str( status )
+            message_node = root.find( "message" )
+            message_node.text = status.message
+        except Exception, err:
+            msg = "error in parsing status %s : %s" % ( File.name , err )
+            _log.error( msg )
+            _log.debug("%f : %s : setStatus UNLOCK " %( time() , fileName ) )
+            fcntl.lockf( File , fcntl.LOCK_UN  )
+            File.close()
+            raise MobyleError( msg )
+        ##################
+        try:
+            tmpFile = open( "%s.%d" %( File.name , os.getpid() )  , 'w' )
+            tmpFile.write( etree.tostring( root , xml_declaration=True , encoding='UTF-8' , pretty_print = True))
+            os.rename( tmpFile.name , File.name )
+        except IOError , err:
+            msg = "cannot write status : " + str( err )
+            _log.error( msg )
+            raise MobyleError( msg )
+        except Exception , err:
+            _log.error( "cannot write status %s " % File.name , exc_info = True )
+            raise err
+        finally:
+            try:
+                _log.debug("%f : %s : setStatus UNLOCK " %( time() , fileName ) )
+                fcntl.lockf( File , fcntl.LOCK_UN  )
+                File.close()
+                tmpFile.close()
+            except Exception , err :
+                _log.error( "%f : %s : setStatus cannot UNLOCK : %s" %( time() , fileName , err ) )
+
+
+
+    def getStatus(self , job_path ):
+        fileName = os.path.normpath( os.path.join( job_path , self.file_name ) )
+        try:
+            File = open( fileName , 'r' )
+        except IOError , err:
+            if err.errno == errno.ENOENT :
+                try:
+                    return self._fall_back_getStatus( job_path )
+                except Exception , err:
+                    _log.error( "%s : cannot open either index.xml nor mobyle_status.xml: %s" % ( job_path , err ))
+                    return Status( code= -1 )
+            else:
+                _log.error( "cannot open %s : %s" % ( fileName , err ))
+                return Status( code= -1 )
+        try:
+            self._lock( File, self.READ  ) #try to acquire a lock
+            #If I do not got a lock a IOError is raised
+        except IOError , err:
+            _log.error( "%f : %s : cannot read status : %s" %( time() , fileName , err ) )
+            return Status( code= -1 )
+        try:
+            parser = etree.XMLParser( no_network = False )
+            #doc = parse_xml_file( File , parser )
+            doc = etree.parse(File, parser)
+            root = doc.getroot()
+        except Exception , err:
+            pass
+        finally:
+            _log.debug("%f : %s : getStatus UNLOCK " %( time() , fileName ) )
+            fcntl.lockf( File , fcntl.LOCK_UN  )
+            File.close()
+        try:
+            value_node = root.find( "value" )
+            message_node = root.find( "message" )
+        except Exception , err:
+            _log.error( "%f : %s : cannot parse status" %( time() , fileName ) , exc_info = True )
+            return Status(code = -1)
+        if message_node is None :
+            return Status( string = value_node.text )
+        else:
+            return Status( string = value_node.text , message = message_node.text )
+
+
+
+    def _fall_back_setStatus(self , job_path  , status ):
+        parser = etree.XMLParser( no_network = False )
+        index_path = os.path.join( job_path ,'index.xml')
+        #doc = parse_xml_file( index_path , parser)
+        doc = etree.parse(index_path, parser)
+        root = doc.getroot()
+        status_node = root.find( "./status" )
+        try:
+            old_message = status_node.find( "value" )
+            if old_message is None:
+                oldStatus = Status( string = str( status_node.find( "value").text ) )
+            else:
+                oldStatus = Status( string = str( status_node.find( "value").text ) ,
+                                    message = str( old_message.text )
+                                    )
+        except AttributeError:
+            oldStatus = Status( code = -1 ) #unknown
+
+        if oldStatus.isKnown() and oldStatus.isEnded():
+            #the job is ended we can refactor status in a separate file
+            root.remove( status_node )
+            try:
+                tmp_file = open( os.path.join( job_path ,'tmp_index.xml' ) , 'w' )
+                tmp_file.write( etree.tostring( doc , pretty_print = True , encoding='UTF-8' ))
+                tmp_file.close()
+                os.rename( tmp_file.name , index_path )
+            except Exception , err :
+                _log.error( 'cannot make index.xml : %s' % err )
+
+            self.create( job_path  , oldStatus )
+            return None
+
+        if status == oldStatus:
+            #this is an old job with status managed by JobState
+            #new status and old status will evolved
+            #do nothing until job finish
+            return None
+        else:
+            if status.isEnded():
+                #the job is ended we can refactor status in a separate file
+                root.remove( status_node )
+                try:
+                    tmp_file = open( os.path.join( job_path ,'tmp_index.xml' ) , 'w' )
+                    tmp_file.write( etree.tostring( doc , pretty_print = True , encoding='UTF-8' ))
+                    tmp_file.close()
+                    os.rename( tmp_file.name , index_path )
+                except Exception , err :
+                    _log.error( 'cannot make index.xml : %s' % err )
+                self.create( job_path  , status)
+            else:
+                #the job is not ended we update the index.xml
+                value_node = status_node.find( 'value' )
+                value_node.text = str( status )
+                message_node = status_node.find( 'message' )
+                if message_node is not None:
+                    message_node.text = str( status.message )
+
+                try:
+                    tmpFile = open( "%s.%d" %( index_path , os.getpid() )  , 'w' )
+                    tmpFile.write( etree.tostring( doc , xml_declaration=True , encoding='UTF-8' ))
+                    tmpFile.close()
+                    os.rename( tmpFile.name , index_path )
+                except IOError, err:
+                    _log.error( "IOError during write index.xml on disk")
+                    raise MobyleError , err
+
+
+
+    def _fall_back_getStatus(self , job_path ):
+        parser = etree.XMLParser( no_network = False )
+        #doc = parse_xml_file( os.path.join( job_path ,'index.xml') , parser)
+        doc = etree.parse(os.path.join(job_path, 'index.xml'), parser)
+        root = doc.getroot()
+        try:
+            status = root.find( "./status/value" ).text
+        except AttributeError:
+            return Status( code = -1 ) #unknown
+        try:
+            message = root.find( "./status/message" ).text
+        except AttributeError:
+            message =  None
+        if message:
+            return Status( string = str( status ) , message = str( message ))
+        else:
+            return Status( string = str( status )  )
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.CLUSTAL b/Src/Mobyle/Test/Converter/DataAlignments/align.CLUSTAL
new file mode 100644
index 0000000..baeec19
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.CLUSTAL
@@ -0,0 +1,292 @@
+CLUSTAL multiple sequence alignment
+
+
+gi|262263371|ref|NM_016661.3|  -----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAG
+gi|281182713|ref|NM_001168497. TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGG
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  GCGTAGATCC-CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCA
+gi|281182713|ref|NM_001168497. GGATAGGGCCGCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCA
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  GTCCTAGCGGAAGCGCCGCCAGCATGTCTGATAAACTGCCCTACAAAGTC
+gi|281182713|ref|NM_001168497. GTCGGAGCGGAAGGGGCGGAACCACGGTGAACAAAGCGC---ACAAGGTC
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  GCGGACATCGGACTGGCCGCCTGGGGACGGAA--GGCTCTGGATATAGCT
+gi|281182713|ref|NM_001168497. CAAGGTGCCGCCCTAGAGACCCTGGGCCCGTACTGGGCGCAGCTACCTCT
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  GAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AGATGTACTCAG
+gi|281182713|ref|NM_001168497. TCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAGCTGTTCCCGA
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  CCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+gi|281182713|ref|NM_001168497. GCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGA
+gi|281182713|ref|NM_001168497. TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTAT
+gi|274325752|ref|NM_007706.4|  --------------------------------------------------
+                                                                                 
+
+gi|262263371|ref|NM_016661.3|  GGTGCGGTGGTCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCG
+gi|281182713|ref|NM_001168497. GCCCCTTCACCCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCC
+gi|274325752|ref|NM_007706.4|  --------------------------------------------GGAGCG
+                                                                             *** 
+
+gi|262263371|ref|NM_016661.3|  GCTGCCATTGC---CAAGGCTGGCAT----TCCAGTGTTTGCCTGGAAGG
+gi|281182713|ref|NM_001168497. GGTGGCTCTGCATCCTGGACCGGGACAAGCTCCAGTGCCCACCCAGGTGC
+gi|274325752|ref|NM_007706.4|  AG-GGAGGCGCG--TCGGGCTGGGAAG---TCGCGCGCACACTCGGCTCC
+                                  *     **      * * ** *     **  * *    *   *    
+
+gi|262263371|ref|NM_016661.3|  GCGAGACAGATGAGGAGTAC-CTGTGGTGCATTGAGCAGACGCTGCACTT
+gi|281182713|ref|NM_001168497. CTGCCCCTGCTCCCGGCTTCGCTCTCTTCCCCTCGCCAGGCCCAGTGGCT
+gi|274325752|ref|NM_007706.4|  G-GGGACAGACGGTTAACTC---TTGCCAAGTCTCGCCGCCTCTGCGGCT
+                                 *   * *          *    *           * * * * *    *
+
+gi|262263371|ref|NM_016661.3|  CAAGGACG----GACCCCTCAACATGATTCTGGATGATGG-TGGTGACCT
+gi|281182713|ref|NM_001168497. CCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGCCTCCTGGCCT
+gi|274325752|ref|NM_007706.4|  CCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTCCTCCCGCAGC
+                               *  ** *     *   *         *          *   *   *    
+
+gi|262263371|ref|NM_016661.3|  TACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+gi|281182713|ref|NM_001168497. CGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+gi|274325752|ref|NM_007706.4|  CGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+                                     *      *         *      *   *       * *     
+
+gi|262263371|ref|NM_016661.3|  -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-C
+gi|281182713|ref|NM_001168497. GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGC
+gi|274325752|ref|NM_007706.4|  --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC--
+                                    *        *         *** *        *         *  
+
+gi|262263371|ref|NM_016661.3|  TCTACAAGATGATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCA
+gi|281182713|ref|NM_001168497. TCCGGAACCGGACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTG
+gi|274325752|ref|NM_007706.4|  -CCGGAAGCTTGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAG
+                                *   **        *    *           ***      *  *     
+
+gi|262263371|ref|NM_016661.3|  ATGTCAACGATTCTGT------CACCAA-------GAGCAAGTTTGACAA
+gi|281182713|ref|NM_001168497. TGCCCGCCTGTGCTGTGGTGCCCGCCGACCATTTCGAATCCGCCTAGCGG
+gi|274325752|ref|NM_007706.4|  ACTTTGCC-ACACCAT----TCTGCCGGA-ATTTGGAGAAAAAGAACCAG
+                                      *    *  *        **         **          *  
+
+gi|262263371|ref|NM_016661.3|  CCTCTATGG-CTGCCGGGAGTCCC----TCATAGATGGCATCAAACGGGC
+gi|281182713|ref|NM_001168497. ACCCTGGGGACCGCGAGGTGCTCCGGCTCCTCCGCCCACTTCATTGTGGC
+gi|274325752|ref|NM_007706.4|  CCGCTTCCAGTCCCCTCCCCCTCCGCCACCATTTCGGACACCCTGCACAC
+                                * **        *        **     *        *  *       *
+
+gi|262263371|ref|NM_016661.3|  CACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGGTGGCAGGCTA
+gi|281182713|ref|NM_001168497. TGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAGTC-CAGGCTC
+gi|274325752|ref|NM_007706.4|  TCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCGAGGTCCACTG
+                                   * *           ** *      ***        *       ** 
+
+gi|262263371|ref|NM_016661.3|  TGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+gi|281182713|ref|NM_001168497. CACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+gi|274325752|ref|NM_007706.4|  GCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+                                            *   * *    **     * * **        *    
+
+gi|262263371|ref|NM_016661.3|  CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-
+gi|281182713|ref|NM_001168497. CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGT
+gi|274325752|ref|NM_007706.4|  CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGC
+                               *   *     *  *    *        *              *  *  * 
+
+gi|262263371|ref|NM_016661.3|  ----CCATGGAGG-------GCTATG----AGGTAACCACTATG-GACGA
+gi|281182713|ref|NM_001168497. AGGGCCTTGCTGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGA
+gi|274325752|ref|NM_007706.4|  GGAGCCAGTGGGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGC
+                                   **     **       *   **    **  * *        *  * 
+
+gi|262263371|ref|NM_016661.3|  AGCCT--GTAAGGAGGGCAACATCTTTGTCACCACCACAGGCTGTGTGGA
+gi|281182713|ref|NM_001168497. AGACTAAGGATGAATCGCGAAGTGTGGGCCGCATCAGCAAGCAGTGGGGA
+gi|274325752|ref|NM_007706.4|  GGC----GCGTCTGGCGAAAGCCCTGCGCGAGCTCAGTCAAACAGGATGG
+                                *     *        *  *    *  *      *          *  * 
+
+gi|262263371|ref|NM_016661.3|  TATCATCCTTGGCCGGCACTTTGAGCAGATGAAGGATGACGCCATTGTCT
+gi|281182713|ref|NM_001168497. GGGCTGCTCCGAGAAGCCCTCACAGATGCCGATGACTTTGGCCTCCAATT
+gi|274325752|ref|NM_007706.4|  TACTGGGGAAGTATGACTGTTAATGAAGCCAAAGAGAAATTAAAAGAGGC
+                                         *     *  *    *  *   * *                
+
+gi|262263371|ref|NM_016661.3|  GTAA--CATTGGACACTTCGATGTGGAG-----ATTGATGTGAA----GT
+gi|281182713|ref|NM_001168497. CCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTGGGAGCCACGT
+gi|274325752|ref|NM_007706.4|  TCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTCGCATTCAGAC
+                                  *        *          *  **     *  * *   *       
+
+gi|262263371|ref|NM_016661.3|  GGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+gi|281182713|ref|NM_001168497. TCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+gi|274325752|ref|NM_007706.4|  TACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+                                 *  *   *  *                *       *      **    
+
+gi|262263371|ref|NM_016661.3|  GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG-
+gi|281182713|ref|NM_001168497. GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGA
+gi|274325752|ref|NM_007706.4|  G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTG
+                               *             *        *   **   *       ***   *   
+
+gi|262263371|ref|NM_016661.3|  -CTGGCTGAAGGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CC
+gi|281182713|ref|NM_001168497. CCAGAATTTAAGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACC
+gi|274325752|ref|NM_007706.4|  TCAAGTCCAAGCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACT
+                                *       *         *     * *   **               * 
+
+gi|262263371|ref|NM_016661.3|  CCAGCTTCGTGATGAGCAACTCC---TTCACAAACCAGGTGATGGCACAG
+gi|281182713|ref|NM_001168497. CTTTCCTTGATATACACTGCGGC---GGACCGACGAGGGTGGCCGAGTGG
+gi|274325752|ref|NM_007706.4|  ACTATGTCCAGATGTGCAAGGATAAACGGACAGGCCCAGAAGCCCCACGG
+                                     *    **   *             *       *          *
+
+gi|262263371|ref|NM_016661.3|  ATTG-AGCTGTGGA--CCCA-----------------------CCCAGAT
+gi|281182713|ref|NM_001168497. TTGGGAGCCGTTGTGTCCCATCCCTTCCCTGCTTCTCCTGTGGCCCTGCA
+gi|274325752|ref|NM_007706.4|  AATGGGACTGTTCA--CCTGT----------------------ACCTGAC
+                                  *   * **     **                          ** *  
+
+gi|262263371|ref|NM_016661.3|  AAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCTGGATGAGGCG
+gi|281182713|ref|NM_001168497. GAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCATCTCCAGGCA
+gi|274325752|ref|NM_007706.4|  CAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTTCTGTCGACTC
+                                **     ***       *    **  *        *             
+
+gi|262263371|ref|NM_016661.3|  GTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+gi|281182713|ref|NM_001168497. GTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+gi|274325752|ref|NM_007706.4|  GCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+                               *    *      **  *   *        *     *    *     *   
+
+gi|262263371|ref|NM_016661.3|  CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTC
+gi|281182713|ref|NM_001168497. CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTC
+gi|274325752|ref|NM_007706.4|  CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTA
+                               *          *     *      * *     **              * 
+
+gi|262263371|ref|NM_016661.3|  AAGCCTGATCACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTT
+gi|281182713|ref|NM_001168497. AAGTTTTACC-CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATG
+gi|274325752|ref|NM_007706.4|  TTTCTCTCTCTTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTC
+                                        *      * *  *   *                      * 
+
+gi|262263371|ref|NM_016661.3|  CCAGCTGCCATCCTTGTTCCGGG---CCCTATC-------TCTCGTTCCC
+gi|281182713|ref|NM_001168497. GCAGACACCACCCTTCCCTTATGGCACTTTAGCCAATTAGTTTAGCTTCC
+gi|274325752|ref|NM_007706.4|  ATATAGACTATCTCCGAATGCAG---CTATGTGAAA------GAGAACCC
+                                 *    * * *          *   *  *              *   **
+
+gi|262263371|ref|NM_016661.3|  AAGAGCAA----------ATGTCACCAACTTTGCAGTAGCCTGGAC-AGT
+gi|281182713|ref|NM_001168497. GATTGTGGCACTCTGAGGGGATCCTTGCCTCCTCACTAATAGCTGT-AGC
+gi|274325752|ref|NM_007706.4|  AGAGGCCCTCC--------TCTGGATAACTGCGCAGAATTCTCTCTTAAG
+                                   *                *      **   **  *         *  
+
+gi|262263371|ref|NM_016661.3|  GCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCTGGTCACTCTG
+gi|281182713|ref|NM_001168497. GGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCTGCGGGTGCTT
+gi|274325752|ref|NM_007706.4|  GACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTAGCTAGGTATT
+                               *         *   * *        *        *     *       * 
+
+gi|262263371|ref|NM_016661.3|  TCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+gi|281182713|ref|NM_001168497. TCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+gi|274325752|ref|NM_007706.4|  TTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+                               *         *    *     *              * * **   *    
+
+gi|262263371|ref|NM_016661.3|  GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGAC
+gi|281182713|ref|NM_001168497. ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTT
+gi|274325752|ref|NM_007706.4|  GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAAC
+                                 **    *       **      *    **          * *      
+
+gi|262263371|ref|NM_016661.3|  ATGATCTCAGAAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGT
+gi|281182713|ref|NM_001168497. GAAACTTTTTAAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACC
+gi|274325752|ref|NM_007706.4|  AAAACAAAACAAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATT
+                                  *      **       * * *    *     * *    *    **  
+
+gi|262263371|ref|NM_016661.3|  GATGGTCACAA-----GCCATG-CACCTTATCAATGAGACCCTCACTT--
+gi|281182713|ref|NM_001168497. GGATAACACAAAGGGAGCGGCGTCAGCCAGCCTGTATAATCTACACTATT
+gi|274325752|ref|NM_007706.4|  C-CTGTCAGAA------TGACTTGCCTTTGTTTTTTGGGTTCCTATGCAC
+                                     ** **                       *         *     
+
+gi|262263371|ref|NM_016661.3|  ----------------GGTCTTTAGATCTGTGTGCCTGGTTTGCAGATCC
+gi|281182713|ref|NM_001168497. TGGATTAAAGGAAAAAAGTCCTTGAGTCCAAAAAGTTAGTTTTCATCACC
+gi|274325752|ref|NM_007706.4|  TGGGTCAAAAGTCCAAGCTCCATAGGAGAGAAAGAAAGGCTTCCATTTCC
+                                                 **  *               * ** **   **
+
+gi|262263371|ref|NM_016661.3|  ATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC--CACCAAAGA
+gi|281182713|ref|NM_001168497. CATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCAATATCTAAGT
+gi|274325752|ref|NM_007706.4|  AGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA-GGCTTCAGC
+                                   *     * *                   *     *        ** 
+
+gi|262263371|ref|NM_016661.3|  GCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+gi|281182713|ref|NM_001168497. TGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+gi|274325752|ref|NM_007706.4|  TC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+                                        *     *  *   *               * *    *   *
+
+gi|262263371|ref|NM_016661.3|  TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTA
+gi|281182713|ref|NM_001168497. GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGG
+gi|274325752|ref|NM_007706.4|  TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-G
+                                     *      * *        *    **  *      *  **     
+
+gi|262263371|ref|NM_016661.3|  CTTTTTTTGGTCCCTATT-TCACAAG--------GGTTAGGA-TAGATTA
+gi|281182713|ref|NM_001168497. CCTCCTCCAAAGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTA
+gi|274325752|ref|NM_007706.4|  CTGACCATGCTTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCA
+                               *               ** *  *  *           *   *  * *  *
+
+gi|262263371|ref|NM_016661.3|  ACCAAGAAAGGACAAGTGACAGACTGAAAGGGGCTGGAAAAACAAAGA--
+gi|281182713|ref|NM_001168497. GGCAGAGTGGACTTGGTCTTGGTCCCTTCAGAGGCCAAGGAGCAGGGCTC
+gi|274325752|ref|NM_007706.4|  ACCAAAAAAAGGAAAAAAAAAAAAAAAAAAGAAACGAAAAAAGAACCATC
+                                 **                          *      *  *  *      
+
+gi|262263371|ref|NM_016661.3|  GGAAAAGGC-----CTGTCACTTCTGTATAGTTGATGGTTCCTGTCACAA
+gi|281182713|ref|NM_001168497. GGAGGAAGCATTAGCCAGTACCTGAGTGCCCTATACACTTCCTGTCACTC
+gi|274325752|ref|NM_007706.4|  ACCATGAGAT----CCTGTATTTGTCT-TTTTTTACTACACGTATGACCT
+                                      *      *    *  *   *    *  *     * * * **  
+
+gi|262263371|ref|NM_016661.3|  GCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAATGTTTATATG
+gi|281182713|ref|NM_001168497. CTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAGACCTCATGTT
+gi|274325752|ref|NM_007706.4|  CCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGGCAGCCTTACT
+                                 * * *      *         *                      *   
+
+gi|262263371|ref|NM_016661.3|  CTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+gi|281182713|ref|NM_001168497. CTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+gi|274325752|ref|NM_007706.4|  TCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+                                     *       *      *    *    **  *     *      * 
+
+gi|262263371|ref|NM_016661.3|  CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTG
+gi|281182713|ref|NM_001168497. GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGA
+gi|274325752|ref|NM_007706.4|  CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTG
+                                *    *       *  *   *     *       *  *   *   *   
+
+gi|262263371|ref|NM_016661.3|  GAATATGTGCATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACT
+gi|281182713|ref|NM_001168497. CTGGCTGCTGACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGT
+gi|274325752|ref|NM_007706.4|  CAA------CTCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATT
+                                                          *       *        **** *
+
+gi|262263371|ref|NM_016661.3|  GTACCTGTGATTCTGGAGGACACCAGATGTCCCATCGCTTTCTTCCATAT
+gi|281182713|ref|NM_001168497. CTGCTCGGGACCGGTTACTAAGGTGGAAGAGC--TGGTCTACCTGTGTGA
+gi|274325752|ref|NM_007706.4|  TTACCC------CTTGATGTAGCTGTGAGTCC--CAACCTAGTGCCATTG
+                                * *            *           *  *       *       *  
+
+gi|262263371|ref|NM_016661.3|  TCCTGAGACAGGGTCCCTCAC------TGGACTAGAGTGAGGCCATTTCT
+gi|281182713|ref|NM_001168497. GCAGGAGGTAGGACAGCTGACCACTAGTGGGCCAAA--CAGGCGACTGGG
+gi|274325752|ref|NM_007706.4|  TTTTTATTTTTATTTTTATTTT--------------TGGAAATGGCTTTA
+                                    *                                 *      *   
+
+gi|262263371|ref|NM_016661.3|  GCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAAGTCAAGCATG
+gi|281182713|ref|NM_001168497. GAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCGGATAGCAGAA
+gi|274325752|ref|NM_007706.4|  GAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACGGACATCAACA
+                               *  * * *        * * * *     **   *      *  *      
+
+gi|262263371|ref|NM_016661.3|  GCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+gi|281182713|ref|NM_001168497. GCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+gi|274325752|ref|NM_007706.4|  GC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+                               **      *  *  *          *            *           
+
+gi|262263371|ref|NM_016661.3|  CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAA
+gi|281182713|ref|NM_001168497. GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTT
+gi|274325752|ref|NM_007706.4|  AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTG
+                                   *      *   *                   *         *    
+
+gi|262263371|ref|NM_016661.3|  CT-CCCTCTTATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCT
+gi|281182713|ref|NM_001168497. TAGCCCTTGTGTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTG
+gi|274325752|ref|NM_007706.4|  TTACTATTTGATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTAC
+                                  *  *    *      *         *  *    **      *     
+
+gi|262263371|ref|NM_016661.3|  GGAAAGCAAATAAA-AACAAGCCCCGTGCACAGGATGGCTAAAAAAAAAA
+gi|281182713|ref|NM_001168497. AGCCCTCACCTCTACAACCTTCCTCTGCCACTCGGGGTCTGACAAGGGAC
+gi|274325752|ref|NM_007706.4|  AAGTTGCACTCAT-------------------------------------
+                                     **                                          
+
+gi|262263371|ref|NM_016661.3|  AAAAAAAAAAA--------------------------------
+gi|281182713|ref|NM_001168497. CTCCAGAGGGGACAGTGGCTTTGCTGGCTCCAGGTGCTCCAGT
+gi|274325752|ref|NM_007706.4|  -------------------------------------------
+                                                                          
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.FASTA b/Src/Mobyle/Test/Converter/DataAlignments/align.FASTA
new file mode 100644
index 0000000..6366108
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.FASTA
@@ -0,0 +1,114 @@
+>gi|262263371|ref|NM_016661.3|
+-----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC-CTGCCCCGCGCCGCCCGGC
+CCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCCAGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCG
+GACTGGCCGCCTGGGGACGGAA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGACCGTGGAGACTGCTG-TTCTC
+ATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGGTCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCG
+GCTGCCATTGC---CAAGGCTGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG-TGGTGACCTTACTAACCTC
+ATCCACACCAAATACCCACAGCTTC--TGTCAGGC------ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGG
+TCCACAAC-CTCTACAAGATGATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+--CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TCATAGATGGCATCAAACGGGC
+CACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGGTGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCA
+GGCCCTGAGGGGTTTTGGGGCCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAACATCTTTGTCACCACCACAG
+GCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGATGAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCG
+ATGTGGAG-----ATTGATGTGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAAGGCCGTCTGGTCAACCTGG-
+-GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAACTCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTG
+TGGA--CCCA-----------------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGACTGAGAAGCAGGCCCAGTAC
+CTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATCACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTT
+CCAGCTGCCATCCTTGTTCCGGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCTGGTCACTCTGTCTTTGACCT
+C---TGCTGTATC----------CCTCATACTGTTCCA--GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATC
+CACAGGGGACATGATCTCAGAAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTGTGTGCCTGGTTTGCAGATCC
+ATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC--CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GC
+CCCCGAGAGCTGGGCCTTTTTACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGACAGACTGAAAGGGGCTGGAAA
+AACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATAGTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA-
+-TTAATTTGTTT---TATAATGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGCATGTATGTGGCTGACATATG
+TGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGACACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACA
+GGGTCCCTCAC------TGGACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCTCTTCACGGATCGCATAATGT
+GAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTTATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCT
+GGAAAGCAAATAAA-AACAAGCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+-------------
+>gi|281182713|ref|NM_001168497.
+TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCCGCAATCACCCTCCAACAGGC
+CAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGAACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCG
+CCCTAGAGACCCTGGGCCCGTACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGACTCCC----CTCCTGATTCCC
+ATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCACCCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCC
+GGTGGCTCTGCATCCTGGACCGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGCCTCCTGGCCTCGAATTCCTA
+GTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAACGAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTA
+TGAACTTCGCTCCGGAACCGGACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCCTCCGCCCACTTCATTGTGGC
+TGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAGTC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTAC
+AGACCTGGCATCCCTTCCTTCCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGAAGTGTGGGCCGCATCAGCAA
+GCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCCGATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGA
+AAGTGAAGGCCGTACTGCTGGGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTAAGATGGTCAGCTGCCCTGGA
+CGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGCGGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCG
+TTGTGTCCCATCCCTTCCCTGCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAGCCTTCCTCTGGGGCCTCTGC
+ATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC-CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATG
+GCAGACACCACCCTTCCCTTATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCTGCGGGTGCTTTCTGCAGCTT
+CCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCA
+TATAAACTTTGAAACTTTTTAAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCAAAAAGTTAGTTTTCATCACC
+CATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCAATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGC
+TAATGACAGTTCCGACGGTTGGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTTGGTCCCTTCAGAGGCCAAGG
+AGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCCCTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCC
+CTCAGCGTGCCTCCGTCAAGACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTGACAGGTCCCACTGGTG-ATG
+CTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTAAGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTA
+GGACAGCTGACCACTAGTGGGCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTAGGTGACACCATGCACACTCA
+GCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGTGTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTG
+AGCCCTCACCTCTACAACCTTCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+CAGGTGCTCCAGT
+>gi|274325752|ref|NM_007706.4|
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------GGAGCG
+AG-GGAGGCGCG--TCGGGCTGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTCCTCCCGCAGCCGCCAACGCT
+GCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT------CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTG
+CCCGGAGC---CCGGAAGCTTGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACCATTTCGGACACCCTGCACAC
+TCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCGAGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTG
+GGACGTGTTGACTCATCTCCCATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAAGCCCTGCGCGAGCTCAGTCA
+AACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCCAAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTT
+GATTAGAG-----ATAGTTCGCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAAGCTTAAACAGTTTGACAGT-
+-GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAGGATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTG
+TTCA--CCTGT----------------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAACAAGACTAAAAGATTACT-T
+GGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTCTTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTC
+ATATAGACTATCTCCGAATGCAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTAGCTAGGTATTTTAAAGTTCC
+CCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATGGCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAAC
+AAAACAAAACAAAACAAAACAAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAGAAAGAAAGGCTTCCATTTCC
+AGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA-GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TT
+CAGTCTGTGTTAGAACCTGTTATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAAAAAAAAAAAAGAAACGAAAA
+AAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TTTTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT-
+-GTAGTCCTCCATCTCAAGGCAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------CTCAATCCTTGAAAACAGAAA
+CCAATGCAGACAGAGACATTTTACCC------CTTGATGTAGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTT
+TATTTTTATTTT--------------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCCAGATATAGAGGG-GTACCTG
+CCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTGATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTAC
+AAGTTGCACTCAT-------------------------------------------------------------------
+-------------
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.MEGA b/Src/Mobyle/Test/Converter/DataAlignments/align.MEGA
new file mode 100644
index 0000000..9dae4b3
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.MEGA
@@ -0,0 +1,153 @@
+#mega
+!Title Multiple Sequence Alignment;
+
+#gi|262263371|ref|NM_016661.3|
+-----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC
+-CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCC
+AGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCGGACTGGCCGCCTGGGGACGG
+AA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGG
+TCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCGGCTGCCATTGC---CAAGGC
+TGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG
+-TGGTGACCTTACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+-ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-CTCTACAAGAT
+GATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+--CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TC
+ATAGATGGCATCAAACGGGCCACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGG
+TGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAA
+CATCTTTGTCACCACCACAGGCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGAT
+GAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCGATGTGGAG-----ATTGATG
+TGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAA
+GGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAAC
+TCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTGTGGA--CCCA----------
+-------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATC
+ACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTTCCAGCTGCCATCCTTGTTCC
+GGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCT
+GGTCACTCTGTCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGACATGATCTCAG
+AAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTG
+TGTGCCTGGTTTGCAGATCCATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC-
+-CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGAC
+AGACTGAAAGGGGCTGGAAAAACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATA
+GTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAA
+TGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGC
+ATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGA
+CACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACAGGGTCCCTCAC------TGG
+ACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTT
+ATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCTGGAAAGCAAATAAA-AACAA
+GCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+-------------
+#gi|281182713|ref|NM_001168497.
+TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCC
+GCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGA
+ACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCGCCCTAGAGACCCTGGGCCCG
+TACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCAC
+CCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCCGGTGGCTCTGCATCCTGGAC
+CGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGC
+CTCCTGGCCTCGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGCTCCGGAACCG
+GACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCC
+TCCGCCCACTTCATTGTGGCTGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAG
+TC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGA
+AGTGTGGGCCGCATCAGCAAGCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCC
+GATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTG
+GGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTA
+AGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGC
+GGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCGTTGTGTCCCATCCCTTCCCT
+GCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC
+-CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATGGCAGACACCACCCTTCCCTT
+ATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCT
+GCGGGTGCTTTCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTTGAAACTTTTT
+AAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCA
+AAAAGTTAGTTTTCATCACCCATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCA
+ATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTT
+GGTCCCTTCAGAGGCCAAGGAGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCC
+CTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAG
+ACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTG
+ACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTA
+AGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTAGGACAGCTGACCACTAGTGG
+GCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGT
+GTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTGAGCCCTCACCTCTACAACCT
+TCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+CAGGTGCTCCAGT
+#gi|274325752|ref|NM_007706.4|
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+------------------------------------------------------------
+----------------------------------GGAGCGAG-GGAGGCGCG--TCGGGC
+TGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTC
+CTCCCGCAGCCGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+--CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC---CCGGAAGCT
+TGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACC
+ATTTCGGACACCCTGCACACTCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCG
+AGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAA
+GCCCTGCGCGAGCTCAGTCAAACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCC
+AAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTC
+GCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAA
+GCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAG
+GATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTGTTCA--CCTGT---------
+-------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTC
+TTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTCATATAGACTATCTCCGAATG
+CAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTA
+GCTAGGTATTTTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAACAAAACAAAAC
+AAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAG
+AAAGAAAGGCTTCCATTTCCAGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA
+-GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAA
+AAAAAAAAAAGAAACGAAAAAAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TT
+TTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGG
+CAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------C
+TCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATTTTACCC------CTTGATGT
+AGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTTTATTTTTATTTT--------
+------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTG
+ATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTACAAGTTGCACTCAT-------
+------------------------------------------------------------
+-------------
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.MSF b/Src/Mobyle/Test/Converter/DataAlignments/align.MSF
new file mode 100644
index 0000000..d415c68
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.MSF
@@ -0,0 +1,299 @@
+!!NA_MULTIPLE_ALIGNMENT 1.0
+ unknown  MSF: 2893  Type: N  February 10, 2010 18:27  Check: 5027 ..
+
+ Name: gi|262263371|ref|NM_016661.3|   Len:  2893  Check:  336  Weight:  1.00
+ Name: gi|281182713|ref|NM_001168497.  Len:  2893  Check: 8790  Weight:  1.00
+ Name: gi|274325752|ref|NM_007706.4|   Len:  2893  Check: 5901  Weight:  1.00
+
+//
+
+                                1                                                   50
+ gi|262263371|ref|NM_016661.3|  ~~~~~~~~~~ ~~~~~~~AAA TAAGAAGATA TTTAAAGACG CCGGCGCCAG 
+gi|281182713|ref|NM_001168497.  TGGGGAGGCA GAGCAGCAAC CCGCGGAAAA GGCGAGGGTC TTGCTGCTGG 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                51                                                 100
+ gi|262263371|ref|NM_016661.3|  GCGTAGATCC .CTGCCCCGC GCCGCCCGGC CCCCTTCACT GAGTTCATCA 
+gi|281182713|ref|NM_001168497.  GGATAGGGCC GCAATCACCC TCCAACAGGC CAGGTCTCCA AGCCTCTGCA 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                101                                                150
+ gi|262263371|ref|NM_016661.3|  GTCCTAGCGG AAGCGCCGCC AGCATGTCTG ATAAACTGCC CTACAAAGTC 
+gi|281182713|ref|NM_001168497.  GTCGGAGCGG AAGGGGCGGA ACCACGGTGA ACAAAGCGC. ..ACAAGGTC 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                151                                                200
+ gi|262263371|ref|NM_016661.3|  GCGGACATCG GACTGGCCGC CTGGGGACGG AA..GGCTCT GGATATAGCT 
+gi|281182713|ref|NM_001168497.  CAAGGTGCCG CCCTAGAGAC CCTGGGCCCG TACTGGGCGC AGCTACCTCT 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                201                                                250
+ gi|262263371|ref|NM_016661.3|  GAGAATGAGA .TGCCAGGAT TGATGCGCAT GCGGG...AG ATGTACTCAG 
+gi|281182713|ref|NM_001168497.  TCGCCTCTGC CTGTCCGTCT TTGTTTCTGT GTCTGTCTAG CTGTTCCCGA 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                251                                                300
+ gi|262263371|ref|NM_016661.3|  CCTCCAAGCC ACTGAAGGGT GCTCGCATTG CTG.GCTGCC TGCACATGAC 
+gi|281182713|ref|NM_001168497.  GCTT.GTCCC ACTCCAGAAC TAAGTCTCCC CTACGCCAAA AGCCCAAGAC 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                301                                                350
+ gi|262263371|ref|NM_016661.3|  CGTGGAGACT GCTG.TTCTC ATTG.AGACT CTCGTGGCCC TGGGTGCTGA 
+gi|281182713|ref|NM_001168497.  TCCC....CT CCTGATTCCC ATGGCAGGCT ACTTGCCCCC CAAAGGCTAT 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                351                                                400
+ gi|262263371|ref|NM_016661.3|  GGTGCGGTGG TCCAGCTGCA ACATCTTCT. CTACTCAGGA CCATGCAGCG 
+gi|281182713|ref|NM_001168497.  GCCCCTTCAC CCCCACCTCC CTACCCCGTG CCATCTGGGT ATCCAGAGCC 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~GGAGCG 
+
+                                401                                                450
+ gi|262263371|ref|NM_016661.3|  GCTGCCATTG C...CAAGGC TGGCAT.... TCCAGTGTTT GCCTGGAAGG 
+gi|281182713|ref|NM_001168497.  GGTGGCTCTG CATCCTGGAC CGGGACAAGC TCCAGTGCCC ACCCAGGTGC 
+ gi|274325752|ref|NM_007706.4|  AG.GGAGGCG CG..TCGGGC TGGGAAG... TCGCGCGCAC ACTCGGCTCC 
+
+                                451                                                500
+ gi|262263371|ref|NM_016661.3|  GCGAGACAGA TGAGGAGTAC .CTGTGGTGC ATTGAGCAGA CGCTGCACTT 
+gi|281182713|ref|NM_001168497.  CTGCCCCTGC TCCCGGCTTC GCTCTCTTCC CCTCGCCAGG CCCAGTGGCT 
+ gi|274325752|ref|NM_007706.4|  G.GGGACAGA CGGTTAACTC ...TTGCCAA GTCTCGCCGC CTCTGCGGCT 
+
+                                501                                                550
+ gi|262263371|ref|NM_016661.3|  CAAGGACG.. ..GACCCCTC AACATGATTC TGGATGATGG .TGGTGACCT 
+gi|281182713|ref|NM_001168497.  CCAGGGCCTC CTGCTCCTTT CGTGCCATTG CCAGGGGTGC CTCCTGGCCT 
+ gi|274325752|ref|NM_007706.4|  CCCGGGCCTT GGGCTTCCCC CCTG.AAGCA TGAGCCTTTC CTCCCGCAGC 
+
+                                551                                                600
+ gi|262263371|ref|NM_016661.3|  TACTAACCTC ATCCACACCA AATACCCACA GCTTC..TGT CAGGC..... 
+gi|281182713|ref|NM_001168497.  CGAATTCCTA GTGCAGATTG ATCAAATCTT GATTCATCAG AAGGCTGAAC 
+ gi|274325752|ref|NM_007706.4|  CGCCAACGCT GCGCGGGTCT CGGACAGTGC GCGCC..... GGGACT.... 
+
+                                601                                                650
+ gi|262263371|ref|NM_016661.3|  .ATCCGAGGT ATCTCTGAG. ...GAGACC. ACGACTGGGG TCCACAAC.C 
+gi|281182713|ref|NM_001168497.  GAGTGGAAAC GTTCCTAGGC TGGGAGACCT GTAATATGTA TGAACTTCGC 
+ gi|274325752|ref|NM_007706.4|  ..CCAGGCGC GCGCCCTCA. ....AGATC. ...CCTTGTG CCCGGAGC.. 
+
+                                651                                                700
+ gi|262263371|ref|NM_016661.3|  TCTACAAGAT GATGTCCAAT GGGATA.... .CTGAAGGTG CCTGCCATCA 
+gi|281182713|ref|NM_001168497.  TCCGGAACCG GACAGCAACT GGGTCAGGCA GCTGAAGAGA GCAACTGTTG 
+ gi|274325752|ref|NM_007706.4|  .CCGGAAGCT TGCGGCAGGC GATCTGTG.. GGTGACAGTG TCTGCGAGAG 
+
+                                701                                                750
+ gi|262263371|ref|NM_016661.3|  ATGTCAACGA TTCTGT.... ..CACCAA.. .....GAGCA AGTTTGACAA 
+gi|281182713|ref|NM_001168497.  TGCCCGCCTG TGCTGTGGTG CCCGCCGACC ATTTCGAATC CGCCTAGCGG 
+ gi|274325752|ref|NM_007706.4|  ACTTTGCC.A CACCAT.... TCTGCCGGA. ATTTGGAGAA AAAGAACCAG 
+
+                                751                                                800
+ gi|262263371|ref|NM_016661.3|  CCTCTATGG. CTGCCGGGAG TCCC....TC ATAGATGGCA TCAAACGGGC 
+gi|281182713|ref|NM_001168497.  ACCCTGGGGA CCGCGAGGTG CTCCGGCTCC TCCGCCCACT TCATTGTGGC 
+ gi|274325752|ref|NM_007706.4|  CCGCTTCCAG TCCCCTCCCC CTCCGCCACC ATTTCGGACA CCCTGCACAC 
+
+                                801                                                850
+ gi|262263371|ref|NM_016661.3|  CACAGATGTG ATG...ATTG CGGGCA..AG GTGGCGGTGG TGGCAGGCTA 
+gi|281182713|ref|NM_001168497.  TGCAGCTGCT GCC...CCTG TGGTCTTCAG GAGATGGAAG TC.CAGGCTC 
+ gi|274325752|ref|NM_007706.4|  TCTCGTTTTG GGGTACCCTG TGACTTCCAG GCAGCACGCG AGGTCCACTG 
+
+                                851                                                900
+ gi|262263371|ref|NM_016661.3|  TGG.TGATGT GGGCAAGGGC ..TGTGCCCA GGCCCTGAGG GGTTTTGGGG 
+gi|281182713|ref|NM_001168497.  CACCTGGCAC CACCATTGGC CATGTGCTAC AGACCTGGCA TCCCTTCCTT 
+ gi|274325752|ref|NM_007706.4|  GCCCCAGCTC GGGC...GAC CAGCTGTCTG GGACGTGTTG ACTCATCTCC 
+
+                                901                                                950
+ gi|262263371|ref|NM_016661.3|  CCCGAGTCAT C.ATCACCGA GATCGACCCC ATCAATGCAC TGCAAGCTG. 
+gi|281182713|ref|NM_001168497.  CCTAAGTTTT CCATCCTGGA TGCTGATCGC CAACCTGTTC TACGAGTTGT 
+ gi|274325752|ref|NM_007706.4|  CATGACCCTG CGGTGCCTGG AGCCCTCCGG GAATGGAGCG GACAGGACGC 
+
+                                951                                               1000
+ gi|262263371|ref|NM_016661.3|  ....CCATGG AGG....... GCTATG.... AGGTAACCAC TATG.GACGA 
+gi|281182713|ref|NM_001168497.  AGGGCCTTGC TGGACTTGTG GCTGTGGTAC AGACACCAAC TTTGAGGTGA 
+ gi|274325752|ref|NM_007706.4|  GGAGCCAGTG GGGGACCGCG GGGTTGCCGG AGG.AACAGT CCCCCGAGGC 
+
+                                1001                                              1050
+ gi|262263371|ref|NM_016661.3|  AGCCT..GTA AGGAGGGCAA CATCTTTGTC ACCACCACAG GCTGTGTGGA 
+gi|281182713|ref|NM_001168497.  AGACTAAGGA TGAATCGCGA AGTGTGGGCC GCATCAGCAA GCAGTGGGGA 
+ gi|274325752|ref|NM_007706.4|  GGC....GCG TCTGGCGAAA GCCCTGCGCG AGCTCAGTCA AACAGGATGG 
+
+                                1051                                              1100
+ gi|262263371|ref|NM_016661.3|  TATCATCCTT GGCCGGCACT TTGAGCAGAT GAAGGATGAC GCCATTGTCT 
+gi|281182713|ref|NM_001168497.  GGGCTGCTCC GAGAAGCCCT CACAGATGCC GATGACTTTG GCCTCCAATT 
+ gi|274325752|ref|NM_007706.4|  TACTGGGGAA GTATGACTGT TAATGAAGCC AAAGAGAAAT TAAAAGAGGC 
+
+                                1101                                              1150
+ gi|262263371|ref|NM_016661.3|  GTAA..CATT GGACACTTCG ATGTGGAG.. ...ATTGATG TGAA....GT 
+gi|281182713|ref|NM_001168497.  CCCAGTCGAT CTAGATGTGA AAGTGAAGGC CGTACTGCTG GGAGCCACGT 
+ gi|274325752|ref|NM_007706.4|  TCCAG..AAG GAACTTTCTT GATTAGAG.. ...ATAGTTC GCATTCAGAC 
+
+                                1151                                              1200
+ gi|262263371|ref|NM_016661.3|  GGCTCAATGA GAACGCGGT. .....GGAGA AAGTGAACAT CAAGCCCCAG 
+gi|281182713|ref|NM_001168497.  TCCTCATCGA TTATATGTTC TTCGAGAAGA GAGGAGGCGC AGGACCCTCT 
+ gi|274325752|ref|NM_007706.4|  TACCTACTAA CTATATCCGT TAAGACGTCA GCTGGACCGA CTAACCTGCG 
+
+                                1201                                              1250
+ gi|262263371|ref|NM_016661.3|  GTGGACCGCT ACTGG..... ..CTAAAGAA TGGGCGCCGC ATCATCTTG. 
+gi|281182713|ref|NM_001168497.  GCCATCACCA GTTAGAAGCC ACCTCAGGAT GAGGAGACCC ATCTCCTTGA 
+ gi|274325752|ref|NM_007706.4|  G.ATTGAGTA CCAAGATGGG AAATTCAGAT TGGAT..TCT ATCATATGTG 
+
+                                1251                                              1300
+ gi|262263371|ref|NM_016661.3|  .CTGGCTGAA GGCCGTCTGG TCAACCTGG. .GTTGTGCCA TGGGACA.CC 
+gi|281182713|ref|NM_001168497.  CCAGAATTTA AGATGGTCAG CTGCCCTGGA CGTTCCCTCC TGAAGCAACC 
+ gi|274325752|ref|NM_007706.4|  TCAAGTCCAA GCTTAAACAG TTTGACAGT. .GTGGTTCAT CTGATTGACT 
+
+                                1301                                              1350
+ gi|262263371|ref|NM_016661.3|  CCAGCTTCGT GATGAGCAAC TCC...TTCA CAAACCAGGT GATGGCACAG 
+gi|281182713|ref|NM_001168497.  CTTTCCTTGA TATACACTGC GGC...GGAC CGACGAGGGT GGCCGAGTGG 
+ gi|274325752|ref|NM_007706.4|  ACTATGTCCA GATGTGCAAG GATAAACGGA CAGGCCCAGA AGCCCCACGG 
+
+                                1351                                              1400
+ gi|262263371|ref|NM_016661.3|  ATTG.AGCTG TGGA..CCCA .......... .......... ...CCCAGAT 
+gi|281182713|ref|NM_001168497.  TTGGGAGCCG TTGTGTCCCA TCCCTTCCCT GCTTCTCCTG TGGCCCTGCA 
+ gi|274325752|ref|NM_007706.4|  AATGGGACTG TTCA..CCTG T......... .......... ...ACCTGAC 
+
+                                1401                                              1450
+ gi|262263371|ref|NM_016661.3|  AAATACCCTG TTGGGGTTCA CTTCCTGCCT AAGA..AGCT GGATGAGGCG 
+gi|281182713|ref|NM_001168497.  GAAGAGCATG TATGAGACCT GTTCCTCCTT CTGTTCACCA TCTCCAGGCA 
+ gi|274325752|ref|NM_007706.4|  CAAACCTCTG TAT.ACATCA GCACCCACTC TGCAGCATTT CTGTCGACTC 
+
+                                1451                                              1500
+ gi|262263371|ref|NM_016661.3|  GTGGCTGAAG CCCACCTGGG CAAGCTGAAT GTGAAGCTGA C.CAAGCTGA 
+gi|281182713|ref|NM_001168497.  GTGCCTGT.G CACACATTAG CTTTTAAACT TCCTTGCACA CTCCTTCCAG 
+ gi|274325752|ref|NM_007706.4|  GCCATTAA.. ..CAAATGTA CCGGTACGAT CTGGGGACTG CCTTTACCAA 
+
+                                1501                                              1550
+ gi|262263371|ref|NM_016661.3|  CTGAGAAGCA GGCCCAGTAC CTGGGCATGC CCATCAACGG CCCC...TTC 
+gi|281182713|ref|NM_001168497.  CCTTCCTCTG GGGCCTCTGC ATAGGCAGGG GCATCTGGAA TCCTGGACTC 
+ gi|274325752|ref|NM_007706.4|  CAAGACTAAA AGATTACT.T GGAAGAATAT AAATTCCAGG TATA..AGTA 
+
+                                1551                                              1600
+ gi|262263371|ref|NM_016661.3|  AAGCCTGATC ACTACCGCTA CTGAGTGCTG GG...GCTGT CCTTCACCTT 
+gi|281182713|ref|NM_001168497.  AAGTTTTACC .CCAGGGCTT GTGGGTAAAA GGCAAGCAGT ACCAAAGATG 
+ gi|274325752|ref|NM_007706.4|  TTTCTCTCTC TTTTTCGTTT TTTTTTAAAA AAAAAAAAAA CACATGCCTC 
+
+                                1601                                              1650
+ gi|262263371|ref|NM_016661.3|  CCAGCTGCCA TCCTTGTTCC GGG...CCCT ATC....... TCTCGTTCCC 
+gi|281182713|ref|NM_001168497.  GCAGACACCA CCCTTCCCTT ATGGCACTTT AGCCAATTAG TTTAGCTTCC 
+ gi|274325752|ref|NM_007706.4|  ATATAGACTA TCTCCGAATG CAG...CTAT GTGAAA.... ..GAGAACCC 
+
+                                1651                                              1700
+ gi|262263371|ref|NM_016661.3|  AAGAGCAA.. ........AT GTCACCAACT TTGCAGTAGC CTGGAC.AGT 
+gi|281182713|ref|NM_001168497.  GATTGTGGCA CTCTGAGGGG ATCCTTGCCT CCTCACTAAT AGCTGT.AGC 
+ gi|274325752|ref|NM_007706.4|  AGAGGCCCTC C........T CTGGATAACT GCGCAGAATT CTCTCTTAAG 
+
+                                1701                                              1750
+ gi|262263371|ref|NM_016661.3|  GCTTCTCCCA CACAGCCCTC CCCTTACACC CTCTGGGGCT GGTCACTCTG 
+gi|281182713|ref|NM_001168497.  GGTTGGGCCC CAGTGCCAAC TCCCTAAGCC CCTGGGCCCT GCGGGTGCTT 
+ gi|274325752|ref|NM_007706.4|  GACAGTTGGG CTCAGTCTAA CTTAAAGGTG TGAAGATGTA GCTAGGTATT 
+
+                                1751                                              1800
+ gi|262263371|ref|NM_016661.3|  TCTTTGACCT C...TGCTGT ATC....... ...CCTCATA CTGTTCCA.. 
+gi|281182713|ref|NM_001168497.  TCTGCAGCTT CCTGTGCCTT ATTTAACCGT TAACCCCTTC CTTCCCCT.. 
+ gi|274325752|ref|NM_007706.4|  TTAAAGTTCC CCTTAGGTAG TTTTAGCTGA ATGATGCTTT CTTTCCTATG 
+
+                                1801                                              1850
+ gi|262263371|ref|NM_016661.3|  GGTGT.GGAG GGGGAATGGA GAGGT.ACCT GGTAGGCATC CACAGGGGAC 
+gi|281182713|ref|NM_001168497.  ACTGTAGGAA GGAGGCTGTG TCTTT.GTAT GTTGTACTCA TATAAACTTT 
+ gi|274325752|ref|NM_007706.4|  GCTGCTCAAG ATCAAATGGC CCTTTTAAAT GAAACAAAAC AAAACAAAAC 
+
+                                1851                                              1900
+ gi|262263371|ref|NM_016661.3|  ATGATCTCAG AAGTCTTGGA AGTCC..TGA GGCTGGAT.. .GTTGCTAGT 
+gi|281182713|ref|NM_001168497.  GAAACTTTTT AAACAGTAGA AGCCGGATGT GATGGGAGAG GGTAGATACC 
+ gi|274325752|ref|NM_007706.4|  AAAACAAAAC AAAAACAAAA TGTCCCAAGG AAAGGCAA.. .GAGAATATT 
+
+                                1901                                              1950
+ gi|262263371|ref|NM_016661.3|  GATGGTCACA A.....GCCA TG.CACCTTA TCAATGAGAC CCTCACTT.. 
+gi|281182713|ref|NM_001168497.  GGATAACACA AAGGGAGCGG CGTCAGCCAG CCTGTATAAT CTACACTATT 
+ gi|274325752|ref|NM_007706.4|  C.CTGTCAGA A......TGA CTTGCCTTTG TTTTTTGGGT TCCTATGCAC 
+
+                                1951                                              2000
+ gi|262263371|ref|NM_016661.3|  .......... ......GGTC TTTAGATCTG TGTGCCTGGT TTGCAGATCC 
+gi|281182713|ref|NM_001168497.  TGGATTAAAG GAAAAAAGTC CTTGAGTCCA AAAAGTTAGT TTTCATCACC 
+ gi|274325752|ref|NM_007706.4|  TGGGTCAAAA GTCCAAGCTC CATAGGAGAG AAAGAAAGGC TTCCATTTCC 
+
+                                2001                                              2050
+ gi|262263371|ref|NM_016661.3|  ATTGGTTTCT CAGTCCAGGA CCCAAGAA.. ..CGAGCTC. .CACCAAAGA 
+gi|281182713|ref|NM_001168497.  CATAGTGATT CTGGACCAAA CCAGAGCAGC TCCCAAGTCA ATATCTAAGT 
+ gi|274325752|ref|NM_007706.4|  AGGAGGACAG CTGAAGGAGG GAAAGACCCT GGCTGACCCA .GGCTTCAGC 
+
+                                2051                                              2100
+ gi|262263371|ref|NM_016661.3|  GCAGGAACCC CTGGAGTTTG AAG.....GC CCCCGAGAGC TGGGCCTTTT 
+gi|281182713|ref|NM_001168497.  TGGGTGCCCC ATAGAGTTTA GGGAAGTAGC TAATGACAGT TCCGACGGTT 
+ gi|274325752|ref|NM_007706.4|  TC..CACTTC TAGACGCCTG GGG.....TT CAGTCTGTGT TAGAACCTGT 
+
+                                2101                                              2150
+ gi|262263371|ref|NM_016661.3|  TACTGTTG.. ..GGCA..GT CTCTTAAACC TCATG.ATAC TGAGTTGGTA 
+gi|281182713|ref|NM_001168497.  GGTCAGTGAT CCGGTATTGC TACTTGATCC TTGTCTGTAG TGAGTAGAGG 
+ gi|274325752|ref|NM_007706.4|  TATAGTTT.. ...GCAT..C CTGATGTATC TAGTAGGAGT TCCGTTAA.G 
+
+                                2151                                              2200
+ gi|262263371|ref|NM_016661.3|  CTTTTTTTGG TCCCTATT.T CACAAG.... ....GGTTAG GA.TAGATTA 
+gi|281182713|ref|NM_001168497.  CCTCCTCCAA AGCTTTTTCT TTCAAGTCCT GTTCAATTGG GAATATAGTA 
+ gi|274325752|ref|NM_007706.4|  CTGACCATGC TTGTATTTAT CCCTCGTCTT ATGCAACTA. .ATCAAATCA 
+
+                                2201                                              2250
+ gi|262263371|ref|NM_016661.3|  ACCAAGAAAG GACAAGTGAC AGACTGAAAG GGGCTGGAAA AACAAAGA.. 
+gi|281182713|ref|NM_001168497.  GGCAGAGTGG ACTTGGTCTT GGTCCCTTCA GAGGCCAAGG AGCAGGGCTC 
+ gi|274325752|ref|NM_007706.4|  ACCAAAAAAA GGAAAAAAAA AAAAAAAAAA GAAACGAAAA AAGAACCATC 
+
+                                2251                                              2300
+ gi|262263371|ref|NM_016661.3|  GGAAAAGGC. ....CTGTCA CTTCTGTATA GTTGATGGTT CCTGTCACAA 
+gi|281182713|ref|NM_001168497.  GGAGGAAGCA TTAGCCAGTA CCTGAGTGCC CTATACACTT CCTGTCACTC 
+ gi|274325752|ref|NM_007706.4|  ACCATGAGAT ....CCTGTA TTTGTCT.TT TTTTACTACA CGTATGACCT 
+
+                                2301                                              2350
+ gi|262263371|ref|NM_016661.3|  GCC.CAGGTC ACAAACAGA. .TTAATTTGT TT...TATAA TGTTTATATG 
+gi|281182713|ref|NM_001168497.  CTCACAGCAG GTAAATCTCC CTCAGCGTGC CTCCGTCAAG ACCTCATGTT 
+ gi|274325752|ref|NM_007706.4|  CCC.CCGTGA GTGAGTACT. .GTAGTCCTC CATCTCAAGG CAGCCTTACT 
+
+                                2351                                              2400
+ gi|262263371|ref|NM_016661.3|  CTATTTAGAA TGTTAACAAA GGA...AGGT GGATAAAATA .CAGTTTCTA 
+gi|281182713|ref|NM_001168497.  CTGATTAGTC CGTAAGGCCA AGCCCTAAGT GGACCAGGTA ACCCATGCTG 
+ gi|274325752|ref|NM_007706.4|  TCAGACACAT TTCAAACTGG TGC...AAAC AGAAAAGACT TCTCTCTTTT 
+
+                                2401                                              2450
+ gi|262263371|ref|NM_016661.3|  CTGCCTA... ..AAGAATTT TGGCTCTATT AAAATGTAAG TGTGTGGCTG 
+gi|281182713|ref|NM_001168497.  GTGTCCATCC CTGAGGATAC AGAGTTCAGG ACCGGGCTAG GATGTAGCGA 
+ gi|274325752|ref|NM_007706.4|  CTTCTGA... ....GGCTAA AGACAAGAAT GTCACGCTA. ..TACAGGTG 
+
+                                2451                                              2500
+ gi|262263371|ref|NM_016661.3|  GAATATGTGC ATGTATGTGG CTGACATATG TGCATGTGTG TGCAGACACT 
+gi|281182713|ref|NM_001168497.  CTGGCTGCTG ACAGGTCCCA CTGGTG.ATG CTCCAGGATG CGACGACAGT 
+ gi|274325752|ref|NM_007706.4|  CAA......C TCAATCCTTG AAAACAGAAA CCAATGCAGA CAGAGACATT 
+
+                                2501                                              2550
+ gi|262263371|ref|NM_016661.3|  GTACCTGTGA TTCTGGAGGA CACCAGATGT CCCATCGCTT TCTTCCATAT 
+gi|281182713|ref|NM_001168497.  CTGCTCGGGA CCGGTTACTA AGGTGGAAGA GC..TGGTCT ACCTGTGTGA 
+ gi|274325752|ref|NM_007706.4|  TTACCC.... ..CTTGATGT AGCTGTGAGT CC..CAACCT AGTGCCATTG 
+
+                                2551                                              2600
+ gi|262263371|ref|NM_016661.3|  TCCTGAGACA GGGTCCCTCA C......TGG ACTAGAGTGA GGCCATTTCT 
+gi|281182713|ref|NM_001168497.  GCAGGAGGTA GGACAGCTGA CCACTAGTGG GCCAAA..CA GGCGACTGGG 
+ gi|274325752|ref|NM_007706.4|  TTTTTATTTT TATTTTTATT TT........ ......TGGA AATGGCTTTA 
+
+                                2601                                              2650
+ gi|262263371|ref|NM_016661.3|  GCTCTTCCCA GATCCCTCAC CCTAGAGCTG G..GGTTAAA GTCAAGCATG 
+gi|281182713|ref|NM_001168497.  GAACCTGCAG AAGGGGTTAG CGTTACCTTG A..GGTTCCG GATAGCAGAA 
+ gi|274325752|ref|NM_007706.4|  GAACTTTCCA AG....TTAT CCTTGAATTG TCTGACCACG GACATCAACA 
+
+                                2651                                              2700
+ gi|262263371|ref|NM_016661.3|  GCCATGTGTA GCTTCTACAA GTGCAAACGG TCTTACCCAC TGAGCCATCT 
+gi|281182713|ref|NM_001168497.  GCCACATCTC .CCCCAGTGG TTCTGAGTGC CACTTGGCAC T..CCTGGTA 
+ gi|274325752|ref|NM_007706.4|  GC..TGCCTC CCTTCTACCA TGTAGAATCC TATGACTTAA CTTTTCTTCC 
+
+                                2701                                              2750
+ gi|262263371|ref|NM_016661.3|  CTTCACGGAT CGCATAATGT GAATCACACA AAAAAGTATG AGAAATTGAA 
+gi|281182713|ref|NM_001168497.  GGTGACACCA TGCACACTCA GCTCTAC.CT GCGGAGGACA AATCTTTGTT 
+ gi|274325752|ref|NM_007706.4|  AGATATAGAG GG.GTACCTG CCTGTTTTTC AAAGTGTTTA TTTACTGCTG 
+
+                                2751                                              2800
+ gi|262263371|ref|NM_016661.3|  CT.CCCTCTT ATTTGCCCAA GTGATCAATT TAC.TGAACC CATTTGACCT 
+gi|281182713|ref|NM_001168497.  TAGCCCTTGT GTCAACCTAC AGGCCCTTTG AACATAAAGT CATTTCAGTG 
+ gi|274325752|ref|NM_007706.4|  TTACTATTTG ATTAGA..AT GTATCAAATA AAAACAAACC TGACTTTTAC 
+
+                                2801                                              2850
+ gi|262263371|ref|NM_016661.3|  GGAAAGCAAA TAAA.AACAA GCCCCGTGCA CAGGATGGCT AAAAAAAAAA 
+gi|281182713|ref|NM_001168497.  AGCCCTCACC TCTACAACCT TCCTCTGCCA CTCGGGGTCT GACAAGGGAC 
+ gi|274325752|ref|NM_007706.4|  AAGTTGCACT CAT~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ 
+
+                                2851
+ gi|262263371|ref|NM_016661.3|  AAAAAAAAAA A~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~ 
+gi|281182713|ref|NM_001168497.  CTCCAGAGGG GACAGTGGCT TTGCTGGCTC CAGGTGCTCC AGT 
+ gi|274325752|ref|NM_007706.4|  ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~ 
+
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.NEXUS b/Src/Mobyle/Test/Converter/DataAlignments/align.NEXUS
new file mode 100644
index 0000000..8894fb2
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.NEXUS
@@ -0,0 +1,202 @@
+#NEXUS
+begin data;
+  dimensions ntax=3 nchar=2893;
+  format datatype=nucleic interleave;
+  matrix
+gi|262263371|ref|NM_016661.3|   -----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC
+gi|281182713|ref|NM_001168497.  TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCC
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   -CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCC
+gi|281182713|ref|NM_001168497.  GCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGA
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   AGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCGGACTGGCCGCCTGGGGACGG
+gi|281182713|ref|NM_001168497.  ACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCGCCCTAGAGACCCTGGGCCCG
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   AA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+gi|281182713|ref|NM_001168497.  TACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+gi|281182713|ref|NM_001168497.  CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGG
+gi|281182713|ref|NM_001168497.  TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCAC
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   TCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCGGCTGCCATTGC---CAAGGC
+gi|281182713|ref|NM_001168497.  CCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCCGGTGGCTCTGCATCCTGGAC
+gi|274325752|ref|NM_007706.4|   ----------------------------------GGAGCGAG-GGAGGCGCG--TCGGGC
+
+gi|262263371|ref|NM_016661.3|   TGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+gi|281182713|ref|NM_001168497.  CGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+gi|274325752|ref|NM_007706.4|   TGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+
+gi|262263371|ref|NM_016661.3|   ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG
+gi|281182713|ref|NM_001168497.  CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGC
+gi|274325752|ref|NM_007706.4|   GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTC
+
+gi|262263371|ref|NM_016661.3|   -TGGTGACCTTACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+gi|281182713|ref|NM_001168497.  CTCCTGGCCTCGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+gi|274325752|ref|NM_007706.4|   CTCCCGCAGCCGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+
+gi|262263371|ref|NM_016661.3|   -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-CTCTACAAGAT
+gi|281182713|ref|NM_001168497.  GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGCTCCGGAACCG
+gi|274325752|ref|NM_007706.4|   --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC---CCGGAAGCT
+
+gi|262263371|ref|NM_016661.3|   GATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+gi|281182713|ref|NM_001168497.  GACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+gi|274325752|ref|NM_007706.4|   TGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+
+gi|262263371|ref|NM_016661.3|   --CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TC
+gi|281182713|ref|NM_001168497.  CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCC
+gi|274325752|ref|NM_007706.4|   TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACC
+
+gi|262263371|ref|NM_016661.3|   ATAGATGGCATCAAACGGGCCACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGG
+gi|281182713|ref|NM_001168497.  TCCGCCCACTTCATTGTGGCTGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAG
+gi|274325752|ref|NM_007706.4|   ATTTCGGACACCCTGCACACTCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCG
+
+gi|262263371|ref|NM_016661.3|   TGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+gi|281182713|ref|NM_001168497.  TC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+gi|274325752|ref|NM_007706.4|   AGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+
+gi|262263371|ref|NM_016661.3|   CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+gi|281182713|ref|NM_001168497.  CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+gi|274325752|ref|NM_007706.4|   CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+
+gi|262263371|ref|NM_016661.3|   AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAA
+gi|281182713|ref|NM_001168497.  TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGA
+gi|274325752|ref|NM_007706.4|   GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAA
+
+gi|262263371|ref|NM_016661.3|   CATCTTTGTCACCACCACAGGCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGAT
+gi|281182713|ref|NM_001168497.  AGTGTGGGCCGCATCAGCAAGCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCC
+gi|274325752|ref|NM_007706.4|   GCCCTGCGCGAGCTCAGTCAAACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCC
+
+gi|262263371|ref|NM_016661.3|   GAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCGATGTGGAG-----ATTGATG
+gi|281182713|ref|NM_001168497.  GATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTG
+gi|274325752|ref|NM_007706.4|   AAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTC
+
+gi|262263371|ref|NM_016661.3|   TGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+gi|281182713|ref|NM_001168497.  GGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+gi|274325752|ref|NM_007706.4|   GCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+
+gi|262263371|ref|NM_016661.3|   GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAA
+gi|281182713|ref|NM_001168497.  GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTA
+gi|274325752|ref|NM_007706.4|   G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAA
+
+gi|262263371|ref|NM_016661.3|   GGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAAC
+gi|281182713|ref|NM_001168497.  AGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGC
+gi|274325752|ref|NM_007706.4|   GCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAG
+
+gi|262263371|ref|NM_016661.3|   TCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTGTGGA--CCCA----------
+gi|281182713|ref|NM_001168497.  GGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCGTTGTGTCCCATCCCTTCCCT
+gi|274325752|ref|NM_007706.4|   GATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTGTTCA--CCTGT---------
+
+gi|262263371|ref|NM_016661.3|   -------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+gi|281182713|ref|NM_001168497.  GCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+gi|274325752|ref|NM_007706.4|   -------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+
+gi|262263371|ref|NM_016661.3|   GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+gi|281182713|ref|NM_001168497.  TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+gi|274325752|ref|NM_007706.4|   CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+
+gi|262263371|ref|NM_016661.3|   CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATC
+gi|281182713|ref|NM_001168497.  CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC
+gi|274325752|ref|NM_007706.4|   CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTC
+
+gi|262263371|ref|NM_016661.3|   ACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTTCCAGCTGCCATCCTTGTTCC
+gi|281182713|ref|NM_001168497.  -CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATGGCAGACACCACCCTTCCCTT
+gi|274325752|ref|NM_007706.4|   TTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTCATATAGACTATCTCCGAATG
+
+gi|262263371|ref|NM_016661.3|   GGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+gi|281182713|ref|NM_001168497.  ATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+gi|274325752|ref|NM_007706.4|   CAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+
+gi|262263371|ref|NM_016661.3|   TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCT
+gi|281182713|ref|NM_001168497.  CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCT
+gi|274325752|ref|NM_007706.4|   GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTA
+
+gi|262263371|ref|NM_016661.3|   GGTCACTCTGTCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+gi|281182713|ref|NM_001168497.  GCGGGTGCTTTCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+gi|274325752|ref|NM_007706.4|   GCTAGGTATTTTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+
+gi|262263371|ref|NM_016661.3|   GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGACATGATCTCAG
+gi|281182713|ref|NM_001168497.  ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTTGAAACTTTTT
+gi|274325752|ref|NM_007706.4|   GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAACAAAACAAAAC
+
+gi|262263371|ref|NM_016661.3|   AAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+gi|281182713|ref|NM_001168497.  AAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+gi|274325752|ref|NM_007706.4|   AAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+
+gi|262263371|ref|NM_016661.3|   TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTG
+gi|281182713|ref|NM_001168497.  CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCA
+gi|274325752|ref|NM_007706.4|   CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAG
+
+gi|262263371|ref|NM_016661.3|   TGTGCCTGGTTTGCAGATCCATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC-
+gi|281182713|ref|NM_001168497.  AAAAGTTAGTTTTCATCACCCATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCA
+gi|274325752|ref|NM_007706.4|   AAAGAAAGGCTTCCATTTCCAGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA
+
+gi|262263371|ref|NM_016661.3|   -CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+gi|281182713|ref|NM_001168497.  ATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+gi|274325752|ref|NM_007706.4|   -GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+
+gi|262263371|ref|NM_016661.3|   TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+gi|281182713|ref|NM_001168497.  GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+gi|274325752|ref|NM_007706.4|   TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+
+gi|262263371|ref|NM_016661.3|   TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGAC
+gi|281182713|ref|NM_001168497.  AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTT
+gi|274325752|ref|NM_007706.4|   TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAA
+
+gi|262263371|ref|NM_016661.3|   AGACTGAAAGGGGCTGGAAAAACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATA
+gi|281182713|ref|NM_001168497.  GGTCCCTTCAGAGGCCAAGGAGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCC
+gi|274325752|ref|NM_007706.4|   AAAAAAAAAAGAAACGAAAAAAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TT
+
+gi|262263371|ref|NM_016661.3|   GTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAA
+gi|281182713|ref|NM_001168497.  CTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAG
+gi|274325752|ref|NM_007706.4|   TTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGG
+
+gi|262263371|ref|NM_016661.3|   TGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+gi|281182713|ref|NM_001168497.  ACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+gi|274325752|ref|NM_007706.4|   CAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+
+gi|262263371|ref|NM_016661.3|   CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGC
+gi|281182713|ref|NM_001168497.  GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTG
+gi|274325752|ref|NM_007706.4|   CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------C
+
+gi|262263371|ref|NM_016661.3|   ATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGA
+gi|281182713|ref|NM_001168497.  ACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTA
+gi|274325752|ref|NM_007706.4|   TCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATTTTACCC------CTTGATGT
+
+gi|262263371|ref|NM_016661.3|   CACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACAGGGTCCCTCAC------TGG
+gi|281182713|ref|NM_001168497.  AGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTAGGACAGCTGACCACTAGTGG
+gi|274325752|ref|NM_007706.4|   AGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTTTATTTTTATTTT--------
+
+gi|262263371|ref|NM_016661.3|   ACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+gi|281182713|ref|NM_001168497.  GCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+gi|274325752|ref|NM_007706.4|   ------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+
+gi|262263371|ref|NM_016661.3|   GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+gi|281182713|ref|NM_001168497.  GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+gi|274325752|ref|NM_007706.4|   GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+
+gi|262263371|ref|NM_016661.3|   CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTT
+gi|281182713|ref|NM_001168497.  GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGT
+gi|274325752|ref|NM_007706.4|   AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTG
+
+gi|262263371|ref|NM_016661.3|   ATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCTGGAAAGCAAATAAA-AACAA
+gi|281182713|ref|NM_001168497.  GTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTGAGCCCTCACCTCTACAACCT
+gi|274325752|ref|NM_007706.4|   ATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTACAAGTTGCACTCAT-------
+
+gi|262263371|ref|NM_016661.3|   GCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+gi|281182713|ref|NM_001168497.  TCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+gi|274325752|ref|NM_007706.4|   ------------------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   -------------
+gi|281182713|ref|NM_001168497.  CAGGTGCTCCAGT
+gi|274325752|ref|NM_007706.4|   -------------
+  ;
+endblock;
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPI b/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPI
new file mode 100644
index 0000000..f83687d
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPI
@@ -0,0 +1,196 @@
+     3  2893
+gi|2622633-----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC
+gi|2811827TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCC
+gi|2743257------------------------------------------------------------
+
+          -CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCC
+          GCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGA
+          ------------------------------------------------------------
+
+          AGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCGGACTGGCCGCCTGGGGACGG
+          ACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCGCCCTAGAGACCCTGGGCCCG
+          ------------------------------------------------------------
+
+          AA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+          TACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+          ------------------------------------------------------------
+
+          ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+          CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+          ------------------------------------------------------------
+
+          CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGG
+          TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCAC
+          ------------------------------------------------------------
+
+          TCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCGGCTGCCATTGC---CAAGGC
+          CCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCCGGTGGCTCTGCATCCTGGAC
+          ----------------------------------GGAGCGAG-GGAGGCGCG--TCGGGC
+
+          TGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+          CGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+          TGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+
+          ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG
+          CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGC
+          GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTC
+
+          -TGGTGACCTTACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+          CTCCTGGCCTCGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+          CTCCCGCAGCCGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+
+          -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-CTCTACAAGAT
+          GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGCTCCGGAACCG
+          --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC---CCGGAAGCT
+
+          GATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+          GACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+          TGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+
+          --CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TC
+          CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCC
+          TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACC
+
+          ATAGATGGCATCAAACGGGCCACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGG
+          TCCGCCCACTTCATTGTGGCTGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAG
+          ATTTCGGACACCCTGCACACTCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCG
+
+          TGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+          TC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+          AGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+
+          CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+          CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+          CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+
+          AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAA
+          TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGA
+          GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAA
+
+          CATCTTTGTCACCACCACAGGCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGAT
+          AGTGTGGGCCGCATCAGCAAGCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCC
+          GCCCTGCGCGAGCTCAGTCAAACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCC
+
+          GAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCGATGTGGAG-----ATTGATG
+          GATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTG
+          AAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTC
+
+          TGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+          GGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+          GCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+
+          GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAA
+          GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTA
+          G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAA
+
+          GGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAAC
+          AGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGC
+          GCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAG
+
+          TCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTGTGGA--CCCA----------
+          GGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCGTTGTGTCCCATCCCTTCCCT
+          GATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTGTTCA--CCTGT---------
+
+          -------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+          GCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+          -------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+
+          GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+          TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+          CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+
+          CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATC
+          CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC
+          CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTC
+
+          ACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTTCCAGCTGCCATCCTTGTTCC
+          -CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATGGCAGACACCACCCTTCCCTT
+          TTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTCATATAGACTATCTCCGAATG
+
+          GGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+          ATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+          CAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+
+          TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCT
+          CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCT
+          GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTA
+
+          GGTCACTCTGTCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+          GCGGGTGCTTTCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+          GCTAGGTATTTTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+
+          GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGACATGATCTCAG
+          ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTTGAAACTTTTT
+          GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAACAAAACAAAAC
+
+          AAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+          AAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+          AAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+
+          TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTG
+          CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCA
+          CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAG
+
+          TGTGCCTGGTTTGCAGATCCATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC-
+          AAAAGTTAGTTTTCATCACCCATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCA
+          AAAGAAAGGCTTCCATTTCCAGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA
+
+          -CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+          ATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+          -GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+
+          TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+          GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+          TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+
+          TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGAC
+          AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTT
+          TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAA
+
+          AGACTGAAAGGGGCTGGAAAAACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATA
+          GGTCCCTTCAGAGGCCAAGGAGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCC
+          AAAAAAAAAAGAAACGAAAAAAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TT
+
+          GTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAA
+          CTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAG
+          TTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGG
+
+          TGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+          ACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+          CAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+
+          CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGC
+          GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTG
+          CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------C
+
+          ATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGA
+          ACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTA
+          TCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATTTTACCC------CTTGATGT
+
+          CACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACAGGGTCCCTCAC------TGG
+          AGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTAGGACAGCTGACCACTAGTGG
+          AGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTTTATTTTTATTTT--------
+
+          ACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+          GCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+          ------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+
+          GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+          GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+          GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+
+          CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTT
+          GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGT
+          AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTG
+
+          ATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCTGGAAAGCAAATAAA-AACAA
+          GTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTGAGCCCTCACCTCTACAACCT
+          ATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTACAAGTTGCACTCAT-------
+
+          GCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+          TCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+          ------------------------------------------------------------
+
+          -------------
+          CAGGTGCTCCAGT
+          -------------
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPS b/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPS
new file mode 100644
index 0000000..ef08074
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.PHYLIPS
@@ -0,0 +1,148 @@
+     3  2893
+gi|2622633-----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC
+          -CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCC
+          AGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCGGACTGGCCGCCTGGGGACGG
+          AA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+          ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+          CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGG
+          TCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCGGCTGCCATTGC---CAAGGC
+          TGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+          ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG
+          -TGGTGACCTTACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+          -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-CTCTACAAGAT
+          GATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+          --CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TC
+          ATAGATGGCATCAAACGGGCCACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGG
+          TGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+          CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+          AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAA
+          CATCTTTGTCACCACCACAGGCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGAT
+          GAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCGATGTGGAG-----ATTGATG
+          TGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+          GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAA
+          GGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAAC
+          TCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTGTGGA--CCCA----------
+          -------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+          GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+          CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATC
+          ACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTTCCAGCTGCCATCCTTGTTCC
+          GGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+          TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCT
+          GGTCACTCTGTCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+          GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGACATGATCTCAG
+          AAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+          TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTG
+          TGTGCCTGGTTTGCAGATCCATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC-
+          -CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+          TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+          TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGAC
+          AGACTGAAAGGGGCTGGAAAAACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATA
+          GTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAA
+          TGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+          CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGC
+          ATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGA
+          CACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACAGGGTCCCTCAC------TGG
+          ACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+          GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+          CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTT
+          ATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCTGGAAAGCAAATAAA-AACAA
+          GCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+          -------------
+gi|2811827TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCC
+          GCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGA
+          ACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCGCCCTAGAGACCCTGGGCCCG
+          TACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+          CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+          TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCAC
+          CCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCCGGTGGCTCTGCATCCTGGAC
+          CGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+          CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGC
+          CTCCTGGCCTCGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+          GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGCTCCGGAACCG
+          GACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+          CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCC
+          TCCGCCCACTTCATTGTGGCTGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAG
+          TC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+          CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+          TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGA
+          AGTGTGGGCCGCATCAGCAAGCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCC
+          GATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTG
+          GGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+          GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTA
+          AGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGC
+          GGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCGTTGTGTCCCATCCCTTCCCT
+          GCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+          TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+          CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC
+          -CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATGGCAGACACCACCCTTCCCTT
+          ATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+          CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCT
+          GCGGGTGCTTTCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+          ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTTGAAACTTTTT
+          AAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+          CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCA
+          AAAAGTTAGTTTTCATCACCCATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCA
+          ATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+          GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+          AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTT
+          GGTCCCTTCAGAGGCCAAGGAGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCC
+          CTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAG
+          ACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+          GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTG
+          ACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTA
+          AGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTAGGACAGCTGACCACTAGTGG
+          GCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+          GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+          GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGT
+          GTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTGAGCCCTCACCTCTACAACCT
+          TCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+          CAGGTGCTCCAGT
+gi|2743257------------------------------------------------------------
+          ------------------------------------------------------------
+          ------------------------------------------------------------
+          ------------------------------------------------------------
+          ------------------------------------------------------------
+          ------------------------------------------------------------
+          ----------------------------------GGAGCGAG-GGAGGCGCG--TCGGGC
+          TGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+          GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTC
+          CTCCCGCAGCCGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+          --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC---CCGGAAGCT
+          TGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+          TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACC
+          ATTTCGGACACCCTGCACACTCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCG
+          AGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+          CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+          GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAA
+          GCCCTGCGCGAGCTCAGTCAAACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCC
+          AAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTC
+          GCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+          G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAA
+          GCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAG
+          GATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTGTTCA--CCTGT---------
+          -------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+          CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+          CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTC
+          TTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTCATATAGACTATCTCCGAATG
+          CAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+          GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTA
+          GCTAGGTATTTTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+          GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAACAAAACAAAAC
+          AAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+          CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAG
+          AAAGAAAGGCTTCCATTTCCAGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA
+          -GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+          TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+          TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAA
+          AAAAAAAAAAGAAACGAAAAAAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TT
+          TTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGG
+          CAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+          CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------C
+          TCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATTTTACCC------CTTGATGT
+          AGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTTTATTTTTATTTT--------
+          ------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+          GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+          AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTG
+          ATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTACAAGTTGCACTCAT-------
+          ------------------------------------------------------------
+          -------------
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.STOCKHOLM b/Src/Mobyle/Test/Converter/DataAlignments/align.STOCKHOLM
new file mode 100644
index 0000000..854f035
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.STOCKHOLM
@@ -0,0 +1,234 @@
+# STOCKHOLM 1.0
+
+gi|262263371|ref|NM_016661.3|   -----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAG
+gi|281182713|ref|NM_001168497.  TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGG
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   GCGTAGATCC-CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCA
+gi|281182713|ref|NM_001168497.  GGATAGGGCCGCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCA
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   GTCCTAGCGGAAGCGCCGCCAGCATGTCTGATAAACTGCCCTACAAAGTC
+gi|281182713|ref|NM_001168497.  GTCGGAGCGGAAGGGGCGGAACCACGGTGAACAAAGCGC---ACAAGGTC
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   GCGGACATCGGACTGGCCGCCTGGGGACGGAA--GGCTCTGGATATAGCT
+gi|281182713|ref|NM_001168497.  CAAGGTGCCGCCCTAGAGACCCTGGGCCCGTACTGGGCGCAGCTACCTCT
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   GAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AGATGTACTCAG
+gi|281182713|ref|NM_001168497.  TCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAGCTGTTCCCGA
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   CCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+gi|281182713|ref|NM_001168497.  GCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGA
+gi|281182713|ref|NM_001168497.  TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTAT
+gi|274325752|ref|NM_007706.4|   --------------------------------------------------
+
+gi|262263371|ref|NM_016661.3|   GGTGCGGTGGTCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCG
+gi|281182713|ref|NM_001168497.  GCCCCTTCACCCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCC
+gi|274325752|ref|NM_007706.4|   --------------------------------------------GGAGCG
+
+gi|262263371|ref|NM_016661.3|   GCTGCCATTGC---CAAGGCTGGCAT----TCCAGTGTTTGCCTGGAAGG
+gi|281182713|ref|NM_001168497.  GGTGGCTCTGCATCCTGGACCGGGACAAGCTCCAGTGCCCACCCAGGTGC
+gi|274325752|ref|NM_007706.4|   AG-GGAGGCGCG--TCGGGCTGGGAAG---TCGCGCGCACACTCGGCTCC
+
+gi|262263371|ref|NM_016661.3|   GCGAGACAGATGAGGAGTAC-CTGTGGTGCATTGAGCAGACGCTGCACTT
+gi|281182713|ref|NM_001168497.  CTGCCCCTGCTCCCGGCTTCGCTCTCTTCCCCTCGCCAGGCCCAGTGGCT
+gi|274325752|ref|NM_007706.4|   G-GGGACAGACGGTTAACTC---TTGCCAAGTCTCGCCGCCTCTGCGGCT
+
+gi|262263371|ref|NM_016661.3|   CAAGGACG----GACCCCTCAACATGATTCTGGATGATGG-TGGTGACCT
+gi|281182713|ref|NM_001168497.  CCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGCCTCCTGGCCT
+gi|274325752|ref|NM_007706.4|   CCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTCCTCCCGCAGC
+
+gi|262263371|ref|NM_016661.3|   TACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+gi|281182713|ref|NM_001168497.  CGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+gi|274325752|ref|NM_007706.4|   CGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+
+gi|262263371|ref|NM_016661.3|   -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-C
+gi|281182713|ref|NM_001168497.  GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGC
+gi|274325752|ref|NM_007706.4|   --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC--
+
+gi|262263371|ref|NM_016661.3|   TCTACAAGATGATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCA
+gi|281182713|ref|NM_001168497.  TCCGGAACCGGACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTG
+gi|274325752|ref|NM_007706.4|   -CCGGAAGCTTGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAG
+
+gi|262263371|ref|NM_016661.3|   ATGTCAACGATTCTGT------CACCAA-------GAGCAAGTTTGACAA
+gi|281182713|ref|NM_001168497.  TGCCCGCCTGTGCTGTGGTGCCCGCCGACCATTTCGAATCCGCCTAGCGG
+gi|274325752|ref|NM_007706.4|   ACTTTGCC-ACACCAT----TCTGCCGGA-ATTTGGAGAAAAAGAACCAG
+
+gi|262263371|ref|NM_016661.3|   CCTCTATGG-CTGCCGGGAGTCCC----TCATAGATGGCATCAAACGGGC
+gi|281182713|ref|NM_001168497.  ACCCTGGGGACCGCGAGGTGCTCCGGCTCCTCCGCCCACTTCATTGTGGC
+gi|274325752|ref|NM_007706.4|   CCGCTTCCAGTCCCCTCCCCCTCCGCCACCATTTCGGACACCCTGCACAC
+
+gi|262263371|ref|NM_016661.3|   CACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGGTGGCAGGCTA
+gi|281182713|ref|NM_001168497.  TGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAGTC-CAGGCTC
+gi|274325752|ref|NM_007706.4|   TCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCGAGGTCCACTG
+
+gi|262263371|ref|NM_016661.3|   TGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+gi|281182713|ref|NM_001168497.  CACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+gi|274325752|ref|NM_007706.4|   GCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+
+gi|262263371|ref|NM_016661.3|   CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-
+gi|281182713|ref|NM_001168497.  CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGT
+gi|274325752|ref|NM_007706.4|   CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGC
+
+gi|262263371|ref|NM_016661.3|   ----CCATGGAGG-------GCTATG----AGGTAACCACTATG-GACGA
+gi|281182713|ref|NM_001168497.  AGGGCCTTGCTGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGA
+gi|274325752|ref|NM_007706.4|   GGAGCCAGTGGGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGC
+
+gi|262263371|ref|NM_016661.3|   AGCCT--GTAAGGAGGGCAACATCTTTGTCACCACCACAGGCTGTGTGGA
+gi|281182713|ref|NM_001168497.  AGACTAAGGATGAATCGCGAAGTGTGGGCCGCATCAGCAAGCAGTGGGGA
+gi|274325752|ref|NM_007706.4|   GGC----GCGTCTGGCGAAAGCCCTGCGCGAGCTCAGTCAAACAGGATGG
+
+gi|262263371|ref|NM_016661.3|   TATCATCCTTGGCCGGCACTTTGAGCAGATGAAGGATGACGCCATTGTCT
+gi|281182713|ref|NM_001168497.  GGGCTGCTCCGAGAAGCCCTCACAGATGCCGATGACTTTGGCCTCCAATT
+gi|274325752|ref|NM_007706.4|   TACTGGGGAAGTATGACTGTTAATGAAGCCAAAGAGAAATTAAAAGAGGC
+
+gi|262263371|ref|NM_016661.3|   GTAA--CATTGGACACTTCGATGTGGAG-----ATTGATGTGAA----GT
+gi|281182713|ref|NM_001168497.  CCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTGGGAGCCACGT
+gi|274325752|ref|NM_007706.4|   TCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTCGCATTCAGAC
+
+gi|262263371|ref|NM_016661.3|   GGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+gi|281182713|ref|NM_001168497.  TCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+gi|274325752|ref|NM_007706.4|   TACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+
+gi|262263371|ref|NM_016661.3|   GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG-
+gi|281182713|ref|NM_001168497.  GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGA
+gi|274325752|ref|NM_007706.4|   G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTG
+
+gi|262263371|ref|NM_016661.3|   -CTGGCTGAAGGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CC
+gi|281182713|ref|NM_001168497.  CCAGAATTTAAGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACC
+gi|274325752|ref|NM_007706.4|   TCAAGTCCAAGCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACT
+
+gi|262263371|ref|NM_016661.3|   CCAGCTTCGTGATGAGCAACTCC---TTCACAAACCAGGTGATGGCACAG
+gi|281182713|ref|NM_001168497.  CTTTCCTTGATATACACTGCGGC---GGACCGACGAGGGTGGCCGAGTGG
+gi|274325752|ref|NM_007706.4|   ACTATGTCCAGATGTGCAAGGATAAACGGACAGGCCCAGAAGCCCCACGG
+
+gi|262263371|ref|NM_016661.3|   ATTG-AGCTGTGGA--CCCA-----------------------CCCAGAT
+gi|281182713|ref|NM_001168497.  TTGGGAGCCGTTGTGTCCCATCCCTTCCCTGCTTCTCCTGTGGCCCTGCA
+gi|274325752|ref|NM_007706.4|   AATGGGACTGTTCA--CCTGT----------------------ACCTGAC
+
+gi|262263371|ref|NM_016661.3|   AAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCTGGATGAGGCG
+gi|281182713|ref|NM_001168497.  GAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCATCTCCAGGCA
+gi|274325752|ref|NM_007706.4|   CAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTTCTGTCGACTC
+
+gi|262263371|ref|NM_016661.3|   GTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+gi|281182713|ref|NM_001168497.  GTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+gi|274325752|ref|NM_007706.4|   GCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+
+gi|262263371|ref|NM_016661.3|   CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTC
+gi|281182713|ref|NM_001168497.  CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTC
+gi|274325752|ref|NM_007706.4|   CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTA
+
+gi|262263371|ref|NM_016661.3|   AAGCCTGATCACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTT
+gi|281182713|ref|NM_001168497.  AAGTTTTACC-CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATG
+gi|274325752|ref|NM_007706.4|   TTTCTCTCTCTTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTC
+
+gi|262263371|ref|NM_016661.3|   CCAGCTGCCATCCTTGTTCCGGG---CCCTATC-------TCTCGTTCCC
+gi|281182713|ref|NM_001168497.  GCAGACACCACCCTTCCCTTATGGCACTTTAGCCAATTAGTTTAGCTTCC
+gi|274325752|ref|NM_007706.4|   ATATAGACTATCTCCGAATGCAG---CTATGTGAAA------GAGAACCC
+
+gi|262263371|ref|NM_016661.3|   AAGAGCAA----------ATGTCACCAACTTTGCAGTAGCCTGGAC-AGT
+gi|281182713|ref|NM_001168497.  GATTGTGGCACTCTGAGGGGATCCTTGCCTCCTCACTAATAGCTGT-AGC
+gi|274325752|ref|NM_007706.4|   AGAGGCCCTCC--------TCTGGATAACTGCGCAGAATTCTCTCTTAAG
+
+gi|262263371|ref|NM_016661.3|   GCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCTGGTCACTCTG
+gi|281182713|ref|NM_001168497.  GGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCTGCGGGTGCTT
+gi|274325752|ref|NM_007706.4|   GACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTAGCTAGGTATT
+
+gi|262263371|ref|NM_016661.3|   TCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+gi|281182713|ref|NM_001168497.  TCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+gi|274325752|ref|NM_007706.4|   TTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+
+gi|262263371|ref|NM_016661.3|   GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGAC
+gi|281182713|ref|NM_001168497.  ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTT
+gi|274325752|ref|NM_007706.4|   GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAAC
+
+gi|262263371|ref|NM_016661.3|   ATGATCTCAGAAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGT
+gi|281182713|ref|NM_001168497.  GAAACTTTTTAAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACC
+gi|274325752|ref|NM_007706.4|   AAAACAAAACAAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATT
+
+gi|262263371|ref|NM_016661.3|   GATGGTCACAA-----GCCATG-CACCTTATCAATGAGACCCTCACTT--
+gi|281182713|ref|NM_001168497.  GGATAACACAAAGGGAGCGGCGTCAGCCAGCCTGTATAATCTACACTATT
+gi|274325752|ref|NM_007706.4|   C-CTGTCAGAA------TGACTTGCCTTTGTTTTTTGGGTTCCTATGCAC
+
+gi|262263371|ref|NM_016661.3|   ----------------GGTCTTTAGATCTGTGTGCCTGGTTTGCAGATCC
+gi|281182713|ref|NM_001168497.  TGGATTAAAGGAAAAAAGTCCTTGAGTCCAAAAAGTTAGTTTTCATCACC
+gi|274325752|ref|NM_007706.4|   TGGGTCAAAAGTCCAAGCTCCATAGGAGAGAAAGAAAGGCTTCCATTTCC
+
+gi|262263371|ref|NM_016661.3|   ATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC--CACCAAAGA
+gi|281182713|ref|NM_001168497.  CATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCAATATCTAAGT
+gi|274325752|ref|NM_007706.4|   AGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA-GGCTTCAGC
+
+gi|262263371|ref|NM_016661.3|   GCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+gi|281182713|ref|NM_001168497.  TGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+gi|274325752|ref|NM_007706.4|   TC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+
+gi|262263371|ref|NM_016661.3|   TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTA
+gi|281182713|ref|NM_001168497.  GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGG
+gi|274325752|ref|NM_007706.4|   TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-G
+
+gi|262263371|ref|NM_016661.3|   CTTTTTTTGGTCCCTATT-TCACAAG--------GGTTAGGA-TAGATTA
+gi|281182713|ref|NM_001168497.  CCTCCTCCAAAGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTA
+gi|274325752|ref|NM_007706.4|   CTGACCATGCTTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCA
+
+gi|262263371|ref|NM_016661.3|   ACCAAGAAAGGACAAGTGACAGACTGAAAGGGGCTGGAAAAACAAAGA--
+gi|281182713|ref|NM_001168497.  GGCAGAGTGGACTTGGTCTTGGTCCCTTCAGAGGCCAAGGAGCAGGGCTC
+gi|274325752|ref|NM_007706.4|   ACCAAAAAAAGGAAAAAAAAAAAAAAAAAAGAAACGAAAAAAGAACCATC
+
+gi|262263371|ref|NM_016661.3|   GGAAAAGGC-----CTGTCACTTCTGTATAGTTGATGGTTCCTGTCACAA
+gi|281182713|ref|NM_001168497.  GGAGGAAGCATTAGCCAGTACCTGAGTGCCCTATACACTTCCTGTCACTC
+gi|274325752|ref|NM_007706.4|   ACCATGAGAT----CCTGTATTTGTCT-TTTTTTACTACACGTATGACCT
+
+gi|262263371|ref|NM_016661.3|   GCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAATGTTTATATG
+gi|281182713|ref|NM_001168497.  CTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAGACCTCATGTT
+gi|274325752|ref|NM_007706.4|   CCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGGCAGCCTTACT
+
+gi|262263371|ref|NM_016661.3|   CTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+gi|281182713|ref|NM_001168497.  CTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+gi|274325752|ref|NM_007706.4|   TCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+
+gi|262263371|ref|NM_016661.3|   CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTG
+gi|281182713|ref|NM_001168497.  GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGA
+gi|274325752|ref|NM_007706.4|   CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTG
+
+gi|262263371|ref|NM_016661.3|   GAATATGTGCATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACT
+gi|281182713|ref|NM_001168497.  CTGGCTGCTGACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGT
+gi|274325752|ref|NM_007706.4|   CAA------CTCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATT
+
+gi|262263371|ref|NM_016661.3|   GTACCTGTGATTCTGGAGGACACCAGATGTCCCATCGCTTTCTTCCATAT
+gi|281182713|ref|NM_001168497.  CTGCTCGGGACCGGTTACTAAGGTGGAAGAGC--TGGTCTACCTGTGTGA
+gi|274325752|ref|NM_007706.4|   TTACCC------CTTGATGTAGCTGTGAGTCC--CAACCTAGTGCCATTG
+
+gi|262263371|ref|NM_016661.3|   TCCTGAGACAGGGTCCCTCAC------TGGACTAGAGTGAGGCCATTTCT
+gi|281182713|ref|NM_001168497.  GCAGGAGGTAGGACAGCTGACCACTAGTGGGCCAAA--CAGGCGACTGGG
+gi|274325752|ref|NM_007706.4|   TTTTTATTTTTATTTTTATTTT--------------TGGAAATGGCTTTA
+
+gi|262263371|ref|NM_016661.3|   GCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAAGTCAAGCATG
+gi|281182713|ref|NM_001168497.  GAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCGGATAGCAGAA
+gi|274325752|ref|NM_007706.4|   GAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACGGACATCAACA
+
+gi|262263371|ref|NM_016661.3|   GCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+gi|281182713|ref|NM_001168497.  GCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+gi|274325752|ref|NM_007706.4|   GC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+
+gi|262263371|ref|NM_016661.3|   CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAA
+gi|281182713|ref|NM_001168497.  GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTT
+gi|274325752|ref|NM_007706.4|   AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTG
+
+gi|262263371|ref|NM_016661.3|   CT-CCCTCTTATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCT
+gi|281182713|ref|NM_001168497.  TAGCCCTTGTGTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTG
+gi|274325752|ref|NM_007706.4|   TTACTATTTGATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTAC
+
+gi|262263371|ref|NM_016661.3|   GGAAAGCAAATAAA-AACAAGCCCCGTGCACAGGATGGCTAAAAAAAAAA
+gi|281182713|ref|NM_001168497.  AGCCCTCACCTCTACAACCTTCCTCTGCCACTCGGGGTCTGACAAGGGAC
+gi|274325752|ref|NM_007706.4|   AAGTTGCACTCAT-------------------------------------
+
+gi|262263371|ref|NM_016661.3|   AAAAAAAAAAA--------------------------------
+gi|281182713|ref|NM_001168497.  CTCCAGAGGGGACAGTGGCTTTGCTGGCTCCAGGTGCTCCAGT
+gi|274325752|ref|NM_007706.4|   -------------------------------------------
+//
diff --git a/Src/Mobyle/Test/Converter/DataAlignments/align.UNKNOWN b/Src/Mobyle/Test/Converter/DataAlignments/align.UNKNOWN
new file mode 100644
index 0000000..30fdf0e
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataAlignments/align.UNKNOWN
@@ -0,0 +1,196 @@
+      2893
+gi|2622633-----------------AAATAAGAAGATATTTAAAGACGCCGGCGCCAGGCGTAGATCC
+gi|2811827TGGGGAGGCAGAGCAGCAACCCGCGGAAAAGGCGAGGGTCTTGCTGCTGGGGATAGGGCC
+gi|2743257------------------------------------------------------------
+
+          -CTGCCCCGCGCCGCCCGGCCCCCTTCACTGAGTTCATCAGTCCTAGCGGAAGCGCCGCC
+          GCAATCACCCTCCAACAGGCCAGGTCTCCAAGCCTCTGCAGTCGGAGCGGAAGGGGCGGA
+          ------------------------------------------------------------
+
+          AGCATGTCTGATAAACTGCCCTACAAAGTCGCGGACATCGGACTGGCCGCCTGGGGACGG
+          ACCACGGTGAACAAAGCGC---ACAAGGTCCAAGGTGCCGCCCTAGAGACCCTGGGCCCG
+          ------------------------------------------------------------
+
+          AA--GGCTCTGGATATAGCTGAGAATGAGA-TGCCAGGATTGATGCGCATGCGGG---AG
+          TACTGGGCGCAGCTACCTCTTCGCCTCTGCCTGTCCGTCTTTGTTTCTGTGTCTGTCTAG
+          ------------------------------------------------------------
+
+          ATGTACTCAGCCTCCAAGCCACTGAAGGGTGCTCGCATTGCTG-GCTGCCTGCACATGAC
+          CTGTTCCCGAGCTT-GTCCCACTCCAGAACTAAGTCTCCCCTACGCCAAAAGCCCAAGAC
+          ------------------------------------------------------------
+
+          CGTGGAGACTGCTG-TTCTCATTG-AGACTCTCGTGGCCCTGGGTGCTGAGGTGCGGTGG
+          TCCC----CTCCTGATTCCCATGGCAGGCTACTTGCCCCCCAAAGGCTATGCCCCTTCAC
+          ------------------------------------------------------------
+
+          TCCAGCTGCAACATCTTCT-CTACTCAGGACCATGCAGCGGCTGCCATTGC---CAAGGC
+          CCCCACCTCCCTACCCCGTGCCATCTGGGTATCCAGAGCCGGTGGCTCTGCATCCTGGAC
+          ----------------------------------GGAGCGAG-GGAGGCGCG--TCGGGC
+
+          TGGCAT----TCCAGTGTTTGCCTGGAAGGGCGAGACAGATGAGGAGTAC-CTGTGGTGC
+          CGGGACAAGCTCCAGTGCCCACCCAGGTGCCTGCCCCTGCTCCCGGCTTCGCTCTCTTCC
+          TGGGAAG---TCGCGCGCACACTCGGCTCCG-GGGACAGACGGTTAACTC---TTGCCAA
+
+          ATTGAGCAGACGCTGCACTTCAAGGACG----GACCCCTCAACATGATTCTGGATGATGG
+          CCTCGCCAGGCCCAGTGGCTCCAGGGCCTCCTGCTCCTTTCGTGCCATTGCCAGGGGTGC
+          GTCTCGCCGCCTCTGCGGCTCCCGGGCCTTGGGCTTCCCCCCTG-AAGCATGAGCCTTTC
+
+          -TGGTGACCTTACTAACCTCATCCACACCAAATACCCACAGCTTC--TGTCAGGC-----
+          CTCCTGGCCTCGAATTCCTAGTGCAGATTGATCAAATCTTGATTCATCAGAAGGCTGAAC
+          CTCCCGCAGCCGCCAACGCTGCGCGGGTCTCGGACAGTGCGCGCC-----GGGACT----
+
+          -ATCCGAGGTATCTCTGAG----GAGACC-ACGACTGGGGTCCACAAC-CTCTACAAGAT
+          GAGTGGAAACGTTCCTAGGCTGGGAGACCTGTAATATGTATGAACTTCGCTCCGGAACCG
+          --CCAGGCGCGCGCCCTCA-----AGATC----CCTTGTGCCCGGAGC---CCGGAAGCT
+
+          GATGTCCAATGGGATA-----CTGAAGGTGCCTGCCATCAATGTCAACGATTCTGT----
+          GACAGCAACTGGGTCAGGCAGCTGAAGAGAGCAACTGTTGTGCCCGCCTGTGCTGTGGTG
+          TGCGGCAGGCGATCTGTG--GGTGACAGTGTCTGCGAGAGACTTTGCC-ACACCAT----
+
+          --CACCAA-------GAGCAAGTTTGACAACCTCTATGG-CTGCCGGGAGTCCC----TC
+          CCCGCCGACCATTTCGAATCCGCCTAGCGGACCCTGGGGACCGCGAGGTGCTCCGGCTCC
+          TCTGCCGGA-ATTTGGAGAAAAAGAACCAGCCGCTTCCAGTCCCCTCCCCCTCCGCCACC
+
+          ATAGATGGCATCAAACGGGCCACAGATGTGATG---ATTGCGGGCA--AGGTGGCGGTGG
+          TCCGCCCACTTCATTGTGGCTGCAGCTGCTGCC---CCTGTGGTCTTCAGGAGATGGAAG
+          ATTTCGGACACCCTGCACACTCTCGTTTTGGGGTACCCTGTGACTTCCAGGCAGCACGCG
+
+          TGGCAGGCTATGG-TGATGTGGGCAAGGGC--TGTGCCCAGGCCCTGAGGGGTTTTGGGG
+          TC-CAGGCTCCACCTGGCACCACCATTGGCCATGTGCTACAGACCTGGCATCCCTTCCTT
+          AGGTCCACTGGCCCCAGCTCGGGC---GACCAGCTGTCTGGGACGTGTTGACTCATCTCC
+
+          CCCGAGTCATC-ATCACCGAGATCGACCCCATCAATGCACTGCAAGCTG-----CCATGG
+          CCTAAGTTTTCCATCCTGGATGCTGATCGCCAACCTGTTCTACGAGTTGTAGGGCCTTGC
+          CATGACCCTGCGGTGCCTGGAGCCCTCCGGGAATGGAGCGGACAGGACGCGGAGCCAGTG
+
+          AGG-------GCTATG----AGGTAACCACTATG-GACGAAGCCT--GTAAGGAGGGCAA
+          TGGACTTGTGGCTGTGGTACAGACACCAACTTTGAGGTGAAGACTAAGGATGAATCGCGA
+          GGGGACCGCGGGGTTGCCGGAGG-AACAGTCCCCCGAGGCGGC----GCGTCTGGCGAAA
+
+          CATCTTTGTCACCACCACAGGCTGTGTGGATATCATCCTTGGCCGGCACTTTGAGCAGAT
+          AGTGTGGGCCGCATCAGCAAGCAGTGGGGAGGGCTGCTCCGAGAAGCCCTCACAGATGCC
+          GCCCTGCGCGAGCTCAGTCAAACAGGATGGTACTGGGGAAGTATGACTGTTAATGAAGCC
+
+          GAAGGATGACGCCATTGTCTGTAA--CATTGGACACTTCGATGTGGAG-----ATTGATG
+          GATGACTTTGGCCTCCAATTCCCAGTCGATCTAGATGTGAAAGTGAAGGCCGTACTGCTG
+          AAAGAGAAATTAAAAGAGGCTCCAG--AAGGAACTTTCTTGATTAGAG-----ATAGTTC
+
+          TGAA----GTGGCTCAATGAGAACGCGGT------GGAGAAAGTGAACATCAAGCCCCAG
+          GGAGCCACGTTCCTCATCGATTATATGTTCTTCGAGAAGAGAGGAGGCGCAGGACCCTCT
+          GCATTCAGACTACCTACTAACTATATCCGTTAAGACGTCAGCTGGACCGACTAACCTGCG
+
+          GTGGACCGCTACTGG-------CTAAAGAATGGGCGCCGCATCATCTTG--CTGGCTGAA
+          GCCATCACCAGTTAGAAGCCACCTCAGGATGAGGAGACCCATCTCCTTGACCAGAATTTA
+          G-ATTGAGTACCAAGATGGGAAATTCAGATTGGAT--TCTATCATATGTGTCAAGTCCAA
+
+          GGCCGTCTGGTCAACCTGG--GTTGTGCCATGGGACA-CCCCAGCTTCGTGATGAGCAAC
+          AGATGGTCAGCTGCCCTGGACGTTCCCTCCTGAAGCAACCCTTTCCTTGATATACACTGC
+          GCTTAAACAGTTTGACAGT--GTGGTTCATCTGATTGACTACTATGTCCAGATGTGCAAG
+
+          TCC---TTCACAAACCAGGTGATGGCACAGATTG-AGCTGTGGA--CCCA----------
+          GGC---GGACCGACGAGGGTGGCCGAGTGGTTGGGAGCCGTTGTGTCCCATCCCTTCCCT
+          GATAAACGGACAGGCCCAGAAGCCCCACGGAATGGGACTGTTCA--CCTGT---------
+
+          -------------CCCAGATAAATACCCTGTTGGGGTTCACTTCCTGCCTAAGA--AGCT
+          GCTTCTCCTGTGGCCCTGCAGAAGAGCATGTATGAGACCTGTTCCTCCTTCTGTTCACCA
+          -------------ACCTGACCAAACCTCTGTAT-ACATCAGCACCCACTCTGCAGCATTT
+
+          GGATGAGGCGGTGGCTGAAGCCCACCTGGGCAAGCTGAATGTGAAGCTGAC-CAAGCTGA
+          TCTCCAGGCAGTGCCTGT-GCACACATTAGCTTTTAAACTTCCTTGCACACTCCTTCCAG
+          CTGTCGACTCGCCATTAA----CAAATGTACCGGTACGATCTGGGGACTGCCTTTACCAA
+
+          CTGAGAAGCAGGCCCAGTACCTGGGCATGCCCATCAACGGCCCC---TTCAAGCCTGATC
+          CCTTCCTCTGGGGCCTCTGCATAGGCAGGGGCATCTGGAATCCTGGACTCAAGTTTTACC
+          CAAGACTAAAAGATTACT-TGGAAGAATATAAATTCCAGGTATA--AGTATTTCTCTCTC
+
+          ACTACCGCTACTGAGTGCTGGG---GCTGTCCTTCACCTTCCAGCTGCCATCCTTGTTCC
+          -CCAGGGCTTGTGGGTAAAAGGCAAGCAGTACCAAAGATGGCAGACACCACCCTTCCCTT
+          TTTTTCGTTTTTTTTTAAAAAAAAAAAAAACACATGCCTCATATAGACTATCTCCGAATG
+
+          GGG---CCCTATC-------TCTCGTTCCCAAGAGCAA----------ATGTCACCAACT
+          ATGGCACTTTAGCCAATTAGTTTAGCTTCCGATTGTGGCACTCTGAGGGGATCCTTGCCT
+          CAG---CTATGTGAAA------GAGAACCCAGAGGCCCTCC--------TCTGGATAACT
+
+          TTGCAGTAGCCTGGAC-AGTGCTTCTCCCACACAGCCCTCCCCTTACACCCTCTGGGGCT
+          CCTCACTAATAGCTGT-AGCGGTTGGGCCCCAGTGCCAACTCCCTAAGCCCCTGGGCCCT
+          GCGCAGAATTCTCTCTTAAGGACAGTTGGGCTCAGTCTAACTTAAAGGTGTGAAGATGTA
+
+          GGTCACTCTGTCTTTGACCTC---TGCTGTATC----------CCTCATACTGTTCCA--
+          GCGGGTGCTTTCTGCAGCTTCCTGTGCCTTATTTAACCGTTAACCCCTTCCTTCCCCT--
+          GCTAGGTATTTTAAAGTTCCCCTTAGGTAGTTTTAGCTGAATGATGCTTTCTTTCCTATG
+
+          GGTGT-GGAGGGGGAATGGAGAGGT-ACCTGGTAGGCATCCACAGGGGACATGATCTCAG
+          ACTGTAGGAAGGAGGCTGTGTCTTT-GTATGTTGTACTCATATAAACTTTGAAACTTTTT
+          GCTGCTCAAGATCAAATGGCCCTTTTAAATGAAACAAAACAAAACAAAACAAAACAAAAC
+
+          AAGTCTTGGAAGTCC--TGAGGCTGGAT---GTTGCTAGTGATGGTCACAA-----GCCA
+          AAACAGTAGAAGCCGGATGTGATGGGAGAGGGTAGATACCGGATAACACAAAGGGAGCGG
+          AAAAACAAAATGTCCCAAGGAAAGGCAA---GAGAATATTC-CTGTCAGAA------TGA
+
+          TG-CACCTTATCAATGAGACCCTCACTT------------------GGTCTTTAGATCTG
+          CGTCAGCCAGCCTGTATAATCTACACTATTTGGATTAAAGGAAAAAAGTCCTTGAGTCCA
+          CTTGCCTTTGTTTTTTGGGTTCCTATGCACTGGGTCAAAAGTCCAAGCTCCATAGGAGAG
+
+          TGTGCCTGGTTTGCAGATCCATTGGTTTCTCAGTCCAGGACCCAAGAA----CGAGCTC-
+          AAAAGTTAGTTTTCATCACCCATAGTGATTCTGGACCAAACCAGAGCAGCTCCCAAGTCA
+          AAAGAAAGGCTTCCATTTCCAGGAGGACAGCTGAAGGAGGGAAAGACCCTGGCTGACCCA
+
+          -CACCAAAGAGCAGGAACCCCTGGAGTTTGAAG-----GCCCCCGAGAGCTGGGCCTTTT
+          ATATCTAAGTTGGGTGCCCCATAGAGTTTAGGGAAGTAGCTAATGACAGTTCCGACGGTT
+          -GGCTTCAGCTC--CACTTCTAGACGCCTGGGG-----TTCAGTCTGTGTTAGAACCTGT
+
+          TACTGTTG----GGCA--GTCTCTTAAACCTCATG-ATACTGAGTTGGTACTTTTTTTGG
+          GGTCAGTGATCCGGTATTGCTACTTGATCCTTGTCTGTAGTGAGTAGAGGCCTCCTCCAA
+          TATAGTTT-----GCAT--CCTGATGTATCTAGTAGGAGTTCCGTTAA-GCTGACCATGC
+
+          TCCCTATT-TCACAAG--------GGTTAGGA-TAGATTAACCAAGAAAGGACAAGTGAC
+          AGCTTTTTCTTTCAAGTCCTGTTCAATTGGGAATATAGTAGGCAGAGTGGACTTGGTCTT
+          TTGTATTTATCCCTCGTCTTATGCAACTA--ATCAAATCAACCAAAAAAAGGAAAAAAAA
+
+          AGACTGAAAGGGGCTGGAAAAACAAAGA--GGAAAAGGC-----CTGTCACTTCTGTATA
+          GGTCCCTTCAGAGGCCAAGGAGCAGGGCTCGGAGGAAGCATTAGCCAGTACCTGAGTGCC
+          AAAAAAAAAAGAAACGAAAAAAGAACCATCACCATGAGAT----CCTGTATTTGTCT-TT
+
+          GTTGATGGTTCCTGTCACAAGCC-CAGGTCACAAACAGA--TTAATTTGTTT---TATAA
+          CTATACACTTCCTGTCACTCCTCACAGCAGGTAAATCTCCCTCAGCGTGCCTCCGTCAAG
+          TTTTACTACACGTATGACCTCCC-CCGTGAGTGAGTACT--GTAGTCCTCCATCTCAAGG
+
+          TGTTTATATGCTATTTAGAATGTTAACAAAGGA---AGGTGGATAAAATA-CAGTTTCTA
+          ACCTCATGTTCTGATTAGTCCGTAAGGCCAAGCCCTAAGTGGACCAGGTAACCCATGCTG
+          CAGCCTTACTTCAGACACATTTCAAACTGGTGC---AAACAGAAAAGACTTCTCTCTTTT
+
+          CTGCCTA-----AAGAATTTTGGCTCTATTAAAATGTAAGTGTGTGGCTGGAATATGTGC
+          GTGTCCATCCCTGAGGATACAGAGTTCAGGACCGGGCTAGGATGTAGCGACTGGCTGCTG
+          CTTCTGA-------GGCTAAAGACAAGAATGTCACGCTA---TACAGGTGCAA------C
+
+          ATGTATGTGGCTGACATATGTGCATGTGTGTGCAGACACTGTACCTGTGATTCTGGAGGA
+          ACAGGTCCCACTGGTG-ATGCTCCAGGATGCGACGACAGTCTGCTCGGGACCGGTTACTA
+          TCAATCCTTGAAAACAGAAACCAATGCAGACAGAGACATTTTACCC------CTTGATGT
+
+          CACCAGATGTCCCATCGCTTTCTTCCATATTCCTGAGACAGGGTCCCTCAC------TGG
+          AGGTGGAAGAGC--TGGTCTACCTGTGTGAGCAGGAGGTAGGACAGCTGACCACTAGTGG
+          AGCTGTGAGTCC--CAACCTAGTGCCATTGTTTTTATTTTTATTTTTATTTT--------
+
+          ACTAGAGTGAGGCCATTTCTGCTCTTCCCAGATCCCTCACCCTAGAGCTGG--GGTTAAA
+          GCCAAA--CAGGCGACTGGGGAACCTGCAGAAGGGGTTAGCGTTACCTTGA--GGTTCCG
+          ------TGGAAATGGCTTTAGAACTTTCCAAG----TTATCCTTGAATTGTCTGACCACG
+
+          GTCAAGCATGGCCATGTGTAGCTTCTACAAGTGCAAACGGTCTTACCCACTGAGCCATCT
+          GATAGCAGAAGCCACATCTC-CCCCAGTGGTTCTGAGTGCCACTTGGCACT--CCTGGTA
+          GACATCAACAGC--TGCCTCCCTTCTACCATGTAGAATCCTATGACTTAACTTTTCTTCC
+
+          CTTCACGGATCGCATAATGTGAATCACACAAAAAAGTATGAGAAATTGAACT-CCCTCTT
+          GGTGACACCATGCACACTCAGCTCTAC-CTGCGGAGGACAAATCTTTGTTTAGCCCTTGT
+          AGATATAGAGGG-GTACCTGCCTGTTTTTCAAAGTGTTTATTTACTGCTGTTACTATTTG
+
+          ATTTGCCCAAGTGATCAATTTAC-TGAACCCATTTGACCTGGAAAGCAAATAAA-AACAA
+          GTCAACCTACAGGCCCTTTGAACATAAAGTCATTTCAGTGAGCCCTCACCTCTACAACCT
+          ATTAGA--ATGTATCAAATAAAAACAAACCTGACTTTTACAAGTTGCACTCAT-------
+
+          GCCCCGTGCACAGGATGGCTAAAAAAAAAAAAAAAAAAAAA-------------------
+          TCCTCTGCCACTCGGGGTCTGACAAGGGACCTCCAGAGGGGACAGTGGCTTTGCTGGCTC
+          ------------------------------------------------------------
+
+          -------------
+          CAGGTGCTCCAGT
+          -------------
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.EMBL b/Src/Mobyle/Test/Converter/DataSequences/sequence.EMBL
new file mode 100644
index 0000000..7d8466c
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.EMBL
@@ -0,0 +1,14 @@
+ID   Q08755; SV 1; XXX; XXX; XXX; XXX; 259 BP.
+AC   Q08755;
+DE   RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1; AltName:
+DE   Full=Insulin-induced growth response protein CL-6; AltName:
+DE   Full=Immediate-early protein CL-6;
+KW   Cholesterol metabolism; Endoplasmic reticulum; Lipid metabolism; Membrane;
+KW   Steroid metabolism; Transmembrane; Ubl conjugation.
+SQ   Sequence 259 BP; 26 A; 6 C; 23 G; 11 T; 193 other;
+     MPRLHDHVWS YPSAGAARPY SLPRGMIAAA LCPQGPGAPE PEPAPRGQRE GTAGFSARPG        60
+     SWHHDLVQRS LVLFSFGVVL ALVLNLLQIQ RNVTLFPDEV IATIFSSAWW VPPCCGTAAA       120
+     VVGLLYPCID SHLGEPHKFK REWASVMRCI AVFVGINHAS AKLDFANNVQ LSLTLAALSL       180
+     GLWWTFDRSR SGLGLGITIA FLATLITQFL VYNGVYQYTS PDFLYIRSWL PCIFFSGGVT       240
+     VGNIGRQLAM GVPEKPHSD                                                    259
+//
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.FASTA b/Src/Mobyle/Test/Converter/DataSequences/sequence.FASTA
new file mode 100644
index 0000000..11a0afa
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.FASTA
@@ -0,0 +1,5 @@
+>INSI1_RAT RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1; AltName: Full=Insulin-induced growth response protein CL-6; AltName: Full=Immediate-early protein CL-6;
+MPRLHDHVWSYPSAGAARPYSLPRGMIAAALCPQGPGAPEPEPAPRGQREGTAGFSARPGSWHHDLVQRSLVLFSFGVVL
+ALVLNLLQIQRNVTLFPDEVIATIFSSAWWVPPCCGTAAAVVGLLYPCIDSHLGEPHKFKREWASVMRCIAVFVGINHAS
+AKLDFANNVQLSLTLAALSLGLWWTFDRSRSGLGLGITIAFLATLITQFLVYNGVYQYTSPDFLYIRSWLPCIFFSGGVT
+VGNIGRQLAMGVPEKPHSD
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.GCG b/Src/Mobyle/Test/Converter/DataSequences/sequence.GCG
new file mode 100644
index 0000000..41a3a1f
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.GCG
@@ -0,0 +1,15 @@
+!!AA_SEQUENCE 1.0
+unknown  Length: 259  February  2, 2010 14:16  Type: P  Check: 2878 ..
+
+       1  MPRLHDHVWS YPSAGAARPY SLPRGMIAAA LCPQGPGAPE PEPAPRGQRE
+
+      51  GTAGFSARPG SWHHDLVQRS LVLFSFGVVL ALVLNLLQIQ RNVTLFPDEV
+
+     101  IATIFSSAWW VPPCCGTAAA VVGLLYPCID SHLGEPHKFK REWASVMRCI
+
+     151  AVFVGINHAS AKLDFANNVQ LSLTLAALSL GLWWTFDRSR SGLGLGITIA
+
+     201  FLATLITQFL VYNGVYQYTS PDFLYIRSWL PCIFFSGGVT VGNIGRQLAM
+
+     251  GVPEKPHSD
+
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.GDE b/Src/Mobyle/Test/Converter/DataSequences/sequence.GDE
new file mode 100644
index 0000000..38a9f53
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.GDE
@@ -0,0 +1,5 @@
+%INSI1_RAT
+MPRLHDHVWSYPSAGAARPYSLPRGMIAAALCPQGPGAPEPEPAPRGQREGTAGFSARPGSWHHDLVQRSLVLFSFGVVL
+ALVLNLLQIQRNVTLFPDEVIATIFSSAWWVPPCCGTAAAVVGLLYPCIDSHLGEPHKFKREWASVMRCIAVFVGINHAS
+AKLDFANNVQLSLTLAALSLGLWWTFDRSRSGLGLGITIAFLATLITQFLVYNGVYQYTSPDFLYIRSWLPCIFFSGGVT
+VGNIGRQLAMGVPEKPHSD
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.GENBANK b/Src/Mobyle/Test/Converter/DataSequences/sequence.GENBANK
new file mode 100644
index 0000000..523d479
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.GENBANK
@@ -0,0 +1,14 @@
+LOCUS       INSI1_RAT                259 bp XXXXXX     XXX      XXX 02-FEB-2010
+DEFINITION  RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1;
+            AltName: Full=Insulin-induced growth response protein CL-6;
+            AltName: Full=Immediate-early protein CL-6;.
+ACCESSION   Q08755
+KEYWORDS    Cholesterol metabolism; Endoplasmic reticulum; Lipid metabolism;
+            Membrane; Steroid metabolism; Transmembrane; Ubl conjugation.
+ORIGIN      
+        1 MPRLHDHVWS YPSAGAARPY SLPRGMIAAA LCPQGPGAPE PEPAPRGQRE GTAGFSARPG
+       61 SWHHDLVQRS LVLFSFGVVL ALVLNLLQIQ RNVTLFPDEV IATIFSSAWW VPPCCGTAAA
+      121 VVGLLYPCID SHLGEPHKFK REWASVMRCI AVFVGINHAS AKLDFANNVQ LSLTLAALSL
+      181 GLWWTFDRSR SGLGLGITIA FLATLITQFL VYNGVYQYTS PDFLYIRSWL PCIFFSGGVT
+      241 VGNIGRQLAM GVPEKPHSD
+//
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.IG b/Src/Mobyle/Test/Converter/DataSequences/sequence.IG
new file mode 100644
index 0000000..8d58212
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.IG
@@ -0,0 +1,8 @@
+; RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1; AltName:
+; Full=Insulin-induced growth response protein CL-6; AltName:
+; Full=Immediate-early protein CL-6;.
+INSI1_RAT
+MPRLHDHVWSYPSAGAARPYSLPRGMIAAALCPQGPGAPEPEPAPRGQREGTAGFSARPGSWHHDLVQRSLVLFSFGVVL
+ALVLNLLQIQRNVTLFPDEVIATIFSSAWWVPPCCGTAAAVVGLLYPCIDSHLGEPHKFKREWASVMRCIAVFVGINHAS
+AKLDFANNVQLSLTLAALSLGLWWTFDRSRSGLGLGITIAFLATLITQFLVYNGVYQYTSPDFLYIRSWLPCIFFSGGVT
+VGNIGRQLAMGVPEKPHSD1
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.NBRF b/Src/Mobyle/Test/Converter/DataSequences/sequence.NBRF
new file mode 100644
index 0000000..8ecdcf9
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.NBRF
@@ -0,0 +1,6 @@
+>P1;INSI1_RAT
+RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1; AltName: Full=Insulin-induced growth response protein CL-6; AltName: Full=Immediate-early protein CL-6;
+MPRLHDHVWSYPSAGAARPYSLPRGMIAAALCPQGPGAPEPEPAPRGQREGTAGFSARPGSWHHDLVQRSLVLFSFGVVL
+ALVLNLLQIQRNVTLFPDEVIATIFSSAWWVPPCCGTAAAVVGLLYPCIDSHLGEPHKFKREWASVMRCIAVFVGINHAS
+AKLDFANNVQLSLTLAALSLGLWWTFDRSRSGLGLGITIAFLATLITQFLVYNGVYQYTSPDFLYIRSWLPCIFFSGGVT
+VGNIGRQLAMGVPEKPHSD*
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.PIR b/Src/Mobyle/Test/Converter/DataSequences/sequence.PIR
new file mode 100644
index 0000000..6ffb305
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.PIR
@@ -0,0 +1,18 @@
+ENTRY           INSI1_  #type complete
+TITLE           RecName: Full=Insulin-induced gene 1 protein; Short=INSIG-1;
+                AltName: Full=Insulin-induced growth response protein CL-6;
+                AltName: Full=Immediate-early protein CL-6;
+ACCESSIONS      Q08755
+SUMMARY         #length 259  #molecular-weight 28231  #checksum 2878
+SEQUENCE
+                 5        10        15        20        25        30
+       1 M P R L H D H V W S Y P S A G A A R P Y S L P R G M I A A A
+      31 L C P Q G P G A P E P E P A P R G Q R E G T A G F S A R P G
+      61 S W H H D L V Q R S L V L F S F G V V L A L V L N L L Q I Q
+      91 R N V T L F P D E V I A T I F S S A W W V P P C C G T A A A
+     121 V V G L L Y P C I D S H L G E P H K F K R E W A S V M R C I
+     151 A V F V G I N H A S A K L D F A N N V Q L S L T L A A L S L
+     181 G L W W T F D R S R S G L G L G I T I A F L A T L I T Q F L
+     211 V Y N G V Y Q Y T S P D F L Y I R S W L P C I F F S G G V T
+     241 V G N I G R Q L A M G V P E K P H S D
+///
diff --git a/Src/Mobyle/Test/Converter/DataSequences/sequence.RAW b/Src/Mobyle/Test/Converter/DataSequences/sequence.RAW
new file mode 100644
index 0000000..0106c1c
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/DataSequences/sequence.RAW
@@ -0,0 +1 @@
+MPRLHDHVWSYPSAGAARPYSLPRGMIAAALCPQGPGAPEPEPAPRGQREGTAGFSARPGSWHHDLVQRSLVLFSFGVVLALVLNLLQIQRNVTLFPDEVIATIFSSAWWVPPCCGTAAAVVGLLYPCIDSHLGEPHKFKREWASVMRCIAVFVGINHASAKLDFANNVQLSLTLAALSLGLWWTFDRSRSGLGLGITIAFLATLITQFLVYNGVYQYTSPDFLYIRSWLPCIFFSGGVTVGNIGRQLAMGVPEKPHSD
diff --git a/Src/Mobyle/Test/Converter/__init__.py b/Src/Mobyle/Test/Converter/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Src/Mobyle/Test/Converter/test_squizz_alignment.py b/Src/Mobyle/Test/Converter/test_squizz_alignment.py
new file mode 100644
index 0000000..285a927
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/test_squizz_alignment.py
@@ -0,0 +1,110 @@
+########################################################################################
+#                                                                                      #
+#   Author: Sandrine Larroude,                                                         #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+import unittest2 as unittest
+import os
+import sys
+import shutil
+import glob
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../../" ) )
+os.environ[ 'MOBYLEHOME' ] = MOBYLEHOME
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+DATADIR = os.path.dirname( __file__ )
+
+import Mobyle.Test.MobyleTest 
+from Mobyle.MobyleError import UnSupportedFormatError
+from Mobyle.Converter import squizz_alignment
+
+squizz_path = Mobyle.Test.MobyleTest.find_exe( "squizz" )
+
+class SquizzAlignmentTest(unittest.TestCase):
+    """Test the Squizz alignment module"""
+    
+    def __init__(self , methodName='runTest'):
+        unittest.TestCase.__init__( self , methodName = methodName )
+        self.s = squizz_alignment(squizz_path)
+    
+    @unittest.skipUnless( squizz_path , "squizz executable not found" )    
+    def setUp(self):
+        self.cfg = Mobyle.ConfigManager.Config()
+        self.list = ['CLUSTAL', 'PHYLIPI', 'PHYLIPS', 'FASTA', 'MEGA', 'MSF', 'NEXUS', 'STOCKHOLM']
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        os.makedirs( self.cfg.test_dir )
+       
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )     
+    
+    def testDetectedFormat(self):
+        #detectedFormat()
+        detectedF = self.s.detectedFormat()
+        self.assertEqual(detectedF.sort(), self.list.sort(), "DetectedFormat not equal")
+        
+    def testConvertedFormat(self):
+        #convertedFormat()
+        conversions = []
+        formats = self.list
+        for inputFormat in formats:
+            for outputFormat in formats:
+                conversions.append( ( inputFormat , outputFormat) )
+
+        conversionAllowed = self.s.convertedFormat()
+
+        self.assertEqual( conversions.sort() , conversionAllowed.sort(), "ConvertedFormat not equal" )
+        
+    def testDetect(self):
+        #detect( dataFileName )
+        
+        t_files = glob.glob( os.path.join(DATADIR,'DataAlignments/align.*') )
+        #There are 3 files: CLUSTAL, PHYLIPI, UNKNOWN(PHYLIPI modified)
+        for ali in t_files:
+            format = os.path.splitext( ali )[1][1:]
+            det_format , det_number = self.s.detect( ali )
+            if format == "UNKNOWN":
+                self.assertEqual( det_format , None, "Unknown case: detected format not ok => %s != %s" % (det_format , format)  )
+                self.assertEqual( det_number , None, "Unknown case: detected number not ok" )
+            else:
+                self.assertEqual( det_format , format, "detected format not ok => %s != %s" % (det_format , format)  )
+                self.assertEqual( det_number , 1, "detected number not ok" )
+        
+    def testConvert(self):
+        #convert( dataFileName , outputFormat , inputFormat = None)
+        
+        #classic conversion
+        t_file = os.path.join(DATADIR,'DataAlignments/align.PHYLIPI') 
+        dst = os.path.join(self.cfg.test_dir, 'align.test')
+        shutil.copyfile( t_file , dst )
+        fmtIn = os.path.splitext( t_file )[1][1:]
+        fmt = "CLUSTAL"
+        conv_FileName = self.s.convert( dst , fmt, fmtIn )
+        det_format , _ = self.s.detect( conv_FileName )
+        self.assertEqual( det_format , fmt, "conversion failed")
+        
+        #conversion with a wrong output format
+        fmt = "tester"
+        self.assertRaises(UnSupportedFormatError, self.s.convert, dst, fmt, fmtIn)
+        
+        #conversion of a bad file
+        t_file = os.path.join(DATADIR,'DataAlignments/align.UNKNOWN')
+        shutil.copyfile( t_file , dst )
+        self.assertRaises(UnSupportedFormatError, self.s.convert, dst, fmt, fmtIn)
+
+      
+if __name__ == '__main__':
+    import logging
+    mobyle = logging.getLogger('Mobyle')
+    defaultHandler = logging.FileHandler( '/dev/null' , 'a' )
+    mobyle.addHandler( defaultHandler  )
+    unittest.main()
+
+    
diff --git a/Src/Mobyle/Test/Converter/test_squizz_sequence.py b/Src/Mobyle/Test/Converter/test_squizz_sequence.py
new file mode 100644
index 0000000..0fc0184
--- /dev/null
+++ b/Src/Mobyle/Test/Converter/test_squizz_sequence.py
@@ -0,0 +1,120 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os 
+import sys
+import shutil
+import glob
+import random
+
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../../" ) )
+os.environ[ 'MOBYLEHOME' ] = MOBYLEHOME
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+    
+DATADIR = os.path.dirname( __file__ )
+
+import Mobyle.Test.MobyleTest 
+from Mobyle.Converter import squizz_sequence
+from Mobyle.MobyleError import MobyleError , UnSupportedFormatError
+
+squizz_path = Mobyle.Test.MobyleTest.find_exe( "squizz" )
+
+class SquizzSequenceTest(unittest.TestCase):
+    
+    def __init__(self , methodName='runTest'):
+        unittest.TestCase.__init__( self , methodName = methodName )
+        self.s = squizz_sequence(squizz_path)
+        
+    @unittest.skipUnless( squizz_path , "squizz executable not found" )    
+    def setUp(self):
+        self.supportedFormats =  [ 'SWISSPROT' , 'EMBL' , 'GENBANK' , 'PIR' , 'NBRF' , 
+                                  'GDE' , 'IG' , 'FASTA' , 'GCG' , 'RAW' ]
+        self.supportedFormats.sort( )
+        self.cfg = Mobyle.ConfigManager.Config()
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        os.makedirs( self.cfg.test_dir )
+
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        
+    def testDetectedFormat(self):
+        detectedFormat = self.s.detectedFormat()
+        detectedFormat.sort()
+        self.assertEqual( detectedFormat , self.supportedFormats )
+        
+    def testConvertedFormat(self):
+        conversions = []
+        formats = self.supportedFormats
+        for inputFormat in formats:
+            for outputFormat in formats:
+                conversions.append( ( inputFormat , outputFormat) )
+        conversions.sort()
+        conversionAllowed = self.s.convertedFormat()
+        conversionAllowed.sort()
+        self.assertEqual( conversions , conversionAllowed )
+        
+    def testDetect(self):
+        #test of detected format
+        sequence_files = glob.glob( os.path.join( DATADIR , 'DataSequences/sequence.*' ) )
+        for sequence_file in sequence_files:
+            format = os.path.splitext( sequence_file )[1][1:]
+            converter_format , converter_number = self.s.detect( sequence_file )
+            self.assertEqual( converter_format , format )
+            self.assertEqual( converter_number , 1 )
+        #test the number of entries in a file   
+        fh = open( os.path.join( DATADIR , "DataSequences/sequence.FASTA" ) )
+        fasta = ''.join( fh.readlines() )
+        fh.close()
+        n = random.randint( 1 , 5 )
+        fastan = fasta +'\n'
+        fastan = fastan * n
+        fileName = os.path.join( self.cfg.test_dir , 'sequence.fasta' )
+        fh = open( fileName , 'w' )
+        fh.write( fastan )
+        fh.close()
+        converter_format , converter_number = self.s.detect( fileName )
+        self.assertEqual( converter_number , n )    
+        #test with a non sequence file
+        fh = open( os.path.join( DATADIR ,"DataSequences/sequence.SWISSPROT" ) )
+        sp = fh.readlines() 
+        fh.close()
+        random.shuffle( sp )
+        sp = ''.join( sp )
+        fileName = os.path.join( self.cfg.test_dir , 'sequence.unkn' )
+        fh = open( fileName , 'w' )
+        fh.write( sp )
+        fh.close()
+        converter_format , converter_number = self.s.detect( fileName )
+        self.assertEqual( converter_format , None )
+        self.assertEqual( converter_number , None )
+        
+    def testConvert(self):    
+        sequence_files = glob.glob( os.path.join( DATADIR , 'DataSequences/sequence.*' ) )
+        for srcFileName in sequence_files:
+            destFileName = os.path.join( self.cfg.test_dir , os.path.basename( srcFileName ) ) +'.ori'
+            shutil.copyfile( srcFileName , destFileName )
+            fmtIn = os.path.splitext( destFileName[ :-4] )[1][1:]
+            convertedFileName = self.s.convert( destFileName , fmtIn , fmtIn )
+            converter_format , _ = self.s.detect( convertedFileName )
+            self.assertEqual( converter_format , fmtIn )
+        destFileName = os.path.join( self.cfg.test_dir , 'sequence.SWISSPROT' )    
+        self.assertRaises( UnSupportedFormatError , self.s.convert , destFileName , "anything" )
+        self.assertRaises( UnSupportedFormatError , self.s.convert , destFileName , "FASTA" , "anything" )
+        self.assertRaises( MobyleError , self.s.convert , destFileName , "FASTA" , "GDE" )
+    
+if __name__ == '__main__':
+    import logging
+    mobyle = logging.getLogger('Mobyle')
+    defaultHandler = logging.FileHandler( '/dev/null' , 'a' )
+    mobyle.addHandler( defaultHandler  )
+    unittest.main()
diff --git a/Src/Mobyle/Test/MobyleTest.py b/Src/Mobyle/Test/MobyleTest.py
new file mode 100644
index 0000000..f16af97
--- /dev/null
+++ b/Src/Mobyle/Test/MobyleTest.py
@@ -0,0 +1,139 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+
+
+from Mobyle.ConfigManager import Config
+import os
+        
+    
+class ConfigTest(Config):
+
+    def __init__(self):
+        self.test_dir = '/tmp/test_Mobyle'
+        self._logdir = "/dev/null" 
+        self._mobylehome = os.environ[ 'MOBYLEHOME' ]
+         
+        self._root_url      = 'file://%s' % self.test_dir
+        self._htdocs_prefix = ''
+        self._mobyle_htdocs = self.test_dir
+        self._cgi_prefix    = 'cgi-bin'
+        self._portal_prefix = '/portal'
+        
+        self._repository_path = os.path.realpath( os.path.join( self._mobyle_htdocs , 'data' ) ) 
+        self._services_path = os.path.realpath( os.path.join( self._repository_path , 'services' ) )
+        self._servers_path = os.path.realpath( os.path.join( self._services_path, 'servers' ) )
+        self._index_path = os.path.realpath( os.path.join( self._services_path ,'index' ) )
+            
+        self._results_path = os.path.realpath( os.path.join( self._repository_path , 'jobs' ) )
+        self._user_sessions_path = os.path.realpath( os.path.join( self._repository_path , 'sessions' ) )
+        self._portal_path = os.path.realpath( os.path.join( self._mobyle_htdocs , 'portal' ) )
+        
+        self._repository_url = "%s/%s" % ( self._root_url , 'data' )
+        self._results_url = "%s/%s" % ( self._repository_url , 'jobs' )
+        self._user_sessions_url = "%s/%s" % ( self._repository_url , 'sessions' )
+        self._servers_url = "%s/%s/%s" % ( self._repository_url , 'services' , 'servers' )
+        
+#        self._debug = 0
+#        self._particular_debug = {}
+#        self._accounting = False
+        self._session_debug = False
+        self._status_debug = False
+#        
+#        self._binary_path = []  
+#        self._databanks_config = {}
+        self._dns_resolver = False 
+#        self._opt_email = False
+#        self._particular_opt_email = {}
+#        self._anonymous_session = "captcha"
+#        self._authenticated_session = 'email'
+#        
+#        self._openid = False
+#        self._openidstore_path = os.path.join( self._mobyle_htdocs , 'data' , 'openidstore' ) 
+#        
+#        self._refresh_frequency = 240
+#        self._dont_email_result = False
+        self._filelimit = 2147483648     #  2 Gib
+        self._sessionlimit = 52428800    # 50 Mib
+        self._previewDataLimit = 1048576 #  1 Mib
+#        self._result_remain = 10 # in day
+        self._lang = 'en'
+#        
+#        self._email_delay = 20
+#        self._maxmailsize = 2097152 
+        self._maintainer = [ 'maintainer at nowhere.com' ]
+        self._help = 'helpy at nowhere.com'
+        self._mailhost = ''
+        self._sender = 'sender at nowhere.com'
+#        
+        self._dataconverter = {}
+#        self._execution_system_alias  = { 'SYS' : Local.Config.Execution.SYSConfig() }
+#        self._execution_system_config = { 'DEFAULT' : self._execution_system_alias[ 'SYS'] }
+#        self._revert_alias = dict( [ ( config , alias ) for alias , config in self._execution_system_alias.items() ] )
+#        self._default_Q = 'default_q'
+#        self._particular_Q = {}
+#        self._programs_deployment_include = ['*']
+#        self._programs_deployment_exclude = []
+#        self._programs_deployment_order = [ 'include' , 'exclude' ]
+#        self._disable_all = False
+#        self._disabled_services =[]
+#        self._authorized_services = {}
+        self._all_portals = {}
+#        self._exported_programs = []
+#        self._admindir =  os.path.join( self._results_path , 'ADMINDIR' )
+#        
+#        self._GAcode = None
+        
+         
+__configTest = ConfigTest()
+Config._ref = __configTest
+
+#disabling the loggers 
+import logging
+class NullHandler(logging.Handler):
+    def emit(self, record):
+        pass
+    
+null_handlers = [ NullHandler() ]
+from Mobyle.MobyleLogger import MLogger
+ml = MLogger()
+logging.getLogger('Mobyle').handlers = null_handlers
+logging.getLogger('Mobyle.Session').handlers = null_handlers
+logging.getLogger('Mobyle.account').handlers = null_handlers
+logging.getLogger('Mobyle.builder').handlers = null_handlers
+logging.getLogger('Mobyle.access').handlers = null_handlers
+
+
+from Mobyle import Registry
+registry_test = Registry.Registry()
+local = Registry.ServerDef( 'local' , 
+                            'file://%s' % __configTest.portal_path() , 
+                            __configTest._help , 
+                            'file://%s' % __configTest.services_path() , 
+                            'file://%s' % __configTest.results_path()
+                             )
+registry_test.addServer( local )
+Registry.registry = registry_test
+
+
+def find_exe(program):
+    def is_exe(fpath):
+        return os.path.exists(fpath) and os.access(fpath, os.X_OK)
+
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split( os.pathsep ):
+            exe_file = os.path.join( path, program )
+            if is_exe( exe_file ):
+                return exe_file
+
+    return None
diff --git a/Src/Mobyle/Test/__init__.py b/Src/Mobyle/Test/__init__.py
new file mode 100644
index 0000000..35497c8
--- /dev/null
+++ b/Src/Mobyle/Test/__init__.py
@@ -0,0 +1,5 @@
+'''
+Created on Mar 11, 2010
+
+ at author: bneron
+'''
diff --git a/Src/Mobyle/Test/main.py b/Src/Mobyle/Test/main.py
new file mode 100755
index 0000000..3368766
--- /dev/null
+++ b/Src/Mobyle/Test/main.py
@@ -0,0 +1,39 @@
+#! /usr/bin/env python
+
+import os
+import sys
+import unittest2 as unittest
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ[ 'MOBYLEHOME' ] = MOBYLEHOME
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+    
+import Mobyle.Test.MobyleTest        
+from optparse import OptionParser    
+parser = OptionParser()
+parser.add_option("-v", "--verbose" , 
+                  dest= "verbosity" , 
+                  action="count" , 
+                  help= "set the verbosity level of output",
+                  default = 0
+                  )
+opt , args = parser.parse_args()    
+
+if not args:
+    suite = unittest.TestLoader().discover( '.' ) 
+else:
+    suite = unittest.TestSuite()
+    for arg in args: 
+        if os.path.exists( arg ):
+            if os.path.isfile( arg ):
+                fpath, fname =  os.path.split( arg )
+                suite.addTests( unittest.TestLoader().discover(  fpath , pattern = fname ) ) 
+            elif os.path.isdir( arg ):  
+                suite.addTests( unittest.TestLoader().discover( arg ) ) 
+        else:
+            sys.stderr.write(  "%s : no such file or directory\n" % arg ) 
+
+unittest.TextTestRunner( verbosity = opt.verbosity ).run( suite )
diff --git a/Src/Mobyle/Test/openTransaction.py b/Src/Mobyle/Test/openTransaction.py
new file mode 100755
index 0000000..c599021
--- /dev/null
+++ b/Src/Mobyle/Test/openTransaction.py
@@ -0,0 +1,44 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import os
+import sys
+import errno
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+
+from Mobyle.Transaction import Transaction
+
+
+def openTransaction( path , type ):
+    try:
+        transaction = Transaction( path , type )
+    except IOError , err:
+        if err.errno == errno.EAGAIN:
+            # the assertion is made on the return status if it True or False
+            # EAGAIN = Resource temporarily unavailable
+            sys.exit(errno.EAGAIN)
+        else:
+            import traceback
+            print >> sys.stderr , traceback.print_exc()
+            raise err
+    transaction.commit()
+    
+    
+if __name__ == '__main__':
+    path = sys.argv[1]
+    type = int( sys.argv[2])
+    openTransaction( path , type)
diff --git a/Src/Mobyle/Test/program_test.py b/Src/Mobyle/Test/program_test.py
new file mode 100644
index 0000000..3fab99f
--- /dev/null
+++ b/Src/Mobyle/Test/program_test.py
@@ -0,0 +1,8 @@
+import sys
+sys.path.append( '/home/bneron/Mobyle/trunk/mobyle/')
+sys.path.append( '/home/bneron/Mobyle/trunk/mobyle/Src')
+from Mobyle import Parser2
+p = Parser2.ServiceParser()
+s = p.parse( "/home/bneron/Mobyle/trunk/mobyle/Src/Mobyle/Test/program.xml" )
+
+
diff --git a/Src/Mobyle/Test/test_Admin.py b/Src/Mobyle/Test/test_Admin.py
new file mode 100644
index 0000000..96a4f96
--- /dev/null
+++ b/Src/Mobyle/Test/test_Admin.py
@@ -0,0 +1,263 @@
+########################################################################################
+#                                                                                      #
+#   Author: Nicolas Joly                                                               #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os, sys, stat
+import shutil
+from time import mktime
+
+MOBYLEHOME = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+if (MOBYLEHOME) not in sys.path:
+    sys.path.append(MOBYLEHOME)
+if (os.path.join(MOBYLEHOME, 'Src')) not in sys.path:
+    sys.path.append(os.path.join(MOBYLEHOME, 'Src'))
+
+import Mobyle.Test.MobyleTest
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Admin import Admin
+
+
+class AdminTest(unittest.TestCase):
+
+    def setUp(self):
+        ## Config
+        self.cfg = Mobyle.ConfigManager.Config()
+        ## Inits
+        shutil.rmtree(self.cfg.test_dir, ignore_errors=True)
+        os.makedirs(self.cfg.test_dir)
+        self.cwd = os.getcwd()
+        os.chdir(self.cfg.test_dir)
+        ## Admin
+        self.adm = self._fakeadmin()
+
+    def tearDown(self):
+        os.chdir(self.cwd)
+        shutil.rmtree(self.cfg.test_dir)
+
+    def testAdmin(self):
+        ## Check valid paths
+        Admin(".")
+        Admin(Admin.FILENAME)
+        ## Check invalid paths
+        adm_dir = "dummy"
+        self.assertRaises(MobyleError, Admin, adm_dir)
+        os.makedirs(adm_dir)
+        self.assertRaises(MobyleError, Admin, adm_dir)
+        ## Empty file (fails silently)
+        open(Admin.FILENAME, 'w').close()
+        Admin(Admin.FILENAME)
+        ## Missing read permissions
+        os.chmod(Admin.FILENAME, 0222)
+        self.assertRaises(MobyleError, Admin, ".")
+
+    def testcreate(self):
+        ## Create a minimal admin file
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        ## Should fail if already exists
+        self.assertTrue(os.access(Admin.FILENAME, os.F_OK))
+        self.assertRaises(MobyleError, Admin.create, self.cfg.test_dir, None, None, None)
+        ## Check alternate path
+        altdir = os.path.join(self.cfg.test_dir, "alternate")
+        os.makedirs(altdir)
+        Admin.create(altdir, None, None, None)
+
+    def testrefresh(self):
+        adm_queue = 'mobyle'
+        self.assertEqual(self.adm.getQueue(), None)
+        adm = Admin(Admin.FILENAME)
+        adm.setQueue(adm_queue)
+        adm.commit()
+        self.assertEqual(self.adm.getQueue(), None)
+        self.adm.refresh()
+        self.assertEqual(self.adm.getQueue(), adm_queue)
+
+    def testcommit(self):
+        ## Commit should update file
+        bef = os.stat(Admin.FILENAME)
+        adm = Admin(Admin.FILENAME)
+        adm.commit()
+        aft = os.stat(Admin.FILENAME)
+        self.assertTrue(bef.st_ino != aft.st_ino and bef.st_mtime <= aft.st_mtime)
+        ## Cannot create/rename temporary file
+        mod = os.stat(".").st_mode
+        os.chmod(".", mod & ~stat.S_IWUSR)
+        self.assertRaises(MobyleError, adm.commit)
+        os.chmod(".", mod)
+        ## Nothing to save (fails silently)
+        adm.me.clear()
+        adm.commit()
+
+    def testDate(self):
+        ## Ensure this is a valid date
+        date = self.adm.getDate()
+        mktime(date)
+        ## Missing key
+        self.adm.me.clear()
+        self.assertEqual(self.adm.getDate(), None)
+
+    def testEmail(self):
+        adm_email = "user at domain.fr"
+        ## Created without email info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getEmail(), None)
+        ## Created with email info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None, userEmail=adm_email)
+        adm = Admin(".")
+        self.assertEqual(adm.getEmail(), adm_email)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getEmail(), None)
+
+    def testRemote(self):
+        adm_remote = "127.0.0.1/localhost"
+        ## Created without remote info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getRemote(), str(None))
+        ## Created with remote info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, adm_remote, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getRemote(), adm_remote)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getRemote(), None)
+
+    def testWorkflow(self):
+        workflowID = "adm_dummy"
+        ## Created without session info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getWorkflowID(), None)
+        ## Created with session info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir , None, None, None, workflowID=workflowID)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getWorkflowID(), workflowID)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getWorkflowID(), None)
+
+    def testSession(self):
+        adm_session = "adm_dummy"
+        ## Created without session info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getSession(), None)
+        ## Created with session info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None, sessionID=adm_session)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getSession(), adm_session)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getSession(), None)
+
+
+    def testJobName(self):
+        adm_jobname = "adm_dummy"
+        ## Created without job name info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getJobName(), str(None))
+        ## Created with job name info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, adm_jobname, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getJobName(), adm_jobname)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getJobName(), None)
+
+    def testJobID(self):
+        adm_jobid = "adm_dummy"
+        ## Created without job id info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getJobID(), str(None))
+        ## Created with job id info
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, adm_jobid)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getJobID(), adm_jobid)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getJobID(), None)
+
+    def testMD5(self):
+        adm_md5 = "adm_dummy"
+        ## Default
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getMd5(), None)
+        ## Set value
+        adm.setMd5(adm_md5)
+        self.assertEqual(adm.getMd5(), adm_md5)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getMd5(), None)
+
+    def testExecutionAlias(self):
+        adm_executionalias = "adm_dummy"
+        ## Default
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getExecutionAlias(), None)
+        ## Set value
+        adm.setExecutionAlias(adm_executionalias)
+        self.assertEqual(adm.getExecutionAlias(), adm_executionalias)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getExecutionAlias(), None)
+
+    def testQueue(self):
+        adm_queue = "adm_dummy"
+        ## Default
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getQueue(), None)
+        ## Set value
+        adm.setQueue(adm_queue)
+        self.assertEqual(adm.getQueue(), adm_queue)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getQueue(), None)
+
+    def testNumber(self):
+        adm_number = "adm_dummy"
+        ## Default
+        os.unlink(Admin.FILENAME)
+        Admin.create(self.cfg.test_dir, None, None, None)
+        adm = Admin(Admin.FILENAME)
+        self.assertEqual(adm.getNumber(), None)
+        ## Set value
+        adm.setNumber(adm_number)
+        self.assertEqual(adm.getNumber(), adm_number)
+        ## Missing key
+        adm.me.clear()
+        self.assertEqual(adm.getNumber(), None)
+
+    def _fakeadmin(self):
+        Admin.create(self.cfg.test_dir, None, None, None)
+        return Admin(".")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_AnonymousSession.py b/Src/Mobyle/Test/test_AnonymousSession.py
new file mode 100644
index 0000000..7325860
--- /dev/null
+++ b/Src/Mobyle/Test/test_AnonymousSession.py
@@ -0,0 +1,70 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron, Nicolas Joly                                               #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os, sys
+import shutil
+
+MOBYLEHOME = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+if (MOBYLEHOME) not in sys.path:
+    sys.path.append(MOBYLEHOME)
+if (os.path.join( MOBYLEHOME, 'Src')) not in sys.path:    
+    sys.path.append(os.path.join(MOBYLEHOME, 'Src'))
+
+import Mobyle.Test.MobyleTest
+from Mobyle.AnonymousSession import AnonymousSession
+from Mobyle.MobyleError import MobyleError, SessionError
+
+
+class AnonymousSessionTest(unittest.TestCase):
+
+    def setUp(self):
+        self.cfg = Mobyle.ConfigManager.Config()
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        self.cfg._anonymous_session = 'yes'
+
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+
+    def testAnonymousSession(self):
+        ## Create a new session
+        session = AnonymousSession(self.cfg)
+        ## Fetch an existing session
+        session_key = session.getKey()
+        session = AnonymousSession(self.cfg, session_key)
+        self.assertEqual(session.getKey(), session_key)
+        ## Creation should fail if missing
+        shutil.rmtree(session.getDir())
+        self.assertRaises(MobyleError, AnonymousSession, self.cfg, session_key)
+        ## Creation should fail if disabled
+        self.cfg._anonymous_session = 'no'
+        self.assertRaises(MobyleError, AnonymousSession, self.cfg)
+        
+    def testAlreadyExist(self):
+        ## Creation should fail if no key is provided but the directory exist
+        fake_key = 'A00000000000000'
+        tmp_newSessionKey = AnonymousSession._AnonymousSession__newSessionKey
+        AnonymousSession._AnonymousSession__newSessionKey = lambda x : fake_key
+        dir = os.path.normpath( os.path.join( self.cfg.user_sessions_path() , AnonymousSession.DIRNAME , fake_key ) )
+        os.makedirs( dir , 0755 )
+        self.assertRaises(SessionError, AnonymousSession, self.cfg )
+        AnonymousSession._AnonymousSession__newSessionKey = tmp_newSessionKey
+    
+    def testNoPermission(self):
+        session_dir = os.path.normpath( os.path.join( self.cfg.user_sessions_path()  ) )
+        os.makedirs( session_dir , 0755 )
+        os.chmod( session_dir , 0000 )
+        self.assertRaises(SessionError, AnonymousSession, self.cfg )
+                                       
+    def testisAuthenticated(self):
+        session = AnonymousSession(self.cfg)
+        self.assertFalse(session.isAuthenticated())
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_AuthenticatedSession.py b/Src/Mobyle/Test/test_AuthenticatedSession.py
new file mode 100644
index 0000000..3164faa
--- /dev/null
+++ b/Src/Mobyle/Test/test_AuthenticatedSession.py
@@ -0,0 +1,222 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os
+import sys
+import shutil
+from lxml import etree
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ[ 'MOBYLEHOME' ] = MOBYLEHOME
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+
+import Mobyle.Test.MobyleTest
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.AnonymousSession import AnonymousSession
+from Mobyle.Session import Session
+from Mobyle.Transaction import Transaction
+from Mobyle.MobyleError import SessionError , AuthenticationError
+from Mobyle.Net import Email , EmailAddress
+Email.send = lambda templateName , subject , msg : None
+from Mobyle import JobState
+from Mobyle.Registry import ProgramDef
+
+DATADIR = os.path.dirname( __file__ )
+
+
+class AuthenticatedSessionTest(unittest.TestCase):
+    """Tests the functionalities of Transaction"""
+
+    def setUp( self ):
+        self.cfg = Mobyle.ConfigManager.Config()
+        self.cfg._authenticated_session = 'yes'
+
+        shutil.rmtree( self.cfg.test_dir , ignore_errors= True )
+        os.makedirs( self.cfg.test_dir )
+        os.makedirs( self.cfg.user_sessions_path() )
+
+        self.registry = Mobyle.Session.registry
+
+        self.fake_prg = ProgramDef( self.registry.getProgramUrl( name = 'fake' ),
+                                    name = 'fake',
+                                    path = self.registry._computeProgramPath( 'fake' ),
+                                    server = self.registry.serversByName[ 'local' ]
+                                   )
+        self.registry.addProgram( self.fake_prg )
+        self.passwd = "un_mot_de_pass"
+        self.email = EmailAddress( "test at mondomain.fr" )
+
+    def tearDown( self ):
+        self.registry.pruneService(self.fake_prg )
+        shutil.rmtree( self.cfg.user_sessions_path() , ignore_errors= True )
+        shutil.rmtree( self.cfg.test_dir , ignore_errors= True )
+
+    def testGetSessionWithPasswd(self):
+        ## Create a new session
+        session1 = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        ## Fetch an existing session
+        session2 = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        self.assertEqual( session1.getDir() , session2.getDir() )
+        self.assertRaises( AuthenticationError , AuthenticatedSession, self.cfg, self.email , passwd= 'bad_'+self.passwd )
+        ## Creation should fail if disabled
+        self.cfg._authenticated_session = 'no'
+        self.assertRaises( SessionError , AuthenticatedSession, self.cfg, self.email , passwd= self.passwd )
+        self.cfg._authenticated_session = 'yes'
+        ##try to authenticated with an invalid email
+        session1._AuthenticatedSession__userEmail.check = lambda x: False
+        session2 = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+
+    def testGetSessionWithTicket(self):
+        ## Create a new session
+        self.cfg._authenticated_session = 'email'
+        # BUG?
+        #pourquoi un ticket n'est cree que dans le cas self.cfg._authenticated_session = 'email'
+        #et pas quand self.cfg._authenticated_session = 'yes' ????
+        session1 = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        ## Fetch an existing session
+        session2 = AuthenticatedSession( self.cfg , self.email , ticket_id= session1.ticket_id )
+        self.assertEqual( session1.getDir() , session2.getDir() )
+        self.assertRaises( SessionError, AuthenticatedSession, self.cfg , self.email , ticket_id= session1.ticket_id+"1" )
+
+
+    def testCheckPasswd( self ):
+        session = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        self.assertFalse( session.checkPasswd( self.passwd + "false") )
+        self.assertFalse( session.checkPasswd( "" ) )
+        self.assertTrue( session.checkPasswd( self.passwd ) )
+        newPasswd = "new_pass_word"
+        session.setPasswd( newPasswd )
+        self.assertFalse( session.checkPasswd( self.passwd ) )
+        self.assertTrue( session.checkPasswd( newPasswd ) )
+        #restore the passwd to test the other methods or further tests
+        #self.session.changePasswd( newPasswd , self.passwd )
+
+    def testChangePasswd( self ):
+        session = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        newPasswd = "new_pass_word"
+        self.assertRaises( AuthenticationError , session.changePasswd , self.passwd + "false" , newPasswd )
+        session.changePasswd( self.passwd , newPasswd )
+        self.assertTrue( session.checkPasswd( newPasswd ))
+        #restore the passwd to test the other methods or further tests
+        #session.changePasswd( newPasswd , self.passwd )
+
+
+    def testConfirmEmail( self ):
+        self.cfg._authenticated_session = 'email'
+        session = AuthenticatedSession( self.cfg , self.email , passwd= self.passwd )
+        transaction = session._getTransaction( Transaction.READ )
+        actKey = transaction.getActivatingKey()
+        transaction.commit()
+
+        self.assertFalse( session.isActivated() )
+        self.assertRaises( AuthenticationError , session.confirmEmail , actKey + "False")
+        session.confirmEmail( actKey )
+        self.assertTrue( session.isActivated() )
+
+    def testMergeWith( self ):
+        ## Create an authenticated session
+        auth_session = AuthenticatedSession( self.cfg , self.email , passwd = self.passwd )
+        ## Merging a session with itself should fail
+        self.assertRaises( SessionError , auth_session.mergeWith , auth_session )
+        ## Create an anonymous session
+        self.cfg._anonymous_session = 'yes'
+        key = 'anonymous_01'
+        self._makeFakeSession( key )
+        anno_session = AnonymousSession( self.cfg , key = key )
+        jobs = anno_session.getAllJobs()
+        datas = anno_session.getAllData()
+        ## Merge sessions
+        auth_session.mergeWith( anno_session )
+        newJobs = auth_session.getAllJobs()
+        newDatas = auth_session.getAllData()
+        self.assertEqual( jobs , newJobs )
+        self.assertEqual( datas , newDatas )
+
+    def _makeFakeSession(self , key ):
+        sessionPath  = os.path.join( self.cfg.user_sessions_path() , AnonymousSession.DIRNAME , key )
+        if not os.path.exists( sessionPath ):
+            os.makedirs( sessionPath , 0755)
+
+        if os.path.exists( sessionPath ):
+            shutil.rmtree( sessionPath )
+        shutil.copytree( os.path.join( DATADIR , 'fake_session' ) , sessionPath )
+        jobID = [ self.cfg.results_url() + "/fake_job/A00000000000000" ,
+                  self.cfg.results_url() + "/fake_job/A00000000000001" ,
+                  self.cfg.results_url() + "/fake_job/A00000000000002" ,
+                ]
+        xmlpath = os.path.join( sessionPath , Session.FILENAME )
+
+        doc = etree.parse( "file://%s" % xmlpath )
+        root = doc.getroot()
+
+        jobNodes = root.xpath( 'jobList/job' )
+        for jobNode in jobNodes:
+            id = jobNode.get( 'id' )
+            jobURL = jobID[ int(id) ]
+            jobNode.set( 'id' , jobURL )
+
+        producedByNodes = root.xpath( 'dataList/data/producedBy' )
+        for producedByNode in producedByNodes:
+            ref = producedByNode.get( 'ref' )
+            jobURL = jobID[ int( ref ) ]
+            producedByNode.set( 'ref' , jobURL )
+
+        usedByNodes = root.xpath( 'dataList/data/usedBy' )
+        for uesdByNode in usedByNodes:
+            ref = uesdByNode.get( 'ref' )
+            jobURL = jobID[ int( ref ) ]
+            uesdByNode.set( 'ref' , jobURL )
+
+        xmlfile = open( xmlpath , 'w' )
+        xmlfile.write( etree.tostring( root , xml_declaration=True , encoding='UTF-8', pretty_print= True ) )
+        xmlfile.close()
+
+        for jobURL in  jobID:
+            self._makeFakeJob( jobURL )
+
+        return sessionPath
+
+
+    def _makeFakeJob(self , jobID ):
+        jobPath = JobState.url2path( jobID )
+        jobdir = os.path.dirname( jobPath )
+        if not os.path.exists( jobdir ):
+            os.makedirs( jobdir , 0755)
+
+        if os.path.exists( jobPath ):
+            shutil.rmtree( jobPath )
+        index = jobID[-1]
+
+        shutil.copytree( os.path.join( DATADIR , 'fake_job'+ index ) , jobPath )
+
+        doc = etree.parse( "file://%s/index.xml" % jobPath )
+        root = doc.getroot()
+
+        text_node = root.find( 'name' )
+        text_node.text = Mobyle.Session.registry.serversByName[ 'local' ].jobsBase  + '/fake.xml'
+
+        text_node = root.find( 'host' )
+        text_node.text = self.cfg.root_url()
+
+        text_node = root.find( 'id' )
+        text_node.text = jobID
+
+        xmlfile = open( os.path.join( jobPath , 'index.xml' ) , 'w' )
+        xmlfile.write( etree.tostring( root , xml_declaration=True , encoding='UTF-8', pretty_print= True ) )
+        xmlfile.close()
+
+        return jobPath
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_JobState.py b/Src/Mobyle/Test/test_JobState.py
new file mode 100644
index 0000000..46161e6
--- /dev/null
+++ b/Src/Mobyle/Test/test_JobState.py
@@ -0,0 +1,265 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os
+import sys
+import shutil
+import time
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+from lxml import etree
+from Mobyle import JobState
+from Mobyle.Status import Status
+from Mobyle.Service import MobyleType
+from Mobyle.Classes.DataType import DataTypeFactory
+
+DATADIR = os.path.dirname( __file__ )
+
+class JobStateTest(unittest.TestCase):
+    """Tests the functionalities of Transaction"""
+    
+    fake_mobyle_path = '/tmp/test_Mobyle'
+    
+    def setUp(self):
+        """ 
+        setting up the configuration for the test, including:
+        - test configuration that does not check dns or try to send confirmation emails
+        - test session email
+        """
+        self.cfg = Mobyle.ConfigManager.Config()
+        #self.cfg._root_url           = 'http://marygay.sis.pasteur.fr:82'
+        #self.cfg._results_path       = os.path.join( self.__class__.fake_mobyle_path , 'results' )
+        #self.cfg._results_url        = "%s%s" % ( self.cfg._root_url , '/mobyle/results')
+        
+        #create space for test
+        if os.path.exists( self.__class__.fake_mobyle_path ):
+                    shutil.rmtree( self.__class__.fake_mobyle_path )
+        os.makedirs( self.__class__.fake_mobyle_path )
+
+        #create jobs directory
+        if os.path.exists( self.cfg.results_path() ):
+                    shutil.rmtree( self.cfg.results_path() )
+        os.makedirs( self.cfg.results_path() )
+        
+        
+        self.jobID = [ self.cfg.results_url() + "/fake_job/A00000000000000",
+                       self.cfg.results_url() + "/fake_job/A00000000000001" 
+                       ]
+        
+        self.jobs = [ {'jobID'       :  self.jobID[0] ,
+                       'name'        : 'http://marygay.sis.pasteur.fr:81/mobyle/programs/dnadist.xml' ,
+                       'host'        : 'http://marygay.sis.pasteur.fr:81' ,
+                       'status'      :  Status( code = 6 ),#'killed' ,
+                       'date'        :  time.strptime( "11/20/08  12:00:00", "%x  %X"),#(2008, 11, 20, 12, 0, 0, 3, 325, -1),
+                       'dataProduced': [] ,
+                       'dataUsed'    : ['16bd6f89e732eb7cb0bbc13c65d202d3.aln'] ,
+                       'sessionKey'  : 'R18991749411106',
+                       'email'       : 'bneron at pasteur.fr',
+                       'args'        : {'infile': 'clustal.aln', 'seqboot': 'True', 'replicates': '50', 'seqboot_seed': '3'} ,
+                       'commandLine' : 'ln -s clustal.aln infile && seqboot < seqboot.params && mv outfile seqboot.outfile && rm infile &&ln -s seqboot.outfile infile && dnadist <dnadist.params && mv outfile dnadist.outfile' ,
+                       'formattedData': {'infile': {'formattedFile': None, 'fileFmt': 'PHYLIPI', 'fmtprogram': 'squizz', 'file': 'clustal.aln', 'formattedFileFmt': None}} ,
+                       'paramfiles'  : [('seqboot.params', 11L), ('dnadist.params', 11L)],
+                      } ,
+                      {'jobID'       : self.jobID[1] ,
+                       'name'        : 'http://marygay.sis.pasteur.fr:81/mobyle/programs/dnadist.xml' ,
+                       'host'        : 'http://marygay.sis.pasteur.fr:81' ,
+                       'status'      :  Status( code = 4 ),#finished Status( code = 3 ),#running
+                       'date'        :  time.strptime( "11/19/08  10:50:33", "%x  %X"),#(2008, 11, 19, 10, 50, 33, 2, 324, -1),
+                       'dataProduced': ['f835ddf28cb6aa776a41d7c71ec9cb5a.out'] ,
+                       'dataUsed'    : ['16bd6f89e732eb7cb0bbc13c65d202d3.aln'] ,
+                       'sessionKey'  : 'R18991749411106',
+                       'email'       : 'bneron at pasteur.fr',
+                       'args'        : {'infile': 'clustal.aln', 'seqboot': 'True', 'replicates': '10', 'seqboot_seed': '3'} ,
+                       'commandLine' : 'n -s clustal.aln infile && seqboot < seqboot.params && mv outfile seqboot.outfile && rm infile &&ln -s seqboot.outfile infile && dnadist <dnadist.params && mv outfile dnadist.outfile' ,
+                       'formattedData': {'infile': {'formattedFile': None, 'fileFmt': 'PHYLIPI', 'fmtprogram': 'squizz', 'file': 'clustal.aln', 'formattedFileFmt': None}} ,
+                       'paramfiles'  : [('seqboot.params', 11L), ('dnadist.params', 11L)],
+                       },
+                       ]
+        self.jobDir = self._makeFakeJob( self.jobID[1] )
+        self.jobState = JobState.JobState( self.jobDir )
+        
+    def testgetOutputs( self ):    
+        outputs_recieved = self.jobState.getOutputs()
+        outputs = [( "P1", 
+                           [('dnadist.outfile', 206400L, 'None')]), 
+                    ("P2", 
+                           [('seqboot.outfile', 976580L, 'None')]), 
+                    ("P3", 
+                            [('dnadist.out', 25411L, 'None')])]
+        self.assertTrue( len( outputs_recieved ) == 3 )
+                 
+    def testgetOutput( self ):    
+        output_recieved = self.jobState.getOutput( 'seqboot_out' )
+        self.assertEqual( output_recieved , [('seqboot.outfile', 976580L, None)] )
+        
+    def testgetInputFiles( self ):   
+        inputs_recieved = self.jobState.getInputFiles()
+        inputs = [("P1", [('clustal.aln', 90044L, 'PHYLIPI')])]
+        
+    def testgetDir( self ):    
+        self.assertEqual( self.jobState.getDir() , self.jobDir )
+    
+    def testID( self ):    
+        self.jobState.setID( self.jobID[0])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getID() , self.jobID[0] )
+        self.jobState.setID( self.jobID[1])
+        self.jobState.commit()
+        
+    def testSessionKey( self ):    
+        self.jobState.setSessionKey( self.jobs[0][ 'sessionKey' ])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getSessionKey() , self.jobs[0][ 'sessionKey' ] )
+                          
+    def testCommandLine( self ):    
+        self.jobState.setCommandLine( self.jobs[0][ 'commandLine' ])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getCommandLine() , self.jobs[0][ 'commandLine' ] )
+    
+    def testName( self ):    
+        self.jobState.setName( self.jobs[0][ 'name' ])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getName() , self.jobs[0][ 'name' ] )
+    
+    def testDate( self ):    
+        self.jobState.setDate( self.jobs[0][ 'date' ])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getDate() , time.strftime( "%x  %X" , self.jobs[0][ 'date' ] ) )
+    
+    def testEmail( self ):    
+        self.jobState.setEmail( self.jobs[0][ 'email' ])
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getEmail() , self.jobs[0][ 'email' ] )
+    
+    def testgetStdout( self ): 
+        stdout_file = open( os.path.join( self.jobDir , os.path.basename( self.jobState.getName()))[:-4]+".out" )
+        stdout = ''.join( stdout_file.readlines() )
+        stdout_file.close()
+        self.assertEqual( self.jobState.getStdout() , stdout )
+        
+    def testgetOutputFile( self ):    
+        fileName = "dnadist.outfile"
+        File = open( os.path.join( self.jobDir , fileName ) )
+        content  = ''.join( File.readlines() )
+        File.close()
+        self.assertEqual( self.jobState.getOutputFile( fileName ) , content )
+        
+#    def testopen( self ):    
+#        pass
+#    
+    def testgetStderr( self ):    
+        stderr_file = open( os.path.join( self.jobDir , os.path.basename( self.jobState.getName()))[:-4]+".err" )
+        stderr = ''.join( stderr_file.readlines() )
+        stderr_file.close()
+        self.assertEqual( self.jobState.getStderr() , stderr )
+    
+    def testsetHost( self ):
+        sent_host =  'http://foo.bar.pasteur.fr'   
+        self.jobState.setHost( sent_host )
+        self.jobState.commit()
+        doc = etree.parse( self.jobDir + "/index.xml" )
+        recieved_host = doc.find( 'host' ).text
+        self.assertEqual( recieved_host , sent_host )
+    
+    def testsetInputDataFile( self ): 
+        dtf = DataTypeFactory()
+        self.dataType1 = dtf.newDataType( 'Text' )
+        dataFile = {  
+                    'paramName'   : "in_param_file",
+                    'File'        : ( 'foo.ori' , 'beautiful.format' , 234 ) ,
+                    'fmtProgram'  : 'squizz',
+                    'formattedFile': ( 'foo.reformat' , 'wonderful.format' , 432 ) ,
+                    }   
+        self.jobState.setInputDataFile( dataFile[ 'paramName' ] ,
+                                        dataFile[ 'File' ],
+                                        dataFile[ 'fmtProgram' ] ,
+                                        dataFile[ 'formattedFile' ],
+                                         )
+        self.jobState.commit()
+        tree = etree.parse( os.path.join( os.path.join( self.jobDir , 'index.xml') ))
+        root = tree.getroot()
+        param = root.xpath( 'data/input/parameter[ name = "'+ dataFile[ 'paramName' ] + '" ]')[0]                    
+        self.assertEqual( param.find( 'name' ).text , dataFile[ 'paramName' ] )
+        
+#    def testrenameInputDataFile( self ):    
+#        pass
+#    
+#    def testsetInputDataValue( self ):    
+#        pass
+#    
+#    def testsetOutputDataFile( self ):    
+#        pass
+#    
+#    def testdelInputData( self ):    
+#        pass
+#    
+    def testgetArgs( self ): 
+        self.assertEqual( self.jobState.getArgs() , self.jobs[1]['args'] )
+
+    
+    def testgetPrompt( self ):    
+        prompt = "Perform a bootstrap before analysis"
+        self.assertEqual( self.jobState.getPrompt( 'seqboot' ) , prompt )     
+        
+    def testgetFormattedData( self ):    
+        self.assertEqual( self.jobState.getFormattedData() , self.jobs[1]['formattedData'] )
+    
+    def testParamfiles( self ):
+        self.assertEqual( self.jobState.getParamfiles() , self.jobs[1]['paramfiles'] )
+        new_paramfiles = ( 'test' , 22L )
+        self.jobs[1]['paramfiles'].append( new_paramfiles )
+        self.jobState.setParamfiles( [ new_paramfiles ] )
+        self.jobState.commit()
+        self.assertEqual( self.jobState.getParamfiles() ,  self.jobs[1]['paramfiles'] )
+        
+    def _makeFakeJob(self , jobID ):
+        jobPath = JobState.url2path( jobID ) 
+        jobdir = os.path.dirname( jobPath )
+        if not os.path.exists( jobdir ):
+            os.makedirs( jobdir , 0755)
+            
+        if os.path.exists( jobPath ):
+            shutil.rmtree( jobPath )
+        index = jobID[-1]
+        
+        shutil.copytree( os.path.join( DATADIR , 'fake_job' + index ) , jobPath )
+        
+        doc = etree.parse( os.path.join( jobPath , 'index.xml' ) )
+        root = doc.getroot()
+        
+        nameNode = root.find( 'name' )
+        nameNode.text = self.cfg.repository_url() + '/fake.xml'
+        
+        hostNode = root.find( 'host' )
+        hostNode.text = self.cfg.root_url()
+        
+        idNode   = root.find( 'id' )
+        idNode.text = jobID
+        
+        xmlfile = open(  os.path.join( jobPath , 'index.xml' ) , 'w' )
+        xmlfile.write( etree.tostring( doc , xml_declaration=True , encoding='UTF-8', pretty_print= True ) )
+        xmlfile.close()
+        
+        return jobPath
+    
+#    @classmethod
+#    def tearDownClass( cls ):
+#        shutil.rmtree( cls.fake_mobyle_path , ignore_errors= True )
+#    
+ 
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_Session.py b/Src/Mobyle/Test/test_Session.py
new file mode 100644
index 0000000..1bcc2cb
--- /dev/null
+++ b/Src/Mobyle/Test/test_Session.py
@@ -0,0 +1,408 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron, Nicolas Joly                                               #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os, sys
+import shutil
+from lxml import etree
+from time import localtime, strftime
+
+MOBYLEHOME = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+if (MOBYLEHOME) not in sys.path:
+    sys.path.append(MOBYLEHOME)
+if (os.path.join(MOBYLEHOME, 'Src')) not in sys.path:    
+    sys.path.append(os.path.join(MOBYLEHOME, 'Src'))
+
+import Mobyle.Test.MobyleTest
+import Mobyle.Session
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.MobyleError import MobyleError, NoSpaceLeftError, SessionError, UserValueError
+from Mobyle.Service import MobyleType
+from Mobyle.Transaction import Transaction
+from Mobyle.Utils import Admin
+
+#tmpdir = '/tmp/mobyle'
+
+class SessionTest(unittest.TestCase):
+
+    def setUp(self):
+        ## Config
+        self.cfg = Mobyle.ConfigManager.Config()
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        ## Session
+        self.session_key = 'fake_session'
+        self.session = self._fakesession(self.session_key)
+        ## Data
+        self.data_name = 'datafile'
+        self.data_text = 'A sample user data string'
+
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+
+    ## Generic methods ...
+
+    def testisLocal(self):
+        self.assertTrue(self.session.isLocal())
+
+    def testgetDir(self):
+        xdir = os.path.join(self.cfg.user_sessions_path(), self.session_key)
+        self.assertEqual(self.session.getDir(), xdir)
+
+    def testgetKey(self):
+        self.assertEqual(self.session.getKey(), self.session_key)
+
+    def testgetBaseInfo(self):
+        email = 'name at domain.ext'
+        self.assertEqual(self.session.getBaseInfo(), (None, False, True))
+        self.session.setEmail(email)
+        self.assertEqual(self.session.getBaseInfo(), (email, False, True))
+
+    def testisActivated(self):
+        self.assertTrue(self.session.isActivated())
+
+    def testgetEmail(self):
+        email = 'name at domain.ext'
+        self.assertEqual(self.session.getEmail(), None)
+        self.session.setEmail(email)
+        self.assertEqual(self.session.getEmail(), email)
+
+    def testsetEmail(self):
+        email = 'name at domain.ext'
+        self.assertNotEqual(self.session.getEmail(), email)
+        self.session.setEmail(email)
+        self.assertEqual(self.session.getEmail(), email)
+        ## Invalid email should be rejected
+        self.assertRaises(UserValueError, self.session.setEmail, 'dummy')
+
+    ## Data methods ...
+
+    def testaddData(self):
+        mtype = self._faketype('Text')
+        ## Add data by content
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        self.assertTrue(self.session.hasData(did))
+        session_data = os.path.join(self.session.getDir(), did)
+        self.assertTrue(os.access(session_data, os.F_OK|os.R_OK))
+        ## Add data from job
+        url = self._fakejob('A00000000000000')
+        job = Mobyle.MobyleJob.MobyleJob(ID = url)
+        dat = os.path.join( job.getDir(), self.data_name )
+        fh = open(dat, 'w')
+        fh.write(self.data_text * 2)
+        fh.close()
+        did = self.session.addData(self.data_name, mtype,
+            producer = job)
+        self.assertTrue(self.session.hasData(did))
+        session_data = os.path.join(self.session.getDir(), did)
+        self.assertTrue(os.access(session_data, os.F_OK|os.R_OK))
+        ## Add data from session
+        session = self._fakesession('temp_session')
+        did = session.addData(self.data_name, mtype,
+            content = self.data_text)
+        did = self.session.addData(did, mtype,
+            producer = session)
+        self.assertTrue(self.session.hasData(did))
+        ## NEEDCARE: Data does not exists in source session or is private
+        self.assertRaises(SessionError, self.session.addData, 'foobar', mtype,
+            producer = session)
+        ## Check that non file types are rejected
+        mtype = self._faketype('String')
+        self.assertRaises(MobyleError, self.session.addData, None, mtype,
+            content = self.data_text)
+        ## Check invalid combinations for content & producer
+        mtype = self._faketype('Text')
+        self.assertRaises(MobyleError, self.session.addData, None, mtype)
+        self.assertRaises(MobyleError, self.session.addData, None, mtype,
+            content = None, producer = None)
+        self.assertRaises(MobyleError, self.session.addData, None, mtype,
+            content = self.data_text, producer = job)
+        ## Exceeding session size limit should fail
+        self.session.sessionLimit = 0
+        self.assertRaises(NoSpaceLeftError, self.session.addData, 'size', mtype,
+            content = self.data_text * 10)
+
+    def testremoveData(self):
+        mtype = self._faketype('Text')
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        self.assertTrue(self.session.hasData(did))
+        self.session.removeData(did)
+        self.assertFalse(self.session.hasData(did))
+        session_data = os.path.join(self.session.getDir(), did)
+        self.assertFalse(os.access(session_data, os.F_OK))
+        ## Cannot remove missing/private data
+        self.assertRaises(SessionError, self.session.removeData, did)
+        self.assertRaises(MobyleError, self.session.removeData,
+            self.session.FILENAME)
+
+    def testrenameData(self):
+        user_name = 'userfile'
+        mtype = self._faketype('Text')
+        ## Add and rename data
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        self.assertNotEqual(self.data_name, user_name)
+        self.session.renameData(did, user_name)
+        ## Cannot rename a missing data
+        self.assertRaises(SessionError, self.session.renameData, None,
+            user_name)
+
+    def testgetContentData(self):
+        mtype = self._faketype('Text')
+        lim = self.cfg.previewDataLimit()
+        ## Add and get small data content
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        (flag, data) = self.session.getContentData(did)
+        self.assertTrue(flag == 'FULL')
+        self.assertTrue(data == self.data_text)
+        ## Add/Get big data content
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text * (lim / len(self.data_text) + 1))
+        (flag, data) = self.session.getContentData(did)
+        self.assertTrue(flag == 'HEAD')
+        self.assertTrue(len(data) == self.cfg.previewDataLimit())
+        (flag, data) = self.session.getContentData(did, False)
+        self.assertTrue(flag == 'HEAD')
+        self.assertTrue(len(data) == self.cfg.previewDataLimit())
+        (flag, data) = self.session.getContentData(did, True)
+        self.assertTrue(flag == 'FULL')
+        self.assertTrue(len(data) > self.cfg.previewDataLimit())
+        ## Cannot get content for missing/private data
+        self.assertRaises(SessionError, self.session.getContentData, 'dummy')
+        self.assertRaises(MobyleError, self.session.getContentData,
+            self.session.FILENAME)
+
+    def testgetDataSize(self):
+        mtype = self._faketype('Text')
+        ## Add data and get its size
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        size = self.session.getDataSize(did)
+        self.assertEqual(size, len(self.data_text))
+        ## Size from missing data should fail
+        self.session.removeData(did)
+        self.assertRaises(SessionError, self.session.getDataSize, did)
+
+    def testhasData(self):
+        mtype = self._faketype('Text')
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        self.assertTrue(self.session.hasData(did))
+        self.session.removeData(did)
+        self.assertFalse(self.session.hasData(did))
+
+    def testgetAllData(self):
+        mtype = self._faketype('Text')
+        data = self.session.getAllData()
+        self.assertTrue(len(data) == 0)
+        self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        data = self.session.getAllData()
+        self.assertTrue(len(data) == 1)
+        self.session.addData(self.data_name, mtype,
+            content = self.data_text * 2)
+        data = self.session.getAllData()
+        self.assertTrue(len(data) == 2)
+
+    def testgetData(self):
+        mtype = self._faketype('Text')
+        ## Add and get data
+        did = self.session.addData(self.data_name, mtype,
+            content = self.data_text)
+        data = self.session.getData(did)
+        self.assertEqual(data['dataName'], did)
+        ## Cannot get missing data
+        self.session.removeData(did)
+        self.assertRaises(SessionError, self.session.getData, did)
+
+    ## Jobs methods ...
+
+    def testhasJob(self):
+        url = self._fakejob('A00000000000000')
+        self.assertFalse(self.session.hasJob(url))
+        self.session.addJob(url)
+        self.assertTrue(self.session.hasJob(url))
+        self.session.removeJob(url)
+        self.assertFalse(self.session.hasJob(url))
+
+    def testgetAllJobs(self):
+        job_list = self.session.getAllJobs()
+        self.assertTrue(len(job_list) == 0)
+        url = self._fakejob('A00000000000000')
+        self.session.addJob(url)
+        job_list = self.session.getAllJobs()
+        self.assertTrue(len(job_list) == 1)
+        url = self._fakejob('A00000000000001')
+        self.session.addJob(url)
+        job_list = self.session.getAllJobs()
+        self.assertTrue(len(job_list) == 2)
+        ## Check cleaned job
+        shutil.rmtree(Mobyle.JobState.url2path(url))
+        job_list = self.session.getAllJobs()
+        self.assertTrue(len(job_list) == 1)
+
+    def testgetJob(self):
+        url = self._fakejob('A00000000000000')
+        ## Non existant job should fail
+        self.assertRaises(SessionError, self.session.getJob, url)
+        ## Add and get job
+        self.session.addJob(url)
+        job = self.session.getJob(url)
+        self.assertTrue(job != None and job['jobID'] == url)
+        ## Check cleaned (= directory removed) job
+        shutil.rmtree(Mobyle.JobState.url2path(url))
+        job = self.session.getJob(url)
+        self.assertTrue(job == None)
+        self.assertFalse(self.session.hasJob(url))
+
+    def testrenameJob(self):
+        url = self._fakejob('A00000000000000')
+        name = 'new user job name'
+        ## Non existant job should fail
+        self.assertRaises(SessionError, self.session.renameJob, url, name)
+        ## Add and rename job
+        self.session.addJob(url)
+        job = self.session.getJob(url)
+        self.assertNotEqual(name, job['userName'])
+        self.session.renameJob(url, name)
+        job = self.session.getJob(url)
+        self.assertEqual(name, job['userName'])
+
+    def testaddJob(self):
+        url = self._fakejob('A00000000000000')
+        ## Add job
+        self.assertFalse(self.session.hasJob(url))
+        self.session.addJob(url)
+        self.assertTrue(self.session.hasJob(url))
+        ## Already added job should fail
+        self.assertRaises(SessionError, self.session.addJob, url)
+
+    def testremoveJob(self):
+        url = self._fakejob('A00000000000000')
+        ## Add and remove job
+        self.assertFalse(self.session.hasJob(url))
+        self.session.addJob(url)
+        self.assertTrue(self.session.hasJob(url))
+        self.session.removeJob(url)
+        self.assertFalse(self.session.hasJob(url))
+        ## Already removed job should fail
+        self.assertRaises(SessionError, self.session.removeJob, url)
+
+    def testjobExists(self):
+        url = self._fakejob('A00000000000000')
+        self.assertTrue(self.session.jobExists(url) == 1)
+        shutil.rmtree(Mobyle.JobState.url2path(url))
+        self.assertTrue(self.session.jobExists(url) == 0)
+
+
+    def testSetAndGetJobLabel(self):
+        #there is not labels yet in jobs
+        url = self._fakejob('A00000000000000')
+        self.session.addJob(url)
+        label_recieved = self.session.getJobLabels(url)
+        self.assertEqual( label_recieved , [] )
+        #add one label
+        label_send = ['label_1']
+        self.session.setJobLabels( url , label_send )
+        #get one label
+        label_recieved = self.session.getJobLabels(url)
+        self.assertEqual( label_recieved , label_send )
+        #add 2 labels
+        label_send = ['label_2', 'label_2_bis']
+        self.session.setJobLabels( url , label_send )
+        #get 2 labels
+        label_recieved = self.session.getJobLabels(url)
+        self.assertEqual( label_recieved , label_send )
+        #add label in job which have already labels
+        new_label = [ 'label_3', 'label_3_bis' ]
+        self.session.setJobLabels( url , new_label )
+        #get 2 new labels
+        label_recieved = self.session.getJobLabels( url )
+        label_recieved = self.session.getJobLabels( url )
+        self.assertEqual( label_recieved , new_label )
+        
+        
+    def testgetAllUniqueLabels(self):
+        url = self._fakejob('A00000000000000')
+        self.session.addJob(url)
+        label_1= [ 'label_1', 'label_1_bis' ]
+        label_2=  label_1 + [ 'label_2' ]
+        self.session.setJobLabels( url , label_1 )
+        self.session.setJobLabels( url , label_2 )
+        label_recieved = self.session.getJobLabels( url )
+        s = set( label_1 + label_2 )
+        unique = list( s )
+        unique.sort()
+        label_recieved.sort()
+        self.assertEqual( label_recieved , unique )
+        
+    def testSetAndGetJobDescription(self):
+        url = self._fakejob('A00000000000000')
+        self.session.addJob(url)
+        job_description = "a new beautiful description for this job"
+        self.session.setJobDescription( url , job_description)
+        jobDescription_recieved = self.session.getJobDescription( url )
+        self.assertEqual( jobDescription_recieved , job_description )
+        url = self._fakejob('A00000000000001')
+        self.session.addJob(url)
+        jobDescription_recieved = self.session.getJobDescription( url )
+        self.assertEqual( None , jobDescription_recieved )
+        
+    ## Workflow methods ...
+
+    ## Utilities ...
+
+    def _fakesession(self, key):
+        session_key = key
+        session_dir = os.path.join(self.cfg.user_sessions_path(), session_key)
+        session = Mobyle.Session.Session(session_dir, session_key, self.cfg)
+        os.makedirs(session_dir)
+        session_xml = os.path.join(session_dir, session.FILENAME)
+        Transaction.create(session_xml, False, True)
+        return session
+
+    def _faketype(self, mtype):
+        dtf = DataTypeFactory()
+        dtyp = dtf.newDataType(mtype)
+        return MobyleType(dtyp, None)
+
+    def _fakejob(self, key):
+        job_name = 'fake_job'
+        job_date = strftime("%x  %X", localtime())
+        job_url = os.path.join(self.cfg.results_url(), job_name, key)
+        job_dir = Mobyle.JobState.url2path(job_url)
+        job_xml = os.path.join(job_dir, 'index.xml')
+        os.makedirs(job_dir)
+        ## index.xml
+        root = etree.Element('jobState')
+        node = etree.Element('date')
+        node.text = job_date
+        root.append(node)
+        node = etree.Element('name')
+        node.text = 'dummy.xml'
+        root.append(node)
+        node = etree.Element('id')
+        node.text = job_url
+        root.append(node)
+        Mobyle.Utils.indent(root)
+        fh = open(job_xml, 'w')
+        fh.write(etree.tostring(root, xml_declaration=True, encoding='UTF-8'))
+        fh.close()
+        ## .admin
+        #cwd = os.getcwd()
+        #os.chdir(job_dir)
+        Admin.create( job_dir, None, None, None)
+        #os.chdir(cwd)
+        return job_url
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/Src/Mobyle/Test/test_SessionFactory.py b/Src/Mobyle/Test/test_SessionFactory.py
new file mode 100644
index 0000000..423a5ae
--- /dev/null
+++ b/Src/Mobyle/Test/test_SessionFactory.py
@@ -0,0 +1,131 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os
+import sys
+import shutil
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ[ 'MOBYLEHOME' ] = MOBYLEHOME
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+    
+import Mobyle.Test.MobyleTest 
+from Mobyle.SessionFactory import SessionFactory
+from Mobyle.MobyleError import SessionError , AuthenticationError
+from Mobyle.Net import Email , EmailAddress
+Email.send = lambda templateName , subject , msg : None
+
+class SessionFactoryTest(unittest.TestCase):
+    
+    
+    def setUp(self):
+        """ 
+        setting up the configuration for the test, including:
+          - test configuration that does not check dns or try to send confirmation emails
+        """
+        self.cfg = Mobyle.ConfigManager.Config()
+        self.cfg._authenticated_session = 'yes'
+
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        os.makedirs( self.cfg.test_dir )
+
+        self.sessionFactory = SessionFactory(self.cfg)
+        self.sessionFactory._SessionFactory__sessions = {}
+        
+    def tearDown( self ):
+        self.sessionFactory._SessionFactory__sessions = {}
+        shutil.rmtree( self.cfg.test_dir , ignore_errors=True )
+        
+    def testAnonymousSessionNormalLifeCycle(self):
+        ## Creation should fail if disabled
+        self.cfg._anonymous_session = 'no'
+        self.assertRaises( SessionError , self.sessionFactory.getAnonymousSession )
+        ## Creation should succeed if enabled
+        self.cfg._anonymous_session = 'yes'
+        s1 = self.sessionFactory.getAnonymousSession()
+        ## Fetch an existing session
+        sessionID = s1.getKey()
+        s2 = self.sessionFactory.getAnonymousSession( key = sessionID ) 
+        self.assertEqual( s1 , s2 )
+        ## Access should fail if missing
+        self.sessionFactory.removeSession( sessionID )
+        self.assertRaises( SessionError , self.sessionFactory.getAnonymousSession , key = sessionID )
+
+    def testGetInvalidAnonymousSession(self):
+        self.assertRaises( SessionError, self.sessionFactory.getAnonymousSession, "__invalid_key__")
+    
+    def testAuthenticatedSessionNormalLifeCycle(self):
+        email = Mobyle.Net.EmailAddress( 'toto at 123.com' )
+        password = 'tutu'
+        #test 
+        self.assertRaises( SessionError, self.sessionFactory.getAuthenticatedSession, email , password )
+        
+        #create 
+        s1 = self.sessionFactory.createAuthenticatedSession( email , password )
+        sessionID = s1.getKey()
+        
+        #creation with same email , passwd
+        self.assertRaises( AuthenticationError , self.sessionFactory.createAuthenticatedSession , email , password )
+    
+        s2 = self.sessionFactory.getAuthenticatedSession( email , password)
+        self.assertEqual( s1 , s2 )
+        self.sessionFactory.removeSession( sessionID )
+        self.assertRaises( AuthenticationError , self.sessionFactory.getAuthenticatedSession, email , password )
+        self.cfg._authenticated_session = 'no'
+        self.assertRaises( SessionError , self.sessionFactory.createAuthenticatedSession , email , password )
+        self.cfg._authenticated_session = 'yes'
+        
+    def testAuthenticatedSessionBadEmail(self):
+        self.cfg._authenticated_session = 'email'
+        To = 'toto at 123.com'
+        email1 = Mobyle.Net.EmailAddress( To )
+        email2 = Mobyle.Net.EmailAddress( To + '.uk' )
+        password = 'tutu'
+        sess = self.sessionFactory.createAuthenticatedSession( email1 , password )
+        self.assertRaises( AuthenticationError, self.sessionFactory.getAuthenticatedSession , email2 , password  )
+        self.sessionFactory.removeSession( sess.getKey() )
+        
+    def testAuthenticatedSessionBadPasswd(self):
+        email = Mobyle.Net.EmailAddress( 'toto at 123.com' )
+        password = 'tutu'
+        sess = self.sessionFactory.createAuthenticatedSession( email , password )
+        self.assertRaises( AuthenticationError , self.sessionFactory.getAuthenticatedSession , email , password + 'bad')
+        self.sessionFactory.removeSession( sess.getKey() )        
+ 
+    
+    def testRemoveSession(self):
+        email = Mobyle.Net.EmailAddress( 'toto at 123.com' )
+        password = 'tutu'
+        sess = self.sessionFactory.createAuthenticatedSession( email , password )
+        sessionDir = sess.getDir()
+        sessKey = sess.getKey()
+        self.sessionFactory.removeSession( sessKey )
+        self.assertFalse( os.path.exists( sessionDir ) )
+    
+    def testOpenIdAuthenticatedSession(self):
+        userEmailAddr = Mobyle.Net.EmailAddress( 'toto at 123.com' )
+        password = 'tutu'
+        self.assertRaises( AuthenticationError, self.sessionFactory.getOpenIdAuthenticatedSession, userEmailAddr )
+        self.cfg._authenticated_session = 'email'
+        sess = self.sessionFactory.createAuthenticatedSession( userEmailAddr , password )
+        ticket_id = sess.ticket_id
+        session2 = self.sessionFactory.getOpenIdAuthenticatedSession( userEmailAddr , ticket_id=ticket_id )
+        self.assertEqual( sess, session2 )
+        
+        #retirieve an existing session but not stored in factory
+        del( self.sessionFactory._SessionFactory__sessions[ sess.getKey() ] )
+        session3 = self.sessionFactory.getOpenIdAuthenticatedSession( userEmailAddr , ticket_id=ticket_id )
+        self.assertEqual( sess.getDir() , session3.getDir() )
+        
+        
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_StatusManager.py b/Src/Mobyle/Test/test_StatusManager.py
new file mode 100644
index 0000000..968d041
--- /dev/null
+++ b/Src/Mobyle/Test/test_StatusManager.py
@@ -0,0 +1,192 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os, sys, fcntl, signal
+import shutil
+from lxml import etree
+from time import sleep
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+from Mobyle.StatusManager import StatusManager
+from Mobyle.Status import Status
+
+DATADIR = os.path.dirname( __file__ )
+
+def handler(signum, frame):
+    pass
+
+class StatusManagerTest(unittest.TestCase):
+    """Tests the functionalities of Transaction"""
+
+    def setUp(self):
+        """
+        setting up the configuration for the test, including:
+        - test configuration that does not check dns or try to send confirmation emails
+        - test session email
+        """
+        self.cfg = Mobyle.ConfigManager.Config()
+        shutil.rmtree(self.cfg.test_dir, ignore_errors=True)
+        os.makedirs(self.cfg.test_dir)
+        self.jobKey = "Q12345678901234"
+        self.jobDir = os.path.join( self.cfg.test_dir , self.jobKey )
+        os.makedirs( self.jobDir )
+        self.filename = os.path.join( self.jobDir , StatusManager.file_name )
+
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors= True )
+
+    def testCreation(self):
+        #create( filename , status )
+        unknown = Status( code = -1 )
+        StatusManager.create( self.jobDir , unknown )
+        doc = etree.parse( self.filename )
+        root = doc.getroot()
+        self.assertEqual( root.tag , 'status' )
+        children = list(root)
+        self.assertEqual( len( children ) , 2 )
+        self.assertEqual( children[0].tag , 'value')
+        self.assertEqual( children[0].text , 'unknown' )
+        self.assertEqual( children[1].tag , 'message')
+        self.assertEqual( children[1].text , None )
+        os.unlink( self.filename )
+
+        building = Status( code = 0 )
+        StatusManager.create( self.jobDir, building )
+        doc = etree.parse( self.filename )
+        root = doc.getroot()
+        self.assertEqual( root.tag , 'status' )
+        children = list(root)
+        self.assertEqual( len( children ) , 2 )
+        self.assertEqual( children[0].tag , 'value')
+        self.assertEqual( children[0].text , 'building' )
+        self.assertEqual( children[1].tag , 'message')
+        self.assertEqual( children[1].text , None )
+        os.unlink( self.filename )
+
+        submitted = Status( code = 1, message= 'test message' )
+        StatusManager.create( self.jobDir, submitted )
+        doc = etree.parse( self.filename )
+        root = doc.getroot()
+        self.assertEqual( root.tag , 'status' )
+        children = list(root)
+        self.assertEqual( len( children ) , 2 )
+        self.assertEqual( children[0].tag , 'value')
+        self.assertEqual( children[0].text , 'submitted' )
+        self.assertEqual( children[1].tag , 'message')
+        self.assertEqual( children[1].text , 'test message' )
+        os.unlink( self.filename )
+
+        running = Status( string='running' )
+        StatusManager.create( self.jobDir, running )
+        doc = etree.parse( self.filename )
+        root = doc.getroot()
+        self.assertEqual( root.tag , 'status' )
+        children = list(root)
+        self.assertEqual( len( children ) , 2 )
+        self.assertEqual( children[0].tag , 'value')
+        self.assertEqual( children[0].text , 'running' )
+        self.assertEqual( children[1].tag , 'message')
+        self.assertEqual( children[1].text , None )
+        os.unlink( self.filename )
+
+
+    def testGetStatus( self ):
+        running = Status( string='running' )
+        StatusManager.create( self.jobDir, running )
+        sm = StatusManager()
+        recieved_status = sm.getStatus( self.jobDir )
+        self.assertEqual( recieved_status , running )
+        os.unlink( self.filename )
+
+        killed = Status( string='killed' , message= "your job has been canceled" )
+        StatusManager.create( self.jobDir, killed )
+        sm = StatusManager()
+        recieved_status = sm.getStatus( self.jobDir )
+        self.assertEqual( recieved_status , killed )
+        os.unlink( self.filename )
+
+
+    def testSetstatus( self):
+        StatusManager.create( self.jobDir, Status( string='submitted' ) )
+
+        pending = Status( string= 'pending' )
+        sm = StatusManager()
+        sm.setStatus( self.jobDir , pending )
+        recieved_status = sm.getStatus( self.jobDir )
+        self.assertEqual( recieved_status , pending )
+
+        finished = Status( string='finished' , message = 'your job finnished with an unusual status code, check youre results carefully')
+        sm.setStatus( self.jobDir , finished )
+        recieved_status = sm.getStatus( self.jobDir )
+        self.assertEqual( recieved_status , finished )
+
+        #an ended status cannot be changed anymore
+        running = Status( string= 'running')
+        sm.setStatus( self.jobDir , running )
+        recieved_status = sm.getStatus( self.jobDir )
+        self.assertNotEqual( recieved_status , running )
+        self.assertEqual( recieved_status , finished )
+        os.unlink( self.filename )
+
+    def testConcurency(self):
+        status = Status( string='submitted' )
+        StatusManager.create( self.jobDir, status )
+
+        ## sub-process start
+        childPid = os.fork()
+        if childPid: #father
+            sleep(1)
+            sm = StatusManager()
+            self.assertEqual( status , sm.getStatus( self.jobDir ) )
+            self.assertRaises( IOError , sm.setStatus , self.jobDir, status )
+            os.kill( childPid , signal.SIGALRM )
+            os.wait()
+
+        else: #child
+            signal.signal(signal.SIGALRM, handler)
+            File = open( self.filename , 'r' )
+            fcntl.lockf( File , fcntl.LOCK_SH | fcntl.LOCK_NB )
+            signal.pause()
+            fcntl.lockf( File , fcntl.LOCK_UN  )
+            File.close()
+            os._exit(0)
+        ## sub-process end
+
+        ## sub-process start
+        childPid = os.fork()
+        if childPid: #father
+            sleep(1)
+            sm = StatusManager()
+            recieved_status = sm.getStatus( self.jobDir )
+            self.assertEqual( recieved_status , Status( string= "unknown" )  )
+            self.assertRaises( IOError , sm.setStatus , self.jobDir, status )
+            os.kill( childPid , signal.SIGALRM )
+            os.wait()
+
+        else: #child
+            signal.signal(signal.SIGALRM, handler)
+            File = open( self.filename , 'r+' )
+            fcntl.lockf( File , fcntl.LOCK_EX | fcntl.LOCK_NB )
+            signal.pause()
+            fcntl.lockf( File , fcntl.LOCK_UN  )
+            File.close()
+            os._exit(0)
+        ## sub-process end
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_Transaction.py b/Src/Mobyle/Test/test_Transaction.py
new file mode 100644
index 0000000..30a6b71
--- /dev/null
+++ b/Src/Mobyle/Test/test_Transaction.py
@@ -0,0 +1,610 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os
+import sys
+import shutil
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+
+from Mobyle.Transaction import Transaction
+from Mobyle.MobyleError import MobyleError, SessionError
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.Service import MobyleType
+from Mobyle.Session import Session
+#import Mobyle.Utils
+
+DATADIR = os.path.dirname( __file__ )
+
+class TransactionTest(unittest.TestCase):
+    """Tests the functionalities of Transaction"""
+
+    def setUp(self):
+        """
+        setting up the configuration for the test, including:
+        - test configuration that does not check dns or try to send confirmation emails
+        - test session email
+        """
+        self.cfg = Mobyle.ConfigManager.Config()
+        shutil.rmtree(self.cfg.test_dir, ignore_errors=True)
+        os.makedirs(self.cfg.test_dir)
+
+        self.sessionKey = "Q12345678901234"
+        self.sessionDir = os.path.join( self.cfg.test_dir , self.sessionKey )
+        os.makedirs( self.sessionDir )
+
+        self.email  = 'dummy at domain.fr'
+        self.passwd = 'dummypass'
+        self.actkey = 'dummykey'
+
+        self.xmlSourcePath = os.path.join( DATADIR , 'session_test.xml')
+        self.sessionPath = os.path.join( self.sessionDir , Session.FILENAME )
+        self.dataID = [ "f835ddf28cb6aa776a41d7c71ec9cb5a.out" , "16bd6f89e732eb7cb0bbc13c65d202d3.aln" ]
+
+        dtf = DataTypeFactory()
+        dataType1   = dtf.newDataType( 'Sequence' )
+        mobyleType1 = MobyleType( dataType1 , bioTypes = [ 'Protein' , 'Nucleic' ] , dataFormat =  'SWISSPROT' )
+        dataType2   = dtf.newDataType( 'Alignment' )
+        mobyleType2 = MobyleType( dataType2 , dataFormat = 'PHYLIPI' )
+        self.datas  = [ {'dataName'   : 'f835ddf28cb6aa776a41d7c71ec9cb5a.out' ,
+                        'userName'   : 'golden.out' ,
+                        'size'       :   4410 ,
+                        'Type'       :  mobyleType1 ,
+                        'dataBegin'    :  'ID   104K_THEPA              Reviewed;         924' ,
+                        'inputModes' : [ 'result' ] ,
+                        'producedBy' : [ 'file://tmp/mobyle/results/dnadist/G25406684175968' ] ,
+                        'usedBy'     : []
+                        } ,
+                        {'dataName'  : '16bd6f89e732eb7cb0bbc13c65d202d3.aln' ,
+                         'userName'   : 'clustal.aln' ,
+                         'size'       :    90044 ,
+                         'Type'       :  mobyleType2 ,
+                         'dataBegin'    :  '    47  1618\nF_75      ---------------------------' ,
+                         'inputModes' : [ 'upload' , 'paste' ] ,
+                         'producedBy' : [] ,
+                         'usedBy'     : [ 'file://tmp/mobyle/results/dnadist/G25406684175968' ,
+                                         'file://tmp/mobyle/results/dnadist/X25423048243999' ]
+                         }
+                        ]
+
+        self.jobID = [ "file://tmp/mobyle/results/dnadist/X25423048243999" ,
+                       "file://tmp/mobyle/results/dnadist/G25406684175968" ]
+        self.jobs  = [ {'jobID'       : 'file://tmp/mobyle/results/dnadist/X25423048243999' ,
+                      'userName'    : 'mon programme a moi' ,
+                      'programName' : 'prog 1' ,
+                      'status'      :  Mobyle.Status.Status( code = 6 ) , #killed
+                      'date'        :  (2008, 11, 20, 12, 0, 0, 3, 325, -1) ,
+                      'dataProduced': [] ,
+                      'dataUsed'    : ['16bd6f89e732eb7cb0bbc13c65d202d3.aln'] ,
+                      } ,
+                      {'jobID'       : 'file://tmp/mobyle/results/dnadist/G25406684175968' ,
+                       'userName'    : 'file://tmp/mobyle/results/dnadist/G25406684175968' ,
+                       'programName' : 'dnadist' ,
+                       'status'      :  Mobyle.Status.Status( code = 3 ) , #running
+                       'date'        :  (2008, 11, 19, 10, 50, 33, 2, 324, -1) ,
+                       'dataProduced': ['f835ddf28cb6aa776a41d7c71ec9cb5a.out'] ,
+                       'dataUsed'    : ['16bd6f89e732eb7cb0bbc13c65d202d3.aln'] ,
+                       }
+                      ]
+
+    def tearDown(self):
+        shutil.rmtree( self.cfg.test_dir , ignore_errors= True )
+
+    def testTransaction(self):
+        Transaction.create( self.sessionPath , False , False )
+        # Check locks types
+        transaction = Transaction( self.sessionPath, Transaction.WRITE )
+        transaction.rollback()
+        transaction = Transaction( self.sessionPath, Transaction.READ )
+        transaction.rollback()
+        self.assertRaises(MobyleError, Transaction, self.sessionPath, None )
+        # Invalid file
+        self.assertRaises(SessionError, Transaction, str(None), Transaction.READ)
+
+    def testCreation(self):
+        #create( filename , authenticated , activated , activatingKey = None , email = None , passwd = None)
+
+        #test anonymous session without email
+        Transaction.create( self.sessionPath , False , False )
+        os.unlink( self.sessionPath )
+
+        #test anonymous session with passwd
+        self.assertRaises( SessionError , Transaction.create , self.sessionPath , False , False , passwd = self.passwd )
+
+        #test anonymous session with email
+        Transaction.create( self.sessionPath , False , False , userEmail = self.email )
+        os.unlink( self.sessionPath )
+
+        #test authenticated session without passwd -> SessionError
+        self.assertRaises( SessionError , Transaction.create , self.sessionPath , True , False , userEmail = self.email )
+
+        #test authenticated session without email -> SessionError
+        self.assertRaises( SessionError , Transaction.create , self.sessionPath , True , False , passwd = self.passwd )
+
+        #test authenticated session with email and passwd
+        Transaction.create( self.sessionPath , True , False , userEmail = self.email , passwd = self.passwd )
+        os.unlink( self.sessionPath )
+
+        #test authenticated session with activatingKey
+        Transaction.create( self.sessionPath , True , False , activatingKey = self.actkey , userEmail = self.email , passwd = self.passwd )
+        os.unlink( self.sessionPath )
+
+        #test that session creation fails if already exists
+        Transaction.create( self.sessionPath , False , False )
+        self.assertRaises( SessionError , Transaction.create , self.sessionPath , False , False )
+        os.unlink( self.sessionPath )
+
+    def testCommit(self):
+        Transaction.create( self.sessionPath , False , False )
+
+        # Ensure that transaction with modification update the file
+        bef = os.stat( self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction._setModified( True )
+        transaction.commit()
+        aft = os.stat( self.sessionPath )
+        self.assertFalse( bef.st_ino == aft.st_ino and bef.st_mtime == aft.st_mtime )
+
+        # Ensure that transaction without modification does not update the file
+        bef = os.stat( self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction._setModified( False )
+        transaction.commit()
+        aft = os.stat( self.sessionPath )
+        self.assertTrue( bef.st_ino == aft.st_ino and bef.st_mtime == aft.st_mtime )
+
+        # By default, an empty transaction should have no effect on the file
+        bef = os.stat( self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.commit()
+        aft = os.stat( self.sessionPath )
+        self.assertTrue( bef.st_ino == aft.st_ino and bef.st_mtime == aft.st_mtime )
+
+    def testRollback(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.removeData( self.dataID[0] )
+        self. assertFalse( transaction.hasData(self.dataID[0] ) )
+        transaction.rollback()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        self.assertTrue( transaction.hasData( self.dataID[0] ) )
+        transaction.commit()
+
+    def testConcurency(self):
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        ## sub-process start
+        childPid = os.fork()
+        if childPid: #father
+            pid , status = os.wait()
+            self.assertFalse( status )
+        else: #child
+            cmd = [ sys.executable, 'openTransaction.py' , self.sessionPath , str( Transaction.READ ) ]
+            os.execv( cmd[0] , cmd )
+        ## sub-process end
+        transaction.commit()
+
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        ## sub-process start
+        childPid = os.fork()
+        if childPid: #father
+            pid , status = os.wait()
+            self.assertTrue( status )
+        else: #child
+            cmd = [ sys.executable, 'openTransaction.py' , self.sessionPath , str( Transaction.READ ) ]
+            os.execv( cmd[0] , cmd )
+        ## sub-process end
+        transaction.commit()
+
+    def testgetID(self):
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedID = transaction.getID()
+        transaction.commit()
+        self.assertEqual( receivedID , self.sessionKey )
+
+    def testEmail(self):
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setEmail( self.email )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedEmail = transaction.getEmail()
+        transaction.commit()
+        self.assertTrue( receivedEmail == self.email )
+
+    def testisActivated(self):
+	# Test unactivated session
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        active = transaction.isActivated()
+        transaction.commit()
+        self.assertFalse( active )
+        os.unlink( self.sessionPath )
+
+	# Test already activated session
+        Transaction.create( self.sessionPath , False , True )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        active = transaction.isActivated()
+        transaction.commit()
+        self.assertTrue( active )
+        os.unlink( self.sessionPath )
+
+	# Test session after activation
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.activate()
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        active = transaction.isActivated()
+        transaction.commit()
+        self.assertTrue( active )
+        os.unlink( self.sessionPath )
+
+     # Test session after inactivation
+        Transaction.create( self.sessionPath , False , True )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        active = transaction.isActivated()
+        self.assertTrue( active )
+        transaction.inactivate()
+        transaction.commit()
+        active = transaction.isActivated()
+        self.assertFalse( active )
+        os.unlink( self.sessionPath )
+
+
+    def testActivatingKey(self):
+        Transaction.create( self.sessionPath , False , True , activatingKey = self.actkey , userEmail = None)
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        actKey = transaction.getActivatingKey()
+        transaction.commit()
+        self.assertTrue( actKey == self.actkey )
+
+
+    def testAuthenticated(self):
+        Transaction.create( self.sessionPath , True , True , activatingKey = self.actkey , userEmail = self.email , passwd = self.passwd )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        auth = transaction.isAuthenticated()
+        transaction.commit()
+        self.assertTrue( auth )
+
+
+    def testPasswd(self):
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        sendPasswd = "monBeauMotDePasse"
+        transaction.setPasswd( sendPasswd )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedPasswd =  transaction.getPasswd()
+        transaction.commit()
+        self.assertTrue( sendPasswd == receivedPasswd )
+
+
+    def testCaptchaSolution( self ):
+        sendSoluce = 'solution'
+        Transaction.create( self.sessionPath , False , False )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setCaptchaSolution( sendSoluce )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedSoluce = transaction.getCaptchaSolution()
+        transaction.commit()
+        self.assertTrue( sendSoluce == receivedSoluce )
+        transaction2 = Transaction( self.sessionPath , Transaction.READ )
+        receivedSoluce = transaction2.getCaptchaSolution()
+        self.assertTrue( sendSoluce == receivedSoluce )
+        transaction2.commit()
+
+    ################################
+    #
+    #  operations on Data
+    #
+    #################################
+
+    def testHasData(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath)
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        self.assertTrue( transaction.hasData( self.dataID[1] ) )
+        transaction.commit()
+
+    def testRenameData( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath)
+        newUserName = '__new_user_name_for_the_data__'
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.renameData( self.dataID[0] , newUserName )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        data = transaction.getData( self.dataID[0] )
+        transaction.commit()
+        self.assertTrue( newUserName == data[ 'userName' ] )
+
+    def testGetAllData(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath)
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedData = transaction.getAllData()
+        transaction.commit()
+        self.assertTrue( len( receivedData ) == 2 )
+        for data in receivedData:
+            self.assertTrue( data in self.datas )
+
+    def testRemoveData( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.removeData( self.dataID[0] )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        self.assertFalse( transaction.hasData( self.dataID[0] ) )
+        transaction.commit()
+
+    def testLinkJobInput2Data( self ):
+        Transaction.create( self.sessionPath , False , True )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        sendData = self.datas[0]
+        transaction.createData( sendData[ 'dataName' ] ,
+                                sendData[ 'userName' ] ,
+                                sendData[ 'size' ] ,
+                                sendData[ 'Type' ] ,
+                                sendData[ 'dataBegin' ] ,
+                                sendData[ 'inputModes' ] ,
+                                producedBy = [] ,
+                                usedBy = []
+                                )
+        sendJob = self.jobs[ 0 ]
+        transaction.createJob( sendJob['jobID'] ,
+                                sendJob['userName'] ,
+                                sendJob['programName'] ,
+                                sendJob['status'] ,
+                                sendJob['date'] ,
+                                [] ,
+                                []
+                                )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.linkJobInput2Data( [ sendData[ 'dataName' ] ] , [ sendJob['jobID'] ])
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        data = transaction.getData( sendData[ 'dataName' ] )
+        job = transaction.getJob( sendJob['jobID'])
+        transaction.commit()
+        self.assertEqual( data[ 'usedBy' ][0] , sendJob[ 'jobID' ] )
+        self.assertEqual( job[ 'dataUsed' ][0] , sendData[ 'dataName' ] )
+
+    def testLinkJobOutput2Data( self ):
+        Transaction.create( self.sessionPath , False , True )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        sendData = self.datas[0]
+        transaction.createData( sendData[ 'dataName' ] ,
+                                sendData[ 'userName' ] ,
+                                sendData[ 'size' ] ,
+                                sendData[ 'Type' ] ,
+                                sendData[ 'dataBegin' ] ,
+                                sendData[ 'inputModes' ] ,
+                                producedBy = [] ,
+                                usedBy = []
+                                )
+        sendJob = self.jobs[ 0 ]
+        transaction.createJob( sendJob['jobID'] ,
+                                sendJob['userName'] ,
+                                sendJob['programName'] ,
+                                sendJob['status'] ,
+                                sendJob['date'] ,
+                                [] ,
+                                []
+                                )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.linkJobOutput2Data( [ sendData[ 'dataName' ] ] , [ sendJob['jobID'] ])
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        data = transaction.getData( sendData[ 'dataName' ] )
+        job = transaction.getJob( sendJob['jobID'])
+        transaction.commit()
+        self.assertEqual( data[ 'producedBy' ][0] , sendJob[ 'jobID' ] )
+        self.assertEqual( job[ 'dataProduced' ][0] , sendData[ 'dataName' ] )
+
+
+    def testAddInputModes(self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.addInputModes( self.dataID[0] , [ 'paste' , 'db' ] )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        data = transaction.getData( self.dataID[0] )
+        transaction.commit()
+        result = ['paste' , 'db' , 'result']
+        result.sort()
+        data[ 'inputModes' ].sort()
+        self.assertEqual( data[ 'inputModes' ] , result )
+        #an inputMode must not be twice in the xml
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.addInputModes( self.dataID[0] , [ 'paste' ] )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        data = transaction.getData( self.dataID[0] )
+        transaction.commit()
+        data[ 'inputModes' ].sort()
+        self.assertEqual( data[ 'inputModes' ] , result )
+
+    def testCreateAndGetData( self ):
+        Transaction.create( self.sessionPath , True , True , activatingKey = 'uneJolieCle' , userEmail = self.email , passwd = self.passwd )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        sendData = self.datas[0]
+        transaction.createData( sendData[ 'dataName' ] ,
+                                sendData[ 'userName' ] ,
+                                sendData[ 'size' ] ,
+                                sendData[ 'Type' ] ,
+                                sendData[ 'dataBegin' ] ,
+                                sendData[ 'inputModes' ] ,
+                                producedBy = sendData[ 'producedBy' ] ,
+                                usedBy = sendData[ 'usedBy' ]
+                                )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedData = transaction.getData( sendData[ 'dataName' ] )
+        transaction.commit()
+        self.assertTrue( receivedData == sendData )
+
+    ####################
+    #
+    #   jobs methods
+    #
+    ####################
+
+    def testHasJob( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        jobid = "file://tmp/mobyle/results/dnadist/G25406684175968"
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        self.assertTrue( transaction.hasJob( jobid ) )
+        self.assertFalse( transaction.hasJob( jobid.replace('G','Z') ) )
+        transaction.commit()
+
+    def testGetAllJobs( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath)
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedJobs = transaction.getAllJobs()
+        transaction.commit()
+        self.assertTrue( len( receivedJobs ) == 2 )
+
+        for job in receivedJobs:
+            self.assertTrue( job in self.jobs )
+
+    def testCreateAndGetJob( self ):
+        Transaction.create( self.sessionPath , False , True )
+        sendJob = self.jobs[ 1 ]
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.createJob( sendJob['jobID'] ,
+                                sendJob['userName'] ,
+                                sendJob['programName'] ,
+                                sendJob['status'] ,
+                                sendJob['date'] ,
+                                sendJob['dataUsed'] ,
+                                sendJob['dataProduced']
+                                )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        receivedJob = transaction.getJob( sendJob[ 'jobID' ] )
+        transaction.commit()
+        self.assertEqual( sendJob , receivedJob )
+
+
+    def testRemoveJob( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.removeJob( self.jobID[1] )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        self.assertFalse( transaction.hasJob( self.jobID[1] ) )
+        transaction.commit()
+
+    def testRenameJob( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        newUserName = "mon beau job"
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.renameJob( self.jobID[1] , newUserName )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        job = transaction.getJob( self.jobID[1] )
+        transaction.commit()
+        self.assertEqual( newUserName , job[ 'userName' ] )
+
+    def testUpdateJobStatus( self ):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        sendStatus = Mobyle.Status.Status( code = 7 ) #hold
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.updateJobStatus( self.jobID[0] , sendStatus )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        job = transaction.getJob( self.jobID[0] )
+        transaction.commit()
+        self.assertEqual( sendStatus , job[ 'status' ] )
+        
+    def testSetAndGetJobLabel(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        #there is not labels yet in jobs
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        label_recieved = transaction.getJobLabels(self.jobID[0])
+        transaction.commit()
+        self.assertEqual( label_recieved , [] )
+        #add one label
+        label_send = ['label_1']
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setJobLabels( self.jobID[0] , label_send )
+        transaction.commit()
+        #get one label
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        label_recieved = transaction.getJobLabels(self.jobID[0])
+        transaction.commit()
+        self.assertEqual( label_recieved , label_send )
+        #add 2 labels
+        label_send = ['label_2', 'label_2_bis']
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setJobLabels( self.jobID[1] , label_send )
+        transaction.commit()
+        #get 2 labels
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        label_recieved = transaction.getJobLabels(self.jobID[1])
+        transaction.commit()
+        self.assertEqual( label_recieved , label_send )
+        #add label in job which have already labels
+        new_label = [ 'label_3', 'label_3_bis' ]
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setJobLabels( self.jobID[1] , new_label )
+        transaction.commit()
+        #get 2 new labels
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        label_recieved = transaction.getJobLabels( self.jobID[1] )
+        label_recieved = transaction.getJobLabels( self.jobID[1] )
+        transaction.commit()
+        self.assertEqual( label_recieved , new_label )
+        
+        
+    def testgetAllUniqueLabels(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        label_1= [ 'label_1', 'label_1_bis' ]
+        label_2=  label_1 + [ 'label_2' ]
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setJobLabels( self.jobID[0] , label_1 )
+        transaction.setJobLabels( self.jobID[1] , label_2 )
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        label_recieved = transaction.getJobLabels( self.jobID[1] )
+        transaction.commit()
+        s = set( label_1 + label_2 )
+        unique = list( s )
+        unique.sort()
+        label_recieved.sort()
+        self.assertEqual( label_recieved , unique )
+        
+    def testSetAndGetJobDescription(self):
+        shutil.copy( self.xmlSourcePath , self.sessionPath )
+        job_description = "a new beautiful description for this job"
+        transaction = Transaction( self.sessionPath , Transaction.WRITE )
+        transaction.setJobDescription( self.jobID[0] , job_description)
+        transaction.commit()
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        jobDescription_recieved = transaction.getJobDescription( self.jobID[0] )
+        transaction.commit()
+        self.assertEqual( jobDescription_recieved , job_description )
+        transaction = Transaction( self.sessionPath , Transaction.READ )
+        jobDescription_recieved = transaction.getJobDescription( self.jobID[1] )
+        transaction.commit()
+        self.assertEqual( None , jobDescription_recieved )
+        
+        
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_Workflow.py b/Src/Mobyle/Test/test_Workflow.py
new file mode 100644
index 0000000..d1949b8
--- /dev/null
+++ b/Src/Mobyle/Test/test_Workflow.py
@@ -0,0 +1,118 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import unittest2 as unittest
+import os, sys
+import tempfile
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+
+from Mobyle.Workflow import Workflow, Task, Link, Parameter, Type, Datatype, Biotype, Parser
+
+DATADIR = os.path.dirname( __file__ )
+
+class WorkflowTest(unittest.TestCase):
+    """Tests the functionalities of Workflow"""
+    
+    def setUp(self):
+        self.p = Parser()
+    
+    def tearDown(self):
+        pass
+    
+    def testSimpleCreate(self):
+        wf = Workflow()
+        wffile = tempfile.NamedTemporaryFile( prefix='mobyle_workflow' )
+        path = wffile.name
+        wffile.write( self.p.tostring(wf))
+        wffile.flush()
+        
+        wf = self.p.parse(path)
+        self.assertTrue(isinstance(wf, Workflow))
+        wffile.close()
+        
+    def testCreation(self):
+        wf = Workflow()
+        wf.name = 'toto'
+        wf.version = '1.1'
+        wf.title = 'test workflow'
+        wf.description = 'this workflow is a test one'
+        t1 = Task()
+        t1.id = 1
+        t1.service = 'clustalw-multialign'
+        t1.suspend = False
+        t1.description = "Run a clustalw"
+        t2 = Task()
+        t2.id = 2
+        t2.service = 'protdist'
+        t2.suspend = True
+        t2.description = "Run a protdist"
+    
+        input = Parameter()
+        input.id = '1'
+        input.name = 'sequences'
+        input.prompt = 'Input sequences'
+        input.type = Type()
+        input.type.datatype = Datatype()
+        input.type.datatype.class_name = "Sequence"
+        input.type.biotypes = [Biotype("Protein")]
+    
+        output_format = Parameter()
+        output_format.id = '2'
+        output_format.name = 'alignment_format'
+        output_format.prompt = 'Alignment format'
+        output_format.type = Type()
+        output_format.type.datatype = Datatype()
+        output_format.type.datatype.class_name = "String"
+        
+        output = Parameter()
+        output.id = '3'
+        output.isout = True
+        output.name = 'matrix'
+        output.prompt = 'Distance matrix'
+        output.type = Type()
+        output.type.datatype = Datatype()
+        output.type.datatype.class_name = "Matrix"
+        output.type.datatype.superclass_name = "AbstractText"
+        output.type.biotypes = [Biotype("Protein")]
+        
+        l1 = Link()
+        l1.to_task = t1.id
+        l1.from_parameter = "1"
+        l1.to_parameter = "infile"
+    
+        l2 = Link()
+        l2.to_task = t1.id
+        l2.from_parameter = "2"
+        l2.to_parameter = "outputformat"
+        
+        l3 = Link()
+        l3.from_task = t1.id
+        l3.to_task = t2.id
+        l3.from_parameter = "aligfile"
+        l3.to_parameter = "infile"
+        
+        l4 = Link()
+        l4.from_task = t2.id
+        l4.from_parameter = "outfile"
+        l4.to_parameter = "3"
+        
+        wf.tasks = [t1, t2]
+        wf.links = [l1, l2, l3, l4]
+        wf.parameters = [input, output_format, output]        
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Src/Mobyle/Test/test_mobdeploy.py b/Src/Mobyle/Test/test_mobdeploy.py
new file mode 100644
index 0000000..065c7db
--- /dev/null
+++ b/Src/Mobyle/Test/test_mobdeploy.py
@@ -0,0 +1,76 @@
+import unittest2 as unittest
+import os
+import sys
+import shutil
+
+MOBYLEHOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ) , "../../../" ) )
+os.environ['MOBYLEHOME'] = MOBYLEHOME
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import Mobyle.Test.MobyleTest
+
+tools_path = os.path.join( MOBYLEHOME , 'Tools' )
+sys.path.append( tools_path )
+mobdeploy_path = os.path.join( tools_path , 'mobdeploy' )
+if not os.path.exists( mobdeploy_path + '.py' ):
+    os.symlink( mobdeploy_path , mobdeploy_path + '.py' )
+try:
+    from Tools import mobdeploy
+finally:
+    os.unlink( mobdeploy_path + ".py" )
+
+
+fake_mobyle_path = '/tmp/test_Mobyle'
+DATADIR = os.path.dirname( __file__ )
+
+class ServicesDeployerTest( unittest.TestCase ):
+    
+    def setUp(self):
+        """ 
+        setting up the configuration for the test, including:
+        - test configuration that does not check dns or try to send confirmation emails
+        - test session email
+        """
+        pass
+    
+    def tearDown(self):
+        shutil.rmtree( fake_mobyle_path , ignore_errors= True )
+        
+        
+    def testMakeTmp(self):
+        pass
+    def testSwitchDeployement(self):
+        pass
+    def testDoCommand(self):
+        pass
+    def testGet_registry_from_args(self):
+        pass
+    def testGet_registry_from_config(self):
+        pass
+    def testRecoverFromExisting(self):
+        pass
+    def testDo_cmd(self):
+        pass
+    def is_exported(self):
+        pass
+    def testGetOldXmlAsIs(self):
+        pass
+    def testGetXml(self):
+        pass
+    def testLoad_xsl_pipe(self):
+        pass
+    def testTemplate(self):
+        pass
+    def process_service(self):
+        pass
+    def testValidateOrDie(self):
+        pass
+    def tesMakeIndexes(self):
+        pass
+    
+    
+    
\ No newline at end of file
diff --git a/Src/Mobyle/Transaction.py b/Src/Mobyle/Transaction.py
new file mode 100644
index 0000000..8f48265
--- /dev/null
+++ b/Src/Mobyle/Transaction.py
@@ -0,0 +1,1256 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+import os 
+import sys
+import fcntl
+import pickle
+from lxml import etree
+from time import strptime , strftime , time , sleep
+from logging import getLogger
+
+from Mobyle.Parser import parseType
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.MobyleError import MobyleError , SessionError , ParserError
+#from Mobyle.Utils import indent, parse_xml_file
+from Mobyle.Utils import indent
+from Mobyle.Status import Status
+
+class Transaction( object ):
+    """
+    This class defines a session, that stores all the information
+    about a user that should be persistent on the server
+    @author: Bertrand Neron
+    @organization: Institut Pasteur
+    @contact:mobyle at pasteur.fr
+    """
+    __ref = {}
+    
+    WRITE  = fcntl.LOCK_EX
+    READ   = fcntl.LOCK_SH
+    
+    def __new__( cls , fileName , lockType ):
+        if not cls.__ref.has_key( fileName ) :
+            self = super( Transaction , cls ).__new__( cls )
+            self._log = getLogger( 'Mobyle.Session.Transaction' )
+            fileName = os.path.normpath( fileName )
+            try:
+                if lockType == self.READ :
+                    self.__File = open( fileName , 'r' )
+                elif lockType == self.WRITE :
+                    self.__File = open( fileName , 'r+' )
+                else:
+                    raise MobyleError , 'invalid lockType : ' +  str( lockType )
+            except IOError , err:
+                msg = "can't open session %s : %s" % ( os.path.basename( os.path.dirname( fileName ) ) , err )
+                self._log.critical( msg )
+                raise SessionError , msg
+            
+            self.__lockType = lockType
+            
+            self._lock() #try to acquire a lock 
+            #If I do not got a lock a IOError is raised
+            try:
+                parser = etree.XMLParser( no_network = False )
+                #self._doc = parse_xml_file(self.__File,parser)
+                self._doc = etree.parse( self.__File , parser )
+                self._root = self._doc.getroot()
+            except Exception , err:
+                import shutil
+                shutil.copy2( self.__File.name , "%s.copy.%d" %( self.__File.name , int( time() ) ))
+                msg = " an error occured during the session %s parsing : %s " % ( os.path.basename( os.path.dirname( fileName ) ) , err )
+                self._log.error( msg , exc_info = True )      
+                raise SessionError , err
+            
+            self._modified = False
+            cls.__ref[ self.__File.name ] = True
+            return self
+        else:
+            raise SessionError , "try to open 2 transactions on the same file at the same time"
+        
+    
+    
+    @staticmethod    
+    def create( fileName , authenticated , activated , activatingKey = None , userEmail = None , passwd = None):
+        """
+        create a minimal xml session 
+        @param fileName: the absolute path of the xml session file
+        @type fileName: string
+        @param authenticated: True if the session is authenticated , false otherwise
+        @type authenticated: boolean
+        @param activated: True if the session is activated, False otherwise.
+        @type activated: boolean
+        @param activatingKey: the key send to the user to activate this session
+        @type activatingKey: boolean
+        @param userEmail: the userEmail of the user
+        @type userEmail: string
+        """
+        id = os.path.basename( os.path.dirname( fileName ))
+        if authenticated and not passwd:
+            msg = "cannot create authenticated session %s for %s without passwd" % ( id , userEmail )
+            raise SessionError, msg
+        
+        if not authenticated and passwd:
+            msg = "cannot create anonymous session %s for %s with passwd" % ( id , userEmail )
+            raise SessionError, msg
+        
+        if authenticated and not userEmail:
+            msg = "cannot create authenticated session %s without userEmail" % ( id )
+            raise SessionError, msg
+        root = etree.Element( "userSpace" , id = id )
+        #doc = etree.ElementTree( root )
+        
+        authenticatedNode = etree.Element( "authenticated" )
+        if authenticated:
+            authenticatedNode.text = 'true'
+            ticketNode = etree.Element( "ticket" )
+            ticket_idNode = etree.Element( "id" )
+            exp_dateNode = etree.Element( "exp_date" )
+            ticketNode.append( ticket_idNode )
+            ticketNode.append( exp_dateNode )
+            root.append( ticketNode )    
+        else:
+            authenticatedNode.text = 'false' 
+        root.append( authenticatedNode )    
+        
+        activatedNode = etree.Element( "activated" )
+        if activated:
+            activatedNode.text = 'true'
+        else:
+            activatedNode.text = 'false' 
+        root.append( activatedNode ) 
+         
+        if userEmail:
+            emailNode = etree.Element( "email" )
+            emailNode.text = str( userEmail )
+            root.append( emailNode ) 
+        if passwd:
+            passwdNode = etree.Element( "passwd" )
+            passwdNode.text = passwd
+            root.append( passwdNode )             
+        if activatingKey:
+            activatingKeyNode = etree.Element( "activatingKey" )
+            activatingKeyNode.text =  activatingKey  
+            root.append( activatingKeyNode ) 
+
+        if os.path.exists( fileName ):
+            msg = "cannot create session %s, file already exists" % fileName
+            raise SessionError, msg
+
+        xmlFile = open( fileName , 'w' )
+        xmlFile.write( etree.tostring( root , xml_declaration=True , encoding='UTF-8', pretty_print= True )) 
+        xmlFile.close()        
+
+
+    def _lock(self):
+        """
+        try to acquire a lock of self.__lockType on self.__File
+        @raise IOError: when it could not acquire a lock
+        """
+        IGotALock = False
+        ID = os.path.basename( os.path.dirname( self.__File.name ))
+        self._log.debug( "%f : %s : _lock Type= %s ( call by= %s )"  %( time() ,
+                                                                        ID,
+                                                                        ( 'UNKNOWN LOCK', 'READ' , 'WRITE' )[ self.__lockType ] ,
+                                                                        os.path.basename( sys.argv[0] ) ,
+                                                                        ))
+        for attempt in range( 4 ):
+            try:             
+                fcntl.lockf( self.__File , self.__lockType | fcntl.LOCK_NB )
+                IGotALock  = True
+                self._log.debug( "%f : %s : _lock IGotALock = True" %(time() , ID ))
+                break
+            except IOError , err:
+                self._log.debug( "%f : %s : _lock IGotALock = False" %(time() , ID))
+                sleep( 0.2 )
+
+        if not IGotALock :
+            self._log.error( "%s : %s" %( ID , err ) )
+            self._log.debug( "%f : %s : _lock Type= %s ( call by= %s )"  %( time() ,
+                                                                            ID ,
+                                                                            ( 'UNKNOWN LOCK', 'READ' , 'WRITE' )[ self.__lockType ] ,
+                                                                            os.path.basename( sys.argv[0] ) 
+                                                                            ))
+            self.__File.close()
+            self.__File = None
+            self.__lockType = None
+               
+            raise IOError , err
+    
+    def _setModified(self , modified ):
+        """
+        to avoid that a 1rst method set modified to True and a 2 method reset it to False
+        """
+        if self._modified is False:
+            self._modified = modified
+    
+    def _decodeData( self , data ):
+        """
+        convert the data in right encoding and replace windows end of line by unix one.
+        """
+        # trying to guess the encoding, before converting the data to ascii
+        try:
+            # trying ascii
+            data = unicode(data.decode('ascii','strict'))
+        except:
+            try:
+                # utf8 codec with BOM support
+                data = unicode(data,'utf_8_sig')
+            except:
+                try:
+                    # utf16 (default Windows Unicode encoding)
+                    data = unicode(data,'utf_16')
+                except:
+                    # latin1
+                    data = unicode(data,'latin1')
+        # converting the unicode data to ascii
+        data = data.encode('ascii','replace')   
+        return data
+         
+    def _addTextNode( self, node , nodeName , content , attr = None):
+        """ 
+        add a text node named nodeName with content to the node node
+        @param node: the node on which the new node will append 
+        @type node: node element
+        @param nodeName: the name of the new node
+        @type nodeName: string
+        @param content: the content of the new node
+        @type content: string
+        """
+        newNode = etree.Element( nodeName )
+        if attr:
+            for attrName in attr.keys():
+                newNode.set( attrName , str( attr[ attrName ] ) )
+        try:
+            newNode.text = content
+        except Exception:
+            data = self._decodeData( content )
+            try:
+                newNode.text = data
+            except Exception:
+                if nodeName != 'headOfData':
+                    self._log.error( "%s :node = %s have not printable characters they will be replaced by'_': %s "  %(
+                                                                                                                         self.getID() ,
+                                                                                                                         nodeName 
+                                                                                                                         ) )
+                from string import printable
+                for char in data :
+                    if char not in printable :
+                        data = data.replace( char , '_')
+                try:
+                    newNode.text =  data
+                except Exception, err:
+                    self._log.warning( "%s : cannot add text to textNode nodeName= %s : %s "  %(
+                                                                                                 self.getID() ,
+                                                                                                 nodeName ,
+                                                                                                 err ,
+                                                                                                ) )
+                    newNode.text = '--- not printable ---'
+        node.append( newNode )
+    
+    def _updateNode( self, node , nodeName , content ):
+        """
+        replace the content of the text_node child of a node by content. if the node nodeName doesn't exist, it make it
+        @param nodeName: the name of the Node to update
+        @type nodeName: String
+        @param content: the content of the text-node child of nodeName
+        @type content: String
+        """
+        # convert non-string node contents to str (e.g. floats...)
+        if not(isinstance(content,basestring)):
+            content = str(content)
+        #handle utf8 data (renamed jobs and data)
+        try:
+            content = unicode(content, 'utf-8')
+        except TypeError:
+            pass
+        node2update = node.find( "./" + nodeName )
+        if node2update is not None:
+            if node2update.text == content:
+                return False
+            else:
+                node2update.text = content
+                return True
+        else:
+            newNode = etree.Element( nodeName )
+            newNode.text = content
+            node.append( newNode )
+            return True
+    
+    
+    def commit(self):
+        """
+        if the transaction was open in WRITE
+         - write the dom in xml file if it was modified 
+        and release the lock and close the session File this transaction could not be used again
+        """
+        if self.__lockType == self.READ :
+            logType = 'READ'
+        elif self.__lockType == self.WRITE :
+            logType = 'WRITE'
+        else:
+            logType = 'UNKNOWN LOCK( ' + str( self.__lockType ) +' )'
+
+        self._log.debug( "%f : %s : _commit Type= %s ( call by= %s )"  %( time(),
+                                                                         self.getID() ,
+                                                                         logType ,
+                                                                         os.path.basename( sys.argv[0] ) 
+                                                                         ))
+        try:
+            if self.__lockType == self.WRITE and self._modified :
+                try:
+                    tmpFile = open( "%s.%d" %( self.__File.name, os.getpid() )  , 'w' )
+                    indent(self._root )
+                    tmpFile.write(etree.tostring( self._doc, xml_declaration=True , encoding='UTF-8'))
+                    tmpFile.close()
+                    os.rename(tmpFile.name , self.__File.name)
+                except IOError , err:
+                    msg = "can't commit this transaction: " + str( err )
+                    self._log.error( msg )
+                    raise SessionError , msg
+                except Exception , err:
+                    self._log.error( "session/%s self.__File = %s" %( self.getID(), self.__File ) , 
+                                 exc_info = True )
+                    raise err
+        finally:
+            key2del = self.__File.name 
+            try:
+                self._log.debug("%f : %s : commit UNLOCK type= %s modified = %s" %(time() ,
+                                                                                          self.getID() ,
+                                                                                          logType, 
+                                                                                          self._modified
+                                                                                          ))   
+                fcntl.lockf( self.__File , fcntl.LOCK_UN  )
+                self.__File.close()
+                self.__File = None
+                self.__lockType = None
+                self._modified = False #reset _modifiedTransaction to false for the next transaction
+                del( Transaction.__ref[ key2del ] )
+            except IOError , err :
+                self._log.error( "session/%s : cannot UNLOCK  transaction type= %s modified= %s: %s" %(self.getID(),
+                                                                                                            logType,
+                                                                                                            self._modified,
+                                                                                                            err ) ) 
+                del( Transaction.__ref[ key2del ] )
+
+                
+    def rollback(self):
+        """
+        release the lock , close the file without modification of the xml file.
+        this transaction could not be used again
+        """
+        if self.__lockType == self.READ :
+            logType = 'READ'
+        elif self.__lockType == self.WRITE :
+            logType = 'WRITE'
+        else:
+            logType = 'UNKNOWN LOCK( ' + str( self.__lockType ) +' )'
+
+        self._log.debug( "%f : %s : _close Type= %s ( call by= %s )"  %( time(),
+                                                                         self.getID() ,
+                                                                         logType ,
+                                                                         os.path.basename( sys.argv[0] ) 
+                                                                         ))
+        try:
+            key2del = self.__File.name 
+            sys.stdout.flush()
+            fcntl.lockf( self.__File , fcntl.LOCK_UN  )
+            self.__File.close()
+            self.__File = None
+            self.__lockType = None
+            self._modified = False
+            self._modified = False
+        except IOError , err :
+            self._log.error( "session/%s : an error occured during closing transaction : %s" %( self.getID() , err )) 
+            # a gerer   
+        except Exception , err :
+            msg = "session/%s : an error occured during closing transaction : %s" % ( self.getID() , err )
+            self._log.error( msg ) 
+            self._log.debug( "%f : %s : self.__File = %s" %(time() , self.getID(), self.__File ))
+            self._log.debug( "%f : %s : self.__lockType = %s" %(time() , self.getID(), logType ))
+            raise SessionError , msg
+        finally:
+            try:
+                del( Transaction.__ref[ key2del ] )
+            except :
+                pass
+            
+    def getID(self):
+        """
+        @return: the id of the session
+        @rtype: string
+        """
+        id = self._root.get( 'id' )
+        if id:
+            return id
+        else:
+            msg = "the session %s has no identifier" % self.__File.name
+            self._log.error( msg )
+            raise SessionError , msg   
+        
+    def setID(self, sessionKey):
+        """
+        set the id of the session
+        @param sessionKey: the sessionKey wchi will used to set the id
+        @type sessionKey: string
+        """
+        self._root.set( 'id', sessionKey )
+        self._setModified( True )
+       
+        
+    def getEmail(self):
+        """
+        @return: the email of the user session
+        @rtype: string 
+        """
+        try:
+            return self._root.find( 'email' ).text
+        except AttributeError:
+            return None
+        
+    def setEmail( self , email ):
+        """
+        set the user email of this session 
+        @param email: the email of the user of this session
+        @type email: string
+        """
+        modified = self._updateNode( self._root , 'email' ,  email )
+        self._setModified( modified )
+        
+        
+    def isActivated( self ):
+        """
+        @return: True if this session is activated, False otherwise.
+        @rtype: boolean
+        """
+        try:
+            activated = self._root.find( 'activated' ).text
+            if activated == 'true':
+                return True
+            else:
+                return False
+        except AttributeError:
+            msg = "the session %s has no tag 'activated'" % self.__File.name
+            self._log.error( msg )
+            raise SessionError , msg
+    
+    def activate(self):
+        """
+        activate this session
+        """
+        modified = self._updateNode( self._root , 'activated' ,  'true' )
+        self._setModified( modified )
+
+    def inactivate(self):
+        """
+        activate this session
+        """
+        modified = self._updateNode( self._root , 'activated' ,  'false' )
+        self._setModified( modified )
+        
+    def getActivatingKey(self):
+        """
+        @return: the key needed to activate this session ( which is send by email to the user )
+        @rtype: string
+        """
+        try:
+            return self._root.find( 'activatingKey' ).text
+        except AttributeError:
+            return None
+    
+    def isAuthenticated(self):
+        """
+        @return: True if this session is authenticated, False otherwise.
+        @rtype: boolean
+        """
+        try:
+            auth = self._root.find( 'authenticated').text
+            if auth == 'true':
+                return True
+            else:
+                return False
+        except AttributeError:
+            return None
+    
+    def getPasswd(self):
+        """
+        @return: the password of the user ( encoded )
+        @rtype: string
+        """
+        try:
+            return self._root.find( 'passwd' ).text
+        except AttributeError:
+            return None
+    
+    def setPasswd( self , passwd ):
+        """
+        set the session user pass word to passwd
+        @param passwd: the encoded user password
+        @type passwd: string
+        """
+        modified = self._updateNode( self._root , 'passwd' ,  passwd )
+        self._setModified( modified )
+
+    def getTicket(self):
+        """
+        get the currently allocated ticket and its expiration date
+        @return: the ticket of the user and the expiration date
+        @rtype: tuple
+        """
+        try:
+            tck = self._root.find( 'ticket' )
+            ticket_id = tck.find('id').text
+            exp_date = tck.find('exp_date' ).text
+            return (ticket_id, exp_date)
+        except AttributeError, ae:
+            self._log.error(ae, exc_info=True)
+            return None
+    
+    def setTicket( self , ticket_id, exp_date ):
+        """
+        set the currently allocated ticket and its expiration date
+        @param ticket_id: the ticket
+        @type ticket_id: string
+        @param exp_date: the expiration date
+        @type exp_date: date
+        """
+        tck = self._root.find( 'ticket' )
+        if tck is not None:
+            m1 = self._updateNode(tck, 'id', ticket_id)
+            m2 = self._updateNode(tck, 'exp_date', exp_date)
+            modified = m1 or m2
+            self._setModified( modified )
+        else:
+            msg = "try to set id and exp-date to a ticket but ticket tag does not exist (%s)" % self.__File.name
+            self._log.error( msg )
+            raise SessionError , msg   
+        
+    def getCaptchaSolution( self ):
+        """
+        @return: the solution of the captcha problem submitted to the user.
+        @rtype: string
+        """
+        try:
+            return self._root.find( 'captchaSolution' ).text
+        except AttributeError:
+            return None
+    
+    def setCaptchaSolution( self  , solution ):
+        """
+        store the solution of the captcha problem submitted to the user.
+        @param solution: the solution of the captcha
+        @type solution: string
+        """
+        modified = self._updateNode( self._root , 'captchaSolution' ,  solution )
+        self._setModified( modified )
+    
+    
+    ################################
+    #
+    #  operations on Data
+    #
+    #################################
+    def hasData(self , dataID ):
+        """
+        @param dataID: the identifier of the data in the session ( mdm5 name + ext )
+        @type dataID: string
+        @return: True if the session structure has an entry for the data corresponding to this ID, False otherwise.
+        @rtype: boolean 
+        """
+        try:
+            self._root.xpath( 'dataList/data[@id = "%s"]'% dataID )[0]
+            return True
+        except IndexError:
+            return False 
+            
+    def renameData( self , dataID  , newUserName ):
+        """
+        change the user name of the data corresponding to the dataID.
+        @param dataID: the identifier of the data in the session ( mdm5 name + ext )
+        @type dataID: string
+        @param newUserName: the new user name of the data.
+        @type newUserName: string
+        @raise ValueError: if dataID does not match any entry in session.
+        """
+        dataNode = self._getDataNode( dataID )
+        modified = self._updateNode( dataNode , 'userName' ,  newUserName )
+        self._setModified( modified )
+    
+    def getData(self , dataID ):
+        """
+        @param dataID: the identifier of the data in the session ( mdm5 name + ext )
+        @type dataID: string
+        @return: the data corresponding to the dataID
+        @rtype: { 'dataName'   =  string ,
+                  'userName'   =  string ,
+                  'size'       =  int ,
+                  'Type'       =  Mobyletype instance,
+                  'dataBegin'    =  string ,
+                  'inputModes' = [ string inputMode1 , string inputMode2 ,... ],
+                  'producedBy' = [ string jobID1 , string jobID2 , ... ],  
+                  'usedBy'     = [ string jobID1 , string jobID2 , ... ]
+                  }
+        @raise ValueError: if dataID does not match any entry in session.
+        """
+        dataNode = self._getDataNode( dataID )
+        dtf = DataTypeFactory()
+        return self._dataNode2dataDict( dataNode , dtf )
+
+             
+    def getAllData(self):
+        """
+        @return: all data in this session
+        @rtype: [ {'dataName'   =  string ,
+                  'userName'   =  string ,
+                  'size'       =  int ,
+                  'Type'       =  Mobyletype instance,
+                  'dataBegin'    =  string ,
+                  'inputModes' = [ string inputMode1 , string inputMode2 ,... ],
+                  'producedBy' = [ string jobID1 , string jobID2 , ... ],  
+                  'usedBy'     = [ string jobID1 , string jobID2 , ... ]
+                  } , ... ]
+        """
+        datas = []
+        dtf = DataTypeFactory()
+        for dataNode in self._root.xpath( 'dataList/data') :
+            data = self._dataNode2dataDict( dataNode , dtf )
+            datas.append( data )
+        return datas
+
+
+    def removeData(self , dataID ):
+        """
+        remove the data entry corresponding to dataID
+        @param dataID: the identifier of the data in the session ( mdm5 name + ext )
+        @type dataID: string
+        @raise ValueError: if dataID does not match any entry in session.
+        """
+        dataNode = self._getDataNode( dataID )
+        dataListNode = dataNode.getparent()
+        dataListNode.remove( dataNode )
+        ## remove the ref of this data from the jobs
+        usedBy = dataNode.xpath('usedBy/@ref' )
+        for jobID in usedBy:
+            try:
+                jobNode = self._getJobNode( jobID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    dataUsedNode = jobNode.xpath( 'dataUsed[@ref="%s"]' %dataID )[0]
+                except IndexError:
+                    continue
+                else:
+                    jobNode.remove( dataUsedNode )
+
+        producedBy = dataNode.xpath('producedBy/@ref' )
+        for jobID in producedBy:
+            try:
+                jobNode = self._getJobNode( jobID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    dataProducedNode = jobNode.xpath( 'dataProduced[@ref="%s"]' %dataID )[0]
+                except IndexError:
+                    continue
+                else:
+                    jobNode.remove( dataProducedNode )
+        self._setModified( True ) 
+    
+    
+    def linkJobInput2Data(self , datasID ,  jobsID ):
+        """
+        add a ref of each job using this data and a ref of this data in the corresponding job
+        @param datasID: the IDs of data which are used by these jobsID
+        @type datasID: list of string
+        @param jobsID: the IDs of job which has used these data
+        @type jobsID: list of string
+        @raise ValueError: if one dataID or jobID does not match any entry in session.
+        """
+        for dataID in datasID:
+            dataNode = self._getDataNode( dataID )
+            for jobID in jobsID :
+                alreadyUsedBy = dataNode.xpath('usedBy/@ref' )
+                if  jobID not in alreadyUsedBy :
+                    newUsedByNode = etree.Element( 'usedBy' , ref = jobID )
+                    dataNode.append( newUsedByNode )
+                    self._setModified( True )
+                try:
+                    jobNode = self._getJobNode( jobID )
+                except ValueError:
+                    continue
+                dataUsed = jobNode.xpath( 'dataUsed/@ref' )
+                if dataID not in dataUsed:
+                    newDataUsedNode = etree.Element( 'dataUsed' , ref = dataID )
+                    jobNode.append( newDataUsedNode )
+                    self._setModified( True )
+    
+    
+    def linkJobOutput2Data(self , datasID , jobsID ):
+        """
+        add a ref of each job producing these data and a ref of these data in corresponding jobs.
+        @param dataID: the list of the data ID
+        @type dataID: list of string
+        @param jobsID: the IDs of job which has produced this data
+        @type jobsID: list of string
+        @raise ValueError: if one dataID or jobID does not match any entry in session.
+        """
+        for dataID in datasID:
+            dataNode = self._getDataNode( dataID )
+            for jobID in jobsID:
+                alreadyDataProduced = dataNode.xpath('producedBy/@ref' )
+                if jobID not in alreadyDataProduced :
+                    newUsedByNode = etree.Element( 'producedBy' , ref = jobID)
+                    dataNode.append( newUsedByNode )
+                    self._setModified( True )
+                try:
+                    jobNode = self._getJobNode( jobID )
+                except ValueError:
+                    continue
+                dataProduced = jobNode.xpath( 'dataProduced/@ref' ) 
+                if dataID not in dataProduced:
+                    newDataProducedNode = etree.Element('dataProduced' , ref = dataID )
+                    jobNode.append( newDataProducedNode )
+                    self._setModified( True )
+
+                
+    def addInputModes(self , dataID , inputModes ):
+        """
+        add an inputmode in the list of inputModes of the data corresponding to the dataID
+        @param dataID: The identifier of one data in this session
+        @type dataID: string
+        @param inputModes: the list of inputMode to add the accepted value are 'db' , 'paste' , 'upload' , 'result'
+        @type inputModes: list of string
+        @raise ValueError: if dataID does not match any entry in session.
+        """
+        dataNode = self._getDataNode( dataID )
+        modesNode = dataNode.find( 'inputModes')
+        if modesNode is not None:
+            oldModes = modesNode.xpath( 'inputMode/text()') 
+        else:
+            modesNode = etree.Element( 'inputModes' )
+            oldModes = []
+        for newMode in inputModes:
+            if newMode in oldModes:
+                continue
+            else:
+                newInputNode = etree.Element( 'inputMode' )
+                newInputNode.text = newMode
+                modesNode.append( newInputNode )
+                self._setModified( True )
+     
+     
+    def createData( self , dataID , userName , size , mobyleType , dataBegining , inputModes , producedBy = [] , usedBy = []):
+        """
+        add a new data entry in the session.
+        @param dataID: The identifier of one data in this session
+        @type dataID: string
+        @param userName: the name given by the user to this data
+        @type userName: string
+        @param size: the size of the file corresponding to this data
+        @type size: int
+        @param Type: the MobyleType of this data
+        @type Type: L{MobyleType} instance
+        @param dataBegining: the 50 first char of the data (if data inherits from Binary a string)
+        @type dataBegining: string
+        @param inputModes: the input modes of this data the accepted data are: 'paste' , 'db' , 'upload' , 'result'
+        @type inputModes: list of string
+        @param format: the format , count , of this data
+        @type format: ( string format , int count , string 'to fill the api (unused)' )
+        @param producedBy: the jobs Id which produced this data
+        @type producedBy: list of string
+        @param usedBy: the jobs ID which used this data
+        @type usedBy: list of string
+        """
+        dataListNode = self._root.find( 'dataList')
+        if dataListNode is None:
+            dataListNode = etree.Element( 'dataList')
+            self._root.append( dataListNode )
+        dataNode = etree.Element( 'data' , id =  dataID , size = str( size ))
+        self._addTextNode( dataNode , "userName" ,  userName )
+        typeNode = mobyleType.toDom()
+        dataNode.append( typeNode )
+        self._addTextNode( dataNode , "headOfData" ,  dataBegining )
+        
+        if inputModes:   
+            inputModesNode = etree.Element( 'inputModes' )
+            for inputMode in inputModes:
+                self._addTextNode( inputModesNode , "inputMode" ,  inputMode )
+                dataNode.append( inputModesNode )
+        
+        for jobId in producedBy:
+            producedByNode = etree.Element( "producedBy" , ref = jobId )
+            dataNode.append( producedByNode )
+        for jobId in usedBy:
+            usedByNode = etree.Element( "usedBy" , ref = jobId )
+            dataNode.append( usedByNode )           
+        dataListNode.append( dataNode )  
+        self._setModified( True )
+
+    def addWorkflowLink( self , id):
+        """
+        add a new workflow definition in the session.
+        @param id: The identifier of the workflow
+        @type id: string
+        """
+        workflowsListNode = self._root.find( 'workflowsLinkList')
+        if workflowsListNode is None:
+            workflowsListNode = etree.Element( 'workflowsLinkList')
+            self._root.append( workflowsListNode )
+        workflowNode = etree.Element( 'workflowLink' , id = str(id))
+        workflowsListNode.append( workflowNode )  
+        self._setModified( True )
+
+    def getWorkflows( self):
+        """
+        get the list of workflow definitions in the session.
+        @return: workflow definition identifiers list
+        @rtype: [ {'id':string } , ... ]
+
+        """
+        list = []
+        for workflow in self._root.xpath( 'workflowsLinkList/workflowLink') :
+            list.append({'id':int(workflow.get('id'))})
+        return list
+      
+    def _getDataNode(self , dataID ):
+        """
+        @param dataID: the identifier of one data in this session
+        @type dataID: string
+        @return: the Node corresponding to the dataID
+        @rtype: Node instance
+        @raise ValueError: if dataID does not match any entry in this session.
+        """
+        try:
+            dataNode = self._root.xpath( 'dataList/data[@id = "%s"]' % dataID )[0]
+            return dataNode
+        except IndexError:
+            msg = "the data %s does not exist in the session %s" % ( dataID , self.getID() )
+            raise ValueError , msg
+               
+                           
+    def _dataNode2dataDict(self, dataNode , dtf ):
+        """
+        @param dataNode: a node representing a data
+        @type dataNode: Node instance
+        @param dtf: a dataTypeFactory
+        @type dtf: a L{Mobyle.Classes.Core.DataType.DataTypeFactory} instance
+        """
+        data = {}
+        data[ 'dataName' ] = dataNode.get( 'id' )
+        try:
+            data[ 'userName' ] = dataNode.find( 'userName' ).text
+        except AttributeError:
+            msg = "data %s has no userName" % data[ 'dataName' ]
+            self._log.error( msg )
+            SessionError , msg
+        try:
+            data[ 'Type' ] = parseType( dataNode.find( 'type' ) , dataTypeFactory = dtf )
+        except ( ParserError , MobyleError ) :
+            msg = "error in type parsing for data %s "% data[ 'dataName' ]
+            self._log.error( msg , exc_info = True )
+            raise SessionError , msg
+        try:
+            data[ 'dataBegin' ] = dataNode.find( 'headOfData').text
+        except AttributeError:
+            data[ 'dataBegin' ] = ''
+        try:
+            data[ 'inputModes' ] = [ inputmode.text  for inputmode  in dataNode.findall( 'inputModes/inputMode' ) ]
+        except AttributeError:
+            pass
+        data[ 'producedBy' ] =  dataNode.xpath( 'producedBy/@ref' ) 
+        data[ 'usedBy' ] = dataNode.xpath( 'usedBy/@ref' ) 
+        data[ 'size' ] = int( dataNode.get( 'size' ) )
+        return data 
+
+
+    #####################
+    #
+    #   jobs methods
+    #
+    #####################
+
+    def _get_userAnnotation_node(self , jobID ):
+        """
+        @return: the userAnnotationNode of job with jobID. 
+        if userAnnotationNode doesnot exist make it
+        @rtype: etree.element instance
+        @param jobID: the job identifer (it's url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        jobNode = self._getJobNode( jobID )
+        userAnnotation_node = jobNode.find( 'userAnnotation' )
+        if userAnnotation_node is not None:
+            self._setModified( False )
+        else:
+            userAnnotation_node = etree.Element( 'userAnnotation' )
+            jobNode.append( userAnnotation_node )
+            self._setModified( True )
+        return userAnnotation_node
+
+    def getAllUniqueLabels(self):
+        labelsList = []
+        #rather to use recursive search 
+        #I used direct path from root
+        for labelNode in self._doc.findall('/jobList/job/userAnnotation/labels/label') :
+            if not labelNode.text in labelsList :
+                labelsList.append( labelNode.text )
+        return labelsList
+
+    def getJobDescription( self, jobID):
+        """
+        @return: the description for job with identifier jobID
+        @rtype: string or None if there is no description for this job 
+        @param jobID: the job identifer (url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        user_annot_node = self._get_userAnnotation_node( jobID )
+        try:
+            description = user_annot_node.find( 'description' ).text 
+            return  description
+        except AttributeError:
+            return None    
+
+    def getJobUserName( self, jobID):
+        """
+        @return: the user name for job with identifier jobID
+        @rtype: string or None if there is no description for this job 
+        @param jobID: the job identifer (url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        user_annot_node = self._get_userAnnotation_node( jobID )
+        try:
+            description = user_annot_node.find( 'userName' ).text 
+            return  description
+        except AttributeError:
+            return None
+
+    def setJobDescription( self, jobID , description):
+        """
+        set description for the job with jobId
+        @param jobID: the job identifer (it's url) of a job
+        @type jobID: string
+        @param description: the description of this job
+        @type description: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        user_annot_node = self._get_userAnnotation_node( jobID )
+        modified = self._updateNode( user_annot_node , 'description' ,  description )
+        self._setModified( modified )
+        
+    def setJobLabels( self, jobID, inputLabels):
+        """
+        set labels for job with  jobId
+        @param jobID: the job identifer (it's url) of a job
+        @type jobID: string
+        @param description: the description of this job
+        @type description: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        user_annot_node = self._get_userAnnotation_node( jobID )
+        labels_node = user_annot_node.find( 'labels' )
+        if labels_node is not None:
+            user_annot_node.remove( labels_node )
+        new_labels_node = etree.Element( 'labels' )   
+        for new_label in inputLabels:
+            new_label_node = etree.Element( 'label' )
+            new_label_node.text = new_label
+            new_labels_node.append( new_label_node )
+        user_annot_node.append( new_labels_node )
+        self._setModified( True )   
+            
+
+    def getJobLabels( self, jobID):
+        """
+        @return: the labels for job with identifier jobID
+        @rtype: list of strings , [ string label1 ,string label2 ,...] or None if this jobs has no labels. 
+        @param jobID: the job identifer (it's url) of a job
+        @type jobID: string
+        @raise ValueError: if there is no job with jobID in the session
+        """
+        user_annot_node = self._get_userAnnotation_node( jobID )
+        labels_node = user_annot_node.find( 'labels' )
+        if labels_node is None:
+            return []
+        else:
+            labels = [ labelNode.text  for labelNode in labels_node.findall( 'label' ) ]
+        return labels
+    
+        
+    def hasJob( self , jobID ):
+        """
+        @param jobID: the identifier of the job in the session ( the url without index.xml )
+        @type jobID: string
+        @return: True if the session structure has an entry for the job corresponding to this ID, False otherwise.
+        @rtype: boolean 
+        """
+        try:
+            self._root.xpath( 'jobList/job[@id="%s"]'% jobID )[0]
+            return True
+        except IndexError:
+            return False 
+
+    
+    def getJob(self , jobID ):    
+        """
+        @param jobID: the identifier of the job in the session ( the url without index.xml )
+        @type jobID: string
+        @return: the job corresponding to the jobID
+        @rtype: {'jobID'       : string ,
+                 'userName'    : string ,
+                 'programName' : string ,
+                 'status'      : Status object ,
+                 'date'        : time struct ,
+                 'dataProduced': [ string dataID1 , string dataID2 , ...] ,
+                 'dataUsed'    : [ string dataID1 , string dataID2 , ...] ,
+                }
+        @raise ValueError: if the jobID does not match any entry in this session
+        """
+        jobNode = self._getJobNode( jobID )
+        return self._jobNode2jobDict( jobNode )
+
+    
+    def getAllJobs(self):
+        """
+        @return: the list of jobs in this session
+        @rtype:  [ {'jobID'       : string ,
+                    'userName'    : string ,
+                    'programName' : string ,
+                    'status'      : Status object ,
+                    'date'        : time struct ,
+                    'dataProduced': [ string dataID1 , string dataID2 , ...] ,
+                    'dataUsed'    : [ string dataID1 , string dataID2 , ...] ,
+                   } , ... ]
+        """
+        jobs = []
+        jobList = self._root.findall( 'jobList/job' )
+        for jobNode in jobList:
+            job = self._jobNode2jobDict( jobNode )
+            jobs.append( job )
+        return jobs
+
+    #OPENID
+    def addOpenIdAuthData(self, authsession):
+        """
+        @param authsession: the session used by OpenId library
+        @type authsession: Session
+        @raise SessionError : if data cannot be saved
+        """
+        authNode = self._root.find( 'authOpenIdData' )
+        if authNode:
+            self._root.remove( authNode )
+        authNode = etree.Element( 'authOpenIdData')
+
+        for name in authsession.keys():
+            self._addTextNode(authNode, name, pickle.dumps(authsession[name]))
+        self._root.append(authNode)
+        self._setModified( True )
+
+    #OPENID
+    def getOpenIdAuthData(self):
+        """
+        @return : Session with openid data
+        @raise SessionError : if data cannot be read
+        """
+        authNode = self._root.find( 'authOpenIdData' )
+        if not authNode:
+            msg = "No auth data available"
+            raise ValueError , msg
+        authsession = {}
+        #Parse sessionData to get name and value
+        for child in authNode.getchildren():
+            if(child.tag!="#text"):
+                authsession[child.tag] = pickle.loads(child.text)
+        return authsession
+
+    def createJob(self , jobID , userName , programName , status , date , dataUsed ,  dataProduced ):
+        """
+        add a new job entry in this session
+        @param jobID: the identifier of the job in the session ( the url without index.xml )
+        @type jobID: string
+        @param userName: the name of this job given by the user
+        @type userName: string
+        @param programName: the name of the program which generate this job
+        @type programName: string
+        @param status: the status of this job
+        @type status: L{Status.Status} instance
+        @param date: the date of job submission
+        @type date: time struct 
+        @param dataUsed: the list of data stored in this session used by this job.
+        @type dataUsed: list of strings
+        @param dataProduced: the list of data stored in this session and produced by this job.
+        @type dataProduced: list of strings
+        """
+        jobListNode = self._root.find( 'jobList' )
+        if jobListNode is None:
+            jobListNode = etree.Element('jobList')
+            self._root.append( jobListNode )
+        jobexist = self._doc.xpath( 'jobList/job[@id="%s"]' % jobID )
+        if jobexist:
+            raise ValueError , "can't create a new job entry in session %s :the jobID %s already exist" % ( self.getID() , jobID ) 
+        jobNode = etree.Element( 'job' , id = jobID )
+        self._addTextNode( jobNode, 'userName' , userName )
+        self._addTextNode( jobNode, 'programName' , programName )
+        self._addTextNode( jobNode, 'date' , strftime( "%x  %X" , date ) )
+        self._addTextNode( jobNode, 'status' , str( status ) ) 
+        if status.message :
+            self._addTextNode( jobNode, 'message' , status.message )
+        
+        for dataID in dataUsed:
+            dataUsedNode = etree.Element( "dataUsed" , ref = dataID )
+            jobNode.append( dataUsedNode )
+        for dataID in dataProduced:
+            dataProducedNode = etree.Element( "dataProduced" , ref = dataID )
+            jobNode.append( dataProducedNode )     
+        jobListNode.append( jobNode )
+        self._setModified( True )
+  
+    
+    def removeJob(self , jobID ):
+        """
+        remove the entry corresponding to this jobID 
+        @param jobID: the identifier of the job in the session ( the url without index.xml )
+        @type jobID: string
+        @return: the job corresponding to jobID as a dict
+        @rtype: dictionary
+        @raise ValueError: if the jobID does not match any entry in this session
+        """
+        jobNode = self._getJobNode(jobID)
+        jobDict = self._jobNode2jobDict( jobNode )
+        jobListNode = jobNode.getparent()
+        jobListNode.remove( jobNode )
+        ## remove the ref of this job from the data
+        dataUsedNodes = jobNode.findall( 'dataUsed' )
+        for dataUsedNode in dataUsedNodes:
+            dataID = dataUsedNode.get( 'ref' )
+            try:
+                dataNode = self._getDataNode( dataID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    usedByNode = dataNode.xpath( 'usedBy[@ref="%s"]' %jobID )[0]
+                except IndexError:
+                    continue
+                else:
+                    dataNode.remove( usedByNode )
+            
+        dataProducedNodes = jobNode.findall( 'dataProduced' )
+        for dataProducedNode in dataProducedNodes:
+            dataID = dataProducedNode.get( 'ref' )
+            try:
+                dataNode = self._getDataNode( dataID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    producedByNode = dataNode.xpath( 'producedBy[@ref="%s"]' %jobID )[0]
+                except IndexError:
+                    continue
+                else:
+                    dataNode.remove( producedByNode )
+        self._setModified( True ) 
+        return jobDict       
+
+               
+    def renameJob( self , jobID , newUserName ):
+        """
+        change the user name of the job corresponding to the jobID.
+        @param jobID: the identifier of the job in the session ( url without index.xml )
+        @type jobID: string
+        @param newUserName: the new user name of the job.
+        @type newUserName: string
+        @raise ValueError: if jobID does not match any entry in session.        
+        """
+        jobNode = self._getJobNode( jobID )
+        modified = self._updateNode( jobNode , 'userName' ,  newUserName )
+        self._setModified( modified )
+        
+        
+    def updateJobStatus( self , jobID , status ):
+        """
+        update the status of the job corresponding to the jobID
+        @param jobID: the identifier of the job in the session ( url without index.xml )
+        @type jobID: string
+        @param status: the status of this job
+        @type status: L{Status.Status} instance
+        @raise ValueError: 
+        """
+        jobNode = self._getJobNode( jobID )
+        modified = self._updateNode( jobNode , 'status' , str( status ) )
+        if status.message:
+            modified = self._updateNode( jobNode , 'message' , status.message )
+        self._setModified( modified )
+        
+            
+    def _getJobNode( self , jobID ):
+        """
+        @param jobID: the identifier of one job in this session
+        @type jobID: string
+        @return: the Node corresponding to the jobID
+        @rtype: Node instance
+        @raise ValueError: if jobID does not match any entry in this session.
+        """
+        try:
+            return self._root.xpath( 'jobList/job[@id="%s"]' % jobID )[0]
+        except IndexError:
+            msg = "the job %s does not exist in the session %s" % ( jobID , self.getID() )
+            raise ValueError , msg
+                    
+    
+    def _jobNode2jobDict( self , jobNode ):
+        """
+        @param jobNode: a node representing a job
+        @type jobNode: Node instance
+        """
+        job = {}
+        job [ 'jobID' ] = jobNode.get( 'id' )
+        try:
+            job[ 'userName' ] = jobNode.find( 'userName' ).text
+        except AttributeError:
+            self._log.error( "the job %s in session %s has no userName" %( job[ 'jobID' ] , self.getID()) )
+        try:
+            job[ 'programName' ] =  jobNode.find( 'programName' ).text 
+        except AttributeError :
+            self._log.error( "the job %s in session %s has no programName" % ( job[ 'jobID' ] , self.getID()) )
+        try:
+            statusString = jobNode.find( 'status' ).text
+        except AttributeError :
+            msg = "the job %s in session %s has no status" % ( job[ 'jobID' ] , self.getID()) 
+            self._log.error( msg )
+            raise MobyleError( msg )
+        else:
+            try:
+                message = jobNode.find( 'message' ).text
+            except AttributeError:
+                message = ''
+            try:
+                job[ 'status' ] = Status( string= statusString , message = message )
+            except KeyError:
+                msg = "error in status %s for job %s in session %s" % ( statusString ,
+                                                                        job[ 'jobID' ] , 
+                                                                        self.getID()
+                                                                      ) 
+                self._log.error( msg )
+                raise MobyleError , msg
+        
+        try:
+            job[ 'date' ] = strptime( jobNode.find( 'date' ).text , "%x  %X")
+        except IndexError:
+            pass  
+        job[ 'dataProduced' ] =  jobNode.xpath( 'dataProduced/@ref' ) 
+        job[ 'dataUsed' ] = jobNode.xpath( 'dataUsed/@ref' ) 
+        return job 
+       
+       
+       
diff --git a/Src/Mobyle/Utils.py b/Src/Mobyle/Utils.py
new file mode 100644
index 0000000..12e23d2
--- /dev/null
+++ b/Src/Mobyle/Utils.py
@@ -0,0 +1,463 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+
+#import os 
+#from time import localtime, strftime , strptime
+from time import strftime
+
+from logging import getLogger
+u_log = getLogger( __name__ )
+
+from Mobyle.MobyleError import MobyleError , UserValueError
+from Mobyle.Admin import Admin
+#from Mobyle.Status import Status
+
+def executionLoader( jobID = None , alias = None , execution_config=None):
+    assert ( bool( jobID ) +  bool( alias ) + bool( execution_config )== 1), "please provide either a jobID, an alias or an execution_config"
+    from Mobyle.ConfigManager import Config
+    cfg = Config()
+    if not execution_config:
+        if jobID:
+            from Mobyle.JobState import normUri
+            from urlparse import urlparse
+            path = normUri( jobID )
+            protocol, host, path, a, b, c = urlparse( path )
+            if protocol == "http":
+                raise  NotImplementedError , "trying to instanciate an Execution system from a remote Job"
+            if path[-9:] == "index.xml":
+                path = path[:-10 ]
+            adm = Admin( path )
+            alias = adm.getExecutionAlias()
+        if not alias:
+            msg = "cant determine the Execution system for %s " % ( jobID ) 
+            u_log.error( msg )
+            raise MobyleError( msg )
+        try:
+            execution_config = cfg.getExecutionConfigFromAlias( alias )
+        except KeyError:
+            msg = "the ExecutionConfig alias %s doesn't match with any alias in Config" % alias
+            u_log.critical( msg )
+            raise MobyleError( msg )     
+    klass_name = execution_config.execution_class_name
+    try:
+        module = __import__( 'Mobyle.Execution.%s' % klass_name )
+    except ImportError , err:
+        msg = "The Execution.%s module is missing" % klass_name
+        u_log.critical( msg )
+        raise MobyleError, msg
+    except Exception , err:
+        msg = "an error occurred during the Execution.%s import: %s" % ( klass_name , err )
+        u_log.critical( msg )
+        raise MobyleError, msg
+    try:
+        klass = module.Execution.__dict__[ klass_name ].__dict__[ klass_name ]
+        return klass( execution_config )
+    except KeyError , err :
+        msg = "The Execution class %s does not exist" % klass_name
+        u_log.critical( msg )
+        raise MobyleError, msg
+    except Exception , err:
+        msg = "an error occurred during the class %s loading : %s" % ( klass_name, err )
+        u_log.critical( msg )
+        raise MobyleError, msg
+    
+    
+def getStatus( jobID ):
+    """
+    @param jobID: the url of the job
+    @type jobID: string
+    @return: the current status of the job
+    @rtype: string
+    @raise MobyleError: if the job has no number or if the job doesn't exist anymore
+    @raise OSError: if the user is not the owner of the process
+    """
+    from Mobyle.JobState import JobState , normUri
+    from urlparse import urlparse
+    from Mobyle.StatusManager import StatusManager 
+    
+    path = normUri( jobID )
+    protocol, host, path, a, b, c = urlparse( path )
+    if protocol == "http":
+        raise  NotImplementedError , "trying to querying a distant server"
+    
+    if path[-9:] == "index.xml":
+        path = path[:-10 ]
+    sm =StatusManager()
+    
+    oldStatus = sm.getStatus( path )
+    #'killed' , 'finished' , 'error' the status cannot change anymore
+    #'building' these jobs have not yet batch number
+
+    #  ( 'finished' , 'error' , 'killed' , 'building' ):
+    if not oldStatus.isQueryable():
+        return oldStatus
+    else: 
+        adm = Admin( path )
+        batch = adm.getExecutionAlias()
+        jobNum = adm.getNumber()
+        
+        if batch is None or jobNum is None:
+            return oldStatus
+        try:
+            exec_engine = executionLoader( jobID = jobID )
+            newStatus = exec_engine.getStatus( jobNum )
+        except Exception , err : 
+            ###u_log.error( str( err ) , exc_info = True )
+            #with sge drmaa when sge_master does not respond
+            #get status is waited forever in sge
+            #unless we exit explicitly the drmaa session
+            #in this case a drmaa error is raise so I must catch all Exceptions
+            #and return oldStatus as if drmaa return unknow status 
+            return oldStatus
+        if not newStatus.isKnown():
+            return oldStatus
+        if newStatus != oldStatus :
+            sm.setStatus( path , newStatus )
+        return newStatus 
+
+def isExecuting( jobID ):
+    """
+    @param jobID: the url of the job
+    @type jobID: string
+    @return True if the job is currently executing ( submitted , running , pending , hold ).
+    False otherwise ( building, finished , error , killed )
+    @rtype: boolean
+    @raise MobyleError: if the job has no number 
+    @raise OSError: if the user is not the owner of the process
+    """
+    from Mobyle.JobState import normUri
+    from urlparse import urlparse 
+    from Mobyle.StatusManager import StatusManager
+    
+    path = normUri( jobID )
+    protocol, host, path, a, b, c = urlparse( path )
+    if protocol == "http":
+        raise  NotImplementedError , "trying to querying a distant server"
+    
+    if path[-9:] == "index.xml":
+        path = path[:-10 ]
+    adm = Admin( path )
+    batch = adm.getExecutionAlias()
+    jobNum = adm.getNumber()
+    
+    if batch is None or jobNum is None:
+        sm = StatusManager()
+        status = sm.getStatus( path )
+        if not status.isQueryable():
+            return False
+        else:
+            raise MobyleError( "inconsistency in .admin file %s" % path )
+    try:
+        execKlass = executionLoader( jobID = jobID )
+        newStatus = execKlass.getStatus( jobNum )
+    except MobyleError , err : 
+        u_log.error( str( err ) , exc_info = True )
+        raise err
+    return newStatus.isQueryable()
+
+
+
+
+
+def killJob( jobID ):
+    """
+    @param jobID: the url of the job or a sequence of jobID
+    @type jobID: string or sequence of jobID
+    @return: 
+    @rtype: 
+    @raise MobyleError: if the job has no number or if the job doesn't exist anymore
+    @todo: tester la partie sge
+    """
+    from types  import StringTypes , TupleType , ListType
+    from Mobyle.MobyleError import MobyleError
+    from Mobyle.JobState import JobState , normUri
+    from Mobyle.Job import Job
+    from Mobyle.WorkflowJob import WorkflowJob
+    
+    if isinstance( jobID , StringTypes ) :
+        jobIDs = [ jobID ]
+    elif isinstance( jobID , ( ListType , TupleType ) ) :
+        jobIDs = jobID
+    else:
+        raise MobyleError , "jobID must be a string or a Sequence of strings :%s"%type( jobID )
+    
+    errors = []
+    for jobID in jobIDs :
+        try:
+            path = normUri( jobID )
+        except MobyleError , err :
+            errors.append( ( jobID , str( err ) ) )
+            continue
+        if path[:4] == 'http' :
+            #the jobID is not on this Mobyle server
+            errors.append( ( jobID , "can't kill distant job" ) )
+            continue
+        js = JobState(uri = jobID )
+        if js.isWorkflow():
+            job = WorkflowJob( id= jobID )
+        else:
+            job= Job( ID= jobID )
+        try:
+            job.kill()
+        except MobyleError , err :
+            errors.append( ( jobID , str( err ) ) )
+            continue
+    if errors:
+        msg = ''
+        for jobID , msgErr in errors :
+            msg = "%s killJob( %s ) - %s\n" % ( msg , jobID , msgErr )
+            
+        raise MobyleError , msg
+
+def safeFileName( fileName ):
+    import string , re
+    
+    if fileName in ( 'index.xml' , '.admin' , '.command' ,'.forChild.dump' ,'.session.xml'):
+        raise UserValueError( msg = "value \"" + str( fileName ) + "\" is not allowed" )
+    
+    for car in fileName :
+        if car not in string.printable : #we don't allow  non ascii char
+            fileName = fileName.replace( car , '_')
+    
+    #SECURITY: substitution of shell special characters
+    fileName = re.sub( "[ ~%#\"\'<>&\*;$`\|()\[\]\{\}\?\s ]" , '_' , fileName )
+                  
+    #SECURITY: transform an absolute path in relative path
+    fileName = re.sub( "^.*[\\\]", "" , fileName )
+    
+    return fileName
+
+
+
+def makeService( programUrl ):
+    import Mobyle.Parser
+    try:
+        service = Mobyle.Parser.parseService( programUrl )
+        return service
+    except IOError , err:
+        raise MobyleError , str( err )
+
+      
+def sizeFormat(bytes, precision=2):
+    """Returns a humanized string for a given amount of bytes"""
+    import math
+    bytes = int(bytes)
+    if bytes is 0:
+        return '0 bytes'
+    log = math.floor( math.log( bytes , 1024 ) )
+    return "%.*f%s" % ( precision , bytes / math.pow(1024, log), [ 'bytes', 'KiB', 'MiB' , 'GiB' ][ int(log) ] )
+
+def zipFiles( zip_filename , files ):
+    """
+    @param zip_filename: the absolute path to the archive to create
+    @type zip_filename: string
+    @param files: a list of tuple each tuple contains 2 elements the absolute path of the file to archive , and the name of this file in the archive
+    @type files: [ ( string abs_path_file_to archive , string arc_name ) , ... ]
+    @return: the abspath of the archive
+    @rtype: string
+    """
+    import zipfile
+    import os
+    from time import localtime
+    from Mobyle.StatusManager import StatusManager
+    
+    myZipFile = zipfile.ZipFile( zip_filename, "w", allowZip64 = True )
+    for filename , arc_filename in files:
+        if arc_filename == 'index.xml':
+            from lxml import etree
+            index_tree = etree.parse( filename )
+            status_tree = etree.parse( os.path.join( os.path.dirname(filename), StatusManager.file_name ) )
+            root_index_tree = index_tree.getroot()
+            pi = root_index_tree.getprevious()
+            pi.set( "href" , "job.xsl")
+            root_index_tree.append( status_tree.getroot() )
+            indent( root_index_tree )
+            index = etree.tostring( index_tree , xml_declaration=True , encoding='UTF-8' )
+            myZipInfo = zipfile.ZipInfo( arc_filename , localtime()[:6] )
+            myZipInfo.external_attr = 2175008768   # set perms to 644
+            myZipFile.writestr(  myZipInfo  , index )
+            continue
+        try:
+            size = os.path.getsize( filename )
+        except OSError , err:
+            u_log.critical( "error during zipping files: %s"%(err) , exc_info = True)
+            continue
+        if size > 0 and size < 10:
+            myZipFile.write( filename   , arc_filename , zipfile.ZIP_STORED )
+        elif size >= 10:  
+            myZipFile.write(filename, arc_filename, zipfile.ZIP_DEFLATED)
+        else:
+            #the file is empty we don't add it to this archive
+            pass   
+    myZipFile.close()
+    return  zip_filename
+
+def emailHelpRequest( cfg, userEmail, registry, job_id, message, session, error_parameter, error_message ):
+    from Mobyle.Net import Email, EmailAddress 
+    if(message is None):
+        raise UserValueError(msg='please provide a request message.')
+    userAddress = EmailAddress(userEmail)            
+    if job_id:
+        jobServer = registry.getServerByJobId(job_id)
+    else:
+        jobServer = registry.serversByName['local']
+    if cfg.mailHelp()!=jobServer.help:
+        # generate a list of unique email addresses from the execution server and portal server help addresses
+        help_addresses = cfg.mailHelp() + list(set(cfg.mailHelp())-set(jobServer.help))
+        helpAddress = EmailAddress(help_addresses)
+    else:
+        helpAddress = EmailAddress(cfg.mailHelp())
+    helpEmail = Email(helpAddress)
+    job_status = "[not provided]"
+    job_date = "[not provided]"
+    execution_alias = "[not provided]"
+    number = "[not provided]"
+    queue = "[not provided]"
+    if job_id is not None:
+        job_info = session.getJob(job_id)
+        job_status = job_info['status']
+        job_date = strftime("%a, %d %b %Y %H:%M:%S +0000", job_info['date'])
+        try:
+            from Mobyle.JobState import url2path
+            job_path = url2path(job_id)
+            adm = Admin(job_path)
+            execution_alias = adm.getExecutionAlias()
+            number = adm.getNumber()
+            queue = adm.getQueue()
+        except MobyleError:
+            pass
+    else:
+        job_id = "[not provided]"
+    msgDict = {
+                 'USER': userAddress,
+                 'SENDER': cfg.sender(),
+                 'MSG': message,
+                 'SESSION_ID': session.getKey(),
+                 'SESSION_EMAIL': session.getEmail(),
+                 'SESSION_ACTIVATED': session.isActivated(),
+                 'SESSION_AUTHENTICATED': session.isAuthenticated(),
+                 'JOB_URL': job_id,
+                 'JOB_DATE': job_date,
+                 'JOB_STATUS': job_status,
+                 'JOB_ERROR_PARAM': error_parameter,   
+                 'JOB_ERROR_MSG': error_message,
+                 'HELP': str(helpAddress),
+                 'EXECUTION_ALIAS': execution_alias,
+                 'NUMBER': number,
+                 'QUEUE': queue
+              } 
+    helpEmail.send('HELP_REQUEST' , msgDict)
+    receiptEmail = Email( userAddress )
+    receiptEmail.send('HELP_REQUEST_RECEIPT' , msgDict)
+    return helpEmail.getBody()
+
+def emailResults( cfg , userEmail, registry, ID, job_path, serviceName, jobKey, FileName = None ):
+    """
+    @param cfg: the configuration of Mobyle    
+    @type cfg: Config instance
+    @param userEmail: the user email address
+    @type userEmail: EmailAddress instance
+    @param registry: the registry of deployed services
+    @type registry: Registry.registry object
+    @param ID: the ID of the job
+    @type ID: string
+    @param job_path: the absolute path to the job 
+    @type job_path: string
+    @param serviceName: the name of the service
+    @type serviceName: string
+    @param jobKey: the key of the job
+    @type jobKey: string
+    @param FileName: the absolute path of zip file to attach to the email
+    @type FileName: string or None
+    """
+    from Mobyle.Net import Email 
+    from Mobyle.MobyleError import EmailError , TooBigError
+    import os
+    dont_email_result , maxmailsize =  cfg.mailResults()
+    if dont_email_result :
+        return
+    else:
+        if userEmail :
+            mail = Email( userEmail )
+            jobInPortalUrl = "%s/portal.py#jobs::%s" %( cfg.cgi_url() ,
+                                                        registry.getJobPID( ID ),
+                                                       )
+            
+            if FileName is not None:
+                zipSize = os.path.getsize( FileName )
+                mailDict = { 'SENDER'         : cfg.sender() ,
+                             'HELP'           : ", ".join( cfg.mailHelp() ) ,
+                             'SERVER_NAME'    : cfg.portal_url() ,
+                             'JOB_URL'        : jobInPortalUrl , 
+                             'RESULTS_REMAIN' : cfg.remainResults() ,
+                             'JOB_NAME'       : serviceName ,
+                             'JOB_KEY'        : jobKey ,
+                             }
+                if zipSize > maxmailsize - 2048 :
+                    #2048 octet is an estimated size of email headers
+                    try:
+                        mail.send( 'RESULTS_TOOBIG' , mailDict )
+                        return
+                    except EmailError ,err :
+                        msg = str(err)
+                        adm = Admin( job_path )
+                        adm.setMessage( msg )
+                        adm.commit()
+                        u_log.error( "%s/%s : %s" %( serviceName ,
+                                                      jobKey ,
+                                                      msg
+                                                      )
+                        )
+                        return
+                else:
+                    try:   
+                        mail.send( 'RESULTS_FILES' , mailDict , files = [ FileName ]  )
+                        return
+                    except TooBigError ,err :
+                        try:
+                            mail.send( 'RESULTS_TOOBIG' , mailDict )
+                        except EmailError ,err :
+                            msg = str(err)
+                            adm = Admin( job_path )
+                            adm.setMessage( msg )
+                            adm.commit()
+                            u_log.error( "%s/%s : %s" %( serviceName ,
+                                                          jobKey ,
+                                                          msg
+                                                          )
+                            )
+                        
+                        return
+            else: #if there is a problem on zip creation
+                mail.send( 'RESULTS_NOTIFICATION' , mailDict )
+        else:
+            return
+
+    
+
+def indent(elem, level=0):
+    """
+    Due to malfunction in pretty_print argument of etree.tostring
+    we use this function found here
+    http://stackoverflow.com/questions/1238988/changing-the-default-indentation-of-etree-tostring-in-lxml
+    to have a correct indentation 
+    """
+    i = "\n" + level*"  "
+    if len(elem):
+        if not elem.text or not elem.text.strip():
+            elem.text = i + "  "
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+        for elem in elem:
+            indent(elem, level+1)
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+    else:
+        if level and (not elem.tail or not elem.tail.strip()):
+            elem.tail = i
diff --git a/Src/Mobyle/Validator.py b/Src/Mobyle/Validator.py
new file mode 100644
index 0000000..b371b47
--- /dev/null
+++ b/Src/Mobyle/Validator.py
@@ -0,0 +1,135 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+"""
+Validator.py
+
+This module is used to validate a Mobyle XML file (program or else?)
+against the Relax NG schema and the schematron rules
+- The Validator class
+"""
+from lxml import etree
+import os
+from subprocess import Popen, PIPE
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+from tempfile import NamedTemporaryFile
+
+from  Mobyle.Parser import parseService
+
+from logging import getLogger
+v_log = getLogger(__name__)
+
+# defining paths for validation external stuff
+mobschPath = os.path.join(_cfg.mobylehome(),'Schema','mobyle.sch')
+schprocPath = os.path.join(_cfg.mobylehome(),'Tools','validation','iso_svrl.xsl')
+rbPath = os.path.join(_cfg.mobylehome(),'Tools','validation','remove_base.xsl')
+jingPath = os.path.join(_cfg.mobylehome(),'Tools','validation','jing','jing.jar')
+
+SVRL_NS = "http://purl.oclc.org/dsdl/svrl"
+svrl_validation_errors = etree.XPath(
+    '//svrl:failed-assert/svrl:text/text()', namespaces={'svrl': SVRL_NS})
+
+# generating schematron validation xsl from its xsl generator
+schVal_transform = etree.XSLT(etree.parse(schprocPath))
+mobVal_transform = etree.XSLT(schVal_transform(etree.parse(mobschPath)))
+
+
+rb_transform = etree.XSLT(etree.parse(rbPath))
+
+net_enabled_parser = etree.XMLParser(no_network=False)
+
+class Validator(object):
+    
+    def __init__(self, type, docPath=None, docString=None, publicURI=None,runRNG=True, runSCH=True, runRNG_Jing=False):
+        assert (docPath is not None and docString is None) or (docPath is None and docString is not None), "Please specify either a path or a string. Your parameters:\n-path=%s\n-string=%s" %(docPath, docString)
+        types = ["program", "viewer", "workflow", "program_or_workflow", "tutorial"]
+        assert type in types, "Supported validator types are: %s - not %s" % (', '.join(types), type)
+
+        self.publicURI = publicURI
+        self.mobrngPath = os.path.join(_cfg.mobylehome(),'Schema','%s.rng' % type)
+        rng_doc = etree.parse(self.mobrngPath)
+        self.mobrngVal = etree.RelaxNG(rng_doc)
+        
+        self.runRNG = runRNG
+        self.runRNG_Jing = runRNG_Jing
+        self.runSCH = runSCH
+        
+        self.file_is_temporary = False
+        if docPath:
+            self.docPath = docPath
+        else:
+            # if we pass an XML string, create a temporary file which
+            # will be parsed and passed to different validation steps
+            tmpXmlFile = NamedTemporaryFile(delete=False)
+            tmpXmlFile.write(docString)
+            tmpXmlFile.close()
+            self.docPath = tmpXmlFile.name
+            # this flag tells the object that the file path is 
+            # temporary and that it should be removed upon object destruction
+            self.file_is_temporary = True
+        self.doc = etree.parse(self.docPath, parser=net_enabled_parser)
+        self.doc.xinclude()
+        self.doc = rb_transform(self.doc)
+
+    def run(self):
+        if self.runRNG:
+            self.rngOk = self.mobrngVal.validate(self.doc)
+            self.rngErrors = [self.mobrngVal.error_log.last_error]
+        if self.runRNG_Jing:
+            try:
+                cp = '-cp %s' % jingPath
+                val_cmd = ("java -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration %s com.thaiopensource.relaxng.util.Driver %s %s" % (cp, self.mobrngPath, self.docPath))
+                val_cmd = val_cmd.split(' ')
+                process = Popen(val_cmd , shell = False , stdout = PIPE, stderr = PIPE )
+                messages = []
+                for line in process.stdout:
+                    if line.find('found attribute "xml:base", but no attributes allowed here')==-1:
+                        messages.append(line)
+                for line in process.stderr:
+                    if line.find('Could not find or load main class com.thaiopensource.relaxng.util.Driver'):
+                        # handle errors like "jing not found"
+                        v_log.error('error during Jing validation: %s' % str(line))
+                        break
+                    if line.find('found attribute "xml:base", but no attributes allowed here')==-1:
+                        messages.append(line)
+                process.wait()
+                #returnCode = process.poll()
+                #self.rng_JingOk = not(returnCode)
+                self.rng_JingOk = len(messages)==0
+                self.rng_JingErrors = messages
+            except OSError, ose:
+                # handle errors like "java not found"...
+                v_log.error('error during Jing validation: %s' % str(ose))
+        if self.runSCH:
+            topLevelParams={}
+            if self.publicURI:
+                topLevelParams={'fileNameParameter':os.path.basename(self.publicURI)}
+            self.eval_report = mobVal_transform(self.doc, **topLevelParams)
+            self.schErrors = [s.strip() for s in svrl_validation_errors(self.eval_report)]
+            self.schOk = len(self.schErrors)==0
+            
+        self.valNotOk = (self.runRNG and not(self.rngOk)) or (self.runSCH and not(self.schOk))
+        self.valOk = not(self.valNotOk)
+        
+    @property
+    def service_name(self):
+        return self.doc.xpath('/*/head/name/text()')[0]
+
+    @property
+    def service(self):
+        service = parseService(self.docPath)
+        service.simulation_path = self.docPath
+        service.pid = self.service_name
+        return service
+    
+    def __del__(self):
+        # remove temporary path object removal
+        if self.file_is_temporary:
+            os.remove(self.docPath)
\ No newline at end of file
diff --git a/Src/Mobyle/Workflow.py b/Src/Mobyle/Workflow.py
new file mode 100644
index 0000000..f5d6ff3
--- /dev/null
+++ b/Src/Mobyle/Workflow.py
@@ -0,0 +1,474 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+from lxml import etree,html #@UnresolvedImport
+import logging
+
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.Service import MobyleType
+
+log = logging.getLogger('Mobyle')
+
+parser = etree.XMLParser(remove_blank_text=True)
+
+def xbool(s):
+    if s in ['False', 'false', '','0']:
+        return False
+    else:
+        return True
+
+def boolx(s):
+    if s==False:
+        return 'false'
+    else:
+        return 'true'
+
+class MobyleElement(etree.ElementBase):
+
+    el_props = {} # properties stored as unique elements
+    
+    text_props = {} # properties stored as text value of an element
+    
+    list_props = {} # properties stored as lists of elements
+
+    att_props = {} # properties stored as attributes
+        
+    PARSER=parser 
+    # the parser used will be wired to the MobyleLookup class defined below
+    # so that the results of xpath queries are the Mobyle custom classes 
+    # instead of generic lxml classes 
+    def _init(self):
+        self.tag=self.mtag
+            
+    def __getattr__(self, name):
+        if self.text_props.get(name):
+            return self._get_text(name)
+        elif self.att_props.get(name):
+            return self._get_att(name)
+        elif self.list_props.get(name):
+            return self._get_list(name)
+        elif self.el_props.get(name):
+            return self._get_el(name)
+        else:
+            raise AttributeError("Attribute '%s' does not exist in '%s' object" % (name, self.__class__.__name__))
+                
+    def _get_text(self, name):
+        return ''.join(self.xpath(self.text_props[name]+'/text()'))
+
+    def _get_att(self, name):
+        r = self.get(self.att_props[name][0])
+        if r is not None:
+            return self.att_props[name][1](r)
+
+    def _get_list(self, name):
+        return self.xpath(self.list_props[name])
+
+    def _get_el(self, name):
+        r = self.xpath(self.el_props[name])
+        if len(r)==1:
+            return r[0]
+        else:
+            return None
+
+    def __setattr__(self, name, value):
+        if self.text_props.get(name):
+            self._build_path(self.text_props[name])
+            self._set_text(name, value)
+            self.xpath(self.text_props[name])[0].text = value
+        elif self.att_props.get(name):
+            self._set_att(name, value)
+        elif self.list_props.get(name):
+            self._build_path(self.list_props[name])
+            self._set_list(name, value)
+        elif self.el_props.get(name):
+            self._build_path(self.el_props[name])
+            self._set_el(name, value)
+        else:
+            etree.ElementBase.__setattr__(self, name, value)
+
+    def _build_path(self, path):
+        if not(self.xpath(path)):
+            p = self
+            for tag in path.replace('//','/').split('/'):
+                if not(p.xpath(tag)):
+                    parser = Parser()
+                    p = etree.SubElement(p,tag)
+                else:
+                    p = p.xpath(tag)[0]
+
+    def _set_text(self, name, value):
+        self.xpath(self.text_props[name])[0].text = value
+
+    def _set_att(self, name, value):
+        self.set(self.att_props[name][0], self.att_props[name][2](value))        
+
+    def _set_list(self, name, value):
+        l = self.xpath(self.list_props[name])
+        parser = Parser()
+        p = l[0].getparent()
+        for i in l:
+            p.remove(i)
+        for i in list(value):
+            p.append(i)
+
+    def _set_el(self, name, value):
+        l = self.xpath(self.el_props[name])
+        p = l[0].getparent()
+        for i in l:
+            p.remove(i)        
+        p.append(value)
+
+class Text(MobyleElement):
+
+    mtag = 'text'
+    
+
+class Workflow(MobyleElement):
+
+    mtag = 'workflow'
+
+    el_props = {
+            'description':'head/doc/description/text',
+            'comment':'head/doc/comment/text'
+    }
+
+    text_props = {
+             'name':'head/name',
+             'version':'head/version',
+             'title':'head/doc/title',
+             'authors':'head/doc/authors'
+             }
+    
+    list_props = {
+             'parameters':'parameters//parameter', # WARNING: we do not handle nested paragraph/parameter structures                   
+             'paragraphs':'parameters/paragraph', # direct child paragraphs                   
+             'tasks':'flow/task',
+             'links':'flow/link'                  
+             }
+
+    att_props = {
+             'id':('id',str,str)
+             }
+
+    owner = None # owner name is added to the workflow name in order to get a meaningfull directory in the Mobyle server jobs folder
+
+    def __repr__(self):
+        return "workflow[%s] %s" % (self.id, self.name)
+
+    def getName(self): # added for Service class compatibility (called from Job)
+        if self.owner is not None: # owner name is added to the workflow name in order to get a meaningfull directory in the Mobyle server jobs folder
+            return str(self.owner.getKey()) + "_" + str(self.id) # name= session key + workflow number
+        return self.name
+
+    def getUrl(self): # added for Service class compatibility (called from Job)
+        if not(self.url):
+            return self.name
+        else:
+            return self.url
+        
+    def isService(self): # added for Service class compatibility (called from Job)
+        return False # cheating Job.py ;)
+    
+    def getUserInputParameterByArgpos(self): # added for Service class compatibility (called from JobFacade)
+        return [p.name for p in self.parameters if not p.isout and not p.isstdout]
+
+    def getParameter(self, name): # added for Service class compatibility (called from JobFacade)
+        return [p for p in self.parameters if p.name==name][0]
+
+    def getVdef(self, name): # added for Service class compatibility (called from JobFacade)
+        return [p for p in self.parameters if p.name==name][0].getVdef()
+
+    def getDataType(self,name): # added for Service class compatibility (called from JobFacade)
+        df = DataTypeFactory()
+        p = [p for p in self.parameters if p.name==name][0]
+        datatype_class = p.type.datatype.class_name
+        datatype_superclass = p.type.datatype.superclass_name
+        if (datatype_superclass in [None,""] ):
+            dt = df.newDataType(datatype_class)
+        else:
+            dt = df.newDataType(datatype_superclass, datatype_class)
+        return dt
+
+class Paragraph(MobyleElement):
+
+    mtag = 'paragraph'
+
+    att_props = {
+             }
+
+    list_props = {
+             'parameters':'parameters//parameter', # WARNING: we do not handle nested paragraph/parameter structures                   
+             'paragraphs':'parameters/paragraph' # direct child paragraphs                   
+    }
+
+    text_props = {
+             'name':'name',
+             'prompt':'prompt'
+             }
+
+    def __repr__(self):
+        return "paragraph (%s)" % (self.name)
+
+    def getName(self): # added for Service.Parameter class compatibility (called from JobFacade)
+        return self.name
+
+    def promptHas_lang(self, lang): # added for UserValueError class compatibility (called from WorkflowJob)
+        return True
+
+    def getPrompt(self, lang):
+        return self.prompt
+
+class Parameter(MobyleElement):
+
+    mtag = 'parameter'
+
+    att_props = {
+             'id':('id',str,str),
+             'ismaininput':('ismaininput',xbool,boolx),
+             'ismandatory':('ismandatory',xbool,boolx),
+             'ishidden':('ishidden',xbool,boolx),
+             'issimple':('issimple',xbool,boolx),
+             'isout':('isout',xbool,boolx),
+             'isstdout':('isstdout',xbool,boolx)
+             }
+
+    text_props = {
+             'name':'name',
+             'prompt':'prompt',
+             'example':'example'
+             }
+
+    list_props = {
+             'vdef':'vdef/value'
+    }
+
+    el_props = {
+             'type':'type',
+             'vlist':'vlist'
+            }
+    
+    def __repr__(self):
+        return "parameter[%s] (%s)" % (self.id, self.name)
+
+    def getName(self): # added for Service.Parameter class compatibility (called from JobFacade)
+        return self.name
+
+    def promptHas_lang(self, lang): # added for UserValueError class compatibility (called from WorkflowJob)
+        return True
+
+    def getPrompt(self, lang):
+        return self.prompt
+
+    def isInfile(self): # added for Service.Parameter class compatibility (called from JobFacade)
+        mt = self.getType()
+        return (not(self.isout) and mt.isFile())
+    
+    def getDataType( self ) :
+        mt = self.getType()
+        return mt.getDataType()
+        
+    def getType(self): # added for Service.Parameter
+        try:
+            return self._mobyleType
+        except AttributeError:
+            df = DataTypeFactory()
+            datatype_class = self.type.datatype.class_name
+            datatype_superclass = self.type.datatype.superclass_name
+            if (datatype_superclass in [None,""] ):
+                dt = df.newDataType(datatype_class)
+            else:
+                dt = df.newDataType(datatype_superclass, datatype_class)
+            self._mobyleType = MobyleType(dt)
+            return self._mobyleType
+    
+    def getVdef(self):
+        """ 
+        WARNING: returns the vdef as currently processed by Mobyle, i.e. a one value list is one value, and a multiple values list is a list
+        this should change with the merge of the "mobyle-multiple branch", which will allow to test if the parameter datatype is a list (or not)
+        """
+        if len( self.vdef ) == 1 :
+            if self.vdef[0].reference in ["",None]:
+                return self.vdef[0].value
+            else:
+                return {'src':self.vdef[0].reference,'srcFileName':self.vdef[0].safe_name}
+        else:
+            return [value.value for value in self.vdef]
+
+        
+class Type(MobyleElement):
+
+    mtag = 'type'
+
+    list_props = {
+             'biotypes':'biotype',
+             }
+
+    el_props = {
+             'datatype':'datatype',
+            }
+
+    def __repr__(self):
+        return "type %s::%s" % (self.biotypes, self.datatype)
+
+
+class Biotype(MobyleElement):
+
+    mtag = 'biotype'    
+
+class Datatype(MobyleElement):
+
+    mtag = 'datatype'    
+
+    text_props = {
+             'class_name':'class',
+             'superclass_name':'superclass'
+             }
+
+    def __repr__(self):
+        return "datatype %s::%s" % (self.class_name, self.superclass_name)
+
+class Task(MobyleElement):
+
+    mtag = 'task'
+    
+    att_props = {
+             'id':('id',str,str),
+             'service':('service',str,str),
+             'service_url':('service_url',str,str),
+             'server':('server',str,str),
+             'suspend':('suspend',xbool,boolx)
+             }
+
+    list_props = {
+             'input_values':'inputValue',
+             }
+
+    text_props = {
+             'description':'description',
+             }
+
+    def __repr__(self):
+        return self.id
+        #return "task[%s] (%s)" % (self.id, self.service)
+
+class InputValue(MobyleElement):
+    
+    mtag = 'inputValue'
+    
+    att_props = {
+             'name':('name',str,str),
+             'reference':('reference',str,str),
+             'user_name':('userName',str,str),
+             'safe_name':('safeName',str,str)
+             }
+
+    text_props = {
+             'value':'.',
+             }
+
+    def __repr__(self):
+        return "inputValue[%s]" % (self.name)
+
+class Value(MobyleElement):
+    
+    mtag = 'value'
+    
+    att_props = {
+             'reference':('reference',str,str),
+             'user_name':('userName',str,str),
+             'safe_name':('safeName',str,str)
+             }
+
+    text_props = {
+             'value':'.',
+             }
+
+    def __repr__(self):
+        return "value[%s]" % (self.value)
+
+class Vlist(MobyleElement):
+    
+    mtag = 'vlist'
+    
+    list_props = {
+             'velems':'velem',
+             }
+
+    def __repr__(self):
+        return "vlist[%s]" % (self.value)
+
+class VElem(MobyleElement):
+    
+    mtag = 'velem'
+    
+    text_props = {
+             'value':'value',
+             'label':'label',
+             }
+
+    def __repr__(self):
+        return "velem[%s(%s)]" % (self.label,self.value)
+
+
+class Link(MobyleElement):
+
+    mtag = 'link'
+    
+    att_props = {
+             'id':('id',str,str),
+             'from_task':('fromTask',str,str),
+             'to_task':('toTask',str,str),
+             'from_parameter':('fromParameter',str,str),
+             'to_parameter':('toParameter',str,str)
+             }
+
+    def __repr__(self):
+        return "link[%s] %s:%s->%s:%s" % (self.id, self.from_task, self.from_parameter, self.to_task, self.to_parameter)
+
+import inspect, sys
+clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)
+tagDict = {}
+for i in clsmembers:
+    if hasattr(i[1],'mtag'):
+        tagDict[i[1].mtag] = i[1]
+
+class MobyleLookup(etree.PythonElementClassLookup):
+
+    def lookup(self, document, element):
+        name = element.tag
+        return tagDict.get(name,None)
+
+parser.set_element_class_lookup(MobyleLookup())
+
+class Parser(object):
+    
+    def __init__(self):
+        self.parser = parser
+    
+    def parse(self, url):
+        """ parse from a URL """
+        o = etree.parse(url,self.parser)
+        return o.getroot()
+    
+    def XML(self, str):
+        """ parse from a string """
+        o = etree.XML(str,self.parser)
+        return o
+    
+    def tostring(self, xml,pretty_print=True):
+        return etree.tostring(xml, xml_declaration=True , encoding='UTF-8', pretty_print=pretty_print)
+
+def parseTextOrHTML(str):
+    frags = html.fragments_fromstring(str)
+    if len(frags)==1 and isinstance(frags[0],basestring):
+        return Text(frags[0])
+    else:
+        if len(frags)==1:
+            wrapper = frags[0]
+        else:
+            wrapper = html.fragment_fromstring('<div xmlns="http://www.w3.org/1999/xhtml">%s</div>' % str)
+        return wrapper
diff --git a/Src/Mobyle/WorkflowDemo.py b/Src/Mobyle/WorkflowDemo.py
new file mode 100644
index 0000000..f7b0284
--- /dev/null
+++ b/Src/Mobyle/WorkflowDemo.py
@@ -0,0 +1,92 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+from Mobyle.Workflow import Workflow, Task, Link, Parameter, Type, Datatype, Biotype, Parser  #@UnusedImport
+
+print __file__
+
+if __name__ == "__main__":
+    
+    wf = Workflow()
+    wf.name = 'toto'
+    wf.version = '1.1'
+    wf.title = 'test workflow'
+    wf.description = 'this workflow is a test one'
+    t1 = Task()
+    t1.id = 1
+    t1.service = 'clustalw-multialign'
+    t1.suspend = False
+    t1.description = "Run a clustalw"
+    t2 = Task()
+    t2.id = 2
+    t2.service = 'protdist'
+    t2.suspend = True
+    t2.description = "Run a protdist"
+
+    input = Parameter()
+    input.id = '1'
+    input.name = 'sequences'
+    input.prompt = 'Input sequences'
+    input.type = Type()
+    input.type.datatype = Datatype()
+    input.type.datatype.class_name = "Sequence"
+    input.type.biotypes = [Biotype("Protein")]
+
+    output_format = Parameter()
+    output_format.id = '2'
+    output_format.name = 'alignment_format'
+    output_format.prompt = 'Alignment format'
+    output_format.type = Type()
+    output_format.type.datatype = Datatype()
+    output_format.type.datatype.class_name = "String"
+    
+    output = Parameter()
+    output.id = '3'
+    output.isout = True
+    output.name = 'matrix'
+    output.prompt = 'Distance matrix'
+    output.type = Type()
+    output.type.datatype = Datatype()
+    output.type.datatype.class_name = "Matrix"
+    output.type.datatype.superclass_name = "AbstractText"
+    output.type.biotypes = [Biotype("Protein")]
+    
+    l1 = Link()
+    l1.to_task = t1.id
+    l1.from_parameter = "1"
+    l1.to_parameter = "infile"
+
+    l2 = Link()
+    l2.to_task = t1.id
+    l2.from_parameter = "2"
+    l2.to_parameter = "outputformat"
+    
+    l3 = Link()
+    l3.from_task = t1.id
+    l3.to_task = t2.id
+    l3.from_parameter = "aligfile"
+    l3.to_parameter = "infile"
+    
+    l4 = Link()
+    l4.from_task = t2.id
+    l4.from_parameter = "outfile"
+    l4.to_parameter = "3"
+    
+    wf.tasks = [t1, t2]
+    wf.links = [l1, l2, l3, l4]
+    wf.parameters = [input, output_format, output]
+
+    mp = Parser()
+    
+    print mp.tostring(wf)
+    
+    wffile = open( '/tmp/workflow.xml', 'w' )
+    wffile.write( mp.tostring(wf)) 
+    wffile.close()
+
+
+    print mp.parse('/tmp/workflow.xml')
\ No newline at end of file
diff --git a/Src/Mobyle/WorkflowDemo2.py b/Src/Mobyle/WorkflowDemo2.py
new file mode 100644
index 0000000..f473fda
--- /dev/null
+++ b/Src/Mobyle/WorkflowDemo2.py
@@ -0,0 +1,63 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+from Mobyle.Workflow import Workflow, Task, Link, Parameter, Type, Datatype, Biotype, Parser, InputValue  #@UnusedImport
+
+print __file__
+
+if __name__ == "__main__":
+    
+    wf = Workflow()
+    wf.name = 'toto'
+    wf.version = '1.1'
+    wf.title = 'test workflow'
+    wf.description = 'this workflow is a test one'
+    t1 = Task()
+    t1.id = 1
+    t1.service = 'clustalw-multialign'
+    t1.suspend = False
+    t1.description = "Run a clustalw"
+
+    iv = InputValue()
+    iv.name = 'infile'
+    iv.value = """>A
+TTTT
+
+>B
+TATA
+"""
+    iv2 = InputValue()
+    iv2.name = 'outputformat'
+    iv2.value = 'PHYLIP'
+
+    t1.input_values = [iv,iv2]
+    
+    t2 = Task()
+    t2.id = 2
+    t2.service = 'protdist'
+    t2.suspend = True
+    t2.description = "Run a protdist"
+    
+    l = Link()
+    l.from_task = t1.id
+    l.to_task = t2.id
+    l.from_parameter = "aligfile"
+    l.to_parameter = "infile"
+    
+    wf.tasks = [t1, t2]
+    wf.links = [l]
+
+    mp = Parser()
+    
+    print mp.tostring(wf)
+    
+    wffile = open( '/tmp/workflow2.xml', 'w' )
+    wffile.write( mp.tostring(wf)) 
+    wffile.close()
+
+
+    print mp.parse('/tmp/workflow2.xml')
\ No newline at end of file
diff --git a/Src/Mobyle/WorkflowJob.py b/Src/Mobyle/WorkflowJob.py
new file mode 100644
index 0000000..415d909
--- /dev/null
+++ b/Src/Mobyle/WorkflowJob.py
@@ -0,0 +1,637 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+import time #@UnresolvedImport
+import os, sys
+import signal
+import logging #@UnresolvedImport
+import atexit #@UnresolvedImport
+
+from Mobyle.Job import Job
+from Mobyle.Registry import registry
+from Mobyle import ConfigManager
+from Mobyle.JobState import JobState
+from Mobyle.Service import MobyleType, Parameter
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.DataProvider import DataProvider
+from Mobyle.Status import Status
+from Mobyle.StatusManager import StatusManager
+from Utils import zipFiles , emailResults
+from Mobyle.MobyleError import MobyleError, UserValueError
+from Mobyle.Net import EmailAddress
+import Mobyle
+import Local.Policy
+
+log = logging.getLogger(__name__)
+
+##to have all debug logs
+#log.setLevel( logging.DEBUG )
+
+##to have only synchronization father<->child process debug logs
+##don't forget to turn on log in RunnerChild (in atexit)
+#log.setLevel( 12 )
+
+##to block debug logs 
+log.setLevel( logging.INFO )
+
+
+class WorkflowJob(object):
+    
+    def __init__(self, id=None, workflow=None, email=None, email_notify = 'auto', session=None, workflowID = None):
+        """
+        @param id: the identifier of this workflow (it's used to rebuild WorkflowJob using it's id)
+        @type id: string 
+        @param workflow: the workflow definition used to create a new job
+        @type workflow: a L{Workflow} instance
+        @param email: the user email address
+        @type email: L{EmailAddress} instance or a string
+        @param email_notify: if the user must be or not notify of the results at the end of the Job.
+        the 3 authorized values for this argument are: 
+          - 'true' to notify the results to the user
+          - 'false' to Not notify the results to the user
+          - 'auto' to notify the results based on the job elapsed time and the config  EMAIL_DELAY
+        @type email_notify: string 
+        @param session: the session owner of this workflow (if session is set workflowID mut be None )
+        @type session: a L{Session} instance
+        @param workflowID: the ID of a the workflow owner of this workflow
+        @type workflowID: string
+        """
+        self.cfg = ConfigManager.Config()
+        self.status_manager = StatusManager()
+        if id:
+            log.debug("accessing WorkflowJob %s" %(id))
+            self.id = id
+            self.jobState = JobState( id )
+        else:
+            log.debug("creating WorkflowJob for workflow '%s'" %(workflow.name))
+            self.workflow = workflow
+            if session and workflowID:
+                msg = "try to instanciate a workflow with 2 owners: session %s & workflowID %s" %( session.getKey(),
+                                                                                                   workflowID
+                                                                                                  )
+                log.error( msg )
+                raise MobyleError( msg )
+            self.session = session
+            if session :
+                email = session.getEmail()
+                if email:
+                    self.email = EmailAddress( email )
+                else:
+                    self.email = None
+            elif email : #there is an email without session
+                if  not isinstance( email , EmailAddress ):
+                    self.email = EmailAddress( email )
+                else:
+                    self.email = email
+            
+            self.email_notify =  email_notify  
+            if self.email_notify != 'false' and not self.email:
+                raise MobyleError( "email adress must be specified when email_notify is set to %s" % email_notify )
+            # job is just an "environment" folder for the job
+            # it contains the instanciation of the job runner which seems to be hardcoded as "command runner"...
+            self._job = Job( service = self.workflow,
+                             cfg = self.cfg,
+                             userEmail = self.email,
+                             session = self.session,
+                             workflowID = workflowID ,
+                             )
+            self.jobState = self._job.jobState
+            self.id = self._job.getURL()
+            self.parameters = {}
+            for parameter in self.workflow.parameters:
+                # setting parameters which have a default value (important for hidden parameters which are not 
+                # accessed by JobFacade...
+                if not(parameter.isout) and len(parameter.vdef)!=0:
+                    vdef = parameter.getVdef()
+                    if type(vdef)==dict:
+                        self.set_value(parameter.name,src=vdef['src'],srcFileName=vdef['srcFileName'])
+                    else:
+                        self.set_value(parameter.name, value=vdef)
+            
+    def getDir(self):        
+        """ returns the absolute path of the workflow job directory """
+        return self.jobState.getDir()
+    
+    def getServiceName(self):
+        return self.workflow.getName()
+    
+    def getKey( self ):
+        """
+        @return: the unique key of this job
+        @rtype: string
+        """
+        return  self._job.getKey()
+    
+    def getEmail( self ):
+        """
+        @return: the email of the user or None if there is no email
+        @rtype: -L{Net.EmailAddress} instance
+        """
+        return self.email
+    
+    def getCommandLine(self):
+        return '' #polymorpism with Job used in LocalPolicy allow_to_be_executed
+    
+    def set_status(self, status):
+        log.debug("setting job %s status to %s" % (self.id, status))
+        self.status_manager.setStatus( self.getDir() , status )
+        
+    def set_value(self, parameter_name, value=None, src=None, srcFileName=None):
+        wf_parameter = [p for p in self.workflow.parameters if p.name==parameter_name][0]
+        if value is not None:
+            log.debug("setting %s parameter value to %s" %(parameter_name, value))
+        elif src is not None:
+            log.debug("copying %s parameter value from %s/%s" %(parameter_name, src,srcFileName))
+        else:
+            log.error("no VALUE or SOURCE URL specified for %s parameter." % parameter_name)
+        """ set a parameter value """
+        self.parameters[parameter_name] = value
+        self.parameters[parameter_name + '.src'] = src
+        self.parameters[parameter_name + '.srcFileName'] = srcFileName
+        if value and value==wf_parameter.getVdef():
+            log.debug("setting %s parameter value to default value %s" %(parameter_name, wf_parameter.vdef))
+            return            
+        # save input value in a file
+        # link this file from the JobState xml
+        datatype_class = wf_parameter.type.datatype.class_name
+        datatype_superclass = wf_parameter.type.datatype.superclass_name
+        df = DataTypeFactory()
+        if (datatype_superclass in [None,""] ):
+            dt = df.newDataType(datatype_class)
+        else:
+            dt = df.newDataType(datatype_superclass, datatype_class)
+        mt = MobyleType(dt)
+        p = Parameter(mt, name=parameter_name)
+        p._isout = wf_parameter.isout
+        if dt.isFile():
+            if not(wf_parameter.isout):
+                if dt.isMultiple():
+                    value.sort(key=lambda item:item[4])
+                    for value, file_name, src, srcFileName, index in value:
+                        file_name = parameter_name+'.data'
+                        if src:
+                            src = DataProvider.get(src)
+                        file_name, size = mt.toFile( value , self , file_name, src , srcFileName  )
+                        self.jobState.addInputDataFile(parameter_name, (file_name, size, None))
+                else:
+                    file_name = parameter_name+'.data'
+                    if src:
+                        src = DataProvider.get(src)
+                    file_name, size = mt.toFile( value , self , file_name, src , srcFileName  )
+                    self.jobState.addInputDataFile(parameter_name, (file_name, size, None))
+            else:
+                file_name = parameter_name+'.data'
+                if src:
+                    src = DataProvider.get(src)
+                file_name, size = mt.toFile( value , self , file_name, src , srcFileName  )
+                self.jobState.setOutputDataFile(parameter_name, [(file_name, size, None)])
+        else:
+            if not(wf_parameter.isout):            
+                self.jobState.setInputDataValue(parameter_name, value)
+            else:
+                raise NotImplementedError() # so far Mobyle does not manage non-file outputs
+        self.jobState.commit()
+    
+    def setValue(self, parameter_name, value=None, src=None, srcFileName=None):
+        """MobyleJob-style set value method, called from JobFacade"""
+        if type(value)==tuple:
+            return self.set_value(parameter_name, value=value[1], src=value[2],srcFileName=value[3])
+        else:
+            return self.set_value(parameter_name, value=value, src=src,srcFileName=srcFileName)
+        
+    def getJobid(self):
+        """MobyleJob-style get job id method, called from JobFacade"""
+        return self.id
+
+    def getDate(self):
+        """MobyleJob-style get date method, called from JobFacade"""
+        return time.strptime(self.get_date(),"%x  %X")
+    
+    def getStatus(self):
+        """MobyleJob-style get status method, called from JobFacade"""
+        return self.status_manager.getStatus( self.getDir() )
+            
+    def get_value(self, parameter_name):
+        """get a parameter value"""
+        return self.parameters.get(parameter_name,None)
+
+    def get_date(self):
+        """get the job date as a string"""
+        return self.jobState.getDate()
+
+    def get_id(self):
+        """get the job id"""
+        return self.id
+    
+    def run(self):
+        """submit the job asynchronously"""
+        self.validate()
+        self.set_status(Status( code = 1 )) # status = submitted
+        
+        #raise a UserValueError if nb of job is over the limit accepted
+        if( self.email is not None ):
+            try:
+                Local.Policy.allow_to_be_executed( self )
+            except UserValueError, err:
+                self.set_status(Status(string="error",message="workflow execution failed"))
+                log.error( str(err))
+                raise err
+            except AttributeError:
+                #allow_to_be_executed is not in local policy
+                #so all jobs are allowed to run
+                pass
+                
+        self._child_pid = os.fork()
+        if self._child_pid==0:
+            #Child code
+            os.setsid()
+            log_fd = os.open("%s/log" % self.jobState.getDir(), os.O_APPEND | os.O_WRONLY | os.O_CREAT , 0664 )  
+            devnull = os.open( "/dev/null" , os.O_RDWR )
+            os.dup2( devnull , sys.stdin.fileno() )
+            os.close( devnull)
+            os.dup2( log_fd  , sys.stdout.fileno() )
+            os.dup2( log_fd  , sys.stderr.fileno() )
+            os.close( log_fd )
+            atexit.register( self.log , "child exit for workflow id: %s" % self.get_id())
+            
+            ################################################
+            service = self._job.getService()
+            serviceName = service.getName()
+            jobKey = self._job.getKey()
+             
+            linkName = ( "%s/%s.%s" %( self.cfg.admindir() ,
+                                       serviceName ,
+                                       jobKey
+                                       )
+                                       )
+            try:
+                
+                os.symlink(
+                           os.path.join( self.getDir() , '.admin') ,
+                           linkName
+                           )
+            except OSError , err:
+                self.set_status(Status(string="error", message="workflow execution failed"))
+                msg = "can't create symbolic link %s in ADMINDIR: %s" %( linkName , err )
+                log.critical( "%s/%s : %s" %( serviceName, jobKey, msg ), exc_info = True )
+                raise WorkflowJobError , msg
+        
+            ################################################       
+            t0 = time.time()
+            self.srun()
+            t1 = time.time()
+            ################################################
+            try:
+                os.unlink( linkName )
+            except OSError , err:
+                self.set_status(Status(string="error", message="workflow execution failed"))
+                msg = "can't remove symbolic link %s in ADMINDIR: %s" %( linkName , err )
+                log.critical( "%s/%s : %s" %( serviceName, jobKey, msg ), exc_info= True )
+                raise WorkflowJobError , msg
+            ################################################
+            try:
+                zipFileName = self.zip_results()
+            except Exception :
+                msg = "an error occured during the zipping results :\n\n"
+                log.critical( "%s : %s" %( self.id , msg ) , exc_info = True)
+                zipFileName = None
+
+            if (self.email_notify == 'auto' and ( t1 - t0 ) > self.cfg.email_delay()) or\
+                self.email_notify == 'true':
+                emailResults(  self.cfg ,
+                               self.email, #userEmail, 
+                               registry, 
+                               self.id, 
+                               self.getDir(), 
+                               self.workflow.getName(),
+                               self._job.getKey(),  
+                               FileName = zipFileName )
+            sys.exit(0) #exit with no error
+        else:
+            #return properly to the cgi
+            return
+        
+    def zip_results(self):
+        
+        job_dir = self.getDir()
+        files2zip = []
+       
+        input_files = self.jobState.getInputFiles() #inputFiles = [ ( parameterName , [ (fileName , format or None ) , ...) , ... ]
+        if input_files is not None:
+            for files in input_files:
+                for item in files[1]: #item = ( filename , size , fmt ) 
+                    files2zip.append( ( os.path.join( job_dir , item[0] ) , item[0] ))
+                    
+        output_files = self.jobState.getOutputs() #inputFiles = [ ( parameterName , [ (fileName , format or None ) , ...) , ... ]
+        if output_files is not None:
+            for files in output_files:
+                for item in files[1]: #item = ( filename , size  ) 
+                    files2zip.append( ( os.path.join( job_dir , item[0] ) , item[0] ) ) 
+        
+        files2zip.append( ( os.path.join( job_dir , "index.xml") , "index.xml" ) )
+
+        xsl_path = os.path.join( self.cfg.portal_path() , "xsl" ,)
+        jobXslPath = os.path.join( xsl_path , "job.xsl" ) 
+        files2zip.append( ( jobXslPath , "job.xsl" ) )
+        cssPath = os.path.join( self.cfg.portal_path() , "css",  "mobyle.css" ) 
+        files2zip.append( ( cssPath , "mobyle.css" ) )
+                
+        identXslPath = os.path.join( xsl_path , "ident.xsl" )
+        files2zip.append( ( identXslPath , "ident.xsl" ) )
+        
+        zipFileName = "%s_%s.zip" %( self.workflow.getName() , self._job.getKey() )
+        zipFileName = os.path.join( job_dir , zipFileName )
+        zip_filename = zipFiles( zipFileName , files2zip ) 
+        return  zip_filename
+                
+    def log(self, message):
+        print >>sys.stderr , message        
+    
+    def srun(self):
+        """run the job synchronously"""
+        try:
+            self._prepare()
+            self._debug_state()
+            self.set_status(Status( code = 3 )) # status = running
+            # run while no error status
+            # and output parameters do not have a value
+            signal.signal( signal.SIGALRM , self.alarm_handler )
+            while True :
+                signal.alarm( 30 )
+                status = self.status_manager.getStatus( self.getDir() )
+                if status.isOnError() and status.code==6:#killed
+                    self._kill_subjobs()
+                    break
+                else:  
+                    self._iterate_processing()
+                if len([j for j in self.sub_jobs if (not(j.has_key('job_status')) or not(j['job_status'].isEnded()))])==0:
+                    # workflow is considered to be finished when all the tasks have completed
+                    log.log( 12 , "all tasks completed." )
+                    self.log("all tasks completed.")
+                    break
+                try:
+                    pid = os.wait()
+                except OSError , err :
+                    if err.errno == 10 :
+                        log.log( 12 , "no more local child process, maybe remote jobs exist -> continue" )
+                        time.sleep(30)
+                        continue
+                    elif err.errno == 4 :
+                        log.log( 12 , "interrupt by a system call ( SIGALRM ? ), continue  err = %s time = %f "%( err , time.time() ) )
+                        continue
+                    else:
+                        log.log( 12 , "unexpected error, err = %s  time = %f -> break"%( err, time.time() ) , exc_info = True )
+                        break
+                log.log( 12, "end of loop at %f pid, returncode = %s" %(time.time(), pid ) )
+            signal.alarm(0)
+            self._finalize()
+            log.debug("job processing ending for job %s, status %s" % (self.get_id(), self.status_manager.getStatus( self.getDir() )))
+            self._debug_state()
+        except WorkflowJobError, we:
+            # if an "known" runtime error occurs
+            self.set_status(Status(string='error', message=we.message))
+        except Exception:
+            # if an unspecified runtime error occurs
+            self.set_status(Status(string="error",message="workflow execution failed"))
+            log.error("Error while running workflow job %s" % self.id, exc_info=True)
+    
+    def alarm_handler( self, signum ,frame ):
+        """
+        @call: when the a SIGALRM is raise
+        """
+        log.log( 12 , " %d : alarm_handler recieved a signal %d "% (os.getpid() , signum) )
+        pass
+        
+        
+    def _prepare(self):
+        """prepare for executions"""
+        self.sub_jobs = []
+        for t in self.workflow.tasks:
+            self.sub_jobs.append({'task':t})
+        # build data transfers scheduling self.data
+        self.data = []
+        for p in self.workflow.parameters:
+            # for each task that has to be run, check if an input is expected, and if expected,
+            # check if this input is already "valued"
+            # TODO at workflow validation time, we should check for each parameter of a service if a parameter
+            # is mandatory and if so if it is linked to a default value or a link
+            log.debug('preparing parameter %s' % p)
+            if(not(bool(p.isout))):
+                self.data.append({'type':'input','parameter':p})
+            else:
+                self.data.append({'type':'output','parameter':p})
+        for t in self.workflow.tasks:
+            # for each task that has to be run, check if an input is expected, and if expected,
+            # check if this input is already "valued"
+            # TODO at workflow validation time, we should check for each parameter of a service if a parameter
+            # is mandatory and if so if it is linked to a default value or a link
+            log.debug('preparing task %s' % t)
+            for i in [l for l in self.workflow.links if l.to_task==t.id]:
+                self.data.append({'type':'task_input','task':t, 'parameter_id':i.to_parameter})
+            for o in [l for l in self.workflow.links if l.from_task==t.id]:
+                self.data.append({'type':'task_output','task':t, 'parameter_id':o.from_parameter})
+            for iv in t.input_values:
+                if iv.name in [l.to_parameter for l in self.workflow.links if l.from_task==t.id]:
+                    log.debug("ignoring input value for task %s parameter %s which has been provided by a link." %\
+                              (t.id, iv.name))
+                    continue
+                ti = {'type':'task_input','task':t, 'parameter_id':iv.name}
+                if iv.reference is not None:
+                    ti['src'] = iv.reference
+                    ti['srcFileName'] = iv.safe_name
+                elif iv.value is not None:
+                    ti['value'] = iv.value
+                self.data.append(ti)
+        for l in self.workflow.links:
+            log.debug('preparing link %s' % l)
+            self.data.append({'type':'link','link':l})
+        # setting parameter values
+        for entry in [entry for entry in self.data if (entry['type']=='input' and not(entry.has_key('value') or entry.has_key('src') or entry.has_key('srcFileName')))]:
+            if self.parameters.get(entry['parameter'].name):
+                entry['value'] = self.parameters.get(entry['parameter'].name,None)
+            else:
+                entry['src'] = self.parameters.get(entry['parameter'].name+'.src',None)
+                entry['srcFileName'] = self.parameters.get(entry['parameter'].name+'.srcFileName',None)
+
+    def validate(self):
+        """ check that a value is provided for each mandatory field """
+        mandatory_list = [p for p in self.workflow.parameters if not(p.isout) and p.ismandatory and (p.vdef is None or p.vdef=='')]
+        for p in mandatory_list:
+            if not(self.parameters.get(p.name)) and not(self.parameters.get('%s.src' % p.name)):
+                raise UserValueError(parameter = p, msg = "This parameter is mandatory" )
+
+    def _iterate_tasks(self):
+        """launch and monitor task executions"""
+        job_signal = False
+        # starting jobs
+        for t in self.workflow.tasks:
+            input_entries = [entry for entry in self.data if (entry['type']=='task_input' and entry['task'].id==t.id)]
+            # if data available and not already running
+            if (len(input_entries)==len([entry for entry in input_entries if entry.has_key('value') or entry.has_key('srcFileName')])):
+                job_entry = [job_entry for job_entry in self.sub_jobs if job_entry['task'].id==t.id][0]
+                if job_entry.has_key('job_id'):
+                    job_id = job_entry['job_id']
+                    if not(job_entry['job_status'].isEnded()):
+                        j = Mobyle.JobFacade.JobFacade.getFromJobId(job_id)
+                        su_signal = self._process_subjob_status_update(j, job_entry, t)
+                        job_signal = job_signal or su_signal
+                else:
+                    # if job is not running, start it
+                    log.debug('starting job for task %s' % t.id)
+                    log.debug('registry.getProgramUrl(t.service = %s,t.server= %s)' %(t.service , t.server) )
+                    if t.server is None:
+                        t.server = 'local'
+                    job_parameters = {}
+                    try:
+                        url = registry.getProgramUrl(t.service,t.server)
+                        j = Mobyle.JobFacade.JobFacade.getFromService(programUrl=url, workflowId=self.id)
+                        job_parameters['programName'] = url
+                    except:
+                        url = registry.getWorkflowUrl(t.service,t.server)
+                        j = Mobyle.JobFacade.JobFacade.getFromService(workflowUrl=url, workflowId=self.id)                        
+                        job_parameters['workflowUrl'] = url
+                    job_signal = True
+                    for i_e in input_entries:
+                        if i_e.has_key('value'):
+                            job_parameters[i_e['parameter_id']]=i_e['value']
+                        else:
+                            job_parameters[i_e['parameter_id']+'.src']=i_e['src']
+                            job_parameters[i_e['parameter_id']+'.srcFileName']=i_e['srcFileName']
+                            job_parameters[i_e['parameter_id']+'.mode']='result'
+                            job_parameters[i_e['parameter_id']+'.name']=i_e['parameter_id']+'.data'
+                    job_parameters['email'] = self.email
+                    try:
+                        resp = j.create(request_dict=job_parameters)
+                        if not(resp.has_key('id')):
+                            raise WorkflowJobError(resp.get('errormsg',''))
+                    except Exception, e:
+                        log.error("error during submission of task %s%s: %s" \
+                                                  %(t.id, "(%s)" % t.description if t.description else "", e.message),exc_info=True)
+                        raise WorkflowJobError("error during submission of task %s%s: %s" \
+                                                  %(t.id, "(%s)" % t.description if t.description else "", e.message))
+                    job_entry['job_id'] = resp['id']
+                    self.jobState.setTaskJob(t,job_entry['job_id'])
+                    self.jobState.commit()
+                    if resp.has_key('errorparam') or resp.has_key('errormsg'):
+                        raise WorkflowJobError("error during submission of task %s%s.\n job %s message: %s: %s." \
+                                                  %(t.id, "(%s)" % t.description if t.description else "",job_entry['job_id'],resp.get('errorparam'),resp.get('errormsg')))
+                    su_signal = self._process_subjob_status_update(j, job_entry, t)
+                    job_signal = job_signal or su_signal
+                    log.debug('job for task %s: %s' % (t.id, job_entry['job_id']))
+        return job_signal
+
+    def _process_subjob_status_update(self, j, job_entry, t):
+        """ check a subjob status and update the workflow job status, process outputs and raise an exception if relevant """
+        new_status = j.getStatus()
+        if not(job_entry.has_key('job_status')) or new_status!=job_entry['job_status']:
+            job_entry['job_status'] = new_status
+            #update it if it changed since last check
+            self.set_status(Status( code = 3 ,
+                                        message="job %s: %s" % (job_entry['job_id'] , job_entry['job_status'])))             
+            if new_status.isOnError():
+                raise WorkflowJobError("job %s for task %s failed." %(job_entry['job_id'], t.id)) # error, so nothing to be done
+            if new_status.isEnded():
+                # if status complete copy job outputs to task output
+                output_entries = [entry for entry in self.data if (entry['type']=='task_output' and entry['task'].id==t.id)]
+                for o in output_entries:
+                    output_values = j.getOutputParameterRefs(o['parameter_id'])
+                    if len(output_values)>0:
+                        o['src'] = output_values[0]['src'] #FIXME WE TAKE ONLY THE FIRST VALUE INTO ACCOUNT!!!
+                        o['srcFileName'] = output_values[0]['srcFileName']
+                    else:
+                        for item in [item for item in self.data if item['type']=='link']:
+                            link = item['link']
+                            if link.to_task is not None and link.from_parameter==o['parameter_id']\
+                              and link.from_task==t.id:
+                                # if the expected result has not been produced
+                                raise WorkflowJobError("expected output %s has not been produced by task %s (job %s)" 
+                                                       %(o['parameter_id'], t.id, job_entry['job_id'])) # error, so nothing to be done
+            return True # jobSignal
+        else:
+            return False
+        
+    def _iterate_data(self):
+        """link data between tasks"""
+        data_signal = False
+        # linking data
+        new_data = self.data
+        for item in [item for item in self.data if item['type']=='link']:
+            log.debug("processing data entry %s" % item)
+            link = item['link']
+            if not(link.from_task):
+                log.debug("from workflow input to task input")
+                source = [entry for entry in self.data if entry['type']=='input' and entry['parameter'].id==link.from_parameter][0]
+                target = [entry for entry in self.data if (entry['type']=='task_input' and entry['task'].id==link.to_task and entry['parameter_id']==link.to_parameter)][0]
+            elif link.to_task:
+                log.debug("from task output to task input")
+                source = [entry for entry in self.data if entry['type']=='task_output' and entry['task'].id==link.from_task and entry['parameter_id']==link.from_parameter][0]
+                target = [entry for entry in self.data if (entry['type']=='task_input' and entry['task'].id==link.to_task and entry['parameter_id']==link.to_parameter)][0]
+            else:
+                log.debug("from task output to workflow output")
+                source = [entry for entry in self.data if entry['type']=='task_output' and entry['task'].id==link.from_task and entry['parameter_id']==link.from_parameter][0]
+                target = [output for output in self.data if output['type']=='output' and output['parameter'].id==link.to_parameter][0]
+            log.debug(source)
+            log.debug(target)
+            if source.has_key('src') or source.has_key('value'):
+                if source.has_key('src'):
+                    target['src'] = source['src']
+                    target['srcFileName'] = source['srcFileName']
+                elif source.has_key('value'):
+                    target['value'] = source['value']                    
+                log.debug("processing %s" % target)
+                data_signal = True
+                log.debug("removing %s" % entry)
+                new_data.remove(item)
+        self.data = new_data
+        return data_signal
+
+    def _iterate_processing(self):
+        """link data between tasks, launch and monitor task executions"""
+        self._debug_state()
+        ts, ds = True, True
+        while (ts or ds) :
+            ts = self._iterate_tasks()
+            ds = self._iterate_data()
+        log.debug(ts)
+        log.debug(ds)
+
+    def _finalize(self):
+        """set job output values"""
+        for output in [output for output in self.data if output['type']=='output' and (output.get('value') or\
+                                                                                        (output.get('src') and output.get('srcFileName')))]:
+            self.set_value(output['parameter'].name,output.get('value'),output.get('src'),output.get('srcFileName'))
+        self.set_status(Status( code = 4 )) # status = finished
+    
+    def kill(self):
+        status = Status( code =  6 , message ="Your job has been cancelled" )# status = killed
+        self.set_status( status ) 
+        
+    def _kill_subjobs(self):
+        for job_entry in self.sub_jobs:
+            if 'job_id' in job_entry:
+                job_id = job_entry['job_id']
+                j = Mobyle.JobFacade.JobFacade.getFromJobId(job_id)
+                if job_entry['job_status'].isQueryable():
+                    try:
+                        j.killJob()
+                    except Exception, err:
+                        log.error("workflow %s cannot kill job %s : %s" %( self.id , job_id , err) )
+                        continue
+                    
+    def _debug_state(self):
+        log.debug("workflow job %s summary" % self.id)
+        log.debug("data summary:")
+        for entry in self.data:
+            log.debug(entry)
+        log.debug("jobs summary:")
+        for entry in self.sub_jobs:
+            log.debug(entry)
+            
+class WorkflowJobError(MobyleError):
+    """ WorkflowJobErrors are raised if something unexpected happens
+    during the execution of a workflow """
+    pass
\ No newline at end of file
diff --git a/Src/Mobyle/WorkflowJobDemo.py b/Src/Mobyle/WorkflowJobDemo.py
new file mode 100644
index 0000000..7e0da54
--- /dev/null
+++ b/Src/Mobyle/WorkflowJobDemo.py
@@ -0,0 +1,33 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+from Mobyle.Workflow import Parser
+from Mobyle.WorkflowJob import WorkflowJob
+
+if __name__ == "__main__":
+    
+    mp = Parser()
+    #wf = mp.parse('/tmp/workflow.xml') # 1st example
+    wf = mp.parse('/tmp/workflow2.xml') # 2nd example
+    class SessionFake(object):
+        def getKey(self):
+            return "workflowjobdemotestmodule"
+    wf.owner = SessionFake()
+
+    parameters = {'sequences': """>A
+TTTT
+
+>B
+TATA""",
+    'alignment_format': 'PHYLIP'}
+    
+    wj = WorkflowJob(workflow=wf,email='hmenager at pasteur.fr')
+    print wj.id
+    #for key, value in parameters.items(): # set values for 1st example
+    #    wj.set_value(key, value)
+    wj.srun() #synchronous calls
+    #wj.run #asynchronous calls
\ No newline at end of file
diff --git a/Src/Mobyle/WorkflowJobFacadeDemo.py b/Src/Mobyle/WorkflowJobFacadeDemo.py
new file mode 100644
index 0000000..d28fc48
--- /dev/null
+++ b/Src/Mobyle/WorkflowJobFacadeDemo.py
@@ -0,0 +1,29 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+from Mobyle.Workflow import Parser
+
+if __name__ == "__main__":
+
+    mp = Parser()
+    wf = mp.parse('/tmp/workflow.xml')
+    class SessionFake(object):
+        def getKey(self):
+            return "workflowjobdemotestmodule"
+    wf.owner = SessionFake()
+
+    parameters = {'sequences': """>A
+TTTT
+
+>B
+TATA""",
+    'alignment_format': 'PHYLIP',
+    'email': 'hmenager at pasteur.fr'}
+
+    import Mobyle.JobFacade
+    wj = Mobyle.JobFacade.JobFacade.getFromService(service=wf)
+    print wj.create(request_dict=parameters)
\ No newline at end of file
diff --git a/Src/Mobyle/WorkflowLayout.py b/Src/Mobyle/WorkflowLayout.py
new file mode 100644
index 0000000..feddf50
--- /dev/null
+++ b/Src/Mobyle/WorkflowLayout.py
@@ -0,0 +1,82 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+import pygraphviz as pgv  #@UnresolvedImport
+from logging  import getLogger#@UnresolvedImport
+log = getLogger(__name__)
+from copy import deepcopy
+
+def layout(workflow, program='dot', format='svg'):
+    workflow = deepcopy(workflow) # we are working on a copy of the original workflow object to be able to tweak the IDs safely
+    g=pgv.AGraph(strict=False,directed=True)
+    g.graph_attr['label']=workflow.title
+    g.graph_attr['rankdir']='LR'
+    g.graph_attr['tooltip']=workflow.title
+    g.graph_attr['fontname']='Helvetica'
+    g.node_attr['fontname']='Helvetica'
+    g.edge_attr['fontname']='Helvetica'
+    links = workflow.links
+    parameters = workflow.parameters
+    # this part
+    # - removes workflow parameters which are not used in two different task inputs
+    # - renames the corresponding task parameters to the prompt of the source workflow parameter 
+    for link in workflow.links:
+        keep_flag = (link.from_task is not None and link.to_task is not None)\
+                    or\
+                    (link.to_task and len([l for l in workflow.links if not(l.from_task) \
+                                           and l.from_parameter==link.from_parameter ])>1)
+        if not keep_flag:
+            links.remove(link)
+            if (link.from_parameter and not link.from_task):
+                p = [param for param in workflow.parameters if link.from_parameter==param.id][0]
+                link.to_parameter=p.prompt.replace('\n','').strip()
+                parameters.remove(p)
+            if link.to_parameter and not link.to_task:
+                p = [param for param in workflow.parameters if link.to_parameter==param.id][0]
+                link.from_parameter=p.prompt.replace('\n','').strip()
+                parameters.remove(p)
+    for task in workflow.tasks:
+        #inputs = '|'.join(list(set(['<%s>%s' % (link.to_parameter,link.to_parameter) for link in workflow.links if link.to_task==task.id])))
+        #outputs = '|'.join(list(set(['<%s>%s' % (link.from_parameter,link.from_parameter) for link in workflow.links if link.from_task==task.id])))
+        #label = "%(service)s|{{%(inputs)s}|%(description)s|{%(outputs)s}}" % {'service':task.service, 'description':task.description.replace('\n','').strip(), 'inputs':inputs,'outputs':outputs}
+        #g.add_node(task,label=label,shape='record',tooltip=task.description)
+        d = task.description.replace('\n','').strip()
+        s = task.service.replace('\n','').strip()
+        if d is not None and d!='':
+            label = '%s (%s)' % (d,s)
+        else:
+            label = '%s' % s
+        g.add_node(task,label=label,shape='box',tooltip=task.description)
+    for param in parameters:
+        g.add_node(param, label=param.prompt.replace('\n','').strip(), shape='oval')
+    for link in links:
+#        sp, tp = None, None
+        if link.from_task:
+            s = [s for s in workflow.tasks if s.id==link.from_task][0]
+#            sp = link.from_parameter
+        else:
+            s = [s for s in workflow.parameters if s.id==link.from_parameter][0]
+        if link.to_task:
+            t = [t for t in workflow.tasks if t.id==link.to_task][0]
+#            tp = link.to_parameter
+        else:
+            t = [t for t in workflow.parameters if t.id==link.to_parameter][0]
+#        if sp and tp:
+#            g.add_edge(g.get_node(s),g.get_node(t), headport=tp, tailport=sp)
+#        elif sp:
+#            g.add_edge(g.get_node(s),g.get_node(t), tailport=sp)            
+#        elif tp:
+#            g.add_edge(g.get_node(s),g.get_node(t), headport=tp)            
+#        else:
+#            g.add_edge(g.get_node(s),g.get_node(t))
+        if not(g.has_edge(g.get_node(s),g.get_node(t))):
+            g.add_edge(g.get_node(s),g.get_node(t))
+    #log.error(g.to_string())
+    g.layout(prog=program)
+    #log.error(g.to_string())
+    #log.error(g.draw(format=format))
+    return g.draw(format=format)
diff --git a/Src/Mobyle/WorkflowSessionHTTPDemo.py b/Src/Mobyle/WorkflowSessionHTTPDemo.py
new file mode 100644
index 0000000..a4fd43d
--- /dev/null
+++ b/Src/Mobyle/WorkflowSessionHTTPDemo.py
@@ -0,0 +1,315 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+MOBYLEHOME = None
+
+import os
+import sys
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append(  os.path.join( MOBYLEHOME , 'Src' )  )
+
+
+
+import urllib, urllib2, cookielib #@UnresolvedImport
+
+from Mobyle.ConfigManager  import Config
+from Workflow import Parser
+
+if __name__ == "__main__":
+    cfg = Config()
+    url = cfg.cgi_url()+'/workflow.py'
+
+        
+    # the cookie jar keeps the Mobyle session cookie in a warm place, so that 
+    # all the HTTP requests connect to the same workspace 
+    cj = cookielib.CookieJar()
+    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
+    mp = Parser()
+
+    def get_str(params):
+        data = urllib.urlencode(params)
+        req = urllib2.Request(url, data)
+        handle = opener.open(req)
+        str = handle.read()
+        return str.strip('\n')
+
+    def get_xml(params):
+        return mp.XML(get_str(params))
+    
+    # create a workflow and store its id
+    params = {
+              'object':'workflow',
+              'action':'create'
+              } 
+    w = get_xml(params)
+    print w
+    
+    # edit workflow properties
+    params = {
+              'object':'workflow',
+              'action':'update',
+              'workflow_id':w.id,
+              'description':'test workflow description',
+              'title':'test workflow title',
+              }
+    w = get_xml(params)
+    print w
+    
+    # create the clustalw-multialign task
+    params = {
+              'object':'task',
+              'action':'create',
+              'workflow_id':w.id,
+              'service_pid':'clustalw-multialign',
+              }
+    t = get_xml(params)
+    print t
+
+    # edit the clustalw-multialign task
+    params = {
+              'object':'task',
+              'action':'update',
+              'workflow_id':w.id,
+              'task_id':t.id,
+              'suspend':'false',
+              'description':'Run a clustalw',
+              }
+    t = get_xml(params)
+    print t
+    
+    # create the protdist task
+    params = {
+              'object':'task',
+              'action':'create',
+              'workflow_id':w.id,
+              'service_pid':'protdist',
+              }
+    t = get_xml(params)
+    print t
+
+    # edit the protdist task
+    params = {
+              'object':'task',
+              'action':'update',
+              'workflow_id':w.id,
+              'task_id':t.id,
+              'suspend':'true',
+              'description':'Run a protdist',
+              }
+    t = get_xml(params)
+    print t
+
+    # create the sequences input parameter
+    params = {
+              'object':'parameter',
+              'action':'create',
+              'workflow_id':w.id,
+              'stream':'input',
+              }
+    p = get_xml(params)
+    print p
+
+    # edit the sequences parameter
+    params = {
+              'object':'parameter',
+              'action':'update',
+              'workflow_id':w.id,
+              'parameter_id':p.id,
+              'name': 'sequences',
+              'prompt': 'Input sequences',
+              'example': """>R
+AAATA
+>S
+AAATT
+              """,
+              'datatype_class': 'Sequence',
+              'biotype':'Protein'
+              }
+    p = get_xml(params)
+    print p
+
+    # create the format input parameter
+    params = {
+              'object':'parameter',
+              'action':'create',
+              'workflow_id':w.id,
+              'stream':'input',
+              }
+    p = get_xml(params)
+    print p
+
+    # edit the sequences parameter
+    params = {
+              'object':'parameter',
+              'action':'update',
+              'workflow_id':w.id,
+              'parameter_id':p.id,
+              'name': 'alignment_format',
+              'prompt': 'Alignment format',
+              'datatype_class': 'String'
+              }
+    p = get_xml(params)
+    print p
+
+    # create the matrix output parameter
+    params = {
+              'object':'parameter',
+              'action':'create',
+              'workflow_id':w.id,
+              'stream':'output',
+              }
+    p = get_xml(params)
+    print p
+
+    # edit the matrix output parameter
+    params = {
+              'object':'parameter',
+              'action':'update',
+              'workflow_id':w.id,
+              'parameter_id':p.id,
+              'name': 'matrix',
+              'prompt': 'Distance matrix',
+              'datatype_class': 'Matrix',
+              'datatype_superclass': 'AbstractText',
+              'biotype':'Protein'
+              }
+    p = get_xml(params)
+    print p
+
+    # create the sequences-to-clustalw link
+    params = {
+              'object':'link',
+              'action':'create',
+              'workflow_id':w.id,
+              }
+    l = get_xml(params)
+    print l
+
+    # edit the sequences-to-clustalw link
+    params = {
+              'object':'link',
+              'action':'update',
+              'workflow_id':w.id,
+              'link_id':l.id,
+              'from_parameter': '1',
+              'to_task': '1',
+              'to_parameter': 'infile'
+              }
+    p = get_xml(params)
+    print p
+
+    # create the alig_format-to-clustalw link
+    params = {
+              'object':'link',
+              'action':'create',
+              'workflow_id':w.id,
+              }
+    l = get_xml(params)
+    print l
+
+    # edit the alig_format-to-clustalw link
+    params = {
+              'object':'link',
+              'action':'update',
+              'workflow_id':w.id,
+              'link_id':l.id,
+              'from_parameter': '2',
+              'to_task': '1',
+              'to_parameter': 'outputformat'
+              }
+    p = get_xml(params)
+    print p
+
+
+    # create the clustalw-to-protdist link
+    params = {
+              'object':'link',
+              'action':'create',
+              'workflow_id':w.id,
+              }
+    l = get_xml(params)
+    print l
+
+    # edit the clustalw-to-protdist link
+    params = {
+              'object':'link',
+              'action':'update',
+              'workflow_id':w.id,
+              'link_id':l.id,
+              'from_task': '1',
+              'from_parameter': 'aligfile',
+              'to_task': '2',
+              'to_parameter': 'infile'
+              }
+    p = get_xml(params)
+    print p
+
+    # create the protdist-to-output link
+    params = {
+              'object':'link',
+              'action':'create',
+              'workflow_id':w.id,
+              }
+    l = get_xml(params)
+    print l
+
+    # edit the protdist-to-output link
+    params = {
+              'object':'link',
+              'action':'update',
+              'workflow_id':w.id,
+              'link_id':l.id,
+              'from_task': '2',
+              'from_parameter': 'outfile',
+              'to_parameter': '3'
+              }
+    p = get_xml(params)
+    print p
+        
+    # get the workflow
+    params = {
+              'object':'workflow',
+              'action':'get',
+              'workflow_id':w.id
+              } 
+    w = get_xml(params)
+    print w
+
+    # get the workflow
+    params = {
+              'object':'workflow',
+              'action':'get_url',
+              'workflow_id':w.id
+              } 
+    w = get_str(params)
+    print "created workflow definition: %s" % w
+
+    # list the session workflow IDs...
+    params = {
+              'object':'workflow',
+              'action':'list'
+              } 
+    l = get_str(params)
+    print "list of workflow definitions for current session: %s" % l
+    
+    url = cfg.cgi_url()+'/session_job_submit.py'
+    params = {'workflowUrl': w,
+    'sequences': """>A
+TTTT
+
+>B
+TATA""",
+    'alignment_format': 'PHYLIP',
+    'email': 'hmenager at pasteur.fr'}
+    resp = get_str(params)
+    print resp
diff --git a/Src/Mobyle/WorkflowSessionHTTPDemo2.py b/Src/Mobyle/WorkflowSessionHTTPDemo2.py
new file mode 100644
index 0000000..b67456e
--- /dev/null
+++ b/Src/Mobyle/WorkflowSessionHTTPDemo2.py
@@ -0,0 +1,129 @@
+########################################################################################
+#                                                                                      #
+#   Author: Herve Menager,                                                             #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+MOBYLEHOME = None
+
+import os
+import sys
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append(  os.path.join( MOBYLEHOME , 'Src' )  )
+
+
+import urllib, urllib2, cookielib #@UnresolvedImport
+from Mobyle.ConfigManager import Config
+from Workflow import Parser
+
+if __name__ == "__main__":
+    cfg = Config()
+    url = cfg.cgi_url()+'/workflow.py'
+
+        
+    # the cookie jar keeps the Mobyle session cookie in a warm place, so that 
+    # all the HTTP requests connect to the same workspace 
+    cj = cookielib.CookieJar()
+    opener = urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1),urllib2.HTTPCookieProcessor(cj))
+    mp = Parser()
+
+    def get_str(params):
+        data = urllib.urlencode(params)
+        req = urllib2.Request(url, data)
+        handle = opener.open(req)
+        str = handle.read()
+        return str.strip('\n')
+
+    def get_xml(params):
+        return mp.XML(get_str(params))
+    
+    # create a workflow and store its id
+    params = {
+              'object':'workflow',
+              'action':'create'
+              } 
+    w = get_xml(params)
+    print w
+    
+    # edit workflow properties
+    params = {
+              'object':'workflow',
+              'action':'update',
+              'workflow_id':w.id,
+              'description':'test workflow description',
+              'title':'test workflow title',
+              }
+    w = get_xml(params)
+    print w
+    
+    # create the clustalw-multialign task
+    params = {
+              'object':'task',
+              'action':'create',
+              'workflow_id':w.id,
+              'service_pid':'clustalw-multialign',
+              'suspend':'false',
+              'description':'Run a clustalw',
+              'parameter::infile':""">A
+TTTT
+
+>B
+TATA""",
+              'parameter::outputformat':'PHYLIP'
+              }
+    t = get_xml(params)
+    print t
+    
+    # create the protdist task
+    params = {
+              'object':'task',
+              'action':'create',
+              'workflow_id':w.id,
+              'service_pid':'protdist',
+              'suspend':'true',
+              'description':'Run a protdist',
+              'from_task': '1',
+              'from_parameter': 'aligfile',
+              'to_task': 'self',
+              'to_parameter': 'infile'
+              }
+    t = get_xml(params)
+    print t
+        
+    # get the workflow
+    params = {
+              'object':'workflow',
+              'action':'get',
+              'workflow_id':w.id
+              } 
+    w = get_xml(params)
+    print mp.tostring(w)
+
+    # get the workflow
+    params = {
+              'object':'workflow',
+              'action':'get_url',
+              'workflow_id':w.id
+              } 
+    w = get_str(params)
+    print "created workflow definition: %s" % w
+#
+    # list the session workflow IDs...
+    params = {
+              'object':'workflow',
+              'action':'list'
+              } 
+    l = get_str(params)
+    print "list of workflow definitions for current session: %s" % l
+    
+    url = cfg.cgi_url()+'/session_job_submit.py'
+    params = {'workflowUrl': w}
+    resp = get_str(params)
+    print resp
diff --git a/Src/Mobyle/__init__.py b/Src/Mobyle/__init__.py
new file mode 100644
index 0000000..c4ec062
--- /dev/null
+++ b/Src/Mobyle/__init__.py
@@ -0,0 +1,33 @@
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.        #
+#                                                                                      #
+########################################################################################
+
+__all__ = ['CommandBuilder',
+           'Evaluation',
+           'Job' ,
+           'MobyleError' ,
+           'MobyleJob' ,
+           'ConfigManager',
+           'Parser' ,
+           'Service',
+           'Session',
+           'AuthenticatedSession',
+           'AnnonymousSession',
+           'Transaction',
+           'RunnerFather',
+           'RunnerChild',
+           'JobState',
+           'Classes',
+           'SequenceConverter' ,
+           'AlignmentConverter' ,
+           'Utils',
+           'Net',
+           'Execution' ,
+           'Converter' ,
+           'JobFacade' ,
+           'Workflowjob',
+           ]
diff --git a/Src/Portal/cgi-bin/MobylePortal/bank_get.py b/Src/Portal/cgi-bin/MobylePortal/bank_get.py
new file mode 100755
index 0000000..93da8a2
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/bank_get.py
@@ -0,0 +1,61 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi, re
+from subprocess import Popen, PIPE
+
+def process( self ):
+    if( not(self.request.has_key('id') ) or not( self.request.has_key('db') )):
+        self.jsonMap['errormsg'] = "Please specify a database and an identifier."
+    else:
+        db = self.request.getfirst('db').strip()
+        id = self.request.getfirst('id').strip()
+        params = {'db':db.encode('string_escape'), 'id':id.encode('string_escape')}
+        path_exe , args = self.cfg.getDatabanksConfig()[db]['command']
+        args =  args % params 
+        args = args.split(' ')
+        cmd= [ path_exe ]
+        cmd.extend( args )
+        checkParameterValue( 'db' , db )
+        checkParameterValue( 'id' , id )
+        # retrieving...
+        process = Popen( cmd , shell = False , stdout = PIPE, stderr = PIPE )
+        outText = ""
+        for line in process.stdout :
+            outText = outText + line
+
+        errText = ""
+        for line in process.stderr :
+            errText = errText + line
+
+        process.wait()
+        returnCode = process.poll()
+        
+        if returnCode != 0 :
+            outText = ""
+        
+        self.jsonMap['returnCode'] = str(returnCode)
+        self.jsonMap['content'] = str(outText)
+        self.jsonMap['errormsg'] = str(errText)
+        
+
+def checkParameterValue(name, value):
+    """
+    I{checkParameterValue} checks that a given parameter contains only alphanumeric characters
+    This method is currently used only to log unusual entries, because the parameter values are escaped anyways to avoid injections.
+    """
+    ss = '^\w+$'
+    m = re.match(ss,value)
+    if(m==None):
+        # TODO this should be properly logged in the Mobyle log system
+        mb_cgi.c_log.warning("Unusual value in parameter " + name + "=" + value)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_bookmark.py b/Src/Portal/cgi-bin/MobylePortal/data_bookmark.py
new file mode 100755
index 0000000..cca902e
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_bookmark.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import Mobyle.MobyleJob 
+from Mobyle.MobyleError import NoSpaceLeftError, UserValueError
+from  Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.Service import MobyleType
+
+def process( self ):
+    try:
+        jobId = self.request.getfirst('job')
+        resultId = self.request.getfirst('safeFileName')
+        producerJob = Mobyle.MobyleJob.MobyleJob(ID=jobId)
+        parameterName = self.request.getfirst('parameter')
+        datatype_class = self.request.getfirst('datatype_class')
+        datatype_superclass = self.request.getfirst('datatype_superclass','')
+        format = self.request.getfirst('format','')
+        biotypes = self.request.getlist('biotypes')
+        df = DataTypeFactory()
+        if (datatype_superclass==''):
+            dt = df.newDataType(datatype_class)
+        else:
+            dt = df.newDataType(datatype_superclass, datatype_class)
+        mt = MobyleType(dt,bioTypes=biotypes)
+        if format:
+            mt.setDataFormat(format)
+        safeFileName = self.session.addData( resultId , mt, producer = producerJob , inputModes = ['result'], producedBy = jobId)
+        if self.request.has_key('userName'):
+            self.session.renameData(safeFileName,self.request.getfirst('userName'))
+    except NoSpaceLeftError, e:
+        mb_cgi.c_log.info("NoSpaceLeftError while bookmarking " + resultId + " - error: " +  str(e))
+        self.jsonMap['errormsg'] = str(e)
+    except UserValueError, e:
+        mb_cgi.c_log.info("UserValueError while bookmarking " + resultId + " as a " + str(mt) + " - error: " +  str(e))        
+        self.jsonMap['errormsg'] = str(e)        
+    else:
+        self.jsonMap['safeFileName'] = safeFileName
+        self.jsonMap['inputModes'] = self.session.getData(safeFileName).get('inputModes')
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process, useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_get.py b/Src/Portal/cgi-bin/MobylePortal/data_get.py
new file mode 100755
index 0000000..3a5baf2
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_get.py
@@ -0,0 +1,36 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from  Mobyle.Classes.DataType import DataTypeFactory
+
+def process( self ):
+    if(not(self.request.has_key('id'))):
+        self.jsonMap['errormsg'] = "Please specify the id of the data that has to be downloaded."
+    else:
+        id = self.request.getfirst('id')
+        # mobyle type determines wether we send back the data or not (if binary)
+        d = self.session.getData(id)
+        mt = d['Type']
+        df = DataTypeFactory()
+        contentData= self.session.getContentData(id)
+        self.jsonMap['userName'] = d['userName']
+        self.jsonMap['safeFileName'] = d['dataName']
+        if df.issubclass(mt.getDataType(),"Binary"):
+            self.jsonMap['content'] = d['dataBegin']          
+        else:
+            self.jsonMap['headFlag'] = contentData[0]
+            if(self.jsonMap['headFlag']=="HEAD"):
+                self.jsonMap['content'] = contentData[1][0:2000]
+            else:
+                self.jsonMap['content'] = contentData[1]
+              
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_remove.html b/Src/Portal/cgi-bin/MobylePortal/data_remove.html
new file mode 100755
index 0000000..8cbb59a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_remove.html
@@ -0,0 +1,9 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" action="data_remove.py" class="modal">
+  <strong>Bookmark removal</strong>
+  <div>This will remove this bookmark from your session. Continue?</div>
+  <input type="hidden" name="id" tal:attributes="value self/bookmarkId" />
+  <div>
+      <a href="#" class="closeModal" title="cancel"><input type="button" value="Cancel" id="bookmarkJobCancel" /></a>
+      <input type="submit" value="Remove" autofocus/>      
+  </div>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_remove.py b/Src/Portal/cgi-bin/MobylePortal/data_remove.py
new file mode 100755
index 0000000..6357754
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_remove.py
@@ -0,0 +1,22 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process( self ):
+    if self.session:
+        bookmark_ids = self.request.getlist('id')
+        for data_id in bookmark_ids:
+            if(self.session.hasData(data_id)):
+                self.session.removeData(data_id)
+    self.jsonMap['ok'] = str(True)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_remove_form.py b/Src/Portal/cgi-bin/MobylePortal/data_remove_form.py
new file mode 100755
index 0000000..ef212aa
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_remove_form.py
@@ -0,0 +1,18 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.bookmarkId = self.request.getfirst('id',None)
+    self.template_file = 'data_remove.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_rename.py b/Src/Portal/cgi-bin/MobylePortal/data_rename.py
new file mode 100755
index 0000000..d9bcba7
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_rename.py
@@ -0,0 +1,23 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process( self ):
+    self.message = ""
+    if self.session:
+        bookmark_id = self.request.getfirst('id')
+        userName = self.request.getfirst('userName').strip()
+        if(self.session.hasData(bookmark_id)):
+            self.session.renameData(bookmark_id,userName)
+            self.message = userName
+
+if __name__ == "__main__":
+    mb_cgi.TextCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_upload.py b/Src/Portal/cgi-bin/MobylePortal/data_upload.py
new file mode 100755
index 0000000..3b31918
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_upload.py
@@ -0,0 +1,67 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.MobyleError import UserValueError, NoSpaceLeftError
+from  Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.Service import MobyleType
+
+def process( self ):
+    if(not(self.request.has_key('data_input_upload'))):
+        self.jsonMap['errormsg'] = "Please specify a file to upload."
+    else:
+        if self.request["data_input_upload"].file is not None:
+            fileContent = self.request["data_input_upload"].file.read()
+            fileName = self.request["data_input_upload"].filename
+        else:
+            fileContent = self.request.getfirst("data_input_upload")
+            fileName = self.request.getfirst("data_input_upload.name","unnamed data bookmark")        
+        if self.request.getfirst("base64encoded")=="true":
+            import base64
+            fileContent = base64.decodestring(fileContent)
+        datatype_class = self.request.getfirst('datatype_class')
+        datatype_superclass = self.request.getfirst('datatype_superclass','')
+        df = DataTypeFactory()
+        if (datatype_superclass==''):
+            dt = df.newDataType(datatype_class)
+        else:
+            dt = df.newDataType(datatype_superclass, datatype_class)
+        
+        dt4session = dt.dataType if dt.isMultiple() else dt
+        mt = MobyleType(dt4session)
+        mt.setDataFormat(self.request.getfirst('format'))
+
+        try:
+            if(fileContent==''):
+                raise UserValueError(msg='empty file, please check your data')
+            safeFileName = self.session.addData( fileName , mt, content = fileContent , inputModes = self.request.getfirst("inputMode","upload"))
+            contentData = self.session.getContentData(safeFileName)
+            if df.issubclass( mt.getDataType() ,"Binary"):
+                self.jsonMap['content'] = "Binary data..."
+            else:
+                self.jsonMap['headFlag'] = contentData[0]
+                if(self.jsonMap['headFlag']=="HEAD"):
+                    self.jsonMap['content'] = contentData[1][0:2000]
+                else:
+                    self.jsonMap['content'] = contentData[1]
+        except UserValueError, e:
+            mb_cgi.c_log.info("UserValueError while uploading " + fileName + " - error: " +  str(e))
+            self.jsonMap['errormsg'] = str(e)
+        except NoSpaceLeftError, e:
+            mb_cgi.c_log.info("NoSpaceLeftError while uploading " + fileName + " - error: " +  str(e))
+            self.jsonMap['errormsg'] = str(e)
+        else:
+            self.jsonMap['userName'] = fileName
+            self.jsonMap['safeFileName'] = safeFileName
+            self.jsonMap['inputModes'] = self.session.getData(safeFileName).get('inputModes')
+            self.jsonMap['sessionUrl'] = self.session.url
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/data_view.py b/Src/Portal/cgi-bin/MobylePortal/data_view.py
new file mode 100755
index 0000000..1c7fa90
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/data_view.py
@@ -0,0 +1,26 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process( self ):
+    data = self.session.getData(self.request.getfirst('id'))
+    self.title = data['userName']
+    self.xmlUrl = self.session.getDir() + '/' + '.session.xml'
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/bookmark.xsl",
+                        {'dataId':"'"+self.request.getfirst('id')+"'",
+                         'sessionUrl':"'"+self.session.url+"'",
+                         'inputModes': "'"+' '.join(data['inputModes'])+"'"
+                         })
+                    ]
+              
+if __name__ == "__main__":
+    mb_cgi.XSLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/error.html b/Src/Portal/cgi-bin/MobylePortal/error.html
new file mode 100755
index 0000000..3a5bf8b
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/error.html
@@ -0,0 +1,8 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="usercaptchaForm" action="session_captcha_check.py" class="modal">
+  <strong style="color:red; font-weight:bold;" tal:content="string: ${self/error_title}"></strong>
+  <hr/>
+  <div>
+    <span style="font-size: 18px" tal:content="string: ${self/error_msg}" />
+  </div>
+  <a href="#" class="closeModal" title="cancel"><input type="button" value="Cancel" tabindex='3' /></a>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/feed_view.py b/Src/Portal/cgi-bin/MobylePortal/feed_view.py
new file mode 100755
index 0000000..ec11330
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/feed_view.py
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    # program exists on the Server
+    self.xmlUrl = self.request.getfirst('url')
+    self.xslPipe = [(self.cfg.portal_path()+"/xsl/"+self.request.getfirst('fmt')+".xsl",{'htdocsDir':"'"+str(self.cfg.portal_url(True))+"'"})]
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/file_load.py b/Src/Portal/cgi-bin/MobylePortal/file_load.py
new file mode 100644
index 0000000..07482cf
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/file_load.py
@@ -0,0 +1,30 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def job_suburls(job):
+    r = [job['jobID']]
+    for j in job.get('subjobs',[]) or []:
+        r += job_suburls(j)
+    return r
+
+def process( self ):
+    url = self.request.getfirst('url')
+    if self.session:
+        jobs = self.session.getAllJobs()
+        urls = [self.session.url]
+        for job in jobs:
+            urls += job_suburls(job)
+        if url.rpartition('/')[0] in urls:
+            self.url = url
+
+if __name__ == "__main__":
+    mb_cgi.ProxyCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/form.py b/Src/Portal/cgi-bin/MobylePortal/form.py
new file mode 100755
index 0000000..39e1762
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/form.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    self.xslParams = {}
+    try:
+        def f(x): return x and x != '.'
+        id = filter(f,self.request.getfirst('id').partition('.'))
+        try:
+            if len(id)==1:
+                self.service = registry.serversByName['local'].programsByName[id[0]]    
+            else:
+                self.service = registry.serversByName[id[0]].programsByName[id[1]]
+        except KeyError:
+            if len(id)==1:
+                self.service = registry.serversByName['local'].workflowsByName[id[0]]            
+            else:
+                self.service = registry.serversByName[id[0]].workflowsByName[id[1]]            
+        if not(self.service.server.name=='local'):
+            self.xslParams['remoteServerName']="'"+self.service.server.name+"'"
+    except Exception:
+        # program does not exist
+        self.error_msg = "Service %s cannot be found on this server." % self.request.getfirst('id','')
+        self.error_title = "Service not found"
+        return
+    if self.service.disabled:
+        # program disabled
+        self.error_msg = "this service has been disabled on this server"
+        self.error_title = "Service disabled"
+    elif not(self.service.authorized):
+        # program restricted
+        self.error_msg = "the access to this service is restricted on this server"
+        self.error_title = "Service unauthorized"
+    else:
+        # ok
+        self.xmlUrl = self.service.path
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/form.xsl",
+                        {'programUri':"'"+self.service.url+"'",
+                         'programPID':"'"+self.service.pid+"'"}),
+                    (self.cfg.portal_path()+"/xsl/remove_ns.xsl",{}) # remove xhtml namespace junk
+                    ]
+    if self.service.server.name=='local':
+        self.title = self.service.name
+    else:
+        self.title = self.request.getfirst('id')
+        tmptitle = self.title.split('.')
+        tmptitle.reverse()
+        self.title = '@'.join(tmptitle)
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/genericService.py b/Src/Portal/cgi-bin/MobylePortal/genericService.py
new file mode 100755
index 0000000..08d583e
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/genericService.py
@@ -0,0 +1,747 @@
+#!/usr/bin/env python
+
+import os
+import string
+import urllib
+import simplejson
+
+import mb_cgi
+
+import re
+from lxml import etree
+from Mobyle.JobFacade import JobFacade
+from Mobyle.Workflow import Workflow, Task, Parameter, Paragraph, Link, Type, Datatype, Biotype, InputValue, Value, VElem, Vlist,\
+    Parser
+from Mobyle.Parser import parseService
+from Mobyle.DataInputsIndex import DataInputsIndex
+from Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.SearchIndex import SearchIndex
+from Mobyle.Registry import registry
+import Mobyle.Net
+from Mobyle.MobyleError import SessionError, UserValueError, MobyleError
+
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.AnonymousSession import AnonymousSession
+from Mobyle.JobState import url2path
+from Mobyle.JobState import JobState, ProgramJobState
+
+# Library import for application category classification
+from Mobyle.Registry import CategoryDef, registry, ServiceTypeDef
+from Mobyle.ClassificationIndex import ClassificationIndex
+from Mobyle.InterfacePreprocessor import InterfacePreprocessor
+from Mobyle.Utils import emailHelpRequest
+
+from Mobyle.BMPSWorkflow import BMPSWorkflow,RenameError
+
+# Import treenode module - used for creating classification data in tree format
+import treenode
+
+
+
+# This CGI script will be used for generic services for the Mobyle Workflow projects
+# the form field 'action' will be used for deciding the functions performed. The possible
+# values of 'action' field are 
+# action=get_wf&wf_name=xxx
+# action=upload_wf_graphml&graphml=xxx&wf_name=yyy
+# action=rename_wf&wf_from_name=xxx&wf_to_name=yyy
+# action=delete_wf&wf_name=xxxx
+# action=graphviz_layout&wf_name=xxxx
+
+# Vivek Gopalan 05JAN2010
+
+graphml_file_name = ".workflow_template_graphml.xml"
+
+def intersect(seq1, seq2):
+    """ Utility method to fetch the common elements from 2 arrays."""
+    res = []                     # start empty
+    for x in seq1:               # scan seq1
+        if x in seq2:            # common item?
+            res.append(x)        # add to end
+    return res
+
+def get_classification(lines):
+    """Utility method to fetch create Tree structure in JSON array from the classification lines of the Mobyle XML elements"""
+    
+    # Create the root node
+    root_node = treenode.TreeNode("All apps");
+    node_data = {}
+    line_iter = iter(lines)
+    # Iterate over the every lines
+    while 1:
+        try:
+            line = line_iter.next()
+            line = line.rstrip()
+            fields = line.split(':') # Split based on ":" separator
+            # Remove the program name in beginning of the array and add it to the end
+            prog_name = fields.pop(0)
+            fields.append(prog_name)
+            no_of_fields = len(fields)
+            # Parse through the fields starting from index 1
+            for i in range(1, no_of_fields) :
+                child = fields[i];
+                parent = fields[ i - 1 ]
+                # Unique identifier based on all the parents  
+                id = ":".join(fields[0: i]);
+
+                # If the classification is already there
+                if node_data.has_key(id) :
+                    node = node_data[id]
+                # If the classification is not there
+                # the create a new node 
+                else :
+                    node = treenode.TreeNode(parent)
+                    node_data[id] = node
+                    # Add the child node structure to the root
+                    # This has to be done in the first iteration
+                    if i == 1 :
+                        root_node.add_child(node)
+                # Add child to the node 
+                if not node.has_child_with_name(child) :
+                    child_node = treenode.TreeNode(child)
+                    node.add_child(child_node)
+                    id = ":".join(fields[0:i + 1])
+                    node_data[id] = child_node
+        except StopIteration:
+            break
+    return root_node
+
+def get_pipeout_params(program_name, param_name,classification_list ):
+    """
+    Utility method to fetch the output applications and its parameters. This is based
+    on the comparison of the superclass,class and biotypes values of all the applications 
+    in Mobyle with that of the input application name and parameter.
+    """
+    out_param_list=[]
+    # Fetch all the program and viewer data input indexes
+    pdi = DataInputsIndex("program")
+    vdi = DataInputsIndex("viewer")
+    # Fetch the unique URL identifier of the program from the registry
+    program_url = registry.serversByName['local'].programsByName[program_name].url
+    # Fetch the input data type of the program
+    service = parseService(program_url)
+    inp_datatype = service.getDataType(param_name)
+    inp_biotypes = service.getBioTypes(param_name)
+    df = DataTypeFactory()
+    # get all the data input options from the indexes
+    dataInputsList = pdi.getList().values() + vdi.getList().values()
+    
+    # parse through every datatype of all the mobyle programs
+    for parameter in dataInputsList:
+        try:
+            # 1. fetch the datatype and biotype
+            if parameter.get('dataTypeSuperClass'):
+                dt = df.newDataType(parameter.get('dataTypeSuperClass'), parameter.get('dataTypeClass'))
+            else:
+                dt = df.newDataType(parameter.get('dataTypeClass'))
+            if dt.isMultiple():
+                dt = dt.dataType
+        except MobyleError, me:
+            mb_cgi.c_log.warning(me)
+            continue
+        datatype_match = True
+        biotype_match = True
+        bio_types = parameter.get('biotypes')
+        # 2. Match the data type and biotype of the mobyle program with the input values
+        if (inp_biotypes is not None and bio_types is not None and len(intersect(inp_biotypes, bio_types)) == 0):
+            biotype_match = False
+        if inp_datatype.name not in dt.ancestors:
+            datatype_match = False
+        if (datatype_match and biotype_match):
+            pattern = re.compile("%s:" % parameter.get('programName'))
+            subs_val = parameter.get('programName') + '|' + parameter.get('name') + ":"
+            c= [re.sub(pattern,subs_val,l.rstrip()) for  l in classification_list if (pattern.match(l.rstrip()) is not None)]
+            # populate output list if there is datatype and biotype match
+            if c:
+                out_param_list.extend(c)
+    return out_param_list
+
+def get_pipeout_status(program_name,param_name,params_string):
+    """ 
+    Utility method to fetch the status of the output parameter of a Mobyle program name.
+    The status is reported only to the subset of parameters specified in the params_string.
+    
+    Used to get the status of an program's parameters for connecting to the already existing
+    programs in the workflow canvas.
+    
+    @param program_name: Mobyle program name
+    @param param_name: Parameter of the program
+    @param params_string: Comma-delimited program name:parameter string 
+    """
+    out_status_list=[]
+    # Open the input data type indexes of all the programs in Mobyle 
+    pdi = DataInputsIndex("program")
+    vdi = DataInputsIndex("viewer")
+    ## For testing
+    #program_name = 'clustalw-multialign'
+    #param_name = 'newtreefile'
+    
+    # Fetch the unique url and datatypes for the input parameter
+    program_url = registry.serversByName['local'].programsByName[program_name].url
+    service = parseService(program_url)
+    inp_datatype = service.getDataType(param_name)
+    inp_biotypes = service.getBioTypes(param_name)
+    df = DataTypeFactory()
+    dataInputsList = pdi.getList().values() + vdi.getList().values()
+    
+    params_list = params_string.split(",")
+    status_hash ={}
+    
+    for parameter in dataInputsList:
+        try:
+            # filter the results only to the input parameters - skip the other program:parameter names
+            if (parameter.get('programName') + ':' + parameter.get('name') not in params_list):
+                continue
+            # Fetch the datatype and biotypes of the mobyle parameter
+            if parameter.get('dataTypeSuperClass'):
+                dt = df.newDataType(parameter.get('dataTypeSuperClass'), parameter.get('dataTypeClass'))
+            else:
+                dt = df.newDataType(parameter.get('dataTypeClass'))
+            if dt.isMultiple():
+                dt = dt.dataType
+            datatype_match = True
+            biotype_match = True
+            bio_types = parameter.get('biotypes')
+            # Camparate the datatype and biotype with that of the input parameter
+            if (inp_biotypes is not None and bio_types is not None and len(intersect(inp_biotypes, bio_types)) == 0) :
+                bioTypes = False
+            if inp_datatype.name not in dt.ancestors:
+                datatype_match = False
+            if (datatype_match and biotype_match):
+                # Store the status if the datatype and biotype matches
+                status_hash[parameter.get('programName') + ':' + parameter.get('name')] = True
+        except:
+            mb_cgi.c_log.error("Error computing pipes", exc_info=True)
+    # Process the results as list
+    for x in params_list:            
+        if(status_hash.has_key(x)):
+            out_status_list.append("True")
+        else:
+            out_status_list.append("False")
+    return ",".join(out_status_list)
+
+def generate_classification(node):
+    """
+    Utility method to generate tree structure for client-side processing from the Tree object
+    """
+    json_node = {'name': node.name}
+    children = []
+    for c in node.getChildCategories():
+        children.append(generate_classification(c))
+    for s in node.getChildServices():
+        children.append({'name':s.name})
+    if len(children)>0:
+        json_node['children']=children
+    return json_node
+
+class CustomResolver(etree.Resolver):
+    """
+    CustomResolver is a Resolver for lxml that allows (among other things) to
+    handle HTTPS protocol, which is not handled natively by lxml/libxml2
+    """
+    def resolve(self, url, id, context):
+        return self.resolve_file(urllib.urlopen(url), context)
+
+parser = etree.XMLParser(no_network=False)
+parser.resolvers.add(CustomResolver())
+
+def process(self):
+    """
+    The overloaded method that performs the processing of results in this web service
+    """
+    
+    # Fetch the form request handler
+    form = self.request
+    # Fetch the action attribute - all the calls to the service must be associated 
+    # with a unique action value -e.g form_submit, get_portal_config, etc..
+    action_name = form['action'].value
+    
+    # The output message content
+    self.message = ''
+    
+    # session id
+    sess_id = self.sessionKey
+    self.anonymousSession = self.session
+    
+    if action_name == 'get_portal_config':
+        # used by BMPS to get the Mobyle authentication possibilities according to the current configuration
+        self.mime_type="application/json"
+        self.message = '{"anonymous_session":"%s","authenticated_session":"%s"}' % (self.cfg.anonymousSession(), self.cfg.authenticatedSession())
+    if (action_name != 'login' and action_name != 'logout') :
+        # performs initialization steps when the actions are not related to login 
+        if self.session is not None:
+            tmp_data_dir = os.path.realpath( os.path.join(self.session.getDir(), 'BMW' ))
+            # Bug: This needs to be changed. Workflow name should be prepended to the
+            # file or the better solution is to directly save the content to the
+            # workflow XML instead of writing to the tasks.xml - Vivek gopalan 15NOV2011
+            wfSessionDirName = AuthenticatedSession.DIRNAME if self.session.isAuthenticated() else AnonymousSession.DIRNAME
+            if not os.path.exists(tmp_data_dir):
+                os.makedirs( tmp_data_dir, 0755 ) #create parent directory
+            tmp_url = "%s/%s/%s/%s" % ( self.cfg.user_sessions_url(),wfSessionDirName,sess_id,'BMW')
+    if action_name == 'graphviz_layout' :
+        # returns the graphviz layout for the workflow in graphml format
+        # action=graphviz_layout&wf_name=xxxx[&graphml_content=xxx&output_graphml=1
+        wf_name = form['wf_name'].value
+        wff = BMPSWorkflow(wf_name, self.session)
+        self.message = wff.graphviz_layout()
+
+    elif action_name =='login':
+        # BMPS and BMID action for login to the Mobyle system
+        # action=login&email=xx at gmail.com&password=xxxx
+        #
+        # replies a JSON message: 
+        # 'status' is true/false according to authentication success/fail
+        # 'message' is an optional error message (if authentication fails)
+        # 'source' is the invalid field name (interface should focus on it)
+        self.mime_type="application/json"
+        response = {'status':True}
+        email          = form.getfirst('email')
+        password       = form.getfirst('password')
+        create_account = False
+        try:
+            mobyle_email = Mobyle.Net.EmailAddress( email )
+        except MobyleError:
+            response = {'status':False, 'source' : 'email','message': 'Invalid e-mail, please provide a valid address'}
+        else:
+            if form.has_key('create_account') and form.getfirst('create_account') == 'yes' :
+                create_account = True 
+                if (create_account == True) :
+                    try:
+                        self.session = self.sessionFactory.createAuthenticatedSession( mobyle_email, password)
+                    except Exception, e:
+                        mb_cgi.c_log.error("error creating a session: %s" % str(e), exc_info=True)
+            try:
+                self.anonymousSession = self.session
+                self.session = self.sessionFactory.getAuthenticatedSession(mobyle_email,passwd=password)
+                if self.anonymousSession and self.anonymousSession != self.session:
+                    self.session.mergeWith(self.anonymousSession)
+                self.sessionKey = self.session.getKey()
+                self.read_session()
+                self.message = str(True)
+            except SessionError, e:
+                response = {'status':False, 'source' : 'email','message': str(e)}
+        self.message = simplejson.dumps(response, encoding='ascii')
+
+    elif action_name =='validate_guest_access':
+        # validate_guest_access = provide an email and/or captcha if necessary to submit
+        # jobs as a guest
+        # replies a JSON message: 
+        # 'status' is true/false according to authentication success/fail
+        # 'message' is an optional error message (if authentication fails)
+        # 'source' is the invalid field name (interface should focus on it)
+        self.mime_type="application/json"
+        email          = form.getfirst('email')
+        captcha_answer       = form.getfirst('captcha_answer')
+        response = {'status':True}
+        try:
+            self.session.setEmail(Mobyle.Net.EmailAddress(email))
+        except MobyleError:
+            response = {'status':False, 'source' : 'email','message': 'Invalid e-mail, please provide a valid address'}
+        else:
+            try:
+                ok = self.cfg.anonymousSession()=='yes' or self.session.checkCaptchaSolution(captcha_answer)
+            except MobyleError:
+                ok = False
+            if not(ok):
+                response = {'status':False, 'source' : 'captcha','message': 'Wrong answer, try again'}
+        self.message = simplejson.dumps(response, encoding='ascii')
+    elif action_name =='logout':
+        # Used in BMPS and BMID module to logout and delete the current session
+        try:
+            # Check if non-anonymous session and then assigns the current session to 
+            # anonymous session
+            if (self.cfg.anonymousSession()!='no'):
+                self.session = self.sessionFactory.getAnonymousSession()
+                self.sessionKey = self.session.getKey()
+            else:
+                self.session = None
+                self.sessionKey = None
+            self.read_session()
+            self.message = str(True)
+        except SessionError, e:
+            self.message  = str(False)
+            self.message  += " - " +str(e)
+    elif action_name == 'list_workflow_with_description':
+        # Used in BMPS to list all the workflow name with the description
+        if self.session is not None:
+            session_folder = self.session.getDir()
+            workflow_list = list_workflow_with_description(session_folder)
+            self.message = simplejson.dumps(workflow_list)
+    elif action_name == 'send_job_report' :
+        # Used in BMPS to send the email about the job report. Job id must be present in the form request 
+        # action=send_job_report&id=<job_id>&message="Job did not run"&param="
+        job_id = self.request.getfirst('id',None)
+        job_url = None
+        if id is not None:
+            try:
+                job_url = registry.getJobURL(job_id)
+            except KeyError:
+                pass
+        emailHelpRequest(self.cfg, self.session.getEmail(), \
+                         registry, job_url, \
+                         form.getfirst('message',None), self.session, \
+                         self.request.getfirst('param',None), \
+                         self.request.getfirst('message',None)) 
+        self.message = "True"
+    elif action_name == 'get_wf' :
+    # Used in BMPS to fetch a specific workflow in graphml format. GraphML format is used in the 
+    # Client-side to display the workflow in the web interface
+    #action=get_wf&wf_name=xxx
+        self.message = "<graphml/>";
+        if self.session is not None:
+            wf_name = form['wf_name'].value
+            wff = BMPSWorkflow(wf_name, self.session)
+            wf_name = form['wf_name'].value
+            self.message = wff.get_graphml()
+    elif action_name == 'upload_wf_graphml' :
+    # Used in the BMPS to save the workflow to the session based on the graphML content 
+    # action=upload_wf_graphml&wf_name=xxxx&graphmla=<graphml content>
+        wf_name = form['wf_name'].value
+        # Create the BMPSWorkflow object
+        wff = BMPSWorkflow(wf_name, self.session)
+        # update the graphml content
+        wff.update_graphml(form['graphml'].value)
+        self.message = "workflow saved successfully: <a href ='" + tmp_url + '/' + wff.mobylexml_filename + "' target='_blank'>" + wff.mobylexml_filename + "</a>"
+        
+    elif action_name == 'delete_wf' :
+        # Used in the BMPS to delete the workflow
+        # action=delete_wf&wf_name=xxxx
+        wf_name = form['wf_name'].value
+        wff = BMPSWorkflow(wf_name, self.session)
+        wff.delete()
+        self.message = "SUCCESS"
+    elif action_name == 'rename_wf' :
+        # Used in the BMPS to rename the workflow name
+        # action=rename_wf&wf_from_name=xxx&wf_to_name=yyy
+        self.mime_type="application/json"
+        old_name = self.request.getfirst('wf_from_name')
+        new_name = self.request.getfirst('wf_to_name')
+        wff = BMPSWorkflow(old_name, self.session)
+        status = True
+        message = "everything is ok" # default error message
+        if old_name!=new_name:
+            try:
+                wff.rename_wf(new_name, self.request.getfirst('wf_description'))
+            except RenameError, r_e:
+                status = False
+                message = r_e.message
+        else:
+            wff.change_wf_description(self.request.getfirst('wf_description'))
+        self.message = simplejson.dumps({'status':status,'message':message}, encoding='ascii')
+    elif action_name == 'get_apps_class_future':
+        self.mime_type="application/json"
+        p_classification = ClassificationIndex("program")
+        #TODO: before to use the workflows:
+        # - they should be made "draggable" into the canvas area (currently it crashes the editor)
+        # - the issue of user defined workflows appearing there should be solved
+        #w_classification = ClassificationIndex("workflow")
+        p_classification.buildRegistryCategories(field=self.request.getfirst('classifyBy','category'),serviceTypeSort=self.request.getfirst('serviceTypeSort','separate'))
+        #w_classification.buildRegistryCategories(field=self.request.getfirst('classifyBy','category'),serviceTypeSort=self.request.getfirst('serviceTypeSort','separate'))
+        registry.name = "All apps"
+        programs = [c for c in registry.getChildCategories() if c.name=="Programs"][0]
+        classification =generate_classification(programs)
+        self.message = simplejson.dumps({"status":"success","details":classification,"message":"success"}, encoding='ascii')
+    elif action_name == 'get_apps_class' :
+        self.mime_type="application/json"
+        s_type = 'program'
+        field ='category'
+        if form.has_key('format') and form.getfirst('format') == 'app_names_only':
+            app_names_list=[p.name for p in registry.programs]
+            app_names_list.sort(key=string.lower) # sort alphabetically
+            self.message += '{"status":"success","details": {"files": ["' + '","'.join(app_names_list)+ '"]},"message":"success"}'
+        else:
+            # fetch the Classification index (generated by mobdeploy command)
+            p_classification = ClassificationIndex(s_type)
+            index = p_classification.index
+            classifications = []
+            for s in getattr( registry, s_type + 's'):
+                cats = index[s.url][field]
+                for cn in cats:# for each classification of the program
+                    out = s.name.strip()+ ':' + cn.strip()
+                    classifications.append(out)
+            if form.has_key('app_name'):
+                app_name = form.getfirst('app_name')
+                param_name = form.getfirst('param_name')
+                classification_list = get_pipeout_params(app_name, param_name,classifications)
+                root_node1 = get_classification(classification_list)
+                self.message += '{"status":"success","details": ' + root_node1.to_json_str() + ',"self.message":"success"}'
+            else:
+                root_node = get_classification(classifications)
+                self.message += '{"status":"success","details": ' + root_node.to_json_str() + ',"message":"success"}'
+    elif action_name == 'check_pipeins' :
+            # Action check whether the status of various input parameters can be piped to the selected input program's parameter
+            # action=check_pipeins&app_name=bowtie&param_name=output&params=tophat:input,soapsnp:input_bam
+            app_name = form.getfirst('app_name')
+            param_name = form.getfirst('param_name')
+            params = form.getfirst('params')
+            out = get_pipeout_status(app_name,param_name, params)
+            self.message += out
+
+    elif action_name == 'form_submit' :
+        # BMPS Action to save a specific program parameters in the Mobyle workflow as BMPSWorkflow object
+        # action=form_submit&wf_name=xxx&app_id=bowtie_14555
+        task_id = form.getfirst('app_id')
+        wf_name = form['wf_name'].value
+        wff = BMPSWorkflow(wf_name, self.session)
+        wff.set_task_values(task_id, form)
+        self.message = "Saved successfully.."
+    elif action_name == 'get_datatype' :
+        # Action used in BMID to get all the class and superclass datatypes of the Mobyle applications
+        # The output is a JSON object with the class and superclass contents
+        # action=get_datatype
+        data_types = {}
+        class_types = set()
+        class_superclass_types = set()
+        #data_types["class_only"]={}
+        data_types["class_superclass"]={}
+        si = SearchIndex("program")
+        servicesList = getattr( registry, si.type + 's')[:]
+        for s in servicesList:
+            s.searchMatches = []
+            param_class = param_superclass = None
+            if si.index.has_key(s.url):
+                for field, value in si.index[s.url].items():
+                    if field == 'parameter class':
+                        param_class = value[0]
+                    if field == 'parameter superclass':
+                        param_superclass = value[0]
+                if not param_superclass :
+                    class_types.add(param_class)
+                else :
+                    class_superclass_types.add(param_class + ":" + param_superclass)
+                    #data_types["class_superclass"][param_class] = param_superclass
+
+        #data_types["class_only"] = list(class_types)
+        data_types["class_superclass"] = sorted(list(class_types)) + sorted(list(class_superclass_types))
+        self.message = data_types
+ 
+    elif action_name == 'get_app_xml':
+        # BMPS action to fetch the mobyle XML of a specific application
+        # This action is used to fetch Mobyle Program XML require for BMID and BMPS Client side form rendering.
+        # The interface and layout tags are removed from main program XML since it is not required by the BMID or Workflow projects
+        #  1. When app_id and wf_name are provided then the Program XML files is returned with for the program associated with the app_id
+        #      action=get_app_xml&wf_name=test&app_id=3434243&app_name=bowtie
+        #  2. When app_id and job_id are specified then the Program XML files is returned for the program name associated with the workflow job id
+        #     the vdef tags in parameters filled with values from job results data.
+        #      action=get_app_xml&job_id=test.1343&app_id=3434243&app_name=bowtie
+        if form.has_key('wf_name'):
+            # Process wf_name (used before job submission)
+            task_id = form.getfirst('app_id')
+            app_name = form.getfirst('app_name')
+            wf_name = form['wf_name'].value
+            wff = BMPSWorkflow(wf_name, self.session)
+            self.message=wff.get_task_xml(task_id,app_name)
+            return
+        else:
+            values = None
+            if form.has_key('job_id'):
+                # Process workflow job (used after job submission)
+                job_id = form.getfirst('job_id')
+                values = []
+                jobUrl = registry.getJobURL(job_id);
+                job = self.session.getJob(jobUrl);
+                jobState = JobState(jobUrl)
+                params = []
+                input_job_files = jobState.getInputFiles()
+                # Process all the parameters
+                for param_name,value in jobState.getArgs().items() :
+                    isResubmit = False
+                    # Process the input file separately
+                    # Since Multiple* parameters can return multiple values
+                    # parameter values are returned as array
+                    for file_param_name,file_values in input_job_files :
+                        if (file_param_name == param_name) :
+                            value =[]
+                            for file_name in [ x[0] for x in file_values] :
+                                value1 = file_name
+                                for data_id in job['dataUsed']:
+                                    data = self.session.getData(data_id)
+                                    if data['userName'] == value1 :
+                                        isResubmit = True
+                                        value.append(data_id + ',' + job['jobID'] + '/' + value1 + ',' +  value1)
+                    values.append((param_name,value,isResubmit))
+            app_name = form.getfirst('app_name')
+            app_xml_file = registry.getProgramPath(app_name)
+            program_node = set_default_value(app_xml_file,values)
+            self.message = etree.tostring(program_node)
+    elif action_name == 'get_job_graphml':
+        # This method is used in BMPS to get the worflow in GraphML format based on the input job ID.
+        # i.e. this action is used to fetch and render workflow diagram in the client after a job is submitted
+        # action=get_job_graphml&id=<job id>
+        wfId =self.request.getfirst('id',None)
+        self.message = get_formatted_job_entries(wfId,self.session)
+
+
+def set_default_value(app_xml_file,task_inp_values):
+    # This method removes the vdef element values in the parameters/values specified in the
+    # task_input_values list and then add the values in the task_input_values for the
+    # parameters as vdef element value.
+    # the modified program Mobyle XML content is sent to the client for rendering by GWT libraries in
+    # Mobyle-client libraries
+    program_node = None
+    if (task_inp_values is not None):
+        program_node = etree.parse(app_xml_file)
+        # 1. Remove the already existing vdef elemet values in the Mobyle program XML
+        for param_name, value, isResubmit in list(task_inp_values):
+            params = program_node.xpath(".//parameters/parameter[name=\"%s\"]" % param_name)
+            for param in params:
+                vdef_node = param.find('vdef')
+                if vdef_node is not None:
+                    param.remove(vdef_node)
+        # 2. Create vdef element and add values based on the input task values
+        for param_name, value,isResubmit in list(task_inp_values):
+            vdef_new = etree.Element("vdef")
+            # Multiple* Parameter values are returned as list
+            if isinstance(value, list) :
+                tmp_values = value;
+            else :
+                tmp_values = [value]
+            for v in tmp_values :
+                value_node = etree.Element("value")
+                value_node.text = v
+                vdef_new.append(value_node)
+            # 3. Add the new vdef element to the program
+            params1 = program_node.xpath(".//parameters/parameter[name=\"%s\"]" % param_name)
+            for param1 in params1:
+                param1.append(vdef_new)
+            if isResubmit :
+                param1.set('isResubmit','1')
+    else:
+        if os.path.exists(app_xml_file):
+            program_node = etree.parse(app_xml_file)
+        if program_node is not None:
+            # removed elements not required for the BMID and Workflow client side form rendering
+            # This save some network bandwidth
+            _remove_from_parent(program_node,".//interface")
+            _remove_from_parent(program_node,".//layout")
+            _remove_from_parent(program_node,".//parameter/[name='stdout']")
+            _remove_from_parent(program_node,".//parameter/[name='stderr']")
+    return program_node
+
+def _remove_from_parent(node,xpath_str) :
+    # private method to remove XML elements from the parent nodes
+    # the XML elements are obtained based on the XSL search on the input XML Node object
+    eles = node.findall(xpath_str)
+    for el in eles:
+        el.getparent().remove(el)
+
+def get_formatted_job_entries(wfId,session):
+    try:
+        wfUrl = registry.getJobURL(wfId)
+        wfJob = session.getJob(wfUrl)
+        jf = JobFacade.getFromJobId(wfJob[ 'jobID' ])
+        wfState = JobState(wfUrl)
+        wfJob['subjobs'] = jf.getSubJobs()
+        graphmlAbsPath = os.path.join(url2path(wfJob['jobID']), graphml_file_name)
+        doc = etree.parse(graphmlAbsPath, parser)
+        root = doc.getroot()
+        for data_ele in root.xpath("graphml/graph/data[@key='workflowJobStatus']"):
+            data_ele.getParent().remove(data_ele)
+        if (wfJob['status']) :
+            for data_ele in root.xpath('/graphml/graph'):
+                dataNode = etree.Element("data")
+                dataNode.set("key", "workflowJobStatus")
+                dataNodeStatus = etree.Element("value")
+                dataNodeStatus.text = str(wfJob['status'])
+                dataNodeMessage = etree.Element("message")
+                dataNodeMessage.text = wfJob['status'].message
+                dataNode.append(dataNodeStatus)
+                dataNode.append(dataNodeMessage)
+                data_ele.append(dataNode)
+        for data_ele in root.xpath("/graphml/graph/node/data[@key='processJobStatus']"):
+            data_ele.getParent().remove(data_ele)
+            dataNode = etree.Element("data")
+            dataNode.set("key","processJobStatus")
+            dataNodeStatus = etree.Element("value")
+            dataNodeStatus.text = "undefined"
+            dataNode.append(dataNodeStatus)
+            data_ele.getParent().append(dataNode)
+        if wfJob.has_key('subjobs') and wfJob['subjobs'] is not None:
+            for subjob in wfJob['subjobs']:
+                jobUrl = subjob['jobID']
+                taskRef = wfState.root.xpath( 'jobLink[@jobId="%s"]/@taskRef' % jobUrl)
+                if ( taskRef and len(taskRef) > 0) :
+                    for data_ele in root.xpath('/graphml/graph/node[@id="%s"]' % taskRef[0]):
+                        dataNode = etree.Element("data")
+                        dataNode.set("key","processJobStatus")
+                        dataNodeStatus = etree.Element("value")
+                        dataNodeStatus.text = str(subjob['status'])
+                        dataNodeMessage = etree.Element("message")
+                        dataNodeMessage.text = subjob['status'].message
+                        dataNode.append(dataNodeStatus)
+                        dataNode.append(dataNodeMessage)
+                        data_ele.append(dataNode)
+                    for data_ele in root.xpath('/graphml/graph/node[@id="%s"]' % taskRef[0]):
+                        dataNode = etree.Element("data")
+                        dataNode.set("key","jobID")
+                        dataNode.text = str(subjob['jobPID'])
+                        data_ele.append(dataNode)
+        for data_ele in root.xpath("/graphml/graph/node[not(data/@key='processJobStatus')]"):
+            dataNode = etree.Element("data")
+            dataNode.set("key","processJobStatus")
+            dataNodeStatus = etree.Element("value")
+            dataNodeStatus.text = "undefined"
+            dataNode.append(dataNodeStatus)
+            data_ele.append(dataNode)
+        return etree.tostring(root,method="xml", pretty_print=True)
+    except Exception:
+        return "<graphml/>"
+
+def sorted_ls(path):
+    """
+    Utility method to sort the file contents in a specific directory by time
+    """
+    mtime = lambda f: os.path.getctime(os.path.join(path, f))
+    return list(sorted(os.listdir(path), key=mtime))
+
+def get_workflow_description(session_folder, workflow_name):
+    """
+    Utility method to the description associated with the workflow short-name from session folder
+    The description is obtained from the graphml's description element tag.
+
+    workflow_name: workflow name
+    return: description
+    """
+    source_folder = os.path.realpath(os.path.join(session_folder, 'BMW'))
+    source_filename = os.path.realpath(os.path.join(source_folder, workflow_name + '.graphml'))
+    if not os.path.exists(source_filename):
+        return ''
+    source_file = open(source_filename, 'r')
+    description = ''
+    try:
+        root_tree = etree.parse(source_file)
+        element = root_tree.find('graph')
+        description = element.get('description')
+        if description is None:
+            description = ''
+    except Exception:
+        pass
+    finally:
+        source_file.close()
+    return description
+
+def list_workflow_with_description(session_folder):
+    """
+    Utility method to fetch the workflow names with its description from the session folder
+    (A better BMPSWorkflow based solution should be provided in future - Vivek Gopalan 09FEB2013)
+    
+    session_folder: user session folder
+    return: list of workflow name and list of workflow description
+    """
+    source_folder = os.path.realpath(os.path.join(session_folder, 'BMW'))
+    sorted_filename_list = sorted_ls(source_folder)
+    pattern = re.compile('\.graphml$', re.IGNORECASE)
+    workflow_name_list = []
+    workflow_description_list = []
+    # Get all the files in the session folder tha ends with graphml
+    for filename in sorted_filename_list :
+        if pattern.search(filename):
+            workflow_name = re.sub('\.graphml$', '', filename)
+            workflow_name_list.append(workflow_name)
+            # fetch description from graphml file
+            workflow_description = get_workflow_description(session_folder, workflow_name)
+            workflow_description_list.append(workflow_description)
+    return [workflow_name_list, workflow_description_list]
+
+if __name__ == "__main__":
+    # the main entry class for this server
+    mb_cgi.HTMLCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/graphml_to_dot.xsl b/Src/Portal/cgi-bin/MobylePortal/graphml_to_dot.xsl
new file mode 100644
index 0000000..6ac5794
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/graphml_to_dot.xsl
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="2.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:output method="html" omit-xml-declaration="yes" indent="yes" />
+
+	<xsl:key name="get_node_id" match="graphml/graph/node" use="@id" />
+
+	<xsl:template match="/">
+		digraph GRAPH_0 {
+		graph [rankdir=LR];
+		node [label="\N", shape=record, style=filled];
+		<xsl:for-each select="graphml/graph/node">
+			<xsl:variable name="node_id" select="@id" />
+			<xsl:variable name="node_name" select="@id" />
+			<xsl:value-of select="$node_id" disable-output-escaping="no" />
+			[color="#EEEEE", shape=plaintext,label=
+			<xsl:text disable-output-escaping="yes"> < </xsl:text>
+			<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
+				<TR>
+					<TD HREF="#">
+						<xsl:attribute name="TITLE">
+                            <xsl:value-of select="$node_name" />
+                        </xsl:attribute>
+
+						<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
+							<TR>
+								<TD>
+									<xsl:if test="port/data[@key ='type'] = 'pipeIn'">
+										<xsl:call-template name="process_param">
+											<xsl:with-param name="node_name" select="$node_name" />
+											<xsl:with-param name="port_type" select="'pipeIn'" />
+										</xsl:call-template>
+									</xsl:if>
+								</TD>
+								<TD>
+									<xsl:value-of select="$node_name" />
+								</TD>
+								<TD>
+									<xsl:if test="port/data[@key ='type'] = 'pipeOut'">
+										<xsl:call-template name="process_param">
+											<xsl:with-param name="node_name" select="$node_name" />
+											<xsl:with-param name="port_type" select="'pipeOut'" />
+										</xsl:call-template>
+									</xsl:if>
+								</TD>
+							</TR>
+						</TABLE>
+					</TD>
+				</TR>
+			</TABLE>
+			<xsl:text disable-output-escaping="yes">></xsl:text>
+			];
+		</xsl:for-each>
+		<xsl:for-each select="graphml/graph/edge">
+			<xsl:value-of select="key('get_node_id', at source)/@id" />
+			:
+			<xsl:value-of select="@sourcePort" />
+			<xsl:text disable-output-escaping="yes">-></xsl:text>
+			<xsl:value-of select="key('get_node_id', at target)/@id" />
+			:
+			<xsl:value-of select="@targetPort" />
+			:c
+			<xsl:text>
</xsl:text>
+		</xsl:for-each>
+		}
+	</xsl:template>
+	<xsl:template name="process_param">
+		<xsl:param name="node_name" />
+		<xsl:param name="port_type" />
+		<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
+			<xsl:for-each select="port">
+				<xsl:if test="data[@key ='type'] = $port_type">
+					<TR>
+						<TD>
+							<xsl:attribute name="TITLE">
+                                <xsl:value-of
+								select="concat($node_name,':', @name)" />
+                            </xsl:attribute>
+							<xsl:attribute name="PORT">
+                                <xsl:value-of select="@name" />
+                        </xsl:attribute>
+							*
+						</TD>
+					</TR>
+				</xsl:if>
+			</xsl:for-each>
+		</TABLE>
+	</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/Src/Portal/cgi-bin/MobylePortal/graphml_to_wf.xsl b/Src/Portal/cgi-bin/MobylePortal/graphml_to_wf.xsl
new file mode 100644
index 0000000..26b43c1
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/graphml_to_wf.xsl
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="2.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
+	
+	<xsl:param name="workflow_id"></xsl:param>
+
+	<xsl:key name="get_node_id" match="/graphml/graph/node" use="@id" />
+
+	<xsl:template match="/">
+		<xsl:for-each select="graphml/graph">
+			<workflow>
+				<xsl:attribute name="id">
+                    <xsl:value-of select="@id" />
+                </xsl:attribute>
+                <head>
+                	<doc>
+                		<xsl:apply-templates select="data" />
+                 	</doc>
+                </head>
+				<flow>
+					<xsl:for-each select="node">
+						<task>
+							<xsl:attribute name="id">
+                                <xsl:value-of select="@id" />
+                            </xsl:attribute>
+							<xsl:attribute name="service">
+                                <xsl:value-of select="data[@key='name']" />
+
+                            </xsl:attribute>
+							<xsl:apply-templates select="data[@key='xy']" />
+							<xsl:apply-templates select="data[@key='position']" />
+						</task>
+					</xsl:for-each>
+  				<xsl:apply-templates select="node/port[data[@key='output' and text()='true']]" mode="link" />
+					<xsl:for-each select="edge">
+						<link>
+							<xsl:attribute name="id">
+                            <xsl:choose>
+                                <xsl:when test="@id !=''">
+                                    <xsl:value-of select="@id" />
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:value-of select="position()" />
+                                </xsl:otherwise>
+                            </xsl:choose>
+
+                        </xsl:attribute>
+							<xsl:attribute name="fromParameter">
+                            <xsl:value-of select="@sourcePort" />
+                        </xsl:attribute>
+							<xsl:attribute name="fromTask">
+                            <xsl:value-of
+								select="key('get_node_id', at source)/@id" />
+                        </xsl:attribute>
+							<xsl:attribute name="toParameter">
+                            <xsl:value-of select="@targetPort" />
+                        </xsl:attribute>
+							<xsl:attribute name="toTask">
+                            <xsl:value-of
+								select="key('get_node_id', at target)/@id" />
+                        </xsl:attribute>
+						</link>
+					</xsl:for-each>
+				</flow>
+				<parameters>
+					<xsl:apply-templates select="node/port[data[@key='output' and text()='true']]" mode="parameter"/>
+				</parameters>
+			</workflow>
+		</xsl:for-each>
+
+	</xsl:template>
+
+
+	<xsl:template match="port[data[@key='output' and text()='true']]"
+		mode="parameter">
+		<parameter isout="1">
+			<xsl:attribute name="id">
+        <xsl:value-of select="concat('workflow_',../@id,'_', at name)" />
+      </xsl:attribute>
+			<name>
+				<xsl:value-of select="concat('workflow_',../@id,'_', at name)" />
+			</name>
+			<prompt></prompt>
+		</parameter>
+	</xsl:template>
+
+	<xsl:template match="port[data[@key='output' and text()='true']]"
+		mode="link">
+		<link>
+			<xsl:attribute name="id">
+                        <xsl:value-of select="../@id" />
+                        <xsl:text>_</xsl:text>
+                        <xsl:value-of select="@name" />
+                        <xsl:text>_to_</xsl:text>
+								        <xsl:value-of select="concat('workflow_',../@id,'_', at name)" />
+                    </xsl:attribute>
+			<xsl:attribute name="fromParameter">
+                        <xsl:value-of select="@name" />
+                    </xsl:attribute>
+			<xsl:attribute name="fromTask">
+                        <xsl:value-of select="../@id" />
+                    </xsl:attribute>
+			<xsl:attribute name="toParameter">
+        <xsl:value-of select="concat('workflow_',../@id,'_', at name)" />
+      </xsl:attribute>
+		</link>
+	</xsl:template>
+	
+	<xsl:template match="data[@key='title']">
+		<title><xsl:value-of select="text()" /></title>
+	</xsl:template>
+
+	<xsl:template match="data[@key='description']">
+		<description>
+			<xsl:choose>
+				<xsl:when test="not(*)">
+					<text><xsl:value-of select="text()" /></text>
+				</xsl:when>
+				<xsl:otherwise>
+					<xsl:copy-of select="*" />
+				</xsl:otherwise>
+			</xsl:choose>
+		</description>
+	</xsl:template>
+
+	<xsl:template match="data[@key='comment']">
+		<comment>
+			<xsl:choose>
+				<xsl:when test="not(*)">
+					<text><xsl:value-of select="text()" /></text>
+				</xsl:when>
+				<xsl:otherwise>
+					<xsl:copy-of select="*" />
+				</xsl:otherwise>
+			</xsl:choose>
+		</comment>
+	</xsl:template>
+	
+	<xsl:template match="data[@key='xy']">
+		<position><xsl:value-of select="text()" /></position>
+	</xsl:template>
+
+	<xsl:template match="text" >
+		<xsl:value-of select="text()" />
+	</xsl:template>
+	
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/help_request.py b/Src/Portal/cgi-bin/MobylePortal/help_request.py
new file mode 100755
index 0000000..1be2d35
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/help_request.py
@@ -0,0 +1,29 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.MobyleError import UserValueError,EmailError
+from Mobyle.Registry import registry
+from Mobyle.Utils import emailHelpRequest
+
+def process( self ):
+    try:
+        self.jsonMap['ok'] = str(False)
+        self.jsonMap['msg'] = emailHelpRequest(self.cfg, self.request.getfirst('helpEmail',None), registry, self.request.getfirst('id',None), self.request.getfirst('helpMessage',None),self.session, self.request.getfirst('param',None), self.request.getfirst('message',None)) 
+        self.jsonMap['ok'] = str(True)
+    except UserValueError, ue:
+        self.jsonMap['errormsg'] = ue.message
+        return
+    except EmailError:
+        self.jsonMap['errormsg'] = "please provide a valid email adress"
+        return
+        
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/jobService.py b/Src/Portal/cgi-bin/MobylePortal/jobService.py
new file mode 100755
index 0000000..7cedb23
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/jobService.py
@@ -0,0 +1,439 @@
+#!/usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import sys, os
+from lxml import etree
+abspath = os.path.dirname(__file__)
+sys.path.append(abspath)
+os.chdir(abspath)
+
+import shutil
+import mb_cgi
+import warnings
+import re
+from Mobyle.JobState import url2path
+from  Mobyle.Parser import parseService
+
+
+#from Mobyle.JobFacade import LocalJobFacade
+from Mobyle.JobFacade import JobFacade
+from Mobyle.Registry import registry
+from Mobyle.StatusManager import StatusManager
+import Mobyle.Net
+import time #@UnresolvedImport
+from Mobyle.JobState import JobState
+from operator import itemgetter,attrgetter
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.AnonymousSession import AnonymousSession 
+
+debug = False
+#debug = True
+
+graphml_file_name = ".workflow_template_graphml.xml"
+if debug:
+    sys.stderr = sys.stdout
+    print "Content-Type: text/plain\n"
+
+
+def process(self):
+    graphmlWorkFlowPath = None
+    action_name = self.request.getfirst('action')
+    self.email = self.request.getfirst('email',None)
+    if action_name == 'form_submit' or action_name == 'submit':
+        if self.session is None:
+            self.jsonMap = [{'errormsg':'you need to login to execute a job on the server'}]
+        else:
+            pUrl  = None
+            wfUrl = None
+            if (self.request.has_key('pr_name')) :
+                pUrl = registry.getProgramUrl(self.request.getfirst('pr_name',None))
+            elif (self.request.has_key('wf_name')) :
+                dirName = AuthenticatedSession.DIRNAME if self.session.isAuthenticated() else AnonymousSession.DIRNAME
+                wfName = self.request.getfirst('wf_name',None)
+                wfUrl= "%s/%s/%s/%s" % ( self.cfg.user_sessions_url(),dirName,self.sessionKey,'BMW') 
+                wfUrl= "%s/%s" % (wfUrl, wfName +'_mobyle.xml')
+                graphmlWorkFlowPath = os.path.realpath( os.path.join(self.session.getDir(), 'BMW', wfName + '.graphml'))
+            else:
+                wfUrl=self.request.getfirst('workflowUrl',None)
+                
+            j = JobFacade.getFromService(programUrl=pUrl,workflowUrl=wfUrl)
+            j.email_notify = self.request.getfirst('_email_notify','auto')
+            job = j.create(self.request,None,self.session)
+            jobDetail= {}
+            jobDetail['wfUrl'] = wfUrl
+            if job.has_key('id') and not(job.has_key('errormsg')):
+                j.addJobToSession(self.session, job)
+                descr = self.request.getfirst('annotation_description',None)
+                if descr:
+                    self.session.setJobDescription(job['id'],descr)
+                graphmlJobDirPath = os.path.join(url2path(job['id']), graphml_file_name) 
+                if (graphmlWorkFlowPath is not None and os.path.exists(graphmlWorkFlowPath)) :
+                    shutil.copyfile(graphmlWorkFlowPath, graphmlJobDirPath)
+                #else :
+                    # Todo : 
+                    # 1. need to run wf_to_graphml.xsl script on the index.xml to graphml file
+                    # 2. convert graphml to dot using graphml_to_dot.xsl (as done in genericService.cgi)
+                    # 3. Get the coordinates of the application by running dot application
+                    #job1 = mb_cgi.get_formatted_job_entries(job)[0]
+                jobUrl= job['id']
+                job = self.session.getJob(jobUrl)
+                jf = JobFacade.getFromJobId( jobUrl )
+                job['subjobs']  = jf.getSubJobs() 
+                job['jobPID'] = registry.getJobPID(jobUrl)
+                job1= get_formatted_job_entries(job,self.session)
+                #job1['jobID'] = registry.getJobPID(jobUrl)
+                #job1['jobID'] = re.sub("/",".",jobId)
+                job1['wfUrl'] = wfUrl
+                self.jsonMap = [job1]
+            else:
+                if job.has_key('pid'):
+                    job['jobID'] = job['pid']
+                job['jobDate'] = time.strftime( "%x  %X", time.localtime())
+                job['status'] = "submit_error"
+                self.jsonMap = [job]
+    elif action_name == 'set_job_description':
+        jobId  = self.request.getfirst('id',None)
+        desc   = self.request.getfirst('description',None)
+        jobUrl = registry.getJobURL(jobId)
+        self.session.setJobDescription(jobUrl,desc)
+        self.jsonMap['ok'] = str(True)
+    elif action_name == 'rename_job':
+        # rename the workflow
+        # action=rename_wf&wf_from_name=xxx&wf_to_name=yyy
+        user_name = self.request.getfirst('userName')
+        job_id = self.request.getfirst('id')
+        job_url = registry.getJobURL(job_id)
+        job_graphml_file_path = os.path.join(url2path(job_url), graphml_file_name)
+        #modify user name in workflow graphml file if it exists
+        if os.path.exists(job_graphml_file_path):
+            # rename in the graphml file directly as well
+            source_file = open(job_graphml_file_path, 'r')
+            root_tree = etree.parse(source_file)
+            source_file.close()
+            graph = root_tree.find('graph')
+            if graph.get('userName')!=user_name:
+                graph.set('userName', user_name)
+                wffile = open(job_graphml_file_path, 'w' )
+                wffile.write( etree.tostring(root_tree)) 
+                wffile.close()
+        self.session.renameJob(job_url,user_name)
+        self.jsonMap['ok'] = str(True)
+    elif action_name == 'set_job_labels':
+        jobId  = self.request.getfirst('id',None)
+        jobUrl = registry.getJobURL(jobId)
+        labels = self.request.getlist('label')
+        if not labels :
+            labels = []
+        else:
+            labels = _unique_list(labels)
+        self.session.setJobLabels(jobUrl,labels)
+        self.jsonMap['ok'] = str(True)
+    elif action_name == 'get_job_description':
+        jobId  = self.request.getfirst('id',None)
+        jobUrl = registry.getJobURL(jobId)
+        desc = self.session.getJobDescription(jobUrl)
+        jobDetail = {}
+        jobDetail['description'] = desc 
+        self.jsonMap = jobDetail
+    elif action_name == 'get_all_labels':
+        jobDetail = {}
+        if self.session is not None:
+            allUniqlabels = self.session.getAllUniqueLabels()
+            if not allUniqlabels:
+                allUniqlabels = []
+            jobDetail['labels'] = allUniqlabels 
+        self.jsonMap = jobDetail
+    elif action_name == 'get_job_labels':
+        jobId  = self.request.getfirst('id',None)
+        jobUrl = registry.getJobURL(jobId)
+        jobDetail = {}
+        if self.session is not None:
+            labels = self.session.getJobLabels(jobUrl)
+            if not labels :
+                labels = []
+            jobDetail['labels'] = labels
+        self.jsonMap = jobDetail
+    elif action_name == 'get_job_status':
+        jobId =self.request.getfirst('id',None)
+        jobUrl = registry.getJobURL(jobId)
+        jobDetails = None
+        if self.session is not None:
+            job = self.session.getJobBMPS(jobUrl)
+            if job is not None:
+                jf = JobFacade.getFromJobId( job[ 'jobID' ] )
+                job['subjobs']  = jf.getSubJobs() 
+                job['jobPID'] = jobId
+                jobDetails = get_formatted_job_entries(job,self.session)
+        self.jsonMap = [jobDetails]
+    elif action_name == 'delete_jobs':
+        pids = self.request.getlist('id')
+        for pid in pids:
+            self.session.removeJob(registry.getJobURL(pid))
+        self.jsonMap['ok'] = str(True)
+    elif action_name == 'get_jobs':
+        self.mime_type="application/json"
+        if self.session is None:
+            jobs = []
+        else:
+            jobs = self.session.getAllJobs()
+        limit = self.request.getfirst('limit',None)
+        offset = self.request.getfirst('offset',None)
+        sortType = self.request.getfirst('order','desc')
+        cgiSortField  = self.request.getfirst('sortedBy','Submitted Time')
+        cgiFilteredByField = self.request.getfirst('filteredBy',"")
+        if debug:
+            print cgiFilteredByField
+        jobsOut = []
+        if sortType == 'desc' :
+            reverse = True
+        else :
+            reverse = False
+
+        if cgiSortField == 'Job ID':
+            sortField = 'jobID'
+        if cgiSortField == 'Job Name':
+            sortField = 'description'
+        elif cgiSortField == 'Job Status':
+            sortField = 'status'
+        else :
+            #cgiSortField =='Submitted Time' :
+            sortField = 'jobSortDate'
+
+        filteredBy= {}
+        cgiFilteredByField = re.sub("%3B",";",cgiFilteredByField)
+        #cgiFilteredByField = re.sub(";*$","",cgiFilteredByField)
+        if len(cgiFilteredByField) > 0 :
+            for eachField in cgiFilteredByField.split(';'):
+                fieldContent = eachField.split(':')
+                if debug:
+                    print fieldContent
+                values = fieldContent[1].split(',') 
+                filteredBy[fieldContent[0]] =  values
+
+
+        fromDate = toDate = None
+        if ('Submitted Time' in filteredBy) :
+            dateValues = filteredBy['Submitted Time']
+            if (len(dateValues) < 2) :
+                toDate      = int(time.strftime( "%Y%m%d%H%M%S"))
+            else:
+                toDate    = int(time.strftime( "%Y%m%d%H%M%S", time.strptime(dateValues[1],"%m/%d/%Y")))
+
+            fromDate    = int(time.strftime( "%Y%m%d%H%M%S", time.strptime(dateValues[0],"%m/%d/%Y")))
+
+        for job in jobs:
+            formatted_job = get_formatted_job_entries(job,self.session,compact=True)
+            jobUrl = registry.getJobURL(job['jobID'])
+            descr = self.session.getJobDescription(jobUrl)
+            if not descr:
+                descr = self.session.getJob(jobUrl).get('userName',job['jobID'])
+                self.session.setJobDescription(jobUrl,descr)
+            formatted_job['description'] = descr
+            labels = self.session.getJobLabels(jobUrl)
+            if not labels :
+                labels = []
+            formatted_job['labels'] = ", ".join(labels)
+            formatted_job['filteredBy'] = filteredBy
+            if ('Job ID' in filteredBy and ( not _match_string(filteredBy['Job ID'], [ formatted_job['jobID'] ] ))):
+                continue
+            if ('Job Name' in filteredBy and ( not _match_string(filteredBy['Job Name'], [ formatted_job['description'] ] ))):
+                continue
+            if ('Job Status' in filteredBy and ( not _match_string(filteredBy['Job Status'], [ formatted_job['status'] ] ))):
+                continue
+            if ('Labels' in filteredBy and ( not _match_string(labels,filteredBy['Labels'], strict = True ))):
+                continue
+            if ('Submitted Time' in filteredBy) :
+                a = int(formatted_job['jobSortDate'])
+                if ( a < fromDate or a > toDate) :
+                    continue
+            
+            jobsOut.append(formatted_job)
+        jobsOut = sorted(jobsOut,key=itemgetter(sortField),reverse=reverse)
+
+        jobDetails = {}
+        jobDetails['total'] = len(jobsOut)
+        
+        if (offset == None ) :
+            offsetInt = 0
+        else :
+            offsetInt = int(offset)
+
+        if (limit == None or limit == "All" or len(jobsOut) < offsetInt + int(limit)):
+            jobDetails['jobs']  = jobsOut[offsetInt:len(jobsOut)]
+        elif (int(limit) > 0 and len(jobsOut) >= int(limit) + offsetInt):
+            jobDetails['jobs']  = jobsOut[offsetInt:int(limit) + offsetInt]
+        self.jsonMap = jobDetails
+    elif action_name == 'get_params_status' :
+        app_name = self.request.getfirst('app_name')
+        task_id = self.request.getfirst('app_id')
+        program_url = registry.serversByName['local'].programsByName[app_name].url
+        service = parseService(program_url)
+        evaluator = service.getEvaluator()
+        paramsStatus = {}
+        for paramName in service.getUserInputParameterByArgpos():
+            param = service.getParameter(paramName)
+            paramValue = self.request.getfirst(paramName,None)
+            if paramValue is not None :
+                param.setValue(paramValue)
+        for paramName in service.getUserInputParameterByArgpos() + service.getAllOutParameter():
+            param = service.getParameter(paramName)
+            preconds = service.getPreconds( paramName , proglang='python' )
+            allPrecondTrue = True
+            for precond in preconds:
+                try:
+                    evaluatedPrecond = evaluator.eval( precond )
+                except EvaluationError, err:
+                    warnings.warn(err)
+                if not evaluatedPrecond :
+                    allPrecondTrue = False
+                    break
+            paramsStatus[paramName]= (allPrecondTrue,", ".join(preconds))
+            #if value is None :
+            #    if service.ismandatory( paramName ):
+            #       paramsStatus[''] = "Mandatory Parameter mission" 
+        self.jsonMap = paramsStatus
+
+def getJobDetails(jobState,name=None):
+    out ={}
+    jobdatestring = jobState.getDate()
+    jobId = jobState.getID()
+    jobId = re.sub("/$","",jobId)
+    m = re.search(".*/(.+?/.+?)$",jobId)
+    if m:
+        out['id'] = m.group(1)
+    else:
+        out['id'] = jobId
+    out['url'] = jobState.getID() 
+    if (name):
+        out['title'] = name
+    elif (name is None and jobState.getName()):
+        out['title'] = jobState.getName() + ' - ' + jobdatestring
+    else:
+        out['title'] =  ' job - ' + jobdatestring
+    status_manager = StatusManager()
+    out['status'] = str(status_manager.getStatus(jobState.getDir()))
+    out['date'] =jobdatestring 
+    return out
+
+def getJobLinks(wfNode):
+    jobLinksEle = wfNode.findall( './jobLink' )
+    out = []
+    for jobLinkEle in jobLinksEle:
+        out.append((jobLinkEle.get('taskRef'),jobLinkEle.get('jobId')))
+    return out
+
+#http://www.peterbe.com/plog/uniqifiers-benchmark - function f5
+def _unique_list(seq, idfun=None): 
+    # order preserving
+    if idfun is None:
+        def idfun(x): return x
+    seen = {}
+    result = []
+    for item in seq:
+        marker = idfun(item)
+        if marker in seen: continue
+        seen[marker] = 1
+        result.append(item)
+    return result
+
+def _match_string(stringVals,theList,strict=False):
+    for s in theList:
+        if debug:
+            print "%s - %s" % (s,"x")
+        for stringVal in stringVals:
+            if not strict and stringVal.lower() in s.lower():
+                return True
+            if strict and stringVal == s :
+                return True
+    return False
+
+def get_formatted_job_entries(job,session,compact=False):
+    """
+    get_formatted_job_entries formats a list of jobs so that it can be both
+    serialized to json and later used by the portal.
+    """
+    #warnings.warn(str(job))
+    if job.has_key('date') and job['date'] is not None and (not isinstance(job['date'],str)):
+        jobdatestring = time.strftime( "%x    %H:%M", job['date'])
+        jobsortdate = time.strftime( "%Y%m%d%H%M%S", job['date'])
+    else:
+        jobdatestring = '?'
+        jobsortdate = '?'
+    job['status_message'] = ""
+    if hasattr(job['status'], 'message'):
+        job['status_message'] = job['status'].message
+    job['status'] = str(job['status'])
+    job['url'] = job['jobID']
+    job['jobID'] = job['jobPID']
+
+    l = job['jobID'].split('.')
+    if len(l)==2:
+        server_name='local'
+    else:
+        server_name= l[0]
+    jobUrl = job['url']
+    description = ''
+    labels = []
+    allUniqlabels = []
+    user_name = job['jobID']
+    # If the job is in the session
+    if session.hasJob(jobUrl):
+        description = session.getJobDescription(jobUrl)
+        user_name = session.getJobUserName(jobUrl)
+        labels = session.getJobLabels(jobUrl)
+        allUniqlabels = session.getAllUniqueLabels()
+    params = []
+    other_params = {}
+    jobState = JobState(jobUrl)
+    for param_name,value in jobState.getInputFiles() or []:
+        params.append(param_name)
+    outputs     = dict(jobState.getOutputs() or [])
+    inputs     = dict(jobState.getInputFiles() or [])
+    for param_name,value in jobState.getArgs().items() :
+        if param_name in params:
+            continue
+        other_params[param_name] = value
+    command_line_str = ''
+    if jobState.isWorkflow() == False:
+        command_line_str = jobState.getCommandLine() 
+    #command_line_str = str(type(jobState))
+    values = {
+             'server':server_name,
+             'programName': job['programName'] ,
+             'jobDate': jobdatestring ,
+             'jobSortDate':jobsortdate,
+             'status':job['status'] ,
+             'status_message':job['status_message'],
+             'dataUsed': job.get('dataUsed'),
+             'url':job['url'],
+             'labels': labels,
+             'description': description,
+             'userName': user_name,
+             'all_unique_labels': allUniqlabels,
+             'jobID': job['jobID']}
+    top_job_data = values
+    top_job_data['userName'] = job['userName']
+    if (not compact) :
+        values['outputs']= outputs
+        values['inputs']=inputs
+        values['other_parameters']=other_params
+        values['command_line_str'] = command_line_str
+        if job.has_key('owner'):
+            values['owner'] = job['owner']
+        subjob_list = []
+        if job.has_key('subjobs') and job['subjobs'] is not None:
+            for subjob in job['subjobs']:
+                subjob_list.append(get_formatted_job_entries(subjob,session))    
+        top_job_data['tasks'] = subjob_list 
+    return top_job_data
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
+
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_kill.py b/Src/Portal/cgi-bin/MobylePortal/job_kill.py
new file mode 100755
index 0000000..71bb892
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_kill.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+from Mobyle.Registry import registry
+
+def process(self):
+    if(self.request.has_key('jobId')):
+        j = JobFacade.getFromJobId(self.request.getfirst('jobId',None))
+    elif(self.request.has_key('jobPid')):
+        pid = registry.getJobURL(self.request.getfirst('jobPid',None))
+        j = JobFacade.getFromJobId(pid)
+    self.jsonMap = j.killJob()
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_provjson.py b/Src/Portal/cgi-bin/MobylePortal/job_provjson.py
new file mode 100755
index 0000000..f8b561c
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_provjson.py
@@ -0,0 +1,98 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+from time import strftime, strptime
+
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+from Mobyle.JobState import JobState
+
+def process(self):
+    j = JobFacade.getFromJobId(self.request.getfirst('jobId',None))
+    js = JobState(j.jobId)
+    status = j.getStatus()
+    job_date = strptime(  js.getDate() , "%x  %X")
+    job_sdate = strftime("%Y-%m-%dT%H:%M:%S+0000", job_date)
+    user = {
+            "@id": "mbox:%s" % js.getEmail(),
+            "@type": "prov:Person",
+            "foaf:mbox": js.getEmail()
+           }
+    inputs = []
+    usages = []
+    for if_t in js.getInputFiles():
+        parameter_name = if_t[0]
+        for input_file in if_t[1]:
+            i = {
+                "@id": input_file[0],
+                "@type": "prov:Entity",
+                "wasAttributedTo": user["@id"]
+            }
+            u = { 
+                "entity": input_file[0],
+                "hadRole": "%s#%s" % (j.jobId, parameter_name)
+            }
+            inputs.append(i)
+            usages.append(u)
+    outputs = []
+    for of_t in js.getOutputs():
+        parameter_name = of_t[0]
+        for output_file in of_t[1]:
+            o = {
+                "@id": output_file[0],
+                "@type": "prov:Entity",
+                "generation": {
+                    "activity": j.jobId,
+                    "hadRole": "%s#%s" % (j.jobId, parameter_name)
+                }
+            }
+            outputs.append(o)
+    self.jsonMap = {
+    "@context": {
+        "xsd": "http://www.w3.org/2001/XMLSchema#",
+        "prov": "http://www.w3.org/ns/prov#",
+        "foaf": "http://xmlns.com/foaf/0.1/",
+
+        "agent": { "@type": "@id", "@id": "prov:agent" },
+        "entity": { "@type": "@id", "@id": "prov:entity" },
+        "activity": { "@type": "@id", "@id": "prov:activity" },
+        "hadPlan": { "@type": "@id", "@id": "prov:hadPlan" },
+        "hadRole": { "@type": "@id", "@id": "prov:hadRole" },
+        "wasAttributedTo": { "@type": "@id", "@id": "prov:wasAttributedTo" },
+        "association": { "@type": "@id", "@id": "prov:qualifiedAssociation" },
+        "usage": { "@type": "@id", "@id": "prov:qualifiedUsage" },
+        "generation": { "@type": "@id", "@id": "prov:qualifiedGeneration" },
+
+        "startedAtTime": { "@type": "xsd:dateTime", "@id": "prov:startedAtTime" },
+        "endedAtTime": { "@type": "xsd:dateTime", "@id": "prov:endedAtTime" },
+
+        "@base": j.jobId
+    },
+    "@graph": [
+        {   "@id": j.jobId,
+            "@type": "prov:Activity",
+            "startedAtTime": job_sdate, #TODO: time zone in python... (+0200)
+            #"endedAtTime": "2013-07-17T14:50:59+02:00",
+            "association": [
+                {
+                    "agent": js.getHost(),
+                    "hadPlan": js.getName()
+                },
+            {
+                    "agent": "mbox:%s" % js.getEmail()
+                }
+            ],
+            "usage": [usages]
+        }, user
+    ]+inputs+outputs
+}
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_simulate.py b/Src/Portal/cgi-bin/MobylePortal/job_simulate.py
new file mode 100755
index 0000000..fe401a6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_simulate.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+from Mobyle.MobyleError import MobyleError
+
+def process(self):
+    try:
+        j = JobFacade.getFromService(programUrl=self.request.getfirst('programName',None),workflowUrl=self.request.getfirst('workflowUrl',None))
+    except MobyleError, err:
+        self.jsonMap = {'errormsg': err.message}
+        return
+    j.create(self.request, simulate=True)
+    self.jsonMap = j.getExecutionDetails()
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_status.py b/Src/Portal/cgi-bin/MobylePortal/job_status.py
new file mode 100755
index 0000000..bb724f5
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_status.py
@@ -0,0 +1,23 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+
+def process(self):
+    j = JobFacade.getFromJobId(self.request.getfirst('jobId',None))
+    status = j.getStatus()
+    self.jsonMap = {
+            'status': str( status ),
+            'msg': status.message
+           }
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_subjobs.py b/Src/Portal/cgi-bin/MobylePortal/job_subjobs.py
new file mode 100644
index 0000000..660d176
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_subjobs.py
@@ -0,0 +1,23 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+
+def process(self):
+    j = JobFacade.getFromJobId(self.request.getfirst('jobId',None))
+    subjobs = j.getSubJobs()
+    jobsList = []
+    for job in subjobs:
+        jobsList += mb_cgi.get_formatted_job_entries(job)
+    self.jsonMap = jobsList
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_submit.py b/Src/Portal/cgi-bin/MobylePortal/job_submit.py
new file mode 100755
index 0000000..581a55b
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_submit.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+from Mobyle.MobyleError import MobyleError
+
+def process(self):
+    try:
+        j = JobFacade.getFromService(programUrl=self.request.getfirst('programName',None),workflowUrl=self.request.getfirst('workflowUrl',None))
+    except MobyleError, err:
+        self.jsonMap = {'errormsg': err.message}
+        return
+    self.jsonMap = j.create(self.request)
+    
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/job_view.py b/Src/Portal/cgi-bin/MobylePortal/job_view.py
new file mode 100755
index 0000000..e5cac2a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/job_view.py
@@ -0,0 +1,85 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import os
+import mb_cgi
+import Mobyle.MobyleJob 
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Registry import registry
+from Mobyle.JobFacade import JobFacade
+from Mobyle.BMPSWorkflow import BMPSWorkflow
+
+def process(self):
+    # program exists on the Server
+    try:
+        self.jobPID = self.request.getfirst('pid')
+        self.rootJobPID = self.jobPID.split('::')[0]
+        self.rootJobUrl = registry.getJobURL(self.rootJobPID)
+        jf = JobFacade.getFromJobId(self.rootJobUrl)
+        if len(self.jobPID.split('::'))==1:
+            self.jobUrl = self.rootJobUrl
+            if self.session and not(self.session.hasJob(self.jobUrl)):
+                self.session.addJob(self.jobUrl)
+        else:
+            subjobs = jf.getSubJobs()
+            self.jobUrl = [job['jobID'] for job in subjobs if job['jobPID']==self.jobPID][0]
+        ownersList = self.jobPID.split('::')
+        ownerPortals = [pid.split('.')[0] for pid in ownersList if len(pid.split('.'))==3]
+        self.job = Mobyle.MobyleJob.MobyleJob(ID=self.jobUrl)
+        jobUserName = None
+        serviceURL = self.job.jobState.getName()
+
+        # computing service PID based on the URL of the service in the Job XML
+        servicePID = serviceURL.split('/')[-1]
+        # if this is a BMPS workflow
+        if BMPSWorkflow.BMW_FOLDER in serviceURL:
+            servicePID = BMPSWorkflow.PID_PREFIX + servicePID.replace(BMPSWorkflow.MOBYLEXML_SUFFIX,'')
+        # if this is a portal level remove '.xml'
+        else:
+            servicePID = servicePID[:-4]
+        # if this is a imported service
+        if len(ownerPortals)>0:
+            servicePID = ownerPortals[-1] + servicePID
+
+        if self.session.hasJob(self.jobUrl):
+            jobUserName = self.session.getJob(self.jobUrl).get('userName',None)
+        if jobUserName:
+            self.title = jobUserName
+        else:
+            jobName = serviceURL.split('/')[-1][:-4]
+            if len(ownerPortals)>0:
+                jobName += '@'+ownerPortals[-1]
+            self.title = "%s - %s" % (jobName, self.job.jobState.getDate())
+    except Exception:
+        if self.jobPID:
+            self.error_title = "Job not found."
+            self.error_msg = "The job %s cannot not be found." % self.jobPID
+            mb_cgi.c_log.error(self.error_msg, exc_info=True)
+        else:
+            self.error_title = "Job error."
+            self.error_msg = "Error."
+            mb_cgi.c_log.error(self.error_msg, exc_info=True)
+        return
+    self.xmlUrl = self.job.getJobid()+'/index.xml'
+    isIE = (os.environ.get("HTTP_USER_AGENT", "unknown").find("MSIE")!=-1)
+    #servicePID = self.jobPID[:self.jobPID.rfind('.')]
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/job.xsl",
+                        {'jobPID':"'"+self.jobPID+"'",
+                         'servicePID':"'"+servicePID+"'",
+                         'previewDataLimit':"'"+str(self.cfg.previewDataLimit())+"'",
+                         'isIE':"'"+str(isIE)+"'"
+                         }),
+                    (self.cfg.portal_path()+"/xsl/remove_ns.xsl",{}) # remove xhtml namespace junk
+                    ]
+    
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/mb_cgi.py b/Src/Portal/cgi-bin/MobylePortal/mb_cgi.py
new file mode 100755
index 0000000..db16f17
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/mb_cgi.py
@@ -0,0 +1,370 @@
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import sys,os
+import logging
+import cgi, Cookie
+import StringIO
+from simpletal import simpleTAL, simpleTALES
+import urllib
+from lxml import etree
+import simplejson
+import urllib2
+import time
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+
+if (os.path.join(MOBYLEHOME,'Src')) not in sys.path:
+    sys.path.append(os.path.join(MOBYLEHOME,'Src'))
+
+
+try:
+    import Mobyle.ConfigManager 
+except ImportError , err:
+    print """Content-type: text/plain\n\n
+    Mobyle Internal Error.
+    
+    Mobyle encountered an internal error:
+    The configuration file %s/Local/Config/Config.py is unreachable: %s
+    Copy the Example/Local/Config/Config.template.py file to Local/Config/Config.py 
+    and edit it to suit your needs (see INSTALL instructions).
+    """% (MOBYLEHOME, err)
+    sys.exit(1)
+
+import Mobyle.MobyleLogger
+from Mobyle.BMPSWorkflow import BMPSWorkflow
+Mobyle.MobyleLogger.MLogger()
+
+c_log = logging.getLogger('Mobyle.portal')
+
+# script exceptions are also sent 
+def cgi_excepthook(type, value, tback):
+    # log the exception here
+    c_log.error("portal error")
+    c_log.error("caller script: %s, IP: %s" % (os.environ.get("SCRIPT_NAME"), os.environ.get("REMOTE_ADDR")))
+    import traceback #@UnresolvedImport
+    for line in traceback.format_exception(type, value, tback):
+        c_log.error(line.strip('\n'))
+    # then call the default handler
+    sys.__excepthook__(type, value, tback) 
+
+sys.excepthook = cgi_excepthook
+
+from Mobyle.MobyleError import MobyleError, SessionError
+from Mobyle.SessionFactory import SessionFactory
+import Mobyle.Net
+from Mobyle.Registry import registry
+
+_extra_epydoc_fields__ = [('call', 'Called by','Called by')]
+_interval = 10
+
+class CustomResolver(etree.Resolver):
+    """
+    CustomResolver is a Resolver for lxml that allows (among other things) to
+    handle HTTPS protocol, which is not handled natively by lxml/libxml2
+    """
+    def resolve(self, url, id, context):
+        return self.resolve_file(urllib.urlopen(url), context)
+
+class MbCGI(object):
+    """
+    This class defines the basic structure of a Mobyle CGI, that should be reused by every CGI in the Portal
+    """
+
+    response = {}
+    
+    request = cgi.FieldStorage()
+
+    mime_type="text/html"
+  
+    def __init__(self, processFunction=None, useSession=False, mime_type=None, wrapper=None):
+        self.osDir = os.path.dirname(__file__)
+        self.cfg = Mobyle.ConfigManager.Config()
+        self.process = processFunction
+        if mime_type:
+            self.mime_type=mime_type
+        if wrapper:
+            self.wrapper = wrapper
+        if useSession:
+            self.load_session()
+        self.process(self)
+        if useSession:
+            self.print_session()
+        self.render()
+
+    def process(self):
+        raise NotImplementedError      
+
+    def render(self):
+        raise NotImplementedError
+    
+    def print_headers(self):
+        xjson = {}
+        if hasattr(self, 'title'):
+            xjson['title']=self.title
+        if hasattr(self, 'error'):
+            xjson['error']=self.error
+        if(len(xjson.keys())):
+            print "X-JSON: " + simplejson.dumps(xjson, encoding='ascii')
+        print "Content-type: "+self.mime_type+"\n"
+
+    def load_session(self):
+        # SESSION LOAD
+        self.sessionFactory = SessionFactory(self.cfg)
+        c = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE"))
+        if c.get("sessionKey"):
+            self.sessionKey = c.get("sessionKey").value
+        if (hasattr(self,'sessionKey') and self.sessionKey):
+            try:
+                if(c.get("authenticated") and c.get("authenticated").value=="True"):
+                    email = Mobyle.Net.EmailAddress( c.get("email").value )
+                    if c.get("ticket_id") is None:
+                        self.session = None
+                        self.sessionKey = None
+                    else:
+                        self.session = self.sessionFactory.getAuthenticatedSession(email,ticket_id=c.get("ticket_id").value)
+                else:
+                    self.session = self.sessionFactory.getAnonymousSession(self.sessionKey)
+            except SessionError, errMsg:
+                # if the session no longer exists, set session to None (should trigger a reload on the client side when reading the cookie)
+                self.session = None
+                self.sessionKey = None
+                c_log.error(errMsg)
+        else:
+            # if no session is defined, create one
+            if (self.cfg.anonymousSession()!='no'):
+                self.session = self.sessionFactory.getAnonymousSession()  
+                self.sessionKey = self.session.getKey()
+            else:
+                self.session = None
+                self.sessionKey = None
+        try:
+            self.read_session()
+        except SessionError:
+            self.session = None
+            self.sessionKey = None
+            self.read_session()
+        if self.session:
+            BMPSWorkflow.load_user_workflows(self.session)
+                
+    def print_session(self):
+        self.response['email'] = self.email
+        print self.__getSessionCookieSetString__()
+    
+    def read_session(self):
+        if self.session:
+            self.email, self.authenticated, self.activated = self.session.getBaseInfo()
+        else:
+            self.email, self.authenticated, self.activated = (None, False, False)      
+        return
+
+    def __getSessionCookieSetString__(self):
+        """
+        I{__getSessionCookieSetString__} returns the mobyle session cookie string
+        """
+        c = Cookie.SimpleCookie()
+        #c["resetPortal"]=str(self.resetPortal)
+        if self.session:
+            self.email, self.authenticated, self.activated = self.session.getBaseInfo()
+            if self.sessionKey:
+                c["sessionKey"]=str(self.sessionKey)
+            else:
+                c["sessionKey"]=''                
+            if self.activated:
+                c["activated"]=str(self.activated)
+            else:
+                c["activated"]=''
+            if self.email:
+                c["email"]=str(self.email)
+            else:
+                c["email"]=''
+            if self.authenticated:
+                c["authenticated"]=str(self.authenticated)
+            else:
+                c["authenticated"]=''
+            if(hasattr(self.session,'ticket_id')):
+                c["ticket_id"]=self.session.ticket_id
+        else:
+            c["sessionKey"]=''
+            c["activated"]=''
+            c["email"]=''
+            c["authenticated"]=''
+            c["ticket_id"]=''
+        for morsel in c.values():
+            morsel['path']='/'
+        return c.output()
+
+class ProxyCGI(MbCGI):
+    """
+    ProxyCGI is used to retrieve a URL on another server and pass its contents as if it originated our server
+    it is used mainly in file_load.py to retrieve data located on another server.
+    """
+
+    url = ""
+    """url of the "retrieved" file."""
+    
+    def render(self):
+        try:
+            handle = urllib2.urlopen(self.url)
+            self.mime_type = handle.info().getheader("Content-Type")
+            self.text = unicode(''.join(handle.readlines()), errors='ignore')
+        except (urllib2.URLError ,urllib2.HTTPError), e:
+            c_log.error('error while loading data from %s' % self.url)
+        self.print_headers()
+        print self.text
+
+
+class TALCGI(MbCGI):
+
+    template_file = ""
+    """name of the template file used for the cache"""
+ 
+    def render(self):
+        os.chdir(self.osDir)
+        self.context = simpleTALES.Context(allowPythonPath=1)    
+        templateFile = open(self.template_file)
+        self.template = simpleTAL.compileHTMLTemplate (templateFile)
+        templateFile.close()
+        self.context.addGlobal ("sitemacros", self.template)
+        self.context.addGlobal("options", self.response)
+        self.context.addGlobal("self", self)
+        html = StringIO.StringIO()
+        self.template.expand (self.context, html)
+        self.print_headers()
+        print html.getvalue()
+
+class XSLCGI(TALCGI):
+
+    xslParams = None
+    """parameters used when calling an XSL file for rendering"""
+  
+    def render(self):
+        if hasattr(self,'error_msg'):
+            self.error=True
+            self.template_file="error.html"
+            super(XSLCGI, self).render()
+        else:
+            parser = etree.XMLParser(no_network = False)
+            parser.resolvers.add(CustomResolver())
+            xml = etree.parse(self.xmlUrl,parser)
+            for uri,params in self.xslPipe:
+                xslt_doc = etree.parse(uri,parser)
+                transform = etree.XSLT(xslt_doc)
+                try:
+                    parser = etree.XMLParser( no_network = False )
+                    xml = transform(xml, **params)
+                except Exception, e:
+                    c_log.error("error while running %s XSL." % str(uri), exc_info=True)
+                    raise e
+            self.print_headers()
+            print xml
+
+class JSONCGI(MbCGI):
+    
+    jsonMap = {}
+    
+    encoder = None
+    
+    mime_type = "text/plain"
+  
+    def render(self):
+        if hasattr(self,'error_msg'):
+            self.error=True
+            self.jsonMap['errormsg']=self.error_msg
+        self.print_headers()
+        try:
+            data = simplejson.dumps(self.jsonMap, encoding='ascii', default=self.encoder)
+        except UnicodeDecodeError:
+            try:
+                data = simplejson.dumps(self.jsonMap, encoding='utf-8', default=self.encoder)
+            except UnicodeDecodeError:
+                try:
+                    data = simplejson.dumps(self.jsonMap, encoding='iso-8859-1', default=self.encoder)
+                except UnicodeDecodeError:
+                    c_log.error("error during json content encoding")
+        if not(hasattr(self, 'wrapper')):
+            print data
+        else:
+            print self.wrapper % data
+
+class XMLCGI(MbCGI):
+    
+    mime_type = "text/xml"
+
+    def render(self):
+        print "Cache-Control: no-cache"
+        self.print_headers()
+        print self.xmltext
+
+class PNGCGI(MbCGI):
+    
+    mime_type = "image/png"
+
+    def render(self):
+        print "Cache-Control: no-cache"
+        self.print_headers()
+        print self.pngImage
+        
+def get_formatted_job_entries(job):
+    """
+    get_formatted_job_entries formats a list of jobs so that it can be both
+    serialized to json and later used by the portal.
+    """
+    if job.has_key('date') and job['date'] is not None:
+        jobdatestring = time.strftime( "%x    %H:%M", job['date'])
+        jobsortdate = time.strftime( "%Y%m%d%H%M%S", job['date'])
+    else:
+        jobdatestring = '?'
+        jobsortdate = '?'
+    job['status_message'] = ""
+    if hasattr(job['status'], 'message'):
+        job['status_message'] = job['status'].message
+    job['status'] = str(job['status'])
+    job['url'] = job['jobID']
+    job['jobID'] = job['jobPID']
+    l = job['jobID'].split('.')
+    if len(l)==2:
+        server_name='local'
+    else:
+        server_name= l[0]
+    values = {'server':server_name,
+                         'programName': job['programName'] ,
+                         'jobDate': jobdatestring ,
+                         'jobSortDate':jobsortdate,
+                         'status':job['status'] ,
+                         'status_message':job['status_message'],
+                         'dataUsed': job.get('dataUsed'),
+                         'url':job['url'],
+                         'jobID': job['jobID'],
+                         'userName': job['userName']}
+    if job.has_key('owner'):
+        values['owner'] = job['owner']
+    list = [values]
+    if job.has_key('subjobs') and job['subjobs'] is not None:
+        for subjob in job['subjobs']:
+            list += get_formatted_job_entries(subjob)    
+    return list
+
+class HTMLCGI(MbCGI):
+
+    mime_type = "text/html"
+    def render(self):
+        print "Cache-Control: no-cache"
+        self.print_headers()
+        print self.message
+
+class TextCGI(MbCGI):
+
+    mime_type = "text/plain"
+    def render(self):
+        print "Cache-Control: no-cache"
+        self.print_headers()
+        print self.message
diff --git a/Src/Portal/cgi-bin/MobylePortal/net_servers_list.py b/Src/Portal/cgi-bin/MobylePortal/net_servers_list.py
new file mode 100755
index 0000000..19c0f3d
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/net_servers_list.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process( self ):
+    for server in registry.servers:
+        self.jsonMap[server.name] = {
+                                     'url':server.url,
+                                     'help':server.help,
+                                     'repository':server.repository,
+                                     'programs':[program.name for program in server.programs]
+                                     }
+        
+
+if __name__ == "__main__":
+    il = mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/net_services.py b/Src/Portal/cgi-bin/MobylePortal/net_services.py
new file mode 100755
index 0000000..bd998dc
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/net_services.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    for service in registry.serversByName['local'].programs+registry.serversByName['local'].workflows:
+        self.jsonMap[service.name] = {
+                                      'url': service.url,
+                                      'name': service.name,
+                                      'type': service.type,
+                                      'disabled': service.disabled,
+                                      'authorized': service.authorized,
+                                      'exported': service.isExported()
+                                      }
+if __name__ == "__main__":
+    il = mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/openidconsumer.py b/Src/Portal/cgi-bin/MobylePortal/openidconsumer.py
new file mode 100755
index 0000000..a18483d
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/openidconsumer.py
@@ -0,0 +1,405 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Olivier Sallou                                  #
+#   Organization: GenOuest BioInformatics Platform,         #
+#                IRISA, Rennes.                             #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+from Cookie import SimpleCookie
+import os
+import cgi
+import urlparse
+import cgitb
+import sys
+import mb_cgi
+from mb_cgi import MbCGI
+from Mobyle.MobyleError import AuthenticationError,SessionError
+import Mobyle.Net
+import glob
+import StringIO
+from simpletal import simpleTAL, simpleTALES
+from simpletal.simpleTALUtils import FastStringOutput
+
+
+def quoteattr(s):
+    qs = cgi.escape(s, 1)
+    return '"%s"' % (qs,)
+
+
+try:
+    import openid
+except ImportError:
+    sys.stderr.write("""
+Failed to import the OpenID library. In order to use this example, you
+must either install the library (see INSTALL in the root of the
+distribution) or else add the library to python's import path (the
+PYTHONPATH environment variable).
+
+For more information, see the README in the root of the library
+distribution or http://www.openidenabled.com/
+""")
+    sys.exit(1)
+
+from openid.store import memstore
+from openid.store import filestore
+from openid.consumer import consumer
+from openid.oidutil import appendArgs
+from openid.cryptutil import randomString
+from openid.fetchers import setDefaultFetcher, Urllib2Fetcher
+from openid.extensions import pape, sreg, ax
+
+
+
+def setSessionCookie(self):
+    sid = self.getSession()['id']
+    session_cookie = '%s=%s;' % (self.SESSION_COOKIE_NAME, sid)
+    self.send_header('Set-Cookie', session_cookie)
+
+def process(self):
+    """Dispatching logic. There are three paths defined:
+
+      / - Display an empty form asking for an identity URL to
+          verify
+      /verify - Handle form submission, initiating OpenID verification
+      /process - Handle a redirect from an OpenID server
+
+    Any other path gets a 404 response. This function also parses
+    the query parameters.
+
+    If an exception occurs in this function, a traceback is
+    written to the requesting browser.
+    """    
+    if(self.cfg.openid()==False):
+        self.errMsg = '<div id="entete"><h2>Sorry, OpenId is not setup for this portal</h2></div>'
+        return
+
+    try:
+        formulaire = cgi.FieldStorage()
+        path = self.step
+        path = formulaire.getvalue('step')
+        if path == 'init':
+            self.step = "init"
+            self.showForm()
+        elif path == 'verify':
+            self.step = "verify"
+            self.openidurl = formulaire.getvalue('openidurl')
+            self.doVerify()
+        elif path == 'process':
+            self.step="process"
+            self.openidurl = formulaire.getvalue('openidurl')
+            self.doProcess()
+        else:
+            self.step= None
+            #self.notFound()
+
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except:
+        self.errMsg = sys.exc_info()
+
+
+
+
+
+def notFound(self):
+    """Render a page with a 404 return code and a message."""
+    fmt = 'The path <q>%s</q> was not understood by this server.'
+    msg = fmt % (self.path,)
+    openid_url = self.query.get('openid_identifier')
+    self.render(msg, 'error', openid_url, status=404)
+
+
+def main():
+    # Instantiate OpenID consumer store and OpenID consumer.  If you
+    # were connecting to a database, you would create the database
+    # connection and instantiate an appropriate store here   
+
+    OPENIDCGI(processFunction=process,useSession=True)
+
+
+
+class OPENIDCGI(MbCGI):
+
+    step = "init"
+    openidurl = None
+    openidsession = {}
+    errMsg = None
+    trust_root = None
+    return_to = None
+    html = None
+    htmlHeader = None
+    store = None
+    # Optionally set it in config to specify file manager usage
+    data_path = None
+
+    def requestRegistrationData(self, request):
+        sreg_request = sreg.SRegRequest(
+            required=['email'], optional=['fullname', 'nickname'])
+        request.addExtension(sreg_request)
+        sreg_ax_request = ax.FetchRequest()
+        sreg_ax_request.add(
+            ax.AttrInfo('http://axschema.org/contact/email',
+                        required=True))
+        request.addExtension(sreg_ax_request)
+
+    def requestPAPEDetails(self, request):
+        pape_request = pape.Request([pape.AUTH_PHISHING_RESISTANT])
+        request.addExtension(pape_request)
+
+
+    def getConsumer(self, stateless=False):
+
+        if(self.cfg.openidstore_path() is not None):
+            self.data_path = self.cfg.openidstore_path()
+
+        if self.store is None:
+            if self.data_path:
+                self.store = filestore.FileOpenIDStore(self.data_path)
+            else:
+                self.store = memstore.MemoryStore()
+        if stateless:
+            store = None
+        else:
+            store = self.store
+        return consumer.Consumer(self.openidsession, self.store)
+
+
+    def showForm(self):
+        os.chdir(self.osDir)
+        self.cssDir = '/'+self.cfg.portal_url(True)+"/css/"
+        self.jsDir = '/'+self.cfg.portal_url(True)+"/js/"
+        self.htDir = '/'+self.cfg.portal_url(True)+"/"
+        self.homeDir = self.cfg.cgi_url()
+
+        self.context = simpleTALES.Context(allowPythonPath=1)
+        templateFile = open('openidform.html')
+        self.template = simpleTAL.compileHTMLTemplate (templateFile)
+        templateFile.close()
+        self.context.addGlobal("self", self)
+        html = StringIO.StringIO()
+        self.template.expand (self.context, html)
+        self.html =  html.getvalue()
+        #self.html = '<form action="openidconsumer.py" method="get">\n'
+        #self.html += '<label>OpenID URL</label>\n'
+        #self.html += '<input name="openidurl" type="text"/>\n'
+        #self.html += '<input name="step" type="hidden" value="verify"/>\n'
+        #self.html += '<input type="submit"/>\n'
+        #self.html += "</form>\n"
+
+
+    def render(self):
+        #print "Content-type: text/html\n"
+        if(self.htmlHeader is not None):
+            print self.htmlHeader
+        print "Content-type: text/html\n"
+        print "\n"
+        if(self.errMsg is not None):
+            print "<html><head></head><body>\n"
+            print self.errMsg
+            print "</body></html>\n"
+            return
+        if (self.html is not None):
+            print "<html><head></head><body>\n"
+            if self.step == 'init':
+                print self.html
+            elif self.step == 'verify':
+                print self.html
+            elif self.step == 'process':
+                print self.html
+            else:
+                print "Error, could not found any rendering for this operation\n"
+            print "</body></html>\n"
+        #else:
+        #    redirect_url = self.request.redirectURL(
+        #        self.trust_root, self.return_to, immediate=immediate)
+
+                
+    def doVerify(self):
+        """Process the form submission, initating OpenID verification.
+        """
+        sid = randomString(16, '0123456789abcdef')
+        self.openidsession = {}
+        self.openidsession['id'] = sid
+        # First, make sure that the user entered something
+        openid_url = self.openidurl     
+        if not openid_url:
+            self.errMsg = 'Enter an OpenID Identifier to verify.'
+            return
+        self.openidsession['openidurl'] = self.openidurl
+
+        immediate = False
+        use_sreg = True
+        use_pape = False
+        use_stateless = False
+        oidconsumer = self.getConsumer(stateless = use_stateless)
+
+        if oidconsumer is None:
+            mb_cgi.c_log.error('ERROR: oidconsumer is None')
+            return
+
+        try:
+            request = oidconsumer.begin(openid_url)           
+        except consumer.DiscoveryFailure, exc:
+            mb_cgi.c_log.error('DISCOVERYFAILURE')
+            self.errMsg = 'Error in discovery: %s' % (
+                cgi.escape(str(exc[0])))
+            mb_cgi.c_log.error(self.errMsg)
+        else:
+            if request is None:
+                self.errMsg = 'No OpenID services found for <code>%s</code>' % (
+                    cgi.escape(openid_url),)
+            else:
+                # Then, ask the library to begin the authorization.
+                # Here we find out the identity server that will verify the
+                # user's identity, and get a token that allows us to
+                # communicate securely with the identity server.
+                if use_sreg:
+                    self.requestRegistrationData(request)
+ 
+                if use_pape:
+                    self.requestPAPEDetails(request)
+
+                self.trust_root = self.cfg.root_url()
+                self.return_to = self.cfg.cgi_url()+"/openidconsumer.py?step=process&password="+self.openidurl+"&openidurl="+self.openidurl
+
+
+                # store auth data with anonymous session
+                self.session.addOpenIdAuthData(self.openidsession)
+
+                if not request.shouldSendRedirect():
+                    self.html = request.htmlMarkup(
+                        self.trust_root, self.return_to,
+                        form_tag_attrs={'id':'openid_message'},
+                        immediate=immediate)
+                else:
+                    redirect_url = request.redirectURL(
+                            trust_root, return_to, immediate=immediate)
+                    self.htmlHeader = 'Location: '+redirect_url+"\n"
+                    self.html = ""
+
+
+
+    def doProcess(self):
+        """Handle the redirect from the OpenID server.
+        """
+        self.openidsession = self.session.getOpenIdAuthData()
+
+        oidconsumer = self.getConsumer()
+
+        # Ask the library to check the response that the server sent
+        # us.  Status is a code indicating the response type. info is
+        # either None or a string containing more information about
+        # the return type.
+        url = self.cfg.cgi_url()+"/openidconsumer.py?step=process"
+
+	query = {}
+
+        form = cgi.FieldStorage()
+        for name in form.keys():
+            value = form[name].value
+            query[name] = value
+
+
+        info = oidconsumer.complete(query, url)
+
+        sreg_resp = None
+        ax_items = None
+        mail = None
+        pape_resp = None
+        css_class = 'error'
+        display_identifier = info.getDisplayIdentifier()
+
+        if info.status == consumer.FAILURE and display_identifier:
+            # In the case of failure, if info is non-None, it is the
+            # URL that we were verifying. We include it in the error
+            # message to help the user figure out what happened.
+            self.html = "Fail to authentify: "+display_identifier+","+info.message+"\n"
+        elif info.status == consumer.SUCCESS:
+            # Success means that the transaction completed without
+            # error. If info is None, it means that the user cancelled
+            # the verification.
+            css_class = 'alert'
+
+            # This is a successful verification attempt. If this
+            # was a real application, we would do our login,
+            # comment posting, etc. here.
+            self.html = "You have successfully verified your identity."
+            sreg_resp = sreg.SRegResponse.fromSuccessResponse(info)
+            pape_resp = pape.Response.fromSuccessResponse(info)
+            if info.endpoint.canonicalID:
+                # You should authorize i-name users by their canonicalID,
+                # rather than their more human-friendly identifiers.  That
+                # way their account with you is not compromised if their
+                # i-name registration expires and is bought by someone else.
+                self.html += "  This is an i-name, and its persistent ID is "+info.endpoint.canonicalID+"\n"
+
+            ax_response = ax.FetchResponse.fromSuccessResponse(info)
+            if ax_response:
+                ax_items = {
+                    'email': ax_response.get(
+                        'http://axschema.org/contact/email'),
+                    }
+            if not sreg_resp is None:
+                mail = sreg_resp.get('email')
+                #sreg_list = sreg_resp.items()
+                #sreg_list.sort()
+                #for k, v in sreg_list:
+                #    field_name = sreg.data_fields.get(k, k)
+                #    value = cgi.escape(v.encode('UTF-8'))
+                #    self.html += field_name+"="+value+"\n"
+            if mail is None:
+                mail = ax_items['email'][0]
+
+            if mail is None:
+                self.html += "No registration data was returned\n"
+                return
+            else:
+                #get email
+                self.email = Mobyle.Net.EmailAddress( mail )                
+
+            # Now create AuthenticatedSession for user
+            try:
+                self.anonymousSession = self.session
+                self.session = self.sessionFactory.getOpenIdAuthenticatedSession(self.email)
+            except SessionError,AuthenticationError:
+                # Session does not exist create it
+                self.session = self.sessionFactory.createAuthenticatedSession(Mobyle.Net.EmailAddress( mail ),passwd="")
+            if self.anonymousSession:
+                try:
+                    self.session.mergeWith(self.anonymousSession)
+                    self.sessionKey = self.session.getKey()
+                    self.read_session()
+                except SessionError, e:
+                    self.errMsg = str(e)
+
+
+
+            self.htmlHeader = "Location: "+self.cfg.cgi_url()+"/portal.py\n"
+            self.html = ""
+
+
+        elif info.status == consumer.CANCEL:
+            # cancelled
+            self.html = 'Verification cancelled'
+        elif info.status == consumer.SETUP_NEEDED:
+            if info.setup_url:
+                self.html = "<a href="+quoteattr(info.setup_url)+">Setup needed</a>"
+            else:
+                # This means auth didn't succeed, but you're welcome to try
+                # non-immediate mode.
+                self.html = 'Setup needed'
+        else:
+            # Either we don't understand the code or there is no
+            # openid_url included with the error. Give a generic
+            # failure message. The library should supply debug
+            # information in a log.
+            self.html = 'Verification failed.'
+
+
+
+if __name__ == '__main__':
+    main()
diff --git a/Src/Portal/cgi-bin/MobylePortal/openidform.html b/Src/Portal/cgi-bin/MobylePortal/openidform.html
new file mode 100644
index 0000000..5a6e569
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/openidform.html
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" >
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<link rel="stylesheet"
+        tal:attributes="href string:${self/cssDir}mobyle.css" type="text/css"
+        media="screen" />
+<link rel="stylesheet"
+        tal:attributes="href string:${self/cssDir}local.css" type="text/css"
+        media="screen" />
+<link rel="stylesheet"
+        tal:attributes="href string:${self/cssDir}openid/openid.css" type="text/css"
+        media="screen" />
+
+<script type="text/javascript"
+        tal:attributes="src string:${self/jsDir}openid/jquery-1.3.2.min.js" />
+<script type="text/javascript"
+        tal:attributes="src string:${self/jsDir}openid/openid.js" />
+
+<title>OpenId signin form</title>
+</head>
+<body>
+          <div id="entete">
+<a tal:attributes="href string:${self/homeDir}/portal.py"  title="Back to home page" id="retour"><span class="hidden">Back to home page</span></a>
+<h1 id="titre"><span class="hidden">OpenId Mobyle login</span></h1>
+          </div>
+          <div id="openid_user_bar">
+<p>With OpenId, you can login with your Gmail,Yahoo,myOpenId... account</p>
+
+  <form name="openid_form" action="openidconsumer.py" class="openIdForm" id="openid_form" method="GET">
+    <h4>Connect with <a href="http://openid.net/get/">OpenID</a></h4>
+                  <label class="openId" for="openid_form_openid_url">Your OpenID</label>      <input type="text" name="openidurl" value="http://" maxlength="255" class="openidUrl" id="openid_form_openid_url" />  <input name="step" type="hidden" value="verify"/>     <input type="submit" value="Login" />
+
+
+    <p>
+      <img onclick="changeServer('http://www.google.com/accounts/o8/id', 0, 8); return false;" tal:attributes="src string:${self/htDir}/images/openid/google.ico" alt="Google" title="Google" height="16," width="16" /> <img onclick="changeServer('http://username.myopenid.com/', 7, 8); return false;" tal:attributes="src string:${self/htDir}/images/openid/myopenid.ico" alt="myOpenID" title="myOpenID" height="16," width="16" /> <img onclick="changeServer('http://username.claimid.com/', 7, 8); [...]
+      <img onclick="changeServer('http://openid.aol.com/screenname', 22, 10); return false;" tal:attributes="src string:${self/htDir}/images/openid/aol.ico" alt="AOL" title="AOL" height="16," width="16" /> <img onclick="changeServer('http://blogname.blogspot.com', 7, 8); return false;" tal:attributes="src string:${self/htDir}/images/openid/blogger.ico" alt="Blogger" title="Blogger" height="16," width="16" /> <img onclick="changeServer('http://www.flickr.com/photos/username', 29, 8); retu [...]
+
+</form>
+<p>Need an OpenId ? <a href="https://www.myopenid.com/signup?affiliate_id=49957">Go to myOpenID!</a></p>
+
+</div>
+
+
+
+</body>
+</html>
+
+
diff --git a/Src/Portal/cgi-bin/MobylePortal/portal.html b/Src/Portal/cgi-bin/MobylePortal/portal.html
new file mode 100755
index 0000000..c71127a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/portal.html
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html>
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <head>
+    <title>Mobyle portal
+      <tal:block tal:condition="python:options.has_key('openedFormNames')" tal:content="python:' - '+', '.join(options['openedFormNames'])">
+      </tal:block>
+      <tal:block tal:condition="python:options.has_key('openedJobs')" tal:content="python:' - '+', '.join(options['openedJobs'])">
+      </tal:block>
+    </title>
+    <link tal:repeat="cssStyleSheet self/cssStyleSheets" rel="stylesheet" tal:attributes="href string:${self/cssDir}${cssStyleSheet/name}?=${cssStyleSheet/lastmod}" type="text/css" media="screen" />
+    <script id="pp" type="text/javascript" tal:attributes="src string:portal_properties.py" />
+    <script tal:content="string:var htDir = '${self/htDir}';" />
+    <script tal:repeat="jsLibrary self/jsLibraries" type="text/javascript" tal:attributes="src string:${self/jsDir}${jsLibrary/name}?=${jsLibrary/lastmod}" />
+    <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" />
+    <meta name="description" tal:condition="python:not(options.has_key('descriptionElements'))" content="Mobyle is a framework and web portal specifically aimed at the integration of bioinformatics software and databanks." />
+    <meta name="description" tal:condition="python:options.has_key('descriptionElements')" tal:attributes="content python:' '.join(options['descriptionElements'])" />
+    <meta name="author" content="The Mobyle Project" />
+  </head>
+  <body>
+    <noscript>
+      <div class="unavailable">
+        <div>
+          <strong><a href="https://projets.pasteur.fr/wiki/mobyle/">Mobyle</a> is a
+            framework and web portal specifically aimed at the integration of
+            bioinformatics software and databanks.</strong>
+        </div>
+        <div>
+          Sorry, the Mobyle Portal requires javascript support. Please
+          use a browser that supports javascript.
+        </div>
+      </div>
+    </noscript>
+    <div class="unavailable" id="nocookie" style="display:none; visibility:hidden;">
+      <div>
+        Sorry, the Mobyle Portal requires that you accept <strong>cookies</strong>. Please
+        configure your browser to accept cookies from our server.
+      </div>
+    </div>
+    <form action="data_upload.py" id="bookmark_upload" style="display:none;">
+      <textarea name="data_input_upload" />
+      <input type="text" name="data_input_upload.name" />
+      <input type="text" name="datatype_class" />
+      <input type="text" name="datatype_superclass" />
+      <input type="text" name="data_format" />
+      <input type="text" name="data_biotype" />
+      <input type="text" name="base64encoded" value="false"/>
+      <input type="text" name="doNotHideModal" value="1"/>
+      <input type="text" name="inputMode" value="upload">
+    </form>
+    <div id="userPopupOverlay" class="popup_overlay"  style="display: none; z-index: 99999">
+      <span id="insertHere" class="popup_background" />
+    </div>
+    <div id="waitOverlay" class="popup_overlay"  style="z-index: 999999">
+      <div class="popup_background">
+        <div class="loading">loading...</div>
+      </div>
+    </div>
+    <table id="banner" style="width: 100%;">
+      <tr>
+        <td rowspan="2" style="width: 50%;">
+         <span id="mobyleHead" tal:condition="python:self.cfg.custom_portal_header() is not None" 
+                tal:content="structure self/cfg/custom_portal_header"/>
+         <h1 id="mobyleHead" tal:condition="python:self.cfg.custom_portal_header() is None">
+          Mobyle
+         </h1>
+        </td>
+        <td style="width: 50%; text-align: right; vertical-align: top;">
+          <div>
+            <span style="font-weight: bold;" id="userEmail"></span>
+            <span style="font-weight: bold; font-style: italic;" id="userStatus"></span>
+          </div>
+          <div>
+            <ul id="userOptions">
+              <li>
+                <a style="font-weight: bold;" href="#user::email" class="modalLink" title="enter your email">set email</a>
+              </li>
+              <li>
+                <a style="font-weight: bold;" href="#user::signin" class="modalLink" title="sign in to a registered account">sign-in</a>
+              </li>
+              <!-- OPENID -->
+              <li tal:condition="python:options['openid']==True">
+                <a style="font-weight: bold;" href="openidconsumer.py?step=init" id="userOpenIdsignInOpen" title="sign in with OpenID" class="modelLink">OpenId sign-in</a>
+              </li>
+              <li>
+                <a style="font-weight: bold;" href="#user::activate" class="modalLink" title="activate an account">activate</a>
+              </li>
+              <li>
+                <a style="font-weight: bold;" href="#user::signout" class="modalLink" title="sign out from this account">sign-out</a>
+              </li>
+            </ul>
+          </div>
+          <a class="refresh_link" href="#">refresh workspace</a>
+        </td>
+      </tr>
+      <tr>
+        <td style="width: 80%;">
+          <span tal:condition="options/announcement" tal:content="structure options/announcement" class="announcement"></span>
+        </td>
+      </tr>
+      <tr tal:condition="python: len(options['messages'])>0">
+        <td class="error" colspan="2" tal:repeat="message options/messages" tal:content="message">
+        </td>
+      </tr>
+    </table>
+    <!-- -->
+    <form style="display: none" name="preload_info">
+      <input tal:condition="options/sessionLimit" tal:attributes="value options/sessionLimit" id="sessionLimit" />
+      <input tal:attributes="value options/anonymousSession" id="anonymousSession" />
+      <input tal:attributes="value options/authenticatedSession" id="authenticatedSession" />
+      <input tal:attributes="value options/refresh_frequency" id="autoRefreshFreq" />
+      <input tal:attributes="value self/cfg/GAcode" id="ga_code" />
+      <tal:block tal:repeat="program options/openedFormParameters">
+        <input tal:attributes="name python:'preload:'+program[0]; id python:'preload:'+program[0]; value program/1" />
+      </tal:block>
+    </form>
+    <form style="display: none" name="file_upload" action="data_upload.py" method="POST" enctype="multipart/form-data" target="data_upload_target">
+      <input type="file" name="data_input_upload" /><input type="hidden" name="datatype_class" /><input type="hidden" name="datatype_superclass" /><input type="hidden" name="data_format" />
+    </form><iframe name="data_upload_target" id="data_upload_target" style="display: none"/>
+    <form style="display: none" name="load" id="mobyle_load">
+      <tal:block tal:repeat="value options/load">
+      <input  tal:attributes="name python:'load::'+value[0]+'::'+value[1]; data-servicename value/0; data-parametername value/1; value value/2" />
+      </tal:block>
+    </form>
+    <table id="mainContainer" style="table-layout:fixed; width:100%; text-overflow:ellipsis; height:100%">
+      <tr>
+        <td id="navbar" style="overflow:hidden; white-space:nowrap;  ">
+          <dl id="drawer" class="accordion">
+            <dd id="programsMenu">
+              <form id="programSearch" name="programSearch" action="programs_list.py" method="get">
+                <table style="width: 100%">
+                  <tr>
+                    <td>
+                      <input type="search" id="searchString" name="searchString" size="10" title="Enter a search string here" autofocus="autofocus"/>
+                      <input id="searchSubmit" name="plSubmit" type="submit" value="Search" />
+                      <span id="listAllSubmitBlock">
+                        or <input id="listAllSubmit" name="plSubmit" class="programsForm" type="submit" value="All" />
+                      </span>
+                    </td>
+                    <td>
+                      <a id="advancedProgramSearchToggle" href="#advancedProgramSearch" class="blindLink closed" title="Click here to access more search/display options">[more]</a>
+                    </td>
+                  </tr>
+                </table>
+                <div id="advancedProgramSearch" style="display: none; left-margin:2px;">
+                  <hr />
+                  <div>
+                    Classify:
+                    <label>
+                      by category <input type="radio" id="classifyByCategory" name="classifyBy" value="category" checked="checked" />
+                    </label>
+                    <label>
+                      by package <input type="radio" id="classifyByPackage" name="classifyBy" value="package" />
+                    </label>
+                  </div>
+                  <div>
+                    Programs workflows and tutorials:
+                    <label>
+                      separate <input type="radio" id="serviceTypeSortSeparated" name="serviceTypeSort" value="separate" checked="checked" />
+                    </label>
+                    <label>
+                      mixed <input type="radio" id="serviceTypeSortMixed" name="serviceTypeSort" value="mixed" />
+                    </label>
+                  </div>
+                </div>
+              </form>
+              <div class="channel" id="programs_list_channel">
+              </div>
+            </dd>
+            <dt href="#userWorkflowsMenu" class="blindLink menu workflows" style="position:relative;">
+              My Workflows
+            </dt>
+            <dd id="userWorkflowsMenu">
+              <div class="sessionList" id="files_list">
+                <ul id="userWorkflowsListUl">
+                </ul>
+              </div>
+            </dd>
+            <dt href="#dataMenu" class="blindLink menu data" style="position:relative;">
+              Data Bookmarks
+              <a href="#data::overview" style="right:0px; position:absolute;" title="Data bookmarks management" class="link">[overview]</a>
+            </dt>
+            <dd id="dataMenu">
+              <div class="sessionList" id="files_list">
+                <ul id="filesListUl">
+                </ul>
+              </div>
+            </dd>
+            <dt href="#jobsMenu" class="blindLink menu jobs" style="position:relative;">
+              Jobs
+              <a href="#jobs::overview" style="right:0px; position:absolute;" title="Jobs management" class="link">[overview]</a>
+            </dt>
+            <dd id="jobsMenu">
+              <div class="sessionList" id="jobs_list">
+                <ul id="jobsListUl">
+                </ul>
+              </div>
+            </dd>
+          </dl>
+        </td>
+        <td id="navbarResize" title="resize the menu" style="width:3px; height:20px; background-color:lightGray; cursor:E-resize;"></td>
+        <td id="portalMain">
+          <ul class="handlesList">
+            <li class="menu welcome">
+              <a class="link" href="#welcome">Welcome</a>
+            </li>
+            <li class="menu forms">
+              <a class="link" href="#forms">Forms</a>
+            </li>
+            <li class="menu data">
+              <a class="link" href="#data">Data Bookmarks</a>
+            </li>
+            <li class="menu jobs">
+              <a class="link" href="#jobs">Jobs</a>
+            </li>
+            <li class="menu tutorials">
+              <a class="link" href="#tutorials">Tutorials</a>
+            </li>
+          </ul>
+          <div class="panelsList">
+          <div id="ms-welcome" class="tabPanel" style="display: none;">
+            <div id="welcome_header">
+              <h3 align="center">Welcome to Mobyle, a portal for bioinformatics analyses</h3>
+              <div>
+                <div id="welcome_frame" tal:condition="python:hasattr(self,'welcome_html')" style="height: 40em; overflow: scroll;" type="text/html" 
+                tal:content="structure self/welcome_html"/>
+                <div tal:condition="python:not(hasattr(self,'welcome_html'))" style="height: 40em">
+                  Welcome!
+                </div>
+              </div>
+            </div>
+            <div id="welcome_footer" tal:attributes="title python:'Mobyle version: %s' % (self.cfg.version() or 'unspecified')">
+              <fieldset>
+                <legend>
+                  <b>Credits</b>
+                </legend>
+                <div>
+                  Mobyle is a platform developed jointly by the <a href="http://www.pasteur.fr" target="_blank">Institut Pasteur</a>
+                  <a href="http://www.pasteur.fr/ip/easysite/pasteur/en/research/Biology-IT-Center" target="_blank">Biology IT Center</a> and the <a href="http://bioserv.rpbs.univ-paris-diderot.fr" target="_blank">Ressource
+                    Parisienne en Bioinformatique Structurale</a>.
+                </div>
+                <div>
+                  More information about this project can be found <a href="https://projets.pasteur.fr/projects/mobyle/wiki" target="_blank">here</a>.
+                </div>
+              </fieldset>
+            </div>
+          </div>
+          <div id="ms-forms" class="tabPanel" style="display: none;">
+            <ul class="handlesList">
+            </ul>
+            <div class="panelsList">
+            </div>
+          </div>
+          <div id="ms-data" class="tabPanel" style="display: none;">
+            <ul class="handlesList">
+              <li class="menu data">
+                 <a class="link" href="#data::overview">Overview</a>
+              </li>
+            </ul>
+            <div class="panelsList" href="">
+              <div id="ms-data::overview" class="tabPanel">
+                <br/>
+                <div>
+                  This page lets you control the data bookmarks stored on the
+                  server.
+                </div>
+                <div id="sessionDataSummary">
+                  Session usage:
+                  <div id="sessionSpace">
+                    <div id="sessionUsage">
+                    </div>
+                  </div>
+                  (total space available: <span tal:condition="options/sessionLimit" tal:content="options/sessionLimit" id="sessionLimit" />KiB)
+                </div>
+                <form name="bookmark" action="data_remove.py" method="post">
+                  <div style="text-align: right">
+                    <input type="submit" id="job_#{key}" value="remove selected bookmarks" />
+                  </div>
+                  <table>
+                    <tbody id="dataManagement"  class="dataTable">
+                    </tbody>
+                  </table>
+                </form>                
+              </div>
+            </div>
+          </div>
+          <div id="ms-jobs" class="tabPanel" style="display: none;">
+            <ul class="handlesList">
+              <li class="menu jobs">
+                <a class="link" href="#jobs::overview">Overview</a>
+              </li>
+            </ul>
+            <div class="panelsList" href="">
+              <div id="ms-jobs::overview" class="tabPanel">
+                <br/>
+                <form name="session_job_remove" id="jobsManagementTable" action="session_job_remove.py" method="post">
+                  <div style="text-align: right">
+                    <label style="display: inline">Sort by 
+                    <select name="sortJobsBy" id="sortJobsBy">
+                      <option value="name_and_id" selected="selected">name and identifier</option>
+                      <option value="jobSortDate">date</option>                    
+                    </select>
+                    </label>
+                    <input type="submit" id="job_#{key}" value="remove selected jobs" />
+                  </div>
+                  <table>
+                    <tbody id="jobsManagement" class="dataTable">
+                    </tbody>
+                  </table>
+                </form>
+              </div>
+            </div>
+          </div>
+          <div id="ms-tutorials" class="tabPanel" />
+          </div>
+          </td>
+          </tr>
+        </table>
+        <tal:block tal:condition="python:self.cfg.custom_portal_footer() is not None" 
+                tal:content="structure self/cfg/custom_portal_footer"/>
+        </body>
+      </html>
diff --git a/Src/Portal/cgi-bin/MobylePortal/portal.py b/Src/Portal/cgi-bin/MobylePortal/portal.py
new file mode 100755
index 0000000..7740ee6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/portal.py
@@ -0,0 +1,78 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import os
+import urllib2
+import socket
+
+def process(self):
+    self.template_file = 'portal.html'
+    if self.session:
+        self.response['authenticated'] = self.session.isAuthenticated()
+        self.response['activated'] = self.session.isActivated()
+        actkey = self.request.getfirst('actkey')
+        if actkey:
+            self.response['actkey'] = actkey
+    else:
+        self.response['sessionId'] = None
+        self.response['authenticated'] = False
+        self.response['activated'] = False
+    self.response['anonymousSession'] = (self.cfg.anonymousSession()!='no')
+    self.response['authenticatedSession'] = (self.cfg.authenticatedSession()!='no')
+    self.response['sessionLimit']='%.2f'%(float(self.cfg.sessionlimit())/1000)
+    self.response['refresh_frequency']=self.cfg.refreshFrequency()
+    #OPENID
+    self.response['openid']=(self.cfg.openid()!=False)
+
+    self.response['messages'] = []
+    annFileName = os.path.normpath(os.path.join( self.cfg.portal_path(),"html/announcement.txt"))
+    self.cssDir = '/'+self.cfg.portal_url(True)+"/css/"
+    self.jsDir = '/'+self.cfg.portal_url(True)+"/js/"
+    self.htDir = '/'+self.cfg.portal_url(True)+"/"
+    cssStyleSheetNames = ['mobyle.css','local.css']
+    self.cssStyleSheets = [{'name': name, 'lastmod':os.path.getmtime(os.path.join(self.cfg.portal_path(), 'css',name))} for name in cssStyleSheetNames]
+    jsLibraryNames = ['prototype-1.7.js', 'rsh.js', 'builder.js','effects.js','controls.js', 'mobyle.js']
+    if self.cfg.GAcode():
+        jsLibraryNames.append('mobyle_ga.js')
+    self.jsLibraries = [{'name': name, 'lastmod':os.path.getmtime(os.path.join(self.cfg.portal_path(), 'js',name))} for name in jsLibraryNames]
+    welcome_url = None
+    if self.cfg.welcome_config().get('format')=='atom':
+        welcome_url =  self.cfg.cgi_url()+"/feed_view.py?url="+self.cfg.welcome_config().get('url')+"&fmt="+self.cfg.welcome_config().get('format')
+    elif self.cfg.welcome_config().get('format')=='html':
+        welcome_url = self.cfg.welcome_config().get('url')
+    if welcome_url:
+        timeout = 1 # we set a reasonable timeout to avoid hanging on this problem before displaying the whole page.
+        backup_timeout = socket.getdefaulttimeout()
+        socket.setdefaulttimeout(timeout)
+        try:
+            self.welcome_html = unicode(''.join(urllib2.urlopen(welcome_url).readlines()), errors='ignore')
+        except (urllib2.URLError ,urllib2.HTTPError):
+            mb_cgi.c_log.error('error while loading welcome frame from %s' % welcome_url)
+            # if we cannot get the custom page in time, we just pass
+            pass
+        socket.setdefaulttimeout(backup_timeout)
+    if os.path.isfile(annFileName):
+        templateCacheFile = open (annFileName,'r')
+        self.response['announcement'] = templateCacheFile.read()
+        templateCacheFile.close()
+    # it is possible to ask for specific preloaded values in the form
+    self.response['load']=[]
+    for name in self.request.keys():
+        if name.startswith("load::"):
+            dict = name.split('::')
+            service_name=dict[1]
+            parameter_name=dict[2]
+            value=self.request.getfirst(name)
+            self.response['load'].append((service_name,parameter_name,value))
+
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/portal_properties.py b/Src/Portal/cgi-bin/MobylePortal/portal_properties.py
new file mode 100755
index 0000000..2ac64c4
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/portal_properties.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.DataInputsIndex import DataInputsIndex
+from  Mobyle.Classes.DataType import DataTypeFactory
+from Mobyle.MobyleError import MobyleError
+
+df = DataTypeFactory()
+
+def getDataType(dataTypeClass,dataTypeSuperClass):
+    if dataTypeSuperClass:
+        dt = df.newDataType(dataTypeSuperClass,dataTypeClass)
+    else:
+        try:
+            dt = df.newDataType(dataTypeClass)
+        except MobyleError , err:
+            return None
+    if dt.isMultiple():
+        return dt.dataType
+    else:
+        return dt
+        
+
+def process( self ):
+    bc = self.cfg.getDatabanksConfig()
+    pdi = DataInputsIndex("program")
+    wdi = DataInputsIndex("workflow")
+    vdi = DataInputsIndex("viewer")
+    datatypes = {}
+    dataInputsList = pdi.getList().values() + vdi.getList().values()
+    for parameter in dataInputsList:
+        if not(datatypes.has_key(parameter.get('dataTypeClass'))):
+            datatypes[parameter.get('dataTypeClass')]={}
+            dt = getDataType(parameter.get('dataTypeClass'),parameter.get('dataTypeSuperClass'))
+            if dt:
+                datatypes[parameter.get('dataTypeClass')]['ancestorTypes']=dt.ancestors
+    program_workflow_inputs = dict(pdi.getList().items() + wdi.getList().items())
+    for key,input in program_workflow_inputs.items():
+        dt = getDataType(input.get('dataTypeClass'),input.get('dataTypeSuperClass'))
+        if dt:
+            input['dataTypeClass']=dt.name
+    email_results =  not(self.cfg.mailResults()[0])
+    self.jsonMap = {'portal_name': self.cfg.portal_name(),
+                    'email_results': email_results,
+                    'simple_forms_active':self.cfg.simple_forms_active(),
+                    'conversions':self.cfg.data_conversions(),
+                    'banks': bc,
+                    'program_workflow_inputs': program_workflow_inputs,
+                    'viewer_inputs': vdi.getList(),
+                    'datatypes':datatypes,
+                    'htbase':'/'+self.cfg.portal_url(True)+"/"}
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,mime_type="application/javascript",wrapper="var portalProperties = %s;")
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/program_validate.py b/Src/Portal/cgi-bin/MobylePortal/program_validate.py
new file mode 100755
index 0000000..733a682
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/program_validate.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Validator import Validator
+from Mobyle.JobFacade import JobFacade
+from Mobyle.MobyleError import MobyleError
+
+def process(self):
+    val = Validator(self.request.getfirst("type","program"),docString = self.request.getfirst("programXmlString"),runRNG_Jing=True)
+    val.run()
+    self.jsonMap = {'validates':val.valOk}
+    if not val.valOk:
+        self.jsonMap['validation_errors'] = ((val.rng_JingErrors or val.rngErrors) or []) + val.schErrors
+    if val.valOk:
+        self.service = val.service
+        try:
+            j = JobFacade.getFromService(service=val.service)
+        except MobyleError, err:
+            self.jsonMap['job_simulation_errormsg'] = err.message
+            mb_cgi.c_log.error(err.message, exc_info=True)
+            return
+        resp = j.create(self.request, session=self.session, simulate=True)
+        if resp.has_key('errormsg'):
+            self.jsonMap['errormsg'] = resp['errormsg']
+            if resp.has_key('errorparam'):
+                self.jsonMap['errorparam'] = resp['errorparam']
+        else:
+            self.jsonMap['job_simulation'] = j.getExecutionDetails()        
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/programs_list.html b/Src/Portal/cgi-bin/MobylePortal/programs_list.html
new file mode 100755
index 0000000..f7ed0f7
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/programs_list.html
@@ -0,0 +1,91 @@
+<metal:recurse xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" define-macro="categoriesList" tal:condition="cnode" tal:define="global x python:x+1">
+	<tal:block tal:condition="python: (cnode.__class__.__name__=='ServerDef' or cnode.__class__.__name__=='CategoryDef' or cnode.__class__.__name__=='ServiceTypeDef' or cnode.__class__.__name__=='Registry')">
+		<tal:block tal:condition="python:cnode.__class__.__name__=='ServiceTypeDef'">
+			<tal:block
+				tal:define="global menuClass string:menu forms; global aClass string:; global ulStyle string:;;;"
+			/>			
+		</tal:block>
+		<tal:block tal:condition="python:cnode.__class__.__name__!='ServiceTypeDef'">
+			<tal:block
+				tal:define="global menuClass string:;;;"
+			/>          
+		</tal:block>
+		<a tal:condition="cnode/name" tal:content="cnode/name" tal:attributes="href string:#pt${x}; class python:'blindLink %s %s %s %s' % (menuClass, aClass, cnode.__class__.__name__, cnode.name.lower())" >server name</a>
+		<ul tal:attributes="id string:pt${x}; style ulStyle">
+			<tal:block tal:repeat="c cnode/getChildCategories">
+				<li tal:attributes="class string:tree_category tree_node">
+					<metal:block tal:define="cnode c">
+						<tal:block tal:condition="python:(options['filterOn']=='false')">
+							<tal:block
+								tal:define="global aClass string:closed; global ulStyle string:display: none;; visibility: hidden;;; "
+							/>
+						</tal:block>
+						<div metal:use-macro="sitemacros/macros/categoriesList"/>
+					</metal:block>
+				</li>
+			</tal:block>
+			<tal:block tal:repeat="c cnode/getChildServices">
+				<metal:block tal:define="cnode c">
+					<tal:block tal:condition="python:(options['filterOn']=='false')">
+						<tal:block
+							tal:define="global aClass string:closed; global ulStyle string:display: none;; visibility: hidden;;; "
+						/>
+					</tal:block>
+					<div metal:use-macro="sitemacros/macros/categoriesList"/>
+				</metal:block>
+			</tal:block>
+		</ul>
+	</tal:block>
+	<tal:block tal:condition="python: (cnode.__class__.__name__=='ProgramDef' or cnode.__class__.__name__=='WorkflowDef' or cnode.__class__.__name__=='ViewerDef' or cnode.__class__.__name__=='TutorialDef' )">
+		<metal:block tal:define="program cnode">
+			<li tal:attributes="class string:tree_program">
+			<tal:block tal:condition="python:(program.server.name!='local')" >
+				<tal:block tal:define="global sName string:${program/server/name}."></tal:block>
+			</tal:block>
+			<tal:block tal:condition="python:(program.server.name=='local')" >
+				<tal:block tal:define="global sName"></tal:block>
+			</tal:block>
+      <tal:block tal:condition="python: (cnode.__class__.__name__=='ProgramDef' or cnode.__class__.__name__=='WorkflowDef' or cnode.__class__.__name__=='ViewerDef')">
+				<a class="link" tal:attributes="href string:#forms::${program/pid}; data-programurl program/url">
+					<tal:block tal:content="program/name">program name</tal:block><tal:block tal:condition="python:(program.server.name!='local')" tal:content="python:'@'+program.server.name">@server name</tal:block>
+				</a>
+      </tal:block>
+      <tal:block tal:condition="python: (cnode.__class__.__name__=='TutorialDef')">
+        <a class="link" tal:attributes="href string:#tutorials::${program/pid}; data-tutorialurl program/url">
+          <tal:block tal:content="program/name">tutorial name</tal:block><tal:block tal:condition="python:(program.server.name!='local')" tal:content="python:'@'+program.server.name">@server name</tal:block>
+        </a>
+      </tal:block>
+			<ul class="tooltip" style="display: none" tal:condition="python:hasattr(program,'description') or (hasattr(program,'searchMatches') and len(program.searchMatches)>0)">
+            <li tal:condition="program/description"><span tal:content="program/description"/></li>
+            <li tal:condition="python:hasattr(program,'searchMatches') and len(program.searchMatches)>0">Match in:
+              <ul >
+                <li tal:repeat="tooltip program/searchMatches">
+                <i><span tal:content="tooltip/0" /></i>: <span tal:content="structure tooltip/1" />
+                </li>
+              </ul>
+            </li>
+          </ul>
+			</li>
+		</metal:block>
+	</tal:block>
+</metal:recurse>
+
+<div id="programs_list_div" class="programs" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" tal:define-macro="categoriesList">
+
+<input id="programsLocalRepository" type="hidden" tal:attributes="value python:options['registry'].serversByName['local'].repository"/>
+
+<a tal:condition="python:(options['emptySearchResults']=='true')" href="#" class="emptySearchResults">no results</a>
+
+<tal:block id="programs_tree"  tal:define="global x python:0">
+	<tal:block tal:condition="python:(options['filterOn']=='false')" >
+		<tal:block tal:define="global aClass string:closed; global ulStyle string:;;; "></tal:block>
+	</tal:block>
+	<tal:block tal:condition="python:not(options['filterOn']=='false')" >
+		<tal:block tal:define="global aClass string:; global ulStyle string:;;;"></tal:block>
+	</tal:block>
+	<metal:block tal:define="cnode options/registry">
+		<div metal:use-macro="sitemacros/macros/categoriesList" />
+	</metal:block>
+</tal:block>
+
+</div>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/programs_list.py b/Src/Portal/cgi-bin/MobylePortal/programs_list.py
new file mode 100755
index 0000000..d8e663a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/programs_list.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+from Mobyle.ClassificationIndex import ClassificationIndex
+from Mobyle.SearchIndex import SearchIndex
+from Mobyle.DescriptionsIndex import DescriptionsIndex
+
+
+def process(self):
+    self.template_file = "programs_list.html"
+    self.response['filterOn'] = 'false'
+    self.response['emptySearchResults'] = 'false'
+    
+    if self.request.getfirst('plSubmit')=='Search':
+        searchString = self.request.getfirst('searchString',None)
+        if searchString:
+            self.response['filterOn'] = 'true'
+            si = SearchIndex("program")
+            si.filterRegistry(searchString.split(' '))
+            sj = SearchIndex("workflow")
+            sj.filterRegistry(searchString.split(' '))
+            st = SearchIndex("tutorial")
+            st.filterRegistry(searchString.split(' '))
+        if len(registry.programs)+len(registry.workflows)==0:
+            self.response['emptySearchResults'] = 'true'
+
+    p_classification = ClassificationIndex("program")
+    w_classification = ClassificationIndex("workflow")
+    t_classification = ClassificationIndex("tutorial")
+    DescriptionsIndex("program").fillRegistry()
+    DescriptionsIndex("workflow").fillRegistry()
+    DescriptionsIndex("tutorial").fillRegistry()
+    p_classification.buildRegistryCategories(field=self.request.getfirst('classifyBy','category'),serviceTypeSort=self.request.getfirst('serviceTypeSort','separate'))
+    w_classification.buildRegistryCategories(field=self.request.getfirst('classifyBy','category'),serviceTypeSort=self.request.getfirst('serviceTypeSort','separate'))
+    t_classification.buildRegistryCategories(field=self.request.getfirst('classifyBy','category'),serviceTypeSort=self.request.getfirst('serviceTypeSort','separate'))
+    registry.servers.sort()
+    self.response['registry']=registry
+      
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/registry.py b/Src/Portal/cgi-bin/MobylePortal/registry.py
new file mode 100755
index 0000000..19cd01d
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/registry.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry, DefNode, CategoryDef, ProgramDef, Registry
+from Mobyle.ClassificationIndex import ClassificationIndex
+from Mobyle.SearchIndex import SearchIndex
+from Mobyle.DescriptionsIndex import DescriptionsIndex
+import simplejson as json
+
+def encode_registry(obj):
+    if isinstance(obj, Registry):
+        return {'categories': [encode_registry(c) for c in obj.getChildCategories()],
+                'programs': [encode_registry(c) for c in obj.getChildServices()]
+                }
+    elif isinstance(obj, CategoryDef):
+        return {'name':obj.name,
+                'categories': [encode_registry(c) for c in obj.getChildCategories()],
+                'programs': [encode_registry(c) for c in obj.getChildServices()]
+                }
+    elif isinstance(obj, ProgramDef):
+        return {'name': obj.name, 
+                'description': obj.description
+                }
+    else:
+        return str(obj)
+
+
+def process(self):
+    searchString = self.request.getfirst('searchString',None)
+    if searchString:
+        si = SearchIndex("program")
+        self.response['filterOn'] = 'true'
+        si.filterRegistry(searchString.split(' '))
+    classification = ClassificationIndex("program")
+    DescriptionsIndex("program").fillRegistry()
+    classification.buildRegistryCategories()
+    self.jsonMap = registry
+    self.encoder = encode_registry
+      
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/schema_get_flattened.py b/Src/Portal/cgi-bin/MobylePortal/schema_get_flattened.py
new file mode 100755
index 0000000..fec0811
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/schema_get_flattened.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import os
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    self.mime_type = "text/xml"
+    self.xslParams = {}
+    self.xmlUrl = os.path.join(mb_cgi.MOBYLEHOME,'Schema',self.request.getfirst('type')+'.rng')
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/schema_flatten.xsl",{}) # remove xhtml namespace junk
+                    ]
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_activate.py b/Src/Portal/cgi-bin/MobylePortal/session_activate.py
new file mode 100755
index 0000000..65c0c69
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_activate.py
@@ -0,0 +1,35 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import Mobyle.Net
+
+def process(self):
+    try:
+        assert self.request.getfirst('email'), "Please provide an e-mail"
+        assert self.request.getfirst('password'), "Please provide a password"
+        assert self.request.getfirst('key'), "Please provide an activation key"
+    except AssertionError, ae:
+        self.error_msg = str(ae)
+        return
+    try:
+        email = Mobyle.Net.EmailAddress(self.request.getfirst('email'))
+        password = self.request.getfirst('password')
+        self.session = self.sessionFactory.getAuthenticatedSession(email,password)
+        self.sessionKey = self.session.getKey()
+        self.read_session()
+        self.session.confirmEmail(self.request.getfirst('key').strip())
+        self.jsonMap['ok'] = str(True)
+    except Exception, e:
+        self.jsonMap['ok'] = str(False)
+        self.jsonMap['msg'] = str(e)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_activate_ano.html b/Src/Portal/cgi-bin/MobylePortal/session_activate_ano.html
new file mode 100755
index 0000000..d4049b1
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_activate_ano.html
@@ -0,0 +1,11 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="usercaptchaForm" action="session_captcha_check.py" class="modal">
+  <img id="captchaImage" tal:attributes="src python:'session_captcha_get.py?key='+str(self.key)" height="96px" width="256px" class="captcha_background"/>
+  <div>
+    To validate your submission, please type the 
+    <br/>
+    above text in the field below.
+  </div>
+  <div>
+    <input type="text" id="captchaSolution" name="solution" tabindex='1' autofocus/>
+  </div><a href="#" class="closeModal" title="cancel"><input type="button" value="Cancel" tabindex='3' /></a><input id="usercaptchaSubmit" name="submit" type="submit" value="Ok" tabindex='2'>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_activate_auth.html b/Src/Portal/cgi-bin/MobylePortal/session_activate_auth.html
new file mode 100755
index 0000000..7e329fe
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_activate_auth.html
@@ -0,0 +1,24 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="useractivateForm" action="session_activate.py" class="modal">
+  <strong>Activate your registered account </strong>
+  <hr/>
+  <div>
+    Please fill the form below using the activation key we sent
+    you by email.
+  </div>
+  <br/>
+  <div>
+    <label for="activateEmail">
+      e-mail 
+    </label><input id="activateEmail" name="email" class="form_parameter mandatory" size="20" type="text" tabindex='1' tal:attributes="value self/session/getEmail" >
+  </div>
+  <div>
+    <label for="activatePassword">
+      password 
+    </label><input id="activatePassword" name="password" class="form_parameter mandatory" size="20" type="password" tabindex='2' autofocus>
+  </div>
+  <div>
+    <label for="activateKey">
+      activation key 
+    </label><input id="activateKey" name="key" class="form_parameter mandatory" size="20" type="text" tabindex='3'>
+  </div><a href="#" class="closeModal" title="cancel"><input name="cancel" type="button" value="Cancel" tabindex='5' /></a><input id="useractivateSubmit" name="submit" type="submit" value="Activate" tabindex='4'/>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_activate_form.py b/Src/Portal/cgi-bin/MobylePortal/session_activate_form.py
new file mode 100755
index 0000000..17a19c1
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_activate_form.py
@@ -0,0 +1,22 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    if self.session.isAuthenticated():
+        self.template_file = 'session_activate_auth.html'
+    else:
+        import random
+        self.key = random.randint(1,1000)
+        self.template_file = 'session_activate_ano.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_captcha_check.py b/Src/Portal/cgi-bin/MobylePortal/session_captcha_check.py
new file mode 100755
index 0000000..645a0dc
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_captcha_check.py
@@ -0,0 +1,22 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    ok = self.session.checkCaptchaSolution(self.request.getfirst('solution'))
+    if not(ok):
+        self.jsonMap['errormsg'] = "Wrong answer, try again"
+        self.jsonMap['reset'] = True
+    self.read_session()
+    self.jsonMap['ok'] = str(ok)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_captcha_get.py b/Src/Portal/cgi-bin/MobylePortal/session_captcha_get.py
new file mode 100755
index 0000000..d277d8d
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_captcha_get.py
@@ -0,0 +1,17 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.pngImage = self.session.getCaptchaProblem()
+
+if __name__ == "__main__":
+    mb_cgi.PNGCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_email_form.html b/Src/Portal/cgi-bin/MobylePortal/session_email_form.html
new file mode 100755
index 0000000..9cbc7a9
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_email_form.html
@@ -0,0 +1,13 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" action="session_setemail.py" class="modal">
+  <strong>Your email </strong>
+  <hr/>
+  <div>
+    Please provide your email in the form below.
+  </div>
+  <br/>
+  <div>
+    <label>
+      e-mail <input id="usersetEmailEmail" name="email" class="form_parameter mandatory" size="20" type="email" tabindex='1' autofocus>
+    </label>
+  </div><a href="#" class="closeModal" title="cancel"><input name="cancel" type="button" value="Cancel" tabindex='3' /></a><input name="submit" type="submit" value="Ok" tabindex='2' />
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_email_form.py b/Src/Portal/cgi-bin/MobylePortal/session_email_form.py
new file mode 100755
index 0000000..7a989d7
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_email_form.py
@@ -0,0 +1,18 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.email = self.session.getEmail()
+    self.template_file = 'session_email_form.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_help.html b/Src/Portal/cgi-bin/MobylePortal/session_job_help.html
new file mode 100755
index 0000000..a24e6e5
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_help.html
@@ -0,0 +1,28 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" action="help_request.py" class="modal help">
+  <div>
+    <strong>Help on your job</strong>
+  </div>
+  <div>
+    You can request help from the support team of this server. Please describe your problem here, and remember to describe your problem <em>precisely</em>, using <em>complete sentences</em>.
+  </div>
+  <div>
+    <input type="hidden" name="id" tal:attributes="value self/jobId"/>
+    <input type="hidden" name="message" tal:attributes="value self/message"/>
+    <input type="hidden" name="param" tal:attributes="value self/param"/>
+    <label class="mandatory" for="helpEmail">
+      your e-mail:
+    </label>
+    <input type="text" name="helpEmail" tal:attributes="value self/email">
+    </input>
+  </div>
+  <label class="mandatory" for="helpMessage">
+    describe your problem:
+  </label>
+  <textarea cols="70" rows="15" name="helpMessage" autofocus></textarea>
+  <div>
+    <a href="#" class="closeModal" title="cancel">
+    <input type="button" value="Cancel"></a>
+    </input><input name="submit" type="submit" value="Send">
+    </input>
+  </div>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_help_form.py b/Src/Portal/cgi-bin/MobylePortal/session_job_help_form.py
new file mode 100755
index 0000000..65c8e2d
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_help_form.py
@@ -0,0 +1,21 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.jobId = self.request.getfirst('id',None)
+    self.message = self.request.getfirst('message',None)
+    self.param = self.request.getfirst('param',None)
+    self.email = self.session.getEmail()
+    self.template_file = 'session_job_help.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_remove.html b/Src/Portal/cgi-bin/MobylePortal/session_job_remove.html
new file mode 100755
index 0000000..5d12e8c
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_remove.html
@@ -0,0 +1,9 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" action="session_job_remove.py" class="modal">
+  <strong>Job removal</strong>
+  <div>This will remove this job from your session. Continue?</div>
+  <input type="hidden" name="id" tal:attributes="value self/jobId" />
+  <div>
+      <a href="#" class="closeModal" title="cancel"><input type="button" value="Cancel" id="removeJobCancel" /></a>
+      <input type="submit" value="Remove" autofocus/>      
+  </div>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_remove.py b/Src/Portal/cgi-bin/MobylePortal/session_job_remove.py
new file mode 100755
index 0000000..d4fa7d6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_remove.py
@@ -0,0 +1,22 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process( self ):
+    if self.session:
+        pids = self.request.getlist('id')
+        for pid in pids:
+            self.session.removeJob(registry.getJobURL(pid))
+    self.jsonMap['ok'] = str(True)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_remove_form.py b/Src/Portal/cgi-bin/MobylePortal/session_job_remove_form.py
new file mode 100755
index 0000000..03dede3
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_remove_form.py
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.jobId = self.request.getfirst('id',None)
+    self.email = self.session.getEmail()
+    self.template_file = 'session_job_remove.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_rename.py b/Src/Portal/cgi-bin/MobylePortal/session_job_rename.py
new file mode 100755
index 0000000..4c47535
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_rename.py
@@ -0,0 +1,43 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import os
+from lxml import etree
+
+from Mobyle.JobState import url2path
+from Mobyle.Registry import registry
+
+graphml_file_name = ".workflow_template_graphml.xml"
+
+def process( self ):
+    if self.session:
+        job_id = registry.getJobURL(self.request.getfirst('id'))
+        user_name = self.request.getfirst('userName').strip()
+        if(self.session.hasJob(job_id) and self.session.getJobUserName(job_id)!=user_name):
+            self.session.renameJob(job_id,user_name)
+            job_graphml_file_path = os.path.join(url2path(job_id), graphml_file_name)
+            #modify user name in workflow graphml file if it exists
+            if os.path.exists(job_graphml_file_path):
+                # rename in the graphml file directly as well
+                source_file = open(job_graphml_file_path, 'r')
+                root_tree = etree.parse(source_file)
+                source_file.close()
+                graph = root_tree.find('graph')
+                if graph.get('userName')!=user_name:
+                    graph.set('userName', user_name)
+                    wffile = open(job_graphml_file_path, 'w' )
+                    wffile.write( etree.tostring(root_tree)) 
+                    wffile.close()
+            self.message = user_name
+
+
+if __name__ == "__main__":
+    mb_cgi.TextCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_job_submit.py b/Src/Portal/cgi-bin/MobylePortal/session_job_submit.py
new file mode 100755
index 0000000..43a1fb6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_job_submit.py
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.JobFacade import JobFacade
+import Mobyle.Net
+import time #@UnresolvedImport
+from Mobyle.MobyleError import MobyleError
+
+def process(self):
+    self.email = self.request.getfirst('email',None)
+    if self.email:
+        self.session.setEmail(Mobyle.Net.EmailAddress( self.email) )
+    try:
+        j = JobFacade.getFromService(programUrl=self.request.getfirst('programName',None),workflowUrl=self.request.getfirst('workflowUrl',None))
+        j.email_notify = self.request.getfirst('_email_notify','auto')
+        self.jsonMap = j.create(self.request, None, self.session)
+        if self.jsonMap.has_key('id') and not(self.jsonMap.has_key('errormsg')):
+            self.jsonMap = j.addJobToSession(self.session, self.jsonMap)
+            job = self.session.getJob(self.jsonMap['id'])
+            jobdatestring = time.strftime( "%x    %X", job['date'])
+            self.jsonMap['title'] = job['programName'] + ' - ' + jobdatestring
+    except MobyleError, err:
+        self.jsonMap = {'errormsg': err.message}
+        if hasattr(err, 'param'):
+            self.jsonMap['errorparam']=err.param.getName()            
+        return
+ 
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_register.html b/Src/Portal/cgi-bin/MobylePortal/session_register.html
new file mode 100755
index 0000000..21eae7a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_register.html
@@ -0,0 +1,28 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="userregisterForm" action="session_register.py" class="modal">
+  <strong>Register for an account </strong>
+  <hr/>
+  <div>
+    Registration enables you to store your bookmarked data and
+    results on the server, without any time limit (as long as we can provide
+    enough disk space of course!). You can also connect using the same
+    e-mail and password from any place.
+  </div>
+  <br/>
+  <div>
+    <label for="registerEmail">
+      e-mail 
+    </label><input id="registerEmail" name="email" class="form_parameter mandatory" size="20" type="text" tabindex='1' autofocus>
+  </div>
+  <div>
+    <label for="registerPassword">
+      password 
+    </label><input id="registerPassword" name="password" class="form_parameter mandatory" size="20" type="password" tabindex='2'>
+  </div>
+  <div>
+    <label for="registerPasswordCheck">
+      password check 
+    </label><input id="registerPasswordCheck" name="passwordCheck" class="form_parameter mandatory" size="20" type="password" tabindex='3'>
+  </div>
+  <br/>
+  <a href="#" class="closeModal" title="cancel"><input name="cancel" type="button" value="Cancel" tabindex='5' /></a><input id="userregisterSubmit" name="submit" type="submit" value="Register" tabindex='4'/>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_register.py b/Src/Portal/cgi-bin/MobylePortal/session_register.py
new file mode 100755
index 0000000..d14c5a6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_register.py
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import Mobyle.Net
+from Mobyle.MobyleError import SessionError
+
+def process(self):
+    try:
+        assert self.request.getfirst('email'), "Please provide an e-mail"
+        assert self.request.getfirst('password'), "Please provide a password"
+        assert self.request.getfirst('password')==self.request.getfirst('passwordCheck'), "The password values you entered need to be identical"
+    except AssertionError, ae:
+        self.error_msg = str(ae)
+        return
+    try:
+        email = Mobyle.Net.EmailAddress( self.request.getfirst('email') )
+        self.anonymousSession = self.session
+        self.session = self.sessionFactory.createAuthenticatedSession( email , self.request.getfirst('password') )
+        if self.anonymousSession:
+            self.session.mergeWith(self.anonymousSession)
+        self.sessionKey = self.session.getKey()
+        self.read_session()
+        self.jsonMap['ok'] = str(True)
+    except SessionError, e:
+        self.jsonMap['ok'] = str(False)
+        self.jsonMap['errormsg'] = str(e)
+      
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_register_form.py b/Src/Portal/cgi-bin/MobylePortal/session_register_form.py
new file mode 100755
index 0000000..cf2bf22
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_register_form.py
@@ -0,0 +1,17 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.template_file = 'session_register.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_setemail.py b/Src/Portal/cgi-bin/MobylePortal/session_setemail.py
new file mode 100755
index 0000000..19b1984
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_setemail.py
@@ -0,0 +1,31 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.MobyleError import UserValueError
+import Mobyle.Net
+
+def process(self):
+    self.email = self.request.getfirst('email',None)    
+    if self.email:
+        try:
+            self.session.setEmail(Mobyle.Net.EmailAddress( self.email))
+            self.jsonMap['ok'] = str(True)
+        except UserValueError:
+            self.jsonMap['ok'] = str(False)
+            self.jsonMap['errormsg'] = "Invalid e-mail, please provide a valid address"            
+    else:
+        self.jsonMap['ok'] = str(False)
+        self.jsonMap['errormsg'] = "Please specify an e-mail"
+  
+
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signin.html b/Src/Portal/cgi-bin/MobylePortal/session_signin.html
new file mode 100755
index 0000000..8a6c337
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signin.html
@@ -0,0 +1,24 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="usersignInForm" action="session_signin.py" class="modal">
+  <strong>Sign in to your registered account </strong>
+  <hr/>
+  <div>
+    If you have signed-up for a registered account, you can connect here.
+  </div>
+  <br/>
+  <div>
+    <label for="signInEmail">
+      e-mail 
+    </label>
+    <input id="signInEmail" name="email" class="form_parameter mandatory" size="20" type="email" tabindex='1' autofocus>
+  </div>
+  <div>
+    <label for="signInPassword">
+      password 
+    </label>
+    <input id="signInPassword" name="password" class="form_parameter mandatory" size="20" type="password" tabindex='2'>
+  </div><a href="#" class="closeModal" title="cancel"><input name="cancel" type="button" value="Cancel" tabindex='4' /></a><input id="usersignInSubmit" name="submit" type="submit" value="Sign in" tabindex='3' /><hr/>
+  <div>
+    Otherwise, you can <a style="font-weight: bold;" href="#user::register" class="modalLink" title="request a registered account">register</a>
+    for an account.
+  </div>
+</form>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signin.py b/Src/Portal/cgi-bin/MobylePortal/session_signin.py
new file mode 100755
index 0000000..de2c6e7
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signin.py
@@ -0,0 +1,38 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import Mobyle.Net
+from Mobyle.MobyleError import SessionError
+
+def process(self):
+    try:
+        assert self.request.getfirst('email'), "Please provide an e-mail"
+        assert self.request.getfirst('password'), "Please provide a password"
+    except AssertionError, ae:
+        self.error_msg = str(ae)
+        return
+    try:
+        self.anonymousSession = self.session
+        email = Mobyle.Net.EmailAddress(self.request.getfirst('email'))
+        password = self.request.getfirst('password')
+        self.session = self.sessionFactory.getAuthenticatedSession(email,passwd=password)
+        if self.anonymousSession:
+            self.session.mergeWith(self.anonymousSession)
+        self.sessionKey = self.session.getKey()
+        self.read_session()
+        self.jsonMap['ok'] = str(True)
+    except SessionError, e:
+        self.jsonMap['ok'] = str(False)
+        self.error_msg = str(e)
+        
+if __name__ == "__main__":
+    import os
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signin_form.py b/Src/Portal/cgi-bin/MobylePortal/session_signin_form.py
new file mode 100755
index 0000000..e748ce5
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signin_form.py
@@ -0,0 +1,17 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.template_file = 'session_signin.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signout.html b/Src/Portal/cgi-bin/MobylePortal/session_signout.html
new file mode 100755
index 0000000..37b63d1
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signout.html
@@ -0,0 +1,14 @@
+<form xmlns:tal="http://xml.zope.org/namespaces/tal" name="usersignOutForm" action="session_signout.py" class="modal">
+  <strong>Sign out of this account </strong>
+  <hr/>
+  <div>
+    Please note that if you are using a guest account, any
+    submitted job will be lost.
+  </div>
+  <br/>
+  <a href="#" class="closeModal" title="cancel"><input name="cancel" type="button" value="Cancel" tabindex='2' /></a><input id="usersignOutSubmit" name="submit" type="submit" value="Sign out" tabindex='1' autofocus/><hr/>
+  <div>
+    To avoid losing your work, you can <a style="font-weight: bold;" href="#user::register" class="modalLink" title="request a registered account">register</a>
+    for an account.
+  </div>
+</form>
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signout.py b/Src/Portal/cgi-bin/MobylePortal/session_signout.py
new file mode 100755
index 0000000..ee8d8fd
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signout.py
@@ -0,0 +1,29 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    try:
+        if (self.cfg.anonymousSession()!='no'):
+            self.session = self.sessionFactory.getAnonymousSession()
+            self.sessionKey = self.session.getKey()
+        else: 
+            self.session = None
+            self.sessionKey = None
+        self.read_session()
+        self.jsonMap['ok'] = str(True)
+        self.jsonMap['msg'] = str("You have been signed out.")
+    except Exception, e:
+        self.jsonMap['ok'] = str(False)        
+        self.jsonMap['msg'] = str(e)
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_signout_form.py b/Src/Portal/cgi-bin/MobylePortal/session_signout_form.py
new file mode 100755
index 0000000..5aeb6cc
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_signout_form.py
@@ -0,0 +1,17 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    self.template_file = 'session_signout.html'
+
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/session_workspace.py b/Src/Portal/cgi-bin/MobylePortal/session_workspace.py
new file mode 100755
index 0000000..fb8b590
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/session_workspace.py
@@ -0,0 +1,39 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+    
+def process( self ):
+    jobsMap = {}
+    dataMap = {}
+    workflows = {}
+    if self.session:
+        tmpjobs = self.session.getAllJobs()
+        jobsMap = {}
+        for job in tmpjobs:
+            jl = mb_cgi.get_formatted_job_entries(job)
+            for entry in jl:
+                jobsMap[entry['jobID']] = entry
+        data = self.session.getAllData()
+        for dataFile in data:
+            dataType = dataFile['Type'].getDataType().name
+            bioTypes = []
+            for bt in dataFile['Type'].getBioTypes():
+                bioTypes.append(str(bt))
+            userModes = []
+            for im in dataFile['inputModes']:
+                userModes.append(im)
+            data_format = dataFile['Type'].getDataFormat()
+            dataMap[dataFile['dataName']] = {'userName':dataFile['userName'], 'dataType': dataType, 'bioTypes': bioTypes, 'userModes': userModes, 'title':dataFile['dataBegin'], 'size': '%.2f'%(float(dataFile['size'])/1000), 'format': data_format}
+        workflows = self.session.getBMPSWorkflows()
+    self.jsonMap = {'jobs': jobsMap, 'data': dataMap, 'workflows':workflows}
+
+if __name__ == "__main__":
+    mb_cgi.JSONCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/sitemap.py b/Src/Portal/cgi-bin/MobylePortal/sitemap.py
new file mode 100755
index 0000000..305cb8a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/sitemap.py
@@ -0,0 +1,32 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import os
+import datetime
+from Mobyle.Registry import registry
+
+def process(self):
+    self.template_file = "sitemap.xml"
+    csl = []
+    for program in registry.programs:
+        try:
+            cs = {}
+            cs['name'] = program.name
+            # the date below is in utc time, with no local time handling so that we do not have to import additional modules
+            cs['mtime'] = datetime.datetime.utcfromtimestamp(os.path.getmtime(program.path)).isoformat() + '+00:00'
+            csl.append(cs)
+        except OSError:
+            pass
+    self.response['baseurl'] = self.cfg.cgi_url()+'/portal.py'
+    self.response['programs'] = csl
+      
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/sitemap.xml b/Src/Portal/cgi-bin/MobylePortal/sitemap.xml
new file mode 100755
index 0000000..e1d7111
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/sitemap.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <url tal:repeat="program options/programs">
+    <loc tal:content="string:${options/baseurl}#forms::${program/name}"/>
+	<lastmod tal:content="program/mtime" />
+  </url>
+</urlset>
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/task_xml.xsl b/Src/Portal/cgi-bin/MobylePortal/task_xml.xsl
new file mode 100644
index 0000000..8583112
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/task_xml.xsl
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	ident.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	<!-- transform a deployed mobyle xml to remove the interface tags (useless so far in BMW) and 
+		modify vdef values according to the default	values set in the workflow template -->
+
+	<xsl:param name="task_id" />	
+
+	<xsl:param name="workflow_url" />
+
+	<xsl:template match="@*|node()|text()" priority="-1">
+		<xsl:call-template name="nodeCopy" />
+	</xsl:template>
+
+	<xsl:template name="nodeCopy">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()|text()" />
+		</xsl:copy>
+	</xsl:template>
+
+	<xsl:template match="/*">
+		<xsl:copy>
+			<xsl:attribute name="id"><xsl:value-of select="$task_id"/></xsl:attribute>
+			<xsl:apply-templates select="@*|node()|text()"/>
+		</xsl:copy>
+	</xsl:template>		
+
+	<xsl:template match="interface" />
+
+	<xsl:template match="vdef">
+		<xsl:call-template name="vdef_replacement" />
+	</xsl:template>		
+
+  <xsl:template name="vdef_replacement">
+		<xsl:choose>
+			<xsl:when test="document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/text()">
+				<vdef><value><xsl:value-of select="document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/text()" /></value></vdef>
+			</xsl:when>
+			<xsl:when test="document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/@reference">
+				<vdef>
+					<ref safeName="{document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/text()}" 
+					     userName="{document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/@userName}"
+					     srcUrl="{document($workflow_url)//task[@id=$task_id]/inputValue[@name=current()/ancestor-or-self::parameter/name/text()]/@reference}" />
+				</vdef>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+  </xsl:template>
+
+	<xsl:template match="parameter[not(vdef)]">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()|text()" />
+			<xsl:call-template name="vdef_replacement" />
+		</xsl:copy>		
+	</xsl:template>	
+
+</xsl:stylesheet>
diff --git a/Src/Portal/cgi-bin/MobylePortal/treenode.py b/Src/Portal/cgi-bin/MobylePortal/treenode.py
new file mode 100755
index 0000000..bd40a41
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/treenode.py
@@ -0,0 +1,65 @@
+from operator import methodcaller
+class TreeNode(object):
+
+   def __init__(self, name):
+      self.name = name
+      self.children = []
+
+   def get_name(self):
+      return self.name
+
+   def set_children(self, children=None):
+       self.children = children
+       
+   def has_children(self):
+       if len(self.children) > 0 :
+           return True
+       else :
+           return False
+       
+   def add_child(self, child=None):
+       self.children.append(child)
+       
+   def has_child_with_name(self, child_name=None):
+       has_child = False 
+       for my_child in self.children :
+          if my_child.get_name() == child_name :
+             has_child = True
+             break
+       return has_child
+   
+   def comparekey(self):
+       """
+       This method allows to compare two nodes of the tree
+       main characteristics:
+       - case-insensitive
+       - places categories above
+       """
+       if self.has_children():
+           # adding a front space to category name places
+           # them above...
+           return ' '+self.name.lower()
+       else:
+           return self.name.lower()
+   
+   def sort_alphabetically(self):
+       if self.has_children() : 
+           self.children.sort(key=methodcaller('comparekey'))
+       for child in self.children :
+           child.sort_alphabetically()
+   
+   def to_json_str(self, node=None):
+       self.sort_alphabetically()
+       if self.has_children() : 
+           json_str = '{"name":"%s"' % self.name
+           json_str += ',"children":['
+       else:
+           json_str = '{"name":"%s"' % self.name
+           
+       for child in self.children :
+           json_str += child.to_json_str() + ','
+       if self.has_children() :
+           json_str = json_str.rstrip(","); 
+           json_str += ']'
+       json_str += '}'
+       return json_str
diff --git a/Src/Portal/cgi-bin/MobylePortal/tutorial.py b/Src/Portal/cgi-bin/MobylePortal/tutorial.py
new file mode 100755
index 0000000..3ad9bb3
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/tutorial.py
@@ -0,0 +1,57 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    self.xslParams = {}
+    try:
+        def f(x): return x and x != '.'
+        id = filter(f,self.request.getfirst('id').partition('.'))
+        if len(id)==1:
+            self.service = registry.serversByName['local'].tutorialsByName[id[0]]            
+        else:
+            self.service = registry.serversByName[id[0]].tutorialsByName[id[1]]            
+        if not(self.service.server.name=='local'):
+            self.xslParams['remoteServerName']="'"+self.service.server.name+"'"
+    except Exception:
+        # program does not exist
+        self.error_msg = "Tutorial %s cannot be found on this server." % self.request.getfirst('id','')
+        self.error_title = "Tutorial Not found"
+        return
+    if self.service.disabled:
+        # program disabled
+        self.error_msg = "this tutorial has been disabled on this server"
+        self.error_title = "Tutorial Disabled"
+    elif not(self.service.authorized):
+        # program restricted
+        self.error_msg = "the access to this tutorial is restricted on this server"
+        self.error_title = "Tutorial Unauthorized"
+    else:
+        # ok
+        self.xmlUrl = self.service.path
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/tutorial.xsl",
+                        {'programUri':"'"+self.service.url+"'",
+                         'programPID':"'"+self.service.pid+"'",
+                         'codebase':"'"+self.cfg.repository_url()+"/'"}),
+                    (self.cfg.portal_path()+"/xsl/remove_ns.xsl",{}) # remove xhtml namespace junk
+                    ]
+    if self.service.server.name=='local':
+        self.title = self.service.name
+    else:
+        self.title = self.request.getfirst('id')
+        tmptitle = self.title.split('.')
+        tmptitle.reverse()
+        self.title = '@'.join(tmptitle)
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process,useSession=True)
diff --git a/Src/Portal/cgi-bin/MobylePortal/update_graphml_with_graphviz_layout.xsl b/Src/Portal/cgi-bin/MobylePortal/update_graphml_with_graphviz_layout.xsl
new file mode 100644
index 0000000..e62330a
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/update_graphml_with_graphviz_layout.xsl
@@ -0,0 +1,27 @@
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+	<xsl:param name="cssPath" />
+
+	<xsl:template match="node()|@*">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()" />
+		</xsl:copy>
+	</xsl:template>
+
+	<xsl:template match="data[ @key = 'xy']">
+		<xsl:variable name="node_id" select="../@id" />
+		<xsl:variable name="coords"
+			select="document(concat('file://',$cssPath))/map/area[@title = $node_id ]/@coords" />
+		<xsl:variable name="x" select="substring-before($coords,',')" />
+		<xsl:variable name="tmp" select="substring-after($coords,',')" />
+		<xsl:variable name="y" select="substring-before($tmp,',')" />
+		<xsl:element name="data">
+			<xsl:attribute name="key">
+                                <xsl:value-of select="'xy'" />
+                            </xsl:attribute>
+			<xsl:value-of select="concat($x,',',$y+50)" />
+		</xsl:element>
+	</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Src/Portal/cgi-bin/MobylePortal/viewer_view.py b/Src/Portal/cgi-bin/MobylePortal/viewer_view.py
new file mode 100755
index 0000000..ccba511
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/viewer_view.py
@@ -0,0 +1,27 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+import os
+
+def process(self):
+    self.xmlUrl = registry.serversByName['local'].viewersByName[self.request.getfirst('program')].url
+    self.dataUrl = '%s/viewer_xml_input.py?%s' % (self.cfg.cgi_url(),os.environ['QUERY_STRING'])
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/viewer.xsl",
+                        {'jobUrl':"'"+self.dataUrl+"'",
+                        'htdocs':"'/"+self.cfg.portal_url(True)+"/'",
+                        'codebase':"'"+self.cfg.repository_url()+"/'"}),
+                    (self.cfg.portal_path()+"/xsl/remove_ns.xsl",{}) # remove xhtml namespace junk
+                    ]
+    
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/viewer_xml_input.py b/Src/Portal/cgi-bin/MobylePortal/viewer_xml_input.py
new file mode 100755
index 0000000..2f1a9f6
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/viewer_xml_input.py
@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+
+def process(self):
+    template = """<jobState> 
+  <data>
+  %s
+  </data>
+</jobState>
+    """
+    paramTemplate = """
+  <parameter> 
+    <name>%s</name> 
+  </parameter> 
+  <file>%s</file> 
+    """
+    self.xmltext = ''
+    if id:
+        self.parameters = self.request.getlist('parameter')
+        for param in self.parameters:
+            name, value = param.split('|')
+            if not(value.startswith(self.cfg.root_url())):
+                value = "%s/file_load.py?url=%s" %( self.cfg.cgi_url() , value)
+            self.xmltext += paramTemplate % (name, value)
+        self.xmltext = template % (self.xmltext)
+
+if __name__ == "__main__":
+    il = mb_cgi.XMLCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/wf_to_graphml.xsl b/Src/Portal/cgi-bin/MobylePortal/wf_to_graphml.xsl
new file mode 100644
index 0000000..c1d68d2
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/wf_to_graphml.xsl
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	<!-- this stylesheet transforms a mobyle workflow from the mobyle XML format to a GRAPHML-based format -->
+	<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
+	<xsl:key name="fromTaskIds" match="link" use="@fromTask" />
+	<xsl:key name="toTaskIds" match="link" use="@toTask" />
+	
+	<xsl:template match="workflow">
+		<graphml>
+			<key id="name" attr.name="name" for="node" attr.type="string" />
+			<key id="pause" attr.name="pause" for="node" attr.type="boolean" />
+			<key id="xy" attr.name="position" for="node" attr.type="string" />
+			<key id="type" attr.name="portType" for="port" attr.type="string" />
+			<key id="status" attr.name="portStatus" for="port" attr.type="string" />
+			<key id="processJobStatus" attr.name="jobStatus" for="node" attr.type="string"/>
+			<key id="workflowJobStatus" attr.name="jobStatus" for="graph" attr.type="string"/>
+			<key id="title" attr.name="workflowTitle" for="graph" attr.type="string" />
+			<key id="description" attr.name="workflowDescription" for="graph" attr.type="string" />
+			<graph>
+					<xsl:attribute name="id">
+    	                <xsl:value-of select="@id" />
+	                </xsl:attribute>
+					<xsl:apply-templates select="head" />
+					<xsl:for-each select="flow/task">
+						<node>
+							<xsl:variable name="serviceDoc" select="document(@service_url)" />							
+							<xsl:attribute name="id">
+                                <xsl:value-of select="@id" />
+                            </xsl:attribute>
+							<data key="name">
+								<xsl:value-of select="@service" />
+							</data>
+							<data key="pause">
+								<xsl:choose>
+									<xsl:when test="@suspend='1' or @suspend='true'">true</xsl:when>
+									<xsl:otherwise>false</xsl:otherwise>
+								</xsl:choose>
+							</data>
+							<xsl:if test="position">
+								<data key="xy"><xsl:value-of select="position/text()" /></data>
+							</xsl:if>
+							<xsl:variable name="task_id" select="@id" />
+							<xsl:for-each select="$serviceDoc//parameter">
+								<xsl:variable name ="dataType">
+									<xsl:choose>
+										<xsl:when test="type/datatype/superclass"><xsl:value-of select="type/datatype/superclass"/></xsl:when>
+										<xsl:otherwise><xsl:value-of select="type/datatype/class"/></xsl:otherwise>
+									</xsl:choose>
+								</xsl:variable>
+								<xsl:if test="not(($dataType = 'Integer')
+												or ($dataType = 'Float')
+												or ($dataType = 'Boolean')
+												or ($dataType = 'Choice')
+												or ($dataType = 'MultipleChoice')
+												or ($dataType = 'Text')
+												or ($dataType = 'Binary')
+												or ($dataType = 'String')
+												or ($dataType = 'Filename')
+												or ($dataType = 'Binary'))">
+									<port>
+										<xsl:attribute name="name">
+											<xsl:value-of select="name/text()" />
+										</xsl:attribute>
+										<xsl:choose>
+											<xsl:when test="@isout or @isstdout">
+												<data key="type">pipeOut</data>												
+											</xsl:when>
+											<xsl:otherwise>
+												<data key="type">pipeIn</data>
+											</xsl:otherwise>
+										</xsl:choose>
+										<data key="status">active</data>
+									</port>									
+								</xsl:if>
+							</xsl:for-each>
+							<!--
+							<xsl:for-each select="../link[@fromTask=$task_id]">
+								<xsl:if test="generate-id() = generate-id(key('fromTaskIds', at fromTask))">
+									<port>
+										<xsl:attribute name="name">
+                                <xsl:value-of select="@fromParameter" />
+                            </xsl:attribute>
+										<data key="type">pipeOut</data>
+										<data key="status">active</data>
+									</port>
+								</xsl:if>
+							</xsl:for-each>
+							<xsl:for-each select="../link[@toTask=$task_id]">
+								<xsl:if test="generate-id() = generate-id(key('toTaskIds', at toTask))">
+
+									<port>
+										<xsl:attribute name="name">
+                                <xsl:value-of select="@toParameter" />
+                            </xsl:attribute>
+										<data key="type">pipeIn</data>
+										<data key="status">active</data>
+									</port>
+								</xsl:if>
+
+							</xsl:for-each>
+							-->
+						</node>
+					</xsl:for-each>
+					<xsl:for-each select="flow/link">
+						<edge>
+							<xsl:attribute name="source">
+                            <xsl:value-of select="@fromTask" />
+                        </xsl:attribute>
+							<xsl:attribute name="target">
+                            <xsl:value-of select="@toTask" />
+                        </xsl:attribute>
+							<xsl:attribute name="sourcePort">
+                            <xsl:value-of select="@fromParameter" />
+                        </xsl:attribute>
+							<xsl:attribute name="targetPort">
+                            <xsl:value-of select="@toParameter" />
+                             </xsl:attribute>
+						</edge>
+					</xsl:for-each>
+
+				</graph>
+		</graphml>
+	</xsl:template>
+	
+	<xsl:template match="head">
+		<xsl:apply-templates select="doc/title" />
+		<xsl:apply-templates select="doc/description" />
+		<xsl:apply-templates select="doc/comment" />
+	</xsl:template>
+	
+	<xsl:template match="title" >
+		<!-- workflow title value -->
+		<data key="title"><xsl:value-of select="text()"></xsl:value-of></data>
+	</xsl:template>
+
+	<xsl:template match="description" >
+		<!-- workflow description value -->
+		<data key="description">
+			<xsl:apply-templates select="text" />
+			<xsl:apply-templates select="xhtml:*" />
+		</data>
+	</xsl:template>	
+
+	<xsl:template match="comment" >
+		<!-- workflow description value -->
+		<data key="comment">
+			<xsl:apply-templates select="text" />
+			<xsl:apply-templates select="xhtml:*" />
+		</data>
+	</xsl:template>
+
+	<xsl:template match="text" >
+		<xsl:value-of select="text()" />
+	</xsl:template>
+
+	<xsl:template match="xhtml:*" >
+		<xsl:copy-of select="." />
+	</xsl:template>	
+
+</xsl:stylesheet>
+
diff --git a/Src/Portal/cgi-bin/MobylePortal/workflow.py b/Src/Portal/cgi-bin/MobylePortal/workflow.py
new file mode 100755
index 0000000..ce84dc4
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/workflow.py
@@ -0,0 +1,357 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+from lxml import etree
+from StringIO import StringIO
+import urllib
+ 
+import mb_cgi
+from Mobyle.Workflow import Workflow, Task, Text, Parameter, Link, Type, Datatype, Biotype, InputValue,\
+    Parser, parseTextOrHTML
+from Mobyle.Registry import registry
+from Mobyle.MobyleError import MobyleError
+
+def process( self ):
+    action = self.request.getfirst("action")
+    if action in ["create","update"]:
+        if action=="create":
+            workflow = Workflow()
+            self.workflow_id = self.session.addWorkflow(workflow)
+        elif action=="update":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+        if self.request.has_key('name'):
+            name = self.request.getfirst('name')
+            workflow.name = name
+        if self.request.has_key('version'):
+            version = self.request.getfirst('version')
+            workflow.version = version
+        if self.request.has_key('title'):
+            title = self.request.getfirst('title')
+            workflow.title = title
+        if self.request.has_key('authors'):
+            authors = self.request.getfirst('authors')
+            workflow.authors = authors
+        if self.request.has_key('description'):
+            description = self.request.getfirst('description')
+            workflow.description = parseTextOrHTML(description)
+        if self.request.has_key('comment'):
+            comment = self.request.getfirst('comment')
+            workflow.comment = parseTextOrHTML(comment)
+        self.session.saveWorkflow(workflow)
+        self.xmltext = render_graphml(workflow)
+    elif action=="get":
+        self.workflow_id = int(self.request.getfirst('wf_id'))
+        workflow = self.session.readWorkflow(self.workflow_id)
+        self.xmltext = render_graphml(workflow)
+    elif action=="list":
+        ids = self.session.getWorkflows()
+        self.xmltext = ids
+    elif action=="get_url":
+        self.workflow_id = int(self.request.getfirst('wf_id'))
+        url = self.session.getWorkflowUrl(self.workflow_id)
+        self.xmltext = url
+    elif action=="create_task":
+        try:
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            service_pid = self.request.getfirst('service_name')
+            service = get_service(service_pid)
+            ids = [int(task.id) for task in workflow.tasks]
+            if ids:
+                self.task_id = max(ids)+1
+            else:
+                self.task_id = 1
+            task = Task()
+            task.id = self.task_id
+            task.service = service_pid
+            task.service_url = service.url
+            workflow.tasks = workflow.tasks + [task]
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_task_xml(service.path, self.task_id,self.session.getWorkflowUrl(self.workflow_id))  
+        except ServiceNotFoundError, snfe:
+            self.xmltext = "<pre>%s</pre>" % snfe.message
+    elif action=="fetch_task":
+        self.workflow_id = int(self.request.getfirst('wf_id'))
+        workflow = self.session.readWorkflow(self.workflow_id)
+        self.task_id = int(self.request.getfirst('task_id'))
+        task = [t for t in workflow.tasks if int(t.id)==self.task_id][0]
+        service_pid = task.service
+        service_path = get_service(service_pid).path
+        self.xmltext = render_task_xml(service_path, self.task_id,self.session.getWorkflowUrl(self.workflow_id))  
+    elif action=="save":
+        self.workflow_id = int(self.request.getfirst('wf_id'))
+        self.graphml = self.request.getfirst('graphml')
+        self.mobylexml = transform_xml(StringIO(self.graphml),'graphml_to_wf.xsl',{'workflow_id':"'%s'" % self.workflow_id})
+        workflow = Parser().XML(str(self.mobylexml))
+        self.session.saveWorkflow(workflow)
+        self.xmltext = self.mobylexml
+    elif action=="fetch":
+        self.workflow_id = int(self.request.getfirst('wf_id'))
+        workflow = self.session.readWorkflow(self.workflow_id)
+        self.xmltext = render_graphml(workflow)
+
+"""
+    elif object=='task':
+        if action in ["create","update"]:
+            if action=="create":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                workflow = self.session.readWorkflow(self.workflow_id)
+                ids = [int(task.id) for task in workflow.tasks]
+                if ids:
+                    id = max(ids)+1
+                else:
+                    id = 1
+                task = Task()
+                task.id = id
+                workflow.tasks = workflow.tasks + [task]
+            elif action=="update":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                task_id = int(self.request.getfirst('task_id'))
+                workflow = self.session.readWorkflow(self.workflow_id)
+                task = [t for t in workflow.tasks if int(t.id)==task_id][0]
+            if self.request.has_key('service_pid'):
+                service_pid = self.request.getfirst('service_pid')
+                task.service = service_pid
+            if self.request.has_key('suspend'):
+                suspend = bool(self.request.getfirst('suspend')=='true')            
+                task.suspend = ("false","true")[suspend]
+            if self.request.has_key('description'):
+                description = self.request.getfirst('description')
+                task.description = description
+            # setting task parameter default values
+            for key in self.request.keys():
+                if key.startswith('parameter::'):
+                    name = key.split('::')[1]
+                    value = self.request.getfirst('parameter::%s' % name,'')
+                    if value!='':
+                        # set a value for a parameter
+                        input_values = [iv for iv in task.input_values if iv.name==name]
+                        if len(input_values)>0:
+                            input_value=input_values[0]
+                        else:
+                            input_value=InputValue()
+                            input_value.name=name
+                            task.input_values = task.input_values + [input_value]
+                        input_value.value=value
+                    else:
+                        # unset a value for a parameter
+                        input_values = [iv for iv in task.input_values if iv.name!=name]
+                        task.input_values = input_values
+            # creating a link from or to the task along the way
+            if self.request.has_key('from_parameter') or self.request.has_key('from_parameter'):
+                ids = [int(link.id) for link in workflow.links]
+                if ids:
+                    id = max(ids)+1
+                else:
+                    id = 1
+                link = Link()
+                link.id = id
+                workflow.links = workflow.links + [link]
+                if self.request.has_key('from_parameter'):
+                    from_parameter = self.request.getfirst('from_parameter')
+                    link.from_parameter = from_parameter
+                if self.request.has_key('from_task'):
+                    from_task = self.request.getfirst('from_task')
+                    if from_task=='self':
+                        from_task = task.id
+                    link.from_task = from_task
+                if self.request.has_key('to_parameter'):
+                    to_parameter = self.request.getfirst('to_parameter')
+                    link.to_parameter = to_parameter
+                if self.request.has_key('to_task'):
+                    to_task = self.request.getfirst('to_task')
+                    if to_task=='self':
+                        to_task = task.id
+                    link.to_task = to_task
+            self.session.saveWorkflow(workflow) 
+            self.xmltext = render_model(task)
+        elif action=="get":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            task_id = int(self.request.getfirst('task_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            task = [t for t in workflow.tasks if int(t.id)==task_id][0]
+            self.xmltext = render_model(task)
+        elif action=="delete":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            task_id = int(self.request.getfirst('task_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            tasks = [t for t in workflow.tasks if int(t.id)!=task_id]
+            workflow.tasks = tasks
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_graphml(workflow)
+    elif object=='link':
+        if action in ["create","update"]:
+            if action=="create":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                workflow = self.session.readWorkflow(self.workflow_id)
+                ids = [int(link.id) for link in workflow.links]
+                if ids:
+                    id = max(ids)+1
+                else:
+                    id = 1
+                link = Link()
+                link.id = id
+                workflow.links = workflow.links + [link]
+            elif action=="update":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                link_id = int(self.request.getfirst('link_id'))
+                workflow = self.session.readWorkflow(self.workflow_id)
+                link = [l for l in workflow.links if int(l.id)==link_id][0]
+            if self.request.has_key('from_parameter'):
+                from_parameter = self.request.getfirst('from_parameter')
+                link.from_parameter = from_parameter
+            if self.request.has_key('from_task'):
+                from_task = self.request.getfirst('from_task')
+                link.from_task = from_task
+            if self.request.has_key('to_parameter'):
+                to_parameter = self.request.getfirst('to_parameter')
+                link.to_parameter = to_parameter
+            if self.request.has_key('to_task'):
+                to_task = self.request.getfirst('to_task')
+                link.to_task = to_task
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_model(link)
+        elif action=="get":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            link_id = int(self.request.getfirst('link_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            link = [t for t in workflow.links if int(t.id)==link_id][0]
+            self.xmltext = render_model(t)
+        elif action=="delete":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            link_id = int(self.request.getfirst('link_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            links = [l for l in workflow.links if int(l.id)!=link_id]
+            workflow.links = links
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_graphml(workflow)
+    elif object=='parameter':
+        if action in ["create","update"]:
+            if action=="create":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                isout = self.request.getfirst('stream')=='output'
+                workflow = self.session.readWorkflow(self.workflow_id)
+                ids = [int(parameter.id) for parameter in workflow.parameters]
+                if ids:
+                    id = max(ids)+1
+                else:
+                    id = 1
+                parameter = Parameter()
+                parameter.id = id
+                parameter.isout = isout
+                workflow.parameters = workflow.parameters + [parameter]
+            elif action=="update":
+                self.workflow_id = int(self.request.getfirst('wf_id'))
+                parameter_id = int(self.request.getfirst('parameter_id'))
+                workflow = self.session.readWorkflow(self.workflow_id)
+                parameter = [p for p in workflow.parameters if int(p.id)==parameter_id][0]
+            if self.request.has_key('name'):
+                name = self.request.getfirst('name')
+                parameter.name = name
+            if self.request.has_key('prompt'):
+                prompt = self.request.getfirst('prompt')
+                parameter.prompt = prompt
+            if self.request.has_key('example'):
+                example = self.request.getfirst('example')
+                parameter.example = example
+            if self.request.has_key('datatype_class'):
+                datatype_class = self.request.getfirst('datatype_class')
+                parameter.type = (Type(),parameter.type) [parameter.type!=None]
+                parameter.type.datatype = (Datatype(),parameter.type.datatype) [parameter.type.datatype!=None]
+                parameter.type.datatype.class_name = datatype_class
+            if self.request.has_key('datatype_superclass'):
+                datatype_superclass = self.request.getfirst('datatype_superclass')
+                parameter.type = (Type(),parameter.type) [parameter.type!=None]
+                parameter.type.datatype = (Datatype(),parameter.type.datatype) [parameter.type.datatype!=None]
+                parameter.type.datatype.superclass_name = datatype_superclass
+            if self.request.has_key('biotype'):
+                biotypes = self.request.getlist('biotype')
+                parameter.biotypes = [Biotype(b) for b in biotypes]
+            if self.request.has_key('example'):
+                example = self.request.getfirst('example')
+                parameter.example = example
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_model(parameter)
+        elif action=="get":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            parameter_id = int(self.request.getfirst('parameter_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            parameter = [p for p in workflow.parameters if int(p.id)==parameter_id][0]
+            self.xmltext = render_model(parameter)
+        elif action=="delete":
+            self.workflow_id = int(self.request.getfirst('wf_id'))
+            parameter_id = int(self.request.getfirst('parameter_id'))
+            workflow = self.session.readWorkflow(self.workflow_id)
+            parameters = [l for l in workflow.parameters if int(l.id)!=parameter_id]
+            workflow.parameters = parameters
+            self.session.saveWorkflow(workflow)
+            self.xmltext = render_graphml(workflow)
+    else:
+        self.xmltext = "error"
+"""
+
+mp = Parser()        
+
+def render_model(e):
+    return mp.tostring(e)
+
+class CustomResolver(etree.Resolver):
+    """
+    CustomResolver is a Resolver for lxml that allows (among other things) to
+    handle HTTPS protocol, which is not handled natively by lxml/libxml2
+    """
+    def resolve(self, url, id, context):
+        return self.resolve_file(urllib.urlopen(url), context)
+
+
+def transform_xml(xml,xslt,parameters={}):
+    parser = etree.XMLParser(no_network = False)
+    parser.resolvers.add(CustomResolver())
+    xml = etree.parse(xml,parser)
+    xslt_doc = etree.parse(xslt,parser)
+    transform = etree.XSLT(xslt_doc)
+    parser = etree.XMLParser( no_network = False )
+    return transform(xml, **parameters)
+
+def render_graphml(e):
+    return transform_xml(StringIO(render_model(e)),'wf_to_graphml.xsl')
+
+def render_task_xml(xml_path,task_id,workflow_url):
+    return transform_xml(xml_path,'task_xml.xsl',{'task_id':"'%s'" % task_id,'workflow_url':"'%s'" % workflow_url})
+
+class ServiceNotFoundError(MobyleError):
+    def __init__(self,pid):
+        self.pid = pid
+
+    @property
+    def message(self):
+        return "Service %s cannot not be found on the server" % self.pid
+
+def get_service(pid):
+    def f(x): return x and x != '.'
+    pid_dict = filter(f,pid.partition('.'))
+    try:
+        try:
+            if len(pid_dict)==1:
+                service = registry.serversByName['local'].programsByName[pid_dict[0]]    
+            else:
+                service = registry.serversByName[pid_dict[0]].programsByName[pid_dict[1]]
+        except KeyError:
+            if len(pid_dict)==1:
+                service = registry.serversByName['local'].workflowsByName[pid_dict[0]]            
+            else:
+                service = registry.serversByName[pid_dict[0]].workflowsByName[pid_dict[1]]
+    except KeyError:
+        raise ServiceNotFoundError(pid)
+    return service
+
+if __name__ == "__main__":
+    mb_cgi.XMLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/workflow_job_layout.py b/Src/Portal/cgi-bin/MobylePortal/workflow_job_layout.py
new file mode 100755
index 0000000..89b5125
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/workflow_job_layout.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    self.mime_type = 'image/svg+xml'
+    self.jobID = registry.getJobURL(self.request.getfirst('id'))
+    self.jobPID = registry.getJobPID(self.jobID)
+    self.xslParams = {}
+    self.xmlUrl = self.jobID + '/index.xml'
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/job_graph.xsl",{'jobPID':"'"+self.jobPID+"'"})
+                    ]
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/workflow_layout.py b/Src/Portal/cgi-bin/MobylePortal/workflow_layout.py
new file mode 100755
index 0000000..27daa9c
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/workflow_layout.py
@@ -0,0 +1,35 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+from Mobyle.Registry import registry
+
+def process(self):
+    self.mime_type = 'image/svg+xml'
+    if self.request.getfirst('id'):
+        def f(x): return x and x != '.'
+        id = filter(f,self.request.getfirst('id').partition('.'))
+        try:
+            if len(id)==1:
+                self.service = registry.serversByName['local'].programsByName[id[0]]    
+            else:
+                self.service = registry.serversByName[id[0]].programsByName[id[1]]
+        except KeyError, e:
+            if len(id)==1:
+                self.service = registry.serversByName['local'].workflowsByName[id[0]]            
+            else:
+                self.service = registry.serversByName[id[0]].workflowsByName[id[1]]            
+    self.xmlUrl = self.service.path
+    self.xslPipe = [
+                    (self.cfg.portal_path()+"/xsl/form_graph.xsl",{})
+                    ]
+
+if __name__ == "__main__":
+    il = mb_cgi.XSLCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/cgi-bin/MobylePortal/workflow_test.html b/Src/Portal/cgi-bin/MobylePortal/workflow_test.html
new file mode 100755
index 0000000..cd304c2
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/workflow_test.html
@@ -0,0 +1,36 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <head>
+  <title>Workflow design API test sandbox</title>
+  </head>
+  <body>
+    <div style="float:right;">
+      <h3>User session information</h3>
+      <div>email :<span tal:content="self/session/getEmail" /></div>
+      <div>key: <span tal:content="self/session/getKey" /></div>
+      <div>type: <span tal:condition="self/session/isAuthenticated">authenticated</span><span tal:condition="not: self/session/isAuthenticated">anonymous</span></div>
+    </div>
+    <form>
+    <input type="reset">
+    <select name="action">
+      <option value="create">create</option>
+      <option value="update">update</option>
+      <option value="create_task">create task</option>
+      <option value="fetch_task">fetch task</option>
+      <option value="save">save</option>
+      <option value="fetch">fetch</option>
+    </select>
+    <input type="submit">
+    <div>Workflow ID: <input type="text" name="wf_id" tal:attributes="value self/workflow_id"></div>
+    <div>Title: <input type="text" name="title" tal:attributes="value python:self.request.getfirst('title')"></div>
+    <div>Description: <input type="text" name="description" tal:attributes="value python:self.request.getfirst('description')"></div>
+    <div>Comment: <input type="text" name="comment" tal:attributes="value python:self.request.getfirst('comment')"></div>
+    <div>Service Name: <input type="text" name="service_name"></div>
+    <div>Task ID:<input type="text" name="task_id" tal:attributes="value self/task_id"></div>
+    <div>GraphML text:<br/><textarea name="graphml" tal:content="self/xmltext"></textarea></div>
+    </form>
+    <div tal:condition="self/xmltext">
+      <h2>the graphml code</h2>
+      <pre tal:content="self/xmltext"></pre>
+    </div>
+  </body>
+</html>
diff --git a/Src/Portal/cgi-bin/MobylePortal/workflow_test.py b/Src/Portal/cgi-bin/MobylePortal/workflow_test.py
new file mode 100755
index 0000000..fea18fd
--- /dev/null
+++ b/Src/Portal/cgi-bin/MobylePortal/workflow_test.py
@@ -0,0 +1,19 @@
+#! /usr/bin/env python
+#############################################################
+#                                                           #
+#   Author: Herve Menager                                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+import mb_cgi
+import workflow
+
+def process(self):
+    self.template_file = 'workflow_test.html'
+    workflow.process(self)
+        
+if __name__ == "__main__":
+    mb_cgi.TALCGI(processFunction=process,useSession=True)
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/css/local.css b/Src/Portal/htdocs/MobylePortal/css/local.css
new file mode 100644
index 0000000..8230093
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/css/local.css
@@ -0,0 +1,83 @@
+/*
+You can use this CSS file to customize the look of your Mobyle portal.
+Please modify this file instead of the mobyle.css file, which can be modified
+in Mobyle newer versions.
+examples:
+#	Mobyle portal title using content that will be included
+	after the "Mobyle" heading  
+	#mobyleHead:AFTER{
+		content: "@myplace";
+	}
+#title and links font colors
+	h1{
+		color: #4682B4;
+	}
+	h2,h3,h4,h5,fieldset,a {
+		color: #164274;
+	}
+#drawer and tab handles background color
+.handlesList li, dl.accordion dt {
+	background-color: #4682B4;
+}
+#selected drawer and tab handles background colors
+.handlesList li:hover, dl.accordion dt:hover{
+	background-color: #86C2F4;
+}
+#selected tab handles background colors
+	.handlesList .selected {
+		background: #66A2D4;
+	}
+	.panelsList{
+		border-top:2px solid #66A2D4;
+	}
+#drawer and tab handles text color
+.handlesList li a, dl.accordion dt {
+	color: white;
+}
+#welcome page "informations" colors
+.presentation {
+	border-color: #444488;
+	background-color: #E0ECFF;
+}
+
+# welcome menu color
+.menu.welcome {
+    background-color: #AE67CB;    
+}
+# active welcome menu color
+.menu.welcome.selected, .menu.welcome:hover {
+    background-color: #CE87EB;
+}
+# forms (programs, workflows,...) menu color
+.menu.forms{
+    background-color: #4682B4;    
+}
+# active forms (programs, workflows,...) menu color
+.menu.forms.selected, .menu.forms:hover {
+    background-color: #86C2F4;    
+}
+# data menu color
+.menu.data{
+    background-color: #488748;    
+}
+# active data menu color
+.menu.data.selected, .menu.data:hover {
+    background-color: #88C788;    
+}
+# jobs menu color
+.menu.jobs{
+    background-color: #D0C56C;    
+}
+# active jobs menu color
+.menu.jobs.selected, .menu.jobs:hover {
+    background-color: #F0E58C;    
+}
+# tutorials menu color
+.menu.tutorials{
+    background-color: #B70303;    
+}
+# active tutorials menu color
+.menu.tutorials.selected, .menu.tutorials:hover {
+    background-color: #D72323;    
+}
+*/
diff --git a/Src/Portal/htdocs/MobylePortal/css/mobyle.css b/Src/Portal/htdocs/MobylePortal/css/mobyle.css
new file mode 100644
index 0000000..17f33e6
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/css/mobyle.css
@@ -0,0 +1,941 @@
+table,tr,td,div,span,ul,ol,li{
+    margin: 0px;
+    padding: 0px;
+    border: 0px;
+}
+
+#portalMain ul, #portalMain ol{
+    list-style-position: inside;
+}
+
+a{
+  outline: none;
+}
+
+.dataTable {
+    text-align: left;
+    border:1px solid #666666;
+    margin-top: 3em;
+}
+
+.dataTable th, .dataTable td, .dataTable tr{
+    border:1px solid #666666;
+    margin:0px;
+}
+
+.dataTable th{
+    color: #448;
+    font-weight: bold;  
+}
+
+.dataTable td{
+    color: black;
+    font-weight: normal;    
+}
+
+.dataTable td{
+        max-width: 200px;
+        overflow: hidden;
+}
+
+
+#sessionSpace{
+    width:20%;
+    height:10px;
+    border:1px solid #ccc;
+    padding:0;
+    margin:0;
+}
+
+#sessionUsage{
+    background-color: blue;
+    height:10px;
+}
+
+dl.accordion {
+    margin: 0;
+    padding: 0;
+    overflow: hidden;
+}
+
+dl.accordion dt {
+    cursor: pointer;
+    display: block;
+    margin: 0;
+    padding: 2px;
+    background: #4682B4 ;
+    font-weight: bold;
+}
+
+.handlesList li.selected{
+  border-bottom: none !important;
+  color: #86C2F4 !important;
+}
+
+.handlesList li input{
+    display:none;
+}
+
+.panelsList .tabPanel{
+  border-top:2px solid #86C2F4;
+}
+
+.parameter .panelsList{
+  border:2px solid #86C2F4;
+}
+
+dl.accordion dd {
+    margin: 0;
+}
+
+fieldset {
+    width:98%; 
+    margin: 2px;
+    padding: 5px;
+    clear: left;
+}
+
+.parameter {
+    margin: 1px;
+    padding: 1px;
+    color: black;
+    font-weight: normal;
+    position: relative;
+}
+
+.modified {
+	background-color: #EDED61;
+	border-color: #EDED61;
+}
+
+.parameter legend {
+	display: table-row-group;
+}
+
+.modified legend {
+	background-color: #FFF8C6;
+}
+
+.popup_overlay{
+    _position: absolute; /* IE6 specific hack to avoid overlay "jump"*/
+    width: 99%; 
+    height: 99%;
+    text-align: center;
+}
+
+.popup_background{
+    position: absolute;
+    background-color: #000;
+    opacity:.30; /*CSS3 capable*/
+    -moz-opacity: 0.3; /*old FF*/
+    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";/*IE8 Standards mode*/
+    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30);/*IE8 Compatibility mode and previous versions*/
+    left:0;
+    overflow:hidden;
+    z-index: 9998;  
+    width: 99%; 
+    position:fixed;
+    top: 0px; 
+}
+
+.popup_background { /*IE hack for min-height*/
+    height: 100%;
+}
+
+html>body .popup_background {
+    height: auto;
+    min-height: 100%;
+}
+
+.modal{
+    border:medium solid #CCCC00;
+    position: absolute;
+    background-color: #FFFFCC;
+    z-index: 100000;
+    overflow: hidden;
+}
+
+.modal{
+    top: 50px;
+    left: 25%;
+    width:50%;
+}
+
+.loading{
+    position: absolute;
+    left: 25%;
+    width:50%;
+    font-size: 4em;
+    color: #FFFFFF;
+    top:45%;
+}
+
+.modal input[type="button"]{
+    margin-left: 20px;
+    margin-right: 20px;
+    margin-top: 20px;
+}
+
+.uploadform {
+    display: inline;
+}
+
+.errormsg {
+    background-position: left;
+    background-repeat: no-repeat;
+    padding-left: 20px;
+    background-image: url(../images/exclamation.gif);
+    color: darkred;
+    font-weight: bold;
+    font-size: small;
+    background-color: #FFE4E1;
+}
+
+.reference{
+    font-style: italic;
+    color: black;
+    margin-bottom: 1em;
+}
+
+.authors:BEFORE{
+    content: "Author(s): ";
+    font-style: normal;
+}
+
+.authors{
+    font-weight: bold;
+    color: black;
+    margin-bottom: 1em;
+}
+
+a{
+    text-decoration: none;
+}
+
+.commentToggle {
+    color:#BB4444 !important;
+    cursor:pointer;
+    font-weight:bold !important;
+    padding-left:0.5em;
+    padding-right:0.5em;
+    text-decoration:underline !important;
+}
+
+.commentText{
+    list-style-type: none;
+    border:thin solid #CCCC00;
+    font-weight: normal!important;
+    color: black!important;
+    background-color: #FFFFCC;  
+}
+
+.example{
+    margin-top:0.5em;
+}
+
+.example pre{
+    border:thin solid #CCCC00;
+    background-color: #EFFFEF;  
+    margin: 0.25em;
+}
+
+.program_information {
+    color: black;
+    background-color: #FFFFCC;  
+    border:thin solid #CCCC00;
+}
+
+h1{
+    color: #4682B4;
+}
+
+h1,h2,h3,h4,h5,fieldset,a,.prompt {
+    color: #164274;
+}
+
+body {
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    width: 97%;
+    min-width: 97%;
+    max-width: 97%;
+}
+
+#mainContainer{
+    vertical-align: top;
+    width: 100%;
+    min-width: 100%;
+    max-width: 100%;
+}
+
+#navbar {
+    vertical-align: top;
+    width: 25%;
+    overflow: hidden;
+}
+
+#drawer{
+    border: 1px solid #666666;
+}
+
+#drawer *{ /* this fixes weird top and bottom margins appearing in IE */
+    margin-top: 0px;
+    margin-bottom: 0px;
+}
+
+#portalMain {
+    vertical-align: top;
+    padding-left: 5px;
+    padding-top: 0px;
+}
+
+.sessionList ul{
+    padding-left: 0px;
+}
+
+.sessionList li {
+    color: darkblue;
+    border: none;   
+    list-style-type: none;
+    line-height: 1.5;
+    cursor: pointer;
+}
+
+.sessionList a.link[data-prefix]:before {
+    content: attr(data-prefix) " : ";
+}
+
+.user-editable {
+    width: 14px;
+    height: 14px;
+    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAB6klEQVR42n3RP0gbcRQH8BcvLsZBREziVhyseqeOrV0EsVTT6qKtg/PZoXQq2EuG6NLBQmsWSWoiNEOVgjkjotjBoyBJBqfY1D+QX6C6VtFU8W7I6/v9iIei8bf87u53n3vvfQ+g0kJ0GIbh5JcDAMo7kA7fP2heplsJ7kXBoEDb+bySiMbyHx+24hSdBNrk7/S46i7miKhqNb9IZXeVTCbDTs6KuL22bk43uAWmysmKaITa+5FcYaf/zvH4+MQ6vzQJr5nTLa3I276BVACB3tbWyhoAW1BfYyH327owLYH/np7h0lw07wOQbyHalYCnqTD/fBBDrjprru8pslzO4u2mU2nGZ7ZRsKfHaSO3l8X6fRh5/MQK1TeUZmrqrG/qOG7oyyyVzQok0g6WE3 [...]
+    cursor: pointer;
+    background-position: 0px 0px;
+    background-repeat: no-repeat no-repeat;
+    border:none;
+    color:transparent;
+    font-size: 0px;
+}
+
+.jobstatus{
+    background-position: left;
+    background-repeat: no-repeat;
+    padding-left: 20px;
+}
+
+.jobstatus.building {
+    background-image: url(../images/hourglass.gif);
+    color: darkorange;
+}
+
+.jobstatus.submitted, .jobstatus.pending, .jobstatus.running, .jobstatus.hold {
+    background-image: url(../images/hourglass.gif);
+    color: darkorange;
+}
+
+.jobstatus.error, .jobstatus.killed {
+    background-image: url(../images/cross.gif);
+    color: red;
+}
+
+.jobstatus.finished {
+    background-image: url(../images/tick.gif);
+    color: green;
+}
+
+a {
+    border: none;
+}
+
+textarea{
+    width: 98%;
+}
+
+.tooltip{
+    border: 1px solid #777;
+    position: absolute;
+    background-color: #FFFFCC;
+    z-index: 20000;
+    /* setting tooltip opacity to opaque */
+    opacity:1;
+    color: black;
+    font-weight: normal;
+    list-style-type: none;
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+/* indicating that disabled inputs are not editable*/
+input[readonly] {
+  background:gray;
+  cursor:default;
+  border: none;
+}
+
+.inactive{ /* inactive user color code */
+    color: gray;
+}
+
+/* general announcements */
+.announcement{
+  display: block;
+  border: 1px solid #777;
+  background-color: #FFFFCC;
+  margin-left: 10%;
+  margin-right: 10%;
+  margin-top: 10px;
+  margin-bottom: 10px;
+  text-align: center;
+  font-size:large;
+  font-weight:bold;
+}
+
+.unavailable{
+  position: absolute;
+  border:medium solid #CCCC00;
+  background-color: #FFFFCC;
+  text-align: center;
+  font-size:large;
+  font-weight:bold;
+  top: 0;
+  left: 0;
+  width:100%;
+  height:100%;
+  z-index: 10001;
+}
+
+.unavailable div{
+  position: relative;
+  top: 45%;
+}
+
+#preload_img{
+  display: none;
+}
+
+ul#userOptions {
+    display:inline;
+    margin:0pt;
+    padding:0pt;
+}
+
+#userOptions li {
+    display:inline;
+    margin:0pt;
+    padding:0pt;
+}
+
+#userOptions li:after {
+  content: " | ";
+}
+
+#programsMenu .blindLink, #programsMenu .link {
+    background-position: left;
+    background-repeat: no-repeat;
+    padding-left: 15px;
+}
+
+#programsMenu .menu.forms{
+    background-image:none!important;
+    line-height: 2em;
+    font-weight: bold;
+    display: block;
+    padding: 2px;
+}
+
+#programsMenu li{
+    width: 100%;
+}
+
+#programsMenu .blindLink, .minimizable > legend{
+    background-image: url("../images/del_stat.gif");
+}
+
+#programsMenu .blindLink.closed, .minimizable > legend.closed{
+    background-image: url("../images/add_stat.gif");
+}
+
+#programsMenu .link {
+    background-image: url(../images/bullet_orange.gif) !important;
+}
+
+.programs ul {
+    padding-left: 0px;
+    margin-left: 0px;
+}
+
+.programs ul ul{
+    margin-left: 0.5em; 
+}
+
+.tree_close a {
+    font-weight: normal;
+}
+
+a.emptySearchResults {
+    background-color: orange;
+    color: white;
+    text-decoration: blink;
+}
+
+#programSearch {
+    border: solid 1px gray;
+}
+
+#searchSubmit, #programsList {
+    margin-left: 0.1em;
+    font-weight: bold;
+}
+
+.resultbox{
+    font-size: small;
+}
+
+.resultbox span{
+    margin-right: 1em;
+}
+
+.results_file h3 {
+    display: inline;
+}
+
+.results_file a {
+    display: inline;
+}
+
+.resultbox form{
+    display: inline;
+}
+
+.resultstable {
+    font-size: 90%;
+    text-align: left;
+}
+
+object, iframe{
+    border: 1px solid #778;
+    width: 100%;
+    background-color: white;
+}
+
+.job object{
+    max-height: 150px;  
+}
+
+.bookmark object{
+    height: 600px;
+}
+
+.resultbox {
+    vertical-align:middle;
+}
+
+ul.paramFiles{
+    margin-top: 0; 
+    margin-bottom: 0;
+    margin-left: 0;
+    padding-left:0;
+    list-style-type:none;
+}
+
+ul.paramFiles li{
+    margin-left: 0;
+    padding-left:0;
+    display:inline;
+}
+
+.saveFileLink{
+    background-position: left;
+    background-repeat: no-repeat;
+    margin-left: 20px;
+    padding-left: 20px;
+    background-image: url(../images/action_save.gif);
+    font-weight: normal;
+    font-size: small;
+    text-align: right;
+}
+
+.handlesList {
+    font-size-adjust:none;
+    font-stretch:normal;
+    font-style:normal;
+    font-variant:normal;
+    font-weight:bold;
+    list-style-type:none;
+    margin:0px;
+    padding:0px 10px 0px 0px;
+    text-align:left;
+}
+
+.program .handlesList li{
+    line-height: 2em;
+    padding-top: 0em;
+    padding-bottom: 0em;
+}
+
+.panelsList .handlesList{
+    margin-top: 2px;
+}
+
+.handlesList:after {
+    color: white;
+    content: "]";
+}
+
+#advancedProgramSearchToggle{
+    font-weight: bolder!important; 
+    font-size: smaller!important;
+    background-image: none!important;
+    padding-left: 0px!important;
+}
+
+#advancedProgramSearch label{
+    display: inline;
+}
+
+.handlesList li {
+    float: left;
+    bottom: 0px;
+    margin-right: 1px;  
+}
+
+.menu{
+    background-color: #4682B4;   
+}
+
+.menu, .menu *{
+    color: #DDDDDD;
+}
+
+.menu.ctrl{
+    outline : none!important;
+    background-color: transparent!important;
+    font-weight: normal!important;
+}
+
+.menu.selected, .menu:hover{
+    outline : 1px solid #606060;
+    background-color: #86C2F4;
+}
+
+.menu.selected, .menu:hover, .menu.selected *, .menu:hover *{
+    color: white;
+}
+
+.handlesList li, .handlesList li a, dl.accordion dt{
+    padding-top:0.5em;
+    padding-bottom:0.5em;
+}
+
+.handlesList li.ctrl button{
+    font-size: small;
+    line-height: 0.8em;
+    color: black;
+}
+
+.handlesList li.ctrl button[disabled]{
+    color: grey;
+}
+
+.handlesList li.ctrl{
+    background-color: white;
+    float: right;
+}
+
+.panelsList{
+    margin-top:0px;
+    clear:both;
+}
+
+.handlesList li * {
+    padding: 0pt 1em;
+    white-space: nowrap;
+}
+
+.handlesList a.closeTab, .controls a.closeModal {
+    font-weight: bold;
+    padding-left: 15px;
+    color: red;
+}
+
+.detachModal{
+    background-image: url(../images/exportapp_wiz.gif);
+    background-repeat: no-repeat;   
+    padding-left: 15px;
+    color: transparent;
+}
+
+.modal.viewer{
+    left: 10%;
+    width: 80%;
+    height: 900px;  
+}
+
+.modal.viewer .header{
+    font-size: smaller;
+    height: 5%;
+    text-align: center;
+}
+
+.viewerRelContainer{
+	position: relative; 
+	height: 100%;
+	width: 100%;
+}
+
+.modal.viewer .header *{
+    margin: 0px;
+}
+
+.modal.viewer .data_view{
+    height: 75%;
+    width: 100%;
+}
+
+.modal.viewer .footer{
+    bottom: 0px;
+    height: auto;
+    position: absolute;
+    text-align: left;
+    width: 100%;
+	height: 20%;
+	overflow-y: scroll;
+}
+
+.modal.viewer .controls{
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    width: 50px;
+}
+.modal iframe /* hack that avoids select boxes overlapping popups in IE */
+{
+        display:none;/*sorry for IE5*/
+        display/**/:block;/*sorry for IE5*/
+        position:absolute;/*must have*/
+        top:0;/*must have*/
+        left:0;/*must have*/
+        z-index:-1;/*must have*/
+        width: 3000px;/*must have for any big value*/
+        height: 3000px;/*must have for any big value*/;
+        filter: Mask();
+        background-color: #FFFFCC;
+        overflow:hidden;
+}
+
+
+a.refresh_link {
+    font-size: smaller;
+}
+
+a.refresh_link.disabled, a.refresh_link.disabled * {
+    color:gray;
+}
+
+.tabPanel {
+     position: relative;
+     clear: both;
+     zoom: 100%; /* this hack corrects some ie7 peekaboo bug that prevents correct form display */
+}
+
+.presentation {
+    padding: 15px; 
+    border: 1px #444488 solid;
+    background-color: #E0ECFF;
+}
+
+#navbar li{
+    overflow: hidden;
+}
+
+/* new form css */
+
+label{
+    display: block;
+    color: #000000;
+}
+
+textarea{
+    display: block;
+}
+
+h1, h2, h3, h4, h5, fieldset, a {
+    color:#164274;
+}
+
+fieldset {
+    clear:left;
+    margin:2px;
+    padding:5px;
+    width:98%;
+}
+
+input[type='text'], input[type='checkbox'], input[type='radio'], input[type='file'], textarea, checkbox {
+    margin:0.2em;
+    padding:0.2em;
+}
+
+form.bookmark input{
+    margin: 0px;
+    padding: 0px;
+}
+
+div.header span{
+    display:table-cell;
+    vertical-align:middle;
+}
+
+form.program .header input[type='submit'], form.program .header input[type='reset'], form.program .header button{
+    margin:20px;
+    padding-left:6px;
+    padding-right:6px;
+    font-size:large;
+}
+
+form.bookmark{
+    float: right;
+    display: inline;
+}
+
+.viewer form.bookmark{
+    float: none;
+    display: block;
+}
+
+
+textarea {
+    height: 7em;
+}
+
+table {
+    width: 100%;
+}
+
+.info, .progressReport {
+    /* information block (footer) */
+    background-color:#FFFFCC;
+    border:thin solid #CCCC00;
+    color:black;
+}
+
+.progressReport {
+	overflow-y: scroll;
+	white-space: pre;
+	text-align: left;
+	font-family: "monospace"; 
+	max-height: 150px;
+	resize: vertical;
+}
+
+.info * {
+    display:block;
+}
+
+.info > .reference:first-child::before {
+    content: 'References :';
+}
+
+.reference {
+    font-style:italic;
+}
+
+/* mandatory parameters management*/
+label.parameter.mandatory:before, fieldset.mandatory legend:before{
+    content: '* ';
+    color: red;
+    font-weight: bolder;
+}
+
+/* mandatory parameters management*/
+label.parameter.mandatory.conditional:before, fieldset.mandatory.conditional legend:before{
+    color: blue!important;
+}
+
+fieldset.parameter{
+    border: none;
+    color: black;
+}
+
+.parameter_value{
+    font-weight: bold;
+}
+
+.ask_for_help{
+    content: url("../images/lightbulb.png");
+}
+
+.back_link{
+    content: url("../images/arrow_left.png");
+}
+
+.remove_link{
+    content: url("../images/bin.png");
+}
+
+.job_controls{
+    color: red;
+    text-align: center;
+}
+
+.minimizable > legend{
+    background-position: left;
+    background-repeat: no-repeat;
+    padding-left: 20px;
+    background-image: url("../images/del_stat.gif");
+}
+
+.minimizable.minimized  > legend{
+    background-image: url("../images/add_stat.gif");    
+    display:inherit;
+}
+
+.minimizable.minimized > *{
+    display:none;
+}
+
+.parameter label.history {
+    float: right;
+    width: 50%;
+}
+
+#programs .parameter textarea{
+    width: 98%;
+}
+
+.parameter label{
+    display: inline;
+}
+
+.controlbox{
+    background-color:#EFEAE5;
+    width: 100%;
+}
+
+.captcha_background{
+    background-image: url('../images/session_captcha_wait.png');
+    background-position: left;
+    background-repeat: no-repeat;
+}
+
+.prompt{
+    font-weight: bold;
+}
+
+.job *[data-parametername]{
+    border: 1px solid #E0ECFF;
+    padding: 5px;
+}
+
+#welcome_footer fieldset{
+    color: black;
+}
+
+.databox_add_button{
+    float: right;
+    font-weight: bold;
+    font-size: bigger;
+}
+
+#userOptions li:last-child:after {
+  content: "";
+}@CHARSET "US-ASCII";
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/css/openid/openid.css b/Src/Portal/htdocs/MobylePortal/css/openid/openid.css
new file mode 100644
index 0000000..ccfae18
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/css/openid/openid.css
@@ -0,0 +1,45 @@
+/* OpenID stuff */
+#cont_openid_user_bar {
+  float:right;
+  padding:0.2em;
+  margin:0;
+  min-width: 300px;
+  text-align:center;
+}
+
+#openid_user_bar {
+  padding:0;
+  margin:0;
+  text-align:center;
+}
+
+form.openIdForm {
+  padding:0;
+  margin:0;
+  border:0px;
+  width:100%;
+  text-align:center;
+}
+
+input.openidUrl {
+  width:18em;
+  padding:3px;
+  padding-right:0px;
+  padding-left:18px;
+  background: url("../../images/openid/openid_small_logo.png") no-repeat;
+  background-color: #FFFFFF;
+  background-position: center left;
+  border: 1px solid;
+}
+
+label.openId {
+  display: none;
+}
+
+
+.submitContainer {
+  text-align:center;
+}
+
+
+
diff --git a/Src/Portal/htdocs/MobylePortal/help/alifmt.html b/Src/Portal/htdocs/MobylePortal/help/alifmt.html
new file mode 100644
index 0000000..ea23e10
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/help/alifmt.html
@@ -0,0 +1,188 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>alifmt</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+
+</head>
+
+<body style="background-color: white">
+
+
+<!-- INDEX BEGIN -->
+<div id="index">
+<p><a name="__index__"></a></p>
+<!--
+
+<ul>
+
+	<li><a href="#name">NAME</a></li>
+	<li><a href="#description">DESCRIPTION</a></li>
+	<li><a href="#see_also">SEE ALSO</a></li>
+	<li><a href="#author">AUTHOR</a></li>
+</ul>
+
+-->
+
+
+</div>
+<!-- INDEX END -->
+
+<p>This document illustrates some common formats used for aligned sequences
+representation.</p>
+<dl>
+<dt><strong><a name="clustal" class="item"><strong>CLUSTAL</strong></a></strong></dt>
+
+<dd>
+<pre>
+CLUSTAL W (1.82) multiple sequence alignment</pre>
+<pre>
+MALK_ECOLI      MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTL
+MALK_SALTY      MASVQLRNVTKAWGDVVVSKDINLDIHDGEFVVFVGPSGCGKSTL
+MALK_ENTAE      MASVQLRNVTKAWGDVVVSKDINLEIQDGEFVVFVGPSGCGKSTL
+MALK_PHOLU      MSSVTLRNVSKAYGETIISKNINLEIQEGEF--------------
+                *:** *:**:**:*:.::**:***:*::***</pre>
+<pre>
+MALK_ECOLI      LRMIAGLETITSGDLACRRLHKEPGV
+MALK_SALTY      LRMIAGLETITSGDLACRRLHQEPGV
+MALK_ENTAE      LRMIAGLETVTSGDL-----------
+MALK_PHOLU      LRM-----------------------
+                ***</pre>
+<p><strong>Warning</strong>: Names must not contain spaces or exceed 30 characters.</p>
+</dd>
+<dt><strong><a name="fasta" class="item"><strong>FASTA</strong></a></strong></dt>
+
+<dd>
+<pre>
+>MALK_ECOLI
+MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHKEPGV
+>MALK_SALTY
+MASVQLRNVTKAWGDVVVSKDINLDIHDGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHQEPGV
+>MALK_ENTAE
+MASVQLRNVTKAWGDVVVSKDINLEIQDGEFVVFVGPSGCGKSTLLRMIA
+GLETVTSGDL-----------
+>MALK_PHOLU
+MSSVTLRNVSKAYGETIISKNINLEIQEGEF--------------LRM--
+---------------------</pre>
+</dd>
+<dt><strong><a name="mega" class="item"><strong>MEGA</strong></a></strong></dt>
+
+<dd>
+<pre>
+#mega
+!Title Multiple Sequence Alignment;</pre>
+<pre>
+#MALK_ECOLI
+MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHKEPGV
+#MALK_SALTY
+MASVQLRNVTKAWGDVVVSKDINLDIHDGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHQEPGV
+#MALK_ENTAE
+MASVQLRNVTKAWGDVVVSKDINLEIQDGEFVVFVGPSGCGKSTLLRMIA
+GLETVTSGDL-----------
+#MALK_PHOLU
+MSSVTLRNVSKAYGETIISKNINLEIQEGEF-------------------
+---------------------</pre>
+</dd>
+<dt><strong><a name="msf" class="item"><strong>MSF</strong></a></strong></dt>
+
+<dd>
+<pre>
+!!AA_MULTIPLE_ALIGNMENT 1.0
+PileUp of: @pep.list</pre>
+<pre>
+ msf.seq       MSF: 55  Type: P  Nov 22, 2001 11:02  Check: 2529 ..</pre>
+<pre>
+ Name: m73237           Len:   655  Check: 7493  Weight:  1.00
+ Name: l28824           Len:   655  Check: 5456  Weight:  1.00
+ Name: u04379           Len:   655  Check: 9580  Weight:  1.00</pre>
+<pre>
+//</pre>
+<pre>
+        1                                                   50
+m73237  ~~~~~MADSA NHLPFFFGQI TREEAEDYLV QGGMSDGLYL LRQSRNYLGG
+l28824  MASSGMADSA NHLPFFFGNI TREEAEDYLV QGGMSDGLYL LRQSRNYLGG
+u04379  ~~~~~MPDPA AHLPFFYGSI SRAEAEEHLK LAGMADGLFL LRQCLRSLGG</pre>
+<pre>
+        51
+m73237  ~~~~~
+l28824  ~~~~~
+u04379  AACG*</pre>
+<p><strong>Warning</strong>: This format cannot handle more than 500 sequences in a
+single alignment.</p>
+</dd>
+<dt><strong><a name="nexus" class="item"><strong>NEXUS</strong></a></strong></dt>
+
+<dd>
+<pre>
+#NEXUS</pre>
+<pre>
+begin data;
+  dimensions ntax=2 nchar=89;
+  format datatype=Protein interleave gap=- missing='.';
+  matrix
+[           1                                               50]
+btdDm       -------AQQQQHHLHMQQAQHH-----------LHLSH------QQAQQ
+TSp1        --------------------AEH-----------PSLR--------GTPL</pre>
+<pre>
+[           51                          80]
+btdDm       YACPICSKKFSRSDHLSKHKKTHF------
+TSp1        FACPICNKRFMRSDHLAKHVKTHN------</pre>
+<pre>
+    ;
+endblock;</pre>
+<p><strong>Warning</strong>: Text enclosed in brackets is considered as comment, and
+thus ignored.</p>
+</dd>
+<dt><strong><a name="phylip" class="item"><strong>PHYLIP</strong></a></strong></dt>
+
+<dd>
+<p>Sequential (PHYLIPS):</p>
+<pre>
+     2   109
+ATISA1    GSPNLYQ-GGRKPWHSINFICAHDGFTLADLVTYNNKNNLANGEENNDG
+          ENHNYSWNCGEEGDFASISVKRLRKRQMRNFFVSLMVSQGVPMIYMGDE
+          YGHTKGGN---
+POTISA1   GSPNLYQKGGRKPWNSINFVCAHDGFTLADLVTYNNKHNLANGEDNKDG
+          ENHNNSWNCGEEGEFASIFVKKLRKRQMRNFFLCLMVSQGVPMIYMGDE
+          YGHTKGGN---</pre>
+<p>Interleaved (PHYLIPI):</p>
+<pre>
+     2   109
+ATISA1    GSPNLYQ-GGRKPWHSINFICAHDGFTLADLVTYNNKNNLANGEENND
+POTISA1   GSPNLYQKGGRKPWNSINFVCAHDGFTLADLVTYNNKHNLANGEDNKD</pre>
+<pre>
+          GENHNYSWNCGEEGDFASISVKRLRKRQMRNFFVSLMVSQGVPMIYMG
+          GENHNNSWNCGEEGEFASIFVKKLRKRQMRNFFLCLMVSQGVPMIYMG</pre>
+<pre>
+          DEYGHTKGGN---
+          DEYGHTKGGN---</pre>
+<p><strong>Warning</strong>: Species names may not contain characters `( ) : ; , [ ]'
+and exceed 10 characters.</p>
+</dd>
+<dt><strong><a name="stockholm" class="item"><strong>STOCKHOLM</strong></a></strong></dt>
+
+<dd>
+<pre>
+# STOCKHOLM 1.0</pre>
+<pre>
+MALK_ECOLI  MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTLLRMIA
+MALK_SALTY  MASVQLRNVTKAWGDVVVSKDINLDIHDGEFVVFVGPSGCGKSTLLRMIA
+MALK_ENTAE  MASVQLRNVTKAWGDVVVSKDINLEIQDGEFVVFVGPSGCGKSTLLRMIA
+MALK_PHOLU  MSSVTLRNVSKAYGETIISKNINLEIQEGEF-------------------</pre>
+<pre>
+MALK_ECOLI  RCHLFREDGTACRRLHKEPGV
+MALK_SALTY  RCHLFREDGSACRRLHQEPGV
+MALK_ENTAE  ---------------------
+MALK_PHOLU  ---------------------
+//</pre>
+</dd>
+</dl>
+
+</body>
+
+</html>
diff --git a/Src/Portal/htdocs/MobylePortal/help/register.html b/Src/Portal/htdocs/MobylePortal/help/register.html
new file mode 100644
index 0000000..5fddc20
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/help/register.html
@@ -0,0 +1,23 @@
+<div id="help_register">
+
+  <h2>What are the benefits of a registered account?</h2>
+
+  <p>
+    You can use the portal without registering at all. This means that you are welcome as a guest. The results of your jobs will be available for a limited time, even if you exit the portal for a short while.
+    However, you will not be able to use them if you connect from another place.
+  </p>
+
+  <p>
+    Registration enables you to store your bookmarked data and results on the server, without any time limit (as long as we can provide enough disk space of course!).
+    You can also connect using the same e-mail and password from any place.
+  </p>
+
+  <h2>How to register?</h2>
+
+  <p>
+    Click on the <i>sign-in</i> link located on the top right of the page, then click on <i>register</i>. Enter your e-mail, and pick a password (this password is specific to 
+    the portal). Once submitted, you will receive an e-mail requesting your confirmation for this registration. 
+    You can then save your data, and access them from anywhere.
+  </p>
+
+</div>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/help/seqfmt.html b/Src/Portal/htdocs/MobylePortal/help/seqfmt.html
new file mode 100644
index 0000000..8057294
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/help/seqfmt.html
@@ -0,0 +1,155 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>seqfmt</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+
+</head>
+
+<body style="background-color: white">
+
+
+<!-- INDEX BEGIN -->
+<div id="index">
+<p><a name="__index__"></a></p>
+<!--
+
+<ul>
+
+	<li><a href="#name">NAME</a></li>
+	<li><a href="#description">DESCRIPTION</a></li>
+	<li><a href="#see_also">SEE ALSO</a></li>
+	<li><a href="#author">AUTHOR</a></li>
+</ul>
+
+-->
+
+
+</div>
+<!-- INDEX END -->
+
+<p>This document illustrates some common formats used for sequences
+representation.</p>
+<dl>
+<dt><strong><a name="embl" class="item"><strong>EMBL</strong></a></strong></dt>
+
+<dd>
+<pre>
+ID   MMVASPHOS  standard; RNA; EST; 140 BP.
+AC   X97897;
+DE   M.musculus mRNA for protein homologous to
+DE   vasodilator-stimulated phosphoprotein
+SQ   Sequence 140 BP; 25 A; 58 C; 39 G; 17 T; 1 other;
+     ttctcccaga agctgactct atggngaccc cgagagagac tgagcagaac      60
+     ccccgcaccc ctgcacttcc aatcaggggc gccccgggag cactccccgt     120
+     ccgccctccg cgcagccatg                                      140
+//</pre>
+</dd>
+<dt><strong><a name="fasta" class="item"><strong>FASTA</strong></a></strong></dt>
+
+<dd>
+<pre>
+>MMVASPHOS
+ttctcccagaagctgactctatggngaccccgagagagactgagcagaacctggagccag
+ccccgcacccctgcacttccaatcaggggcgccccgggagcactccccgtggcgcgccgc
+ccgccctccgcgcagccatg</pre>
+</dd>
+<dt><strong><a name="gcg" class="item"><strong>GCG</strong></a></strong></dt>
+
+<dd>
+<pre>
+!!NA_SEQUENCE 1.0
+ (No documentation)
+dna1.txt  Length: 88  Nov 22, 2001 14:38  Type: N  Check: 3818  ..</pre>
+<pre>
+       1  TAGTCGTAGT CGGAGCGATG CTGACGATGA CGATGACGAT CGTAGCTGAT</pre>
+<pre>
+      51  CGATCGAGCT GATGCTGATC GAGCTAGCTG ATCGATCG</pre>
+</dd>
+<dt><strong><a name="gde" class="item"><strong>GDE</strong></a></strong></dt>
+
+<dd>
+<pre>
+#sample1
+TTCAAGAGAAACAGCGGCCAAGGAAAAGACTCGGCATGATTGTCCATAGCTTACAAAGCG
+#sample2
+TTCAAGAGAAACAGCGGCTGGGGGAAAGACTCGTCCTGATTGCCTGTAGATGGTAAAGCG</pre>
+</dd>
+<dt><strong><a name="genbank" class="item"><strong>GENBANK</strong></a></strong></dt>
+
+<dd>
+<pre>
+LOCUS       HUMHBV1       130 bp    DNA         PRI     17-JUN-1993
+DEFINITION  Human DNA/endogenous Hepatitis B virus (HBV) DNA, left
+            host viral junction.
+ACCESSION   M15770
+BASE COUNT       32 a     43 c     29 g     26 t
+ORIGIN
+      1 agcgggcagt gcagctgctt ggacagcagg ggtgtttctt caacccaggc
+     61 ctcctgtcac aacaggccca ttcaattctg aacctgcaag ccaactccaa
+    121 cctcttttcc cagggggaac caaaaaccct
+//</pre>
+</dd>
+<dt><strong><a name="ig" class="item"><strong>IG</strong></a></strong></dt>
+
+<dd>
+<pre>
+; comment
+U03518
+AACCTGCGGAAGGATCATTACCGAGTGCGGGTCCTTTGGGCCCAACCTCCCATCCGTGTC
+TATTGTACCCTGTTGCTTCGGCGGGCCCGCCGCTTGTCGGCCGCCGGGGGGGCGCCTCTG
+TGAGTTGATTGAATGCAATCAGTTAAAACTTTCAACAATGGATCTCTTGGTTCCGGC1</pre>
+</dd>
+<dt><strong><a name="nbrf" class="item"><strong>NBRF</strong></a></strong></dt>
+
+<dd>
+<pre>
+>P1;CCHU
+cytochrome c [validated] - human
+MGDVEKGKKIFIMKCSQCHTVEKGGKHKTGPNLHGLFGRKTGQAPGYSYTAANKNKGIIW
+GEDTLMEYLENPKKYIPGTKMIFVGIKKKEERADLIAYLKKATNE*</pre>
+</dd>
+<dt><strong><a name="pir" class="item"><strong>PIR</strong></a></strong></dt>
+
+<dd>
+<pre>
+ENTRY          CCHU  #type complete
+TITLE          cytochrome c [validated] - human
+ACCESSIONS     A31764; A05676; I55192; A00001
+SUMMARY        #length 105  #molecular-weight 11749  #checksum 3247
+SEQUENCE
+                5        10        15        20        25        30
+      1 M G D V E K G K K I F I M K C S Q C H T V E K G G K H K T G
+     31 P N L H G L F G R K T G Q A P G Y S Y T A A N K N K G I I W
+     61 G E D T L M E Y L E N P K K Y I P G T K M I F V G I K K K E
+     91 E R A D L I A Y L K K A T N E
+///</pre>
+</dd>
+<dt><strong><a name="raw" class="item"><strong>RAW</strong></a></strong></dt>
+
+<dd>
+<pre>
+ttctcccagaagctgactctatggngaccccgagagagactgagcagaacctggagccag
+ccccgcacccctgcacttccaatcaggggcgccccgggagcactccccgtggcgcgccgc
+ccgccctccgcgcagccatg</pre>
+<p><strong>Warning</strong>: This format cannot handle more than one sequence per file.</p>
+</dd>
+<dt><strong><a name="swissprot" class="item"><strong>SWISSPROT</strong></a></strong></dt>
+
+<dd>
+<pre>
+ID   100K_RAT       STANDARD;      PRT;   149 AA.
+AC   Q62671;
+DE   100 kDa protein (EC 6.3.2.-).
+SQ   SEQUENCE   149 AA;  17004 MW;  D06484B8BC29112E CRC64;
+     MMSARGDFLN YALSLMRSHN DEHSDVLPVL DVCSLKHVAY VFQALIYWIK
+     PQLERKRTRE LLELGIDNED SEHENDDDTS QSATLNDKDD ESLPAETGQN
+     SITIRPPDDQ HLPTANTCIS RLYVPLYSSK QILKQKLLLA IKTKNFGFV
+//</pre>
+</dd>
+</dl>
+
+</body>
+
+</html>
diff --git a/Src/Portal/htdocs/MobylePortal/help/stepbystep.html b/Src/Portal/htdocs/MobylePortal/help/stepbystep.html
new file mode 100644
index 0000000..f3e5f2c
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/help/stepbystep.html
@@ -0,0 +1,86 @@
+<div id="help_stepbystep">
+
+  <h2>Step by step tutorial</h2>
+
+  <p>
+    Welcome to Mobyle, a portal to run bioinformatics analyses. This tutorial is interactive: it helps you to open forms, navigate through the portal and fill some fields. At any time, you can come back here by clicking on the <a href="#" onClick="$('tutorials').showTab();">Tutorials</a> tab above.
+  </p>
+
+  <p>
+    <h3>Running an analysis</h3>
+
+    <p>
+      Let's start by running a first analysis: predict a protein membrane topology with the <b>toppred</b> program. 
+      Clicking <a href="#forms::toppred" class="link">here</a> opens the <b>toppred</b> form.
+
+      The first field is for the protein sequence. You can paste the following data:
+      <pre>>sp|P02914|MALK_ECOLI MALTOSE/MALTODEXTRIN TRANSPORT ATP-BINDING PROTEIN MALK.
+MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTLLRMIAGLETITSGDL
+FIGEKRMNDTPPAERGVGMVFQSYALYPHLSVAENMSFGLKPAGAKKEVINQRVNQVAEV
+LQLAHLLDRKPKALSGGQRQRVAIGRTLVAEPSVFLLDEPLSNLDAALRVQMRIEISRLH
+KRLGRTMIYVTHDQVEAMTLADKIVVLDAGRVAQVGKPLELYHYPADRFVAGFIGSPKMN
+FLPVKVTATAIDQVQVELPMPNRQQVWLPVESRDVQVGANMSLGIRPEHLLPSDIADVIL
+EGEVQVVEQLGNETQIHIQIPSIRQNLVYRQNDVVLVEEGATFAIGLPPERCHLFREDGT
+ACRRLHKEPGV
+      </pre>
+      Then launch the program by clicking on the Run button.
+    </p>
+    <p>
+      When finished, the results are available under the <a href="#jobs" class="link">Jobs</a> tab. The output starts by some details on the job, followed by the output files. You can return to the form anytime by clicking on <a href="#forms::toppred" class="link">its tab</a>, at the top of the page.
+    </p>
+
+  </p>
+
+  <p>
+    <h3>Using the databox</h3>
+
+    <p>
+      You can input data either by copy/paste, file loading, database fetch, or selection of the output from a previous analysis. Let us try this with some examples. 
+    </p>
+
+    <p>
+      <h4>- file loading</h4>
+      If you don't have any sequence file, you can save the following sequences into a file on your disk.</br> 
+      <textarea rows="11">
+>MALK_ECOLI
+MASVQLQNVTKAWGEVVVSKDINLDIHEGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHKEPGV
+>MALK_SALTY
+MASVQLRNVTKAWGDVVVSKDINLDIHDGEFVVFVGPSGCGKSTLLRMIA
+GLETITSGDLACRRLHQEPGV
+>MALK_ENTAE
+MASVQLRNVTKAWGDVVVSKDINLEIQDGEFVVFVGPSGCGKSTLLRMIA
+GLETVTSGDL
+>MALK_PHOLU
+MSSVTLRNVSKAYGETIISKNINLEIQEGEFLRM      
+      </textarea>
+      Then open the <a href="#forms::clustalw-multialign" class="link">clustalw-multialign</a> form and load your sequences file. Then run the program.
+    </p>
+    <p>
+      <h4>- database</h4>
+      Go back to our <a href="#forms::toppred" class="link">toppred</a> form, hit the "DB" checkbox and enter: MALK_ECOLI into the input field. This fetchs you the corresponding Swissprot entry.
+    </p>
+    <p>
+      <h4>- previous analysis</h4>
+      Open the <a href="#forms::protpars" class="link">protpars</a> form and hit the "Result" checkbox. A menu is available providing access to the sequences previously aligned by Clustalw. Note that you could also have directly input your alignment from the <a href="#jobs" class="link">clustalw result</a>: go to the alignment, select "protpars-infile" from the "pipe it to" menu below the box, and hit "go".
+    </p>
+  </p>
+
+  <p>
+    <h3>Retrieving results later</h3>
+    <p>
+      Results will be available as long as the server keeps the corresponding files. To access to the results of a previously run job, use the "Jobs" drawer on the left.
+    </p>
+  </p>
+
+  <p>
+    <h3>Searching for programs</h3>
+
+    <p>
+      The left panel displays 4 "drawers", in order to help you navigate in the portal. The first one provides a list of the available programs, classified by bioinformatics domain. By default, all programs are listed, but you can filter this long list by providing a search term in the <b>search for</b> field. For instance, enter the term "matrix" - notice that you don't need to hit return to start the search. Only the program having the word "matrix" in their description will be display [...]
+    </p>
+
+  </p>
+
+
+</div>
diff --git a/Src/Portal/htdocs/MobylePortal/html/announcement.txt b/Src/Portal/htdocs/MobylePortal/html/announcement.txt
new file mode 100644
index 0000000..e69de29
diff --git a/Src/Portal/htdocs/MobylePortal/images/action_save.gif b/Src/Portal/htdocs/MobylePortal/images/action_save.gif
new file mode 100644
index 0000000..6e6f7de
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/action_save.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/add_stat.gif b/Src/Portal/htdocs/MobylePortal/images/add_stat.gif
new file mode 100644
index 0000000..2b8008e
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/add_stat.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/application_side_expand.png b/Src/Portal/htdocs/MobylePortal/images/application_side_expand.png
new file mode 100644
index 0000000..030cf7c
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/application_side_expand.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/arrow_down.gif b/Src/Portal/htdocs/MobylePortal/images/arrow_down.gif
new file mode 100644
index 0000000..f0bb6a4
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/arrow_down.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/arrow_left.png b/Src/Portal/htdocs/MobylePortal/images/arrow_left.png
new file mode 100644
index 0000000..5fab13e
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/arrow_left.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/arrow_rightup.png b/Src/Portal/htdocs/MobylePortal/images/arrow_rightup.png
new file mode 100644
index 0000000..286916a
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/arrow_rightup.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/asterisk_orange.png b/Src/Portal/htdocs/MobylePortal/images/asterisk_orange.png
new file mode 100644
index 0000000..1ebebde
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/asterisk_orange.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/bullet_orange.gif b/Src/Portal/htdocs/MobylePortal/images/bullet_orange.gif
new file mode 100644
index 0000000..8c9fea4
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/bullet_orange.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_minus.png b/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_minus.png
new file mode 100644
index 0000000..b47ce55
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_minus.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_plus.png b/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_plus.png
new file mode 100644
index 0000000..9ab4a89
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/bullet_toggle_plus.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/cross.gif b/Src/Portal/htdocs/MobylePortal/images/cross.gif
new file mode 100644
index 0000000..1057763
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/cross.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/cross.png b/Src/Portal/htdocs/MobylePortal/images/cross.png
new file mode 100644
index 0000000..1514d51
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/cross.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/del_stat.gif b/Src/Portal/htdocs/MobylePortal/images/del_stat.gif
new file mode 100644
index 0000000..233b8c3
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/del_stat.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/delete_obj.gif b/Src/Portal/htdocs/MobylePortal/images/delete_obj.gif
new file mode 100644
index 0000000..b6922ac
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/delete_obj.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/exclamation.gif b/Src/Portal/htdocs/MobylePortal/images/exclamation.gif
new file mode 100644
index 0000000..d86fffc
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/exclamation.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/exportapp_wiz.gif b/Src/Portal/htdocs/MobylePortal/images/exportapp_wiz.gif
new file mode 100644
index 0000000..f64dda2
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/exportapp_wiz.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/hourglass.gif b/Src/Portal/htdocs/MobylePortal/images/hourglass.gif
new file mode 100644
index 0000000..f2a8e7d
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/hourglass.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/loading.gif b/Src/Portal/htdocs/MobylePortal/images/loading.gif
new file mode 100644
index 0000000..5bb90fd
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/loading.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/aol.ico b/Src/Portal/htdocs/MobylePortal/images/openid/aol.ico
new file mode 100644
index 0000000..87c8152
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/aol.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/blogger.ico b/Src/Portal/htdocs/MobylePortal/images/openid/blogger.ico
new file mode 100644
index 0000000..1b9730b
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/blogger.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/claimid.ico b/Src/Portal/htdocs/MobylePortal/images/openid/claimid.ico
new file mode 100644
index 0000000..2b80f49
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/claimid.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/flickr.ico b/Src/Portal/htdocs/MobylePortal/images/openid/flickr.ico
new file mode 100644
index 0000000..11f6e07
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/flickr.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/google.ico b/Src/Portal/htdocs/MobylePortal/images/openid/google.ico
new file mode 100644
index 0000000..ee7c943
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/google.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/myopenid.ico b/Src/Portal/htdocs/MobylePortal/images/openid/myopenid.ico
new file mode 100644
index 0000000..3232921
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/myopenid.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/openid_small_logo.png b/Src/Portal/htdocs/MobylePortal/images/openid/openid_small_logo.png
new file mode 100644
index 0000000..2829b00
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/openid_small_logo.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/orange.ico b/Src/Portal/htdocs/MobylePortal/images/openid/orange.ico
new file mode 100644
index 0000000..a7910bf
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/orange.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/technorati.ico b/Src/Portal/htdocs/MobylePortal/images/openid/technorati.ico
new file mode 100644
index 0000000..fa1083c
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/technorati.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/verisign.ico b/Src/Portal/htdocs/MobylePortal/images/openid/verisign.ico
new file mode 100644
index 0000000..ab9c65d
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/verisign.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/openid/yahoo.ico b/Src/Portal/htdocs/MobylePortal/images/openid/yahoo.ico
new file mode 100644
index 0000000..3858134
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/openid/yahoo.ico differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/processing.gif b/Src/Portal/htdocs/MobylePortal/images/processing.gif
new file mode 100644
index 0000000..bde2a9e
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/processing.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/rem_co.gif b/Src/Portal/htdocs/MobylePortal/images/rem_co.gif
new file mode 100644
index 0000000..64b4384
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/rem_co.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/session_captcha_wait.png b/Src/Portal/htdocs/MobylePortal/images/session_captcha_wait.png
new file mode 100644
index 0000000..216cedc
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/session_captcha_wait.png differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/tick.gif b/Src/Portal/htdocs/MobylePortal/images/tick.gif
new file mode 100644
index 0000000..7e21249
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/tick.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/tr.gif b/Src/Portal/htdocs/MobylePortal/images/tr.gif
new file mode 100644
index 0000000..0158f85
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/tr.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/images/tr_open.gif b/Src/Portal/htdocs/MobylePortal/images/tr_open.gif
new file mode 100644
index 0000000..697c649
Binary files /dev/null and b/Src/Portal/htdocs/MobylePortal/images/tr_open.gif differ
diff --git a/Src/Portal/htdocs/MobylePortal/js/blank.html b/Src/Portal/htdocs/MobylePortal/js/blank.html
new file mode 100644
index 0000000..2488c5b
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/blank.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<!--
+Copyright (c) 2007 Brian Dillard and Brad Neuberg:
+Brian Dillard | Project Lead | bdillard at pathf.com | http://blogs.pathf.com/agileajax/
+Brad Neuberg | Original Project Creator | http://codinginparadise.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<script language="JavaScript">
+function pageLoaded() {
+	window.parent.dhtmlHistory.iframeLoaded(window.location);
+	document.getElementById("output").innerHTML = window.location;
+}
+</script>
+
+</head>
+
+<body onload="pageLoaded();" style="width:700px;padding:2px;margin:0;">
+
+	<p>blank.html - Needed for Internet Explorer's hidden iframe</p>
+	<p id="output"></p>
+
+</body>
+</html>
diff --git a/Src/Portal/htdocs/MobylePortal/js/controls.js b/Src/Portal/htdocs/MobylePortal/js/controls.js
new file mode 100644
index 0000000..5137ab5
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/controls.js
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
+
+// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { };
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element);
+    this.element     = element;
+    this.update      = $(update);
+    this.hasFocus    = false;
+    this.changed     = false;
+    this.active      = false;
+    this.index       = 0;
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow ||
+      function(element, update){
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false,
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide ||
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string')
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix &&
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update,
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer =
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex)
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;
+  },
+
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ?
+          Element.addClassName(this.getEntry(i),"selected") :
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) {
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  markPrevious: function() {
+    if(this.index > 0) this.index--;
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++;
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount =
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else {
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+
+    var entry = encodeURIComponent(this.options.paramName) + '=' +
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams)
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&
+          ret.length < instance.options.choices ; i++) {
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ?
+            elem.toLowerCase().indexOf(entry.toLowerCase()) :
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) {
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars &&
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ?
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd at tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+};
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML.unescapeHTML();
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.style.backgroundColor = this._originalBackground;
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw('Server returned an invalid collection representation.');
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      ipe.element.style.backgroundColor = ipe.options.highlightColor;
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element);
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/js/mobyle.js b/Src/Portal/htdocs/MobylePortal/js/mobyle.js
new file mode 100644
index 0000000..8f3a913
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/mobyle.js
@@ -0,0 +1,3365 @@
+/**
+ * Author: Herve Menager
+ * Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.
+ * Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.
+ */
+
+Array.prototype.isArray = true;
+// this piece of code lets you iterate synchronously/asynchronously with a function
+// over a list
+// stolen and extended from "http://blog.jcoglan.com/2010/08/30/the-potentially-asynchronous-loop/"
+Array.prototype.asyncEach = function(iterator, callback) {
+  var list = this,
+      n = list.length,
+      i = -1,
+      calls = 0,
+      looping = false;
+
+  var iterate = function() {
+    calls -= 1;
+    i += 1;
+    if (i === n) {
+      if (callback) callback();
+      return;
+    }
+    iterator(list[i], resume);
+  };
+
+  var loop = function() {
+    if (looping) return;
+    looping = true;
+    while (calls > 0) iterate();
+    looping = false;
+  };
+
+  var resume = function() {
+    calls += 1;
+    if (typeof setTimeout === 'undefined') loop();
+    else setTimeout(iterate, 1);
+  };
+  resume();
+};
+
+if (!('console' in window)) {
+    window.console = {};
+}
+var names = ['log', 'debug', 'info', 'warn', 'error', 'assert', 'dir', 'dirxml', 'group', 'groupEnd', 'time', 'timeEnd', 'count', 'trace', 'profile', 'profileEnd'];
+for (var i = 0; i < names.length; ++i) {
+    if (!(names[i] in console))
+      window.console[names[i]] = function() {
+    };
+}
+
+Effect.DefaultOptions.duration = 0.2;
+
+Form.encodingMethods = {
+  // these methods are a workaround to the limitations of IE6 (at least) which does not understand the enctype property
+
+  setMultipartEncoding: function(form, options) {
+    if (Prototype.Browser.IE) {
+      form.encoding = 'multipart/form-data';
+    }else {
+      form.enctype = 'multipart/form-data';
+    }
+  },
+
+  setUrlEncoding: function(form, options) {
+    if (Prototype.Browser.IE) {
+      form.encoding = 'application/x-www-form-urlencoded';
+    }else {
+      form.enctype = 'application/x-www-form-urlencoded';
+    }
+  }
+
+};
+Object.extend(Form, Form.encodingMethods);
+
+/**
+ * @class Feed is a class that downloads/updates JSON Data from a given URL
+ *        and stores it.
+ */
+var Feed = Class.create({
+
+    initialize: function(url) {
+        this.url = url;
+        this.data = new Object();
+        this._datastr = '';
+        this.getEnabled = true;
+        // kept for comparison sake
+        document.observe('item:remove',
+        function(e) {
+            if (e.memo.feed == this) {
+                this.get();
+            }
+        }
+        .bind(this));
+        this.callbacks = new Array();
+    },
+
+    get: function(callback) {
+      if (callback) {this.callbacks.push(callback)}
+      if (this.getEnabled) {
+        this.getEnabled = false;
+        updateProgressReports();
+        $$('.refresh_link').invoke('addClassName', 'disabled');
+        new Ajax.Request(this.url, {
+            method: 'get',
+            onSuccess: function(transport) {
+                this._process(transport);
+                while (this.callbacks.length > 0) {
+                	var cb = this.callbacks.shift();
+                	cb();
+				}
+                $$('.refresh_link').invoke('removeClassName', 'disabled');
+            }.bind(this),
+            onComplete: function() {
+              this.getEnabled = true;
+            }.bind(this)
+        });
+      }
+    },
+
+    _process: function(transport) {
+        if (this._datastr != transport.responseText) {
+            this._datastr = transport.responseText;
+            var response = this._datastr.evalJSON();
+            this.previousData = this.data;
+            this.data = response;
+            document.fire('feed:update/' + this.url, {});
+        }
+    }
+    
+});
+
+var updateProgressReports = function() {
+  $$('.progressReport').each(
+    function(el) {
+      new Ajax.Request(el.readAttribute('data-url'),
+        {
+           method: 'get',
+           onSuccess: function(transport) {
+             $(el).value = transport.responseText;
+           },
+           onComplete: function(transport) {
+             this.scrollTop = 999999;
+           }.bind(el)
+        }
+      );
+    }
+  );
+};
+
+/**
+ * @class FeedView is a class that provides a view to a Feed object which is actually a list
+ */
+var FeedView = Class.create({
+
+    data: null,
+    property: null,
+    feed: null,
+
+    initialize: function(el) {
+        this.el = $(el);
+        this.onUpdate();
+        document.observe('feed:update/' + this.feed.url,
+        function(e) {
+            this.onUpdate();
+        }.bind(this));
+    },
+
+    onUpdate: function() {
+        //this.el.setLoading();
+        this.data = this.extract(this.feed.data);
+        this.build();
+        //this.el.unsetLoading();
+    },
+
+    check: function() {
+
+        },
+
+    build: function() {
+        },
+
+    extract: function(data) {
+        if (this.path) {
+            this.path.split('.').each(function(property) {
+                data = data[property];
+            });
+        }
+        return data;
+    }
+});
+
+/**
+ * @class ListView
+ */
+var ListView = Class.create(FeedView, {
+
+    data: $H(),
+    filterFn: null,
+    sortFn: null,
+    htmlProperties: $A([]),
+
+    extract: function($super, data) {
+        var data = $H($super(data));
+        this.htmlProperties.each(function(prop) {
+            data.each(function(item) {
+                if (item.value[prop]) {
+                    item.value[prop] = item.value[prop].escapeHTML();
+                }
+            });
+        });
+        if (this.filterFn && data) {
+            data.each(function(pair) {
+                if (!this.filterFn(pair)) {
+                    data.unset(pair.key);
+                }
+            }
+            .bind(this));
+        }
+        if (this.sortFn) {
+            data = data.sortBy(this.sortFn);
+        }
+        if (!data) {
+            return $H();
+        }
+        else {
+            return data;
+        }
+    },
+
+    check: function() {
+        }
+
+});
+
+
+// user-editable part, wraps and enhances scriptaculous' InPlaceEditor
+// el: element that contains the editable text
+// url: edition server callback
+// title: title attribute that should override scriptaculous "click to edit"
+// parameters: parameter string that finishes with "[name of the parameter for new value]="
+var UserEditable = function(el, url, title, parameters) {
+    var editButton = Builder.node('button', {className: 'user-editable'},'edit').hide();
+    el.insert({after: editButton});
+    var to;
+    el.observe('mouseover', function(e) {
+        if (to) window.clearTimeout(to);
+        editButton.show();
+        });
+    el.observe('mouseout', function(e) {
+        to = setTimeout(function() {editButton.hide()},1000);
+        });
+    new Ajax.InPlaceEditor(el, url, {
+      callback: function(form, value) { return parameters + encodeURIComponent(value)},
+      clickToEditText: title,
+      externalControl: editButton,
+      externalControlOnly: editButton,
+      rows: 1,
+      onComplete: function() {editButton.hide();}
+    });
+};
+
+var LinksList = Class.create(ListView, {
+
+	editUrl: null,
+
+    build: function() {
+        // remove previous options
+        this.el.immediateDescendants().invoke('remove');
+        // add html list items
+        if (this.data) {
+      	  this.el.show();
+          this.data.each(function(item) {
+            var a = new Element('a', {
+              'href': this.liHrefT.evaluate(item).gsub('/', '--'),
+              'title': this.liTitleT.evaluate(item),
+              'class': this.liClassT.evaluate(item)
+              }).update(this.liTextT.evaluate(item));
+            if (this.liDataPrefixT) {a.setAttribute('data-prefix', this.liDataPrefixT.evaluate(item))}
+            var li = new Element('li').update(a);
+            this.el.appendChild(li);
+            if (this.editUrl) {
+              UserEditable(a, this.editUrl, this.liTitleT.evaluate(item), 'id=' + item.key + '&userName=');
+            }
+            }.bind(this));
+        }
+        var containerAndTitle = $A([this.el.up('dd'),this.el.up('dd').previous('dt')]);
+        if(this.data.size()==0){
+          containerAndTitle.invoke('hide');
+        }else{
+          containerAndTitle.invoke('show');
+        }
+    }
+
+});
+
+/**
+ * @class BookmarkLinksList is a View of the list of files It displays the list of
+ *        files in an HTML unordered list
+ *
+ * @extends LinksList
+ */
+var BookmarkLinksList = Class.create(LinksList, {
+
+    liHrefT: new Template('#data::#{key}'),
+    // to add support for bookmark tabs, add "::#{key}"
+    liTitleT: new Template('#{value.title}'),
+    liDataPrefixT: new Template('#{value.bioTypes} #{value.dataType}'),
+    liClassT: new Template('link'),
+    liTextT: new Template('#{value.userName}'),
+    path: 'data',
+	editUrl: 'data_rename.py',
+
+    sortFn: function(data) {
+        return data.value.dataType + data.value.userName;
+    }
+
+});
+
+/**
+ * @class JobLinksList is a View of the list of jobs It displays the list of
+ *        jobs in an HTML unordered list
+ *
+ * @extends LinksList
+ */
+var JobLinksList = Class.create(LinksList, {
+    filterFn: function(pair) {
+      return !pair.value.owner;
+    },
+    liHrefT: new Template('#jobs::#{key}'),
+    liTitleT: new Template('status: #{value.status}'),
+    liClassT: new Template('link jobstatus #{value.status}'),
+    liTextT: new Template('#{value.userName}'),
+    path: 'jobs',
+	editUrl: 'session_job_rename.py',
+
+    build: function($super) {
+      // add the remote server name to the displayed job name if not local
+      if (this.data) {
+        this.data.each(function(item) {
+          if (item.value.server != 'local') {
+            item.value.programName += '@' + item.value.server;
+          }
+         });
+      }
+      // then proceed...
+      $super();
+    },
+
+    sortFn: function(job) {
+        return job.value.jobSortDate;
+    }
+
+});
+
+/**
+ * @class WorkflowsLinksList is a View of the list of 
+ *        user workflows. It displays the list of files
+ *        in an HTML unordered list
+ *
+ * @extends LinksList
+ */
+var WorkflowsLinksList = Class.create(LinksList, {
+
+    liHrefT: new Template('#forms::#{key}'),
+    liTitleT: new Template('#{value.id}'),
+    liClassT: new Template('link'),
+    liTextT: new Template('#{value.id}'),
+    path: 'workflows',
+
+    sortFn: function(data) {
+        return data.id;
+    }
+
+});
+
+/**
+ * @class DataTable is a View of the Data class It displays
+ *        filtered elements of a list as an HTML table It is the equivalent to a
+ *        View in a classical MVC
+ *
+ */
+var DataTable = Class.create(ListView, {
+
+	editColumns: $H({}),
+
+    build: function() {
+        // remove previous options
+        this.el.childElements().invoke('remove');
+        // table headers
+        var tr = document.createElement('tr');
+        this.columns.each(function(column) {
+            var th = document.createElement('th');
+            th.innerHTML = column.key;
+            tr.appendChild(th);
+        }
+        .bind(this));
+        this.el.appendChild(tr);
+        // table body (data)
+        if (this.data) {
+            this.data.each(function(item) {
+                var tr = document.createElement('tr');
+                this.el.appendChild(tr);
+                this.columns.each(function(column) {
+                    var td = document.createElement('td');
+                    td.innerHTML = column.value.evaluate(item);
+                    var valueEl = td.down();
+                    tr.appendChild(td);
+                    var editUrl = this.editColumns.get(column.key);
+                    if (editUrl) {
+                        UserEditable(valueEl, editUrl, '', 'id=' + item.key + '&userName=');
+                    }
+                }
+                .bind(this));
+            }
+            .bind(this));
+        }
+    }
+
+});
+
+var checkAllCheckBoxes = function(selector,value) {
+  $$(selector).each(function(el) {el.checked = value;});
+};
+
+/**
+ * @class BookmarksTable is a view of the list of files stored in a
+ *        MobyleSession. Additionally, it provides control (removal, renaming)
+ *        over these data
+ * @extends DataTable
+ */
+var BookmarksTable = Class.create(DataTable, {
+
+    filter: null,
+
+	editColumns: $H({
+        'Name': 'data_rename.py'
+	}),
+
+    columns: $H({
+        'Name': new Template('<a class="link" href="#data::#{key}">#{value.userName}</a>'),
+        'Type': new Template('#{value.bioTypes} #{value.dataType}'),
+        'Format': new Template('#{value.format}'),
+        'Size': new Template('#{value.size} KiB'),
+        'Text': new Template('#{value.title}'),
+        '<label><input name="remove_all" type="checkbox" onclick="checkAllCheckBoxes(\'.data_item\',this.checked);" />select all/none</label>': new Template('<input type="checkbox" class="data_item" name="id" value="#{key}" />')
+    }),
+    path: 'data',
+    htmlProperties: $A(['title']),
+
+    initialize: function($super, el) {
+        $super(el);
+        this.el.observe('submit',
+        function(e) {
+            e.stop();
+            new Ajax.Request(e.element().action, {
+                method: 'post',
+                postBody: e.element().serialize(),
+                onCreate: e.element().disable.bind(e.element()),
+                onComplete: function() {
+                    document.fire('item:remove', {
+                        feed: user.workspace
+                    });
+                }
+            });
+        });
+    },
+
+    sortFn: function(data) {
+        return data.value.dataType + data.value.userName;
+    }
+
+});
+
+/**
+ * @class JobsTable is a view of the list of jobs stored in a
+ *        MobyleSession. Additionally, it provides control (removal)
+ *        over these jobs
+ * @extends DataTable
+ */
+var JobsTable = Class.create(DataTable, {
+
+	editColumns: $H({
+        'Name': 'session_job_rename.py'
+	}),
+
+    filterFn: function(pair) {
+      return !pair.value.owner;
+    },
+    columns: $H({
+        'Name': new Template('<a class="link" href="#jobs::#{key}">#{value.userName}</a>'),
+        'Identifier': new Template('<a class="link" href="#jobs::#{key}">#{value.simple_id}</a>'),
+        'Date': new Template('<a class="link" href="#jobs::#{key}">#{value.jobDate}</a>'),
+        'Status': new Template('<span class="jobstatus #{value.status}">#{value.status}</span>'),
+        'Message': new Template('#{value.status_message}'),
+        '<label><input name="remove_all" type="checkbox" onclick="checkAllCheckBoxes(\'.job_item\',this.checked);" />select all/none</label>': new Template('<input type="checkbox" class="job_item" name="id" value="#{key}" />')
+    }),
+    path: 'jobs',
+    htmlProperties: $A(['title']),
+
+    initialize: function($super, el) {
+        $super(el);
+        this.el.observe('submit',
+        function(e) {
+            e.stop();
+            new Ajax.Request(e.element().action, {
+                method: 'post',
+                postBody: e.element().serialize(),
+                onCreate: e.element().disable.bind(e.element()),
+                onComplete: function() {
+                    document.fire('item:remove', {
+                        feed: user.workspace
+                    });
+                }
+            });
+        });
+        this.build = this.build.wrap(function(callOriginal) {
+                        if (this.data) {
+                          this.data.each(function(item) {
+                            item.value.simple_id = item.value.jobID.split('.').last();
+                            item.value.name_and_id = item.value.userName + item.value.simple_id;
+                          });
+                        }
+                        callOriginal();
+                     }.bind(this));
+        $('sortJobsBy').observe('change', function() {
+          this.onUpdate();
+        }.bind(this));
+    },
+
+    sortFn: function(job) {
+        return job.value[$('sortJobsBy').value];
+    }
+
+});
+
+
+Data = {};
+
+Data.View = Class.create({
+
+    view_id: 0,
+
+    initialize: function(source, url, extract, path) {
+        this.view_id = Data.View.view_id++;
+        this.source = source;
+        this.url = url;
+        this.extract = extract;
+        this.path = path;
+        this.build();
+        if (this.url) {
+            document.observe('feed:update/' + this.url, this.build.bindAsEventListener(this));
+        }
+    },
+
+    build: function(e) {
+        this.data = this.source;
+        if (this.path) {
+            this.path.split('.').each(function(property) {
+                this.data = this.data[property];
+            }.bind(this));
+        }
+        this.data = this.extract(this.data);
+        if (this.url) {
+            document.fire('view:build/' + this.url + '/' + this.view_id);
+        }
+    }
+});
+
+Data.extract = function(property, filter, sort) {
+    return function(data) {
+        if (property) {
+            property.split('.').each(function(pitem) {
+                data = data[pitem];
+            }.bind(this));
+        }
+        data = $H(data);
+        if (filter) {
+            data.each(function(pair) {
+                if (!filter(pair)) {
+                    data.unset(pair.key);
+                }
+            });
+        }
+        data = sort ? data.sortBy(sort) : data;
+        return data ? data : $H();
+    }
+};
+
+Builder.dataSelect = function(view, tpls, attrs) {
+    var sel = Builder.node('select', attrs);
+    sel.selectedIndex = -1;
+    var build = function() {
+        // backing up current selected entry before rebuilding it
+        var selEntryBackup = sel.selectedIndex && sel.selectedIndex != -1 ? sel.options[sel.selectedIndex] : null;
+        // disable and hide control if no data is to be shown
+        sel.style.visibility = view.data.size() == 0 ? 'hidden' : 'visible';
+        // remove previous options
+        sel.immediateDescendants().invoke('remove');
+        // default blank value
+        sel.insert(Builder.node('option', {'selected': 'selected'}));
+        // other options
+        view.data.each(function(item) {
+            sel.insert({
+                'bottom': Builder.node('option', {
+                    'value': tpls['value'].evaluate(item),
+                    'title': tpls['title'].evaluate(item)
+                },
+                tpls['text'].evaluate(item))
+            });
+        });
+        if (selEntryBackup) {
+            $A(sel.options).each(function(opt) {
+                if (opt.value == selEntryBackup.value) {
+                    opt.selected = true;
+                }
+            }.bind(this));
+        }else {
+            sel.selectedIndex = -1;
+        }
+        sel.fire('component:build');
+    }
+    build();
+    if (view.url) {
+        document.observe('view:build/' + view.url + '/' + view.view_id,
+        function(e) {
+            build();
+        }.bind(this));
+    }
+    return sel;
+};
+
+Builder.list = function(view, root, buildItemFn) {
+    var build = function() {
+        root.immediateDescendants().invoke('remove');
+        view.data.each(function(item) {
+            root.insert(buildItemFn(item));
+        });
+        root.fire('component:build');
+    }
+    build();
+    if (view.url) {
+        document.observe('view:build/' + view.url,
+        function(e) {
+            build();
+        }.bind(this));
+    }
+    return root;
+};
+
+Builder.historySelect = function(pdef, userMode, control) {
+    var getFilter = function() {
+        return function(bookmark) {
+            var bookmark = bookmark.value;
+            if (filterPipe(bookmark.dataType, bookmark.format, bookmark.bioTypes, pdef.datatype, pdef.formats, pdef.biotype, true)) {
+                return bookmark.userModes.include(userMode);
+            }
+            return false;
+        }
+    }
+    var sb = Builder.node('input', {
+        type: 'button',
+        value: 'select'
+    });
+    var view = new Data.View(user, user.workspace.url, Data.extract('workspace.data.data', getFilter()));
+    var ds = Builder.dataSelect(view, {
+        'value': new Template('#{key}'),
+        'title': new Template('#{value.userName} first line: #{value.title}'),
+        'text': new Template('#{value.userName}')
+    },
+    {
+        id: pdef.program + '::' + pdef.name + '::' + userMode + '::history'
+    });
+    var el = Builder.node('span', [ds, sb]);
+    var build = function() {
+        sb.style.visibility = ds.style.visibility;
+    };
+    build();
+    ds.observe('component:build', build);
+    var load = function() {
+        if (ds.value != '') {
+            // call to history
+            new Ajax.Request('data_get.py', {
+                method: 'get',
+                parameters: {
+                    'id': ds.value
+                },
+                onSuccess: function(r) {
+                    pdef.json_load(control, r.responseText);
+                },
+                onComplete: function() {
+                    ds.selectedIndex = 0;
+                    ds.enable();
+                }
+            });
+        }
+    }
+    sb.observe('click', load);
+    sb.observe('databox:history_load', load);
+    return el;
+};
+
+var filterPipe = function(sourceDatatype, sourceFormat, sourceBiotypes, destDatatype, destFormats, destBiotypes, allowConversions) {
+    var sourceDatatypeHierarchy = portal.properties.datatypes[sourceDatatype];
+    if (sourceDatatypeHierarchy && sourceDatatypeHierarchy.ancestorTypes.include(destDatatype)) {
+        if (sourceBiotypes == null || destBiotypes == null ||
+        sourceBiotypes.size() == 0 || destBiotypes.size() == 0 ||
+        sourceBiotypes.any(function(sourceBiotype) {
+            return destBiotypes.include(sourceBiotype);
+        })
+        ) {
+            if (destFormats.include(sourceFormat) || destFormats.size() == 0 || sourceFormat == null) {
+                // formats identical
+                return true;
+            } else if (allowConversions && portal.properties.conversions.any(function(conversion) {
+                return sourceDatatypeHierarchy.ancestorTypes.include(conversion.dataType) &&
+                conversion.fromFormat == sourceFormat && destFormats.include(conversion.toFormat);
+            })) {
+                // formats compatible using converters
+                return true;
+            }
+        }
+    }
+    // any incompatibility case
+    return false;
+};
+
+Builder.pipesSelect = function(datatype, biotypes, format, insertElement) {
+    var getFilter = function() {
+        return function(input) {
+            var input = input.value;
+            return filterPipe(datatype, format, biotypes, input.dataTypeClass, input.dataTypeFormats, input.bioTypes, true);
+        }
+    }
+    var sb = Builder.node('input', {
+        type: 'submit',
+        value: 'further analysis'
+    });
+    sb.observe('click', function(ev) {
+    	var form = ev.element().up('form');
+    	form.callType.value = 'further';
+    }.bindAsEventListener(this));
+    var sort = function(item) {
+        return item.value.programName.toLowerCase() + item.value.name.toLowerCase();
+    }
+    var view = new Data.View(portal.properties, null, Data.extract(null, getFilter(), sort), 'program_workflow_inputs');
+    var ds = Builder.dataSelect(view, {
+        'value': new Template('#{value.programPID}|#{value.name}'),
+        'title': new Template('#{value.programTitle} - #{value.prompt}'),
+        'text': new Template('#{value.programName} (#{value.name})')
+    },
+    {
+        name: 'next',
+        style: 'width: 15em;'
+    });
+    var el = Builder.node('span', [insertElement, ds, sb]);
+    var build = function() {
+        el.style.visibility = ds.style.visibility;
+    };
+    build();
+    ds.observe('component:build', build);
+    return el;
+};
+
+Builder.uploadClient = function(pdef, control) {
+    var file = Builder.node('input', {
+        type: 'file',
+        name: 'data_input_upload'
+    });
+    var datatype = Builder.node('input', {
+        type: 'hidden',
+        name: 'datatype_class',
+        value: pdef.datatype,
+        className: 'protected'
+    });
+    var datatype_superclass = Builder.node('input', {
+        type: 'hidden',
+        name: 'datatype_superclass',
+        value: pdef.datatypeSuperClass,
+        className: 'protected'
+    });
+    var target = $('data_upload_target');
+    var el = Builder.node('span', {
+        className: 'upload'
+    },
+    [file, datatype, datatype_superclass]);
+    var onLoaded = function() {
+        file.value = '';
+        var iframeDoc;
+        if (window.frames && window.frames.data_upload_target &&
+        (iframeDoc = window.frames.data_upload_target.document)) {
+            var iframeBody = iframeDoc.body;
+            var txt = iframeBody.innerText ? iframeBody.innerText : iframeBody.textContent;
+        }
+        pdef.json_load(control, txt);
+        target.stopObserving('load');
+    }
+    var upload = function() {
+        var form = el.up('form');
+        disList = form.select('select', 'input', 'textarea').filter(function(el) {
+            return ! el.disabled;
+        });
+        disList.invoke('disable');
+        file.enable();
+        datatype.enable();
+        datatype_superclass.enable();
+        target.observe('load', onLoaded);
+        var actionBck = form.getAttribute('action');
+        var targetBck = form.getAttribute('target');
+        var methodBck = form.getAttribute('method');
+        form.setAttribute('action', 'data_upload.py');
+        form.setAttribute('target', target.identify());
+        form.setAttribute('method', 'POST');
+        Form.setMultipartEncoding(form);
+        form.submit();
+        form.setAttribute('action', actionBck);
+        disList.invoke('enable');
+        delete form['target'];
+        form.setAttribute('method', methodBck);
+        Form.setUrlEncoding(form);
+    }
+    file.observe('change', upload);
+    return el;
+};
+
+Builder.dbSelect = function(pdef, control) {
+    var getFilter = function() {
+        return function(bank) {
+            bank = bank.value;
+            if (bank.dataType == pdef.datatype) {
+                // datatype filter
+                if (pdef.biotype == null ||
+                bank.bioTypes == null ||
+                // biotype(s) filter
+                pdef.biotype.blank &&
+                pdef.biotype.blank() ||
+                bank.bioTypes.blank &&
+                bank.bioTypes.blank() ||
+                pdef.biotype.size &&
+                pdef.biotype.size() == 0 ||
+                bank.bioTypes.size &&
+                bank.bioTypes.size() == 0 ||
+                bank.bioTypes.include(pdef.biotype) ||
+                pdef.biotype.findAll &&
+                pdef.biotype.findAll(function(bt) {
+                    if (bank.bioTypes.include(bt))
+                    return bt;
+                }).size() >
+                0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+    var id = Builder.node('input', {
+        type: 'text'
+    });
+    var sb = Builder.node('input', {
+        type: 'button',
+        value: 'select'
+    });
+    var view = new Data.View(portal.properties, null, Data.extract(null, getFilter()), 'banks');
+    var ds = Builder.dataSelect(view, {
+        'value': new Template('#{key}'),
+        'title': new Template('#{value.label}'),
+        'text': new Template('#{key}')
+    },
+    {});
+    var el = Builder.node('span', [ds, id, sb]);
+    var build = function() {
+        id.style.visibility = ds.style.visibility;
+        sb.style.visibility = ds.style.visibility;
+    };
+    build();
+    ds.observe('component:build', build);
+    sb.observe('click',
+    function(e) {
+        if (ds.value != '' && id.value != '') {
+            portal.startWaiting();
+            // call to history
+            new Ajax.Request('bank_get.py', {
+                method: 'get',
+                parameters: {
+                    'db': ds.value,
+                    'id': id.value
+                },
+                onSuccess: function(r) {
+                    pdef.json_load(control, r.responseText);
+                },
+                onComplete: function() {
+                    portal.stopWaiting();
+                    ds.selectedIndex = -1;
+                    id.value = '';
+                    ds.enable();
+                }
+            });
+        }
+    });
+    return el;
+};
+
+/**
+ * @class DataSelect is a View of the Data class It displays
+ *        filtered elements of a list as an HTML select input It is the
+ *        equivalent to a View in a classical MVC
+ *
+ */
+var DataSelect = Class.create(ListView, {
+
+    cpEls: [],
+
+    build: function() {
+        // backing up current selected entry before rebuilding it
+        if (this.el.selectedIndex && this.el.selectedIndex != -1) {
+            var selEntryBackup = this.el.options[this.el.selectedIndex];
+        }
+        // set visibility for select, label and button
+        if (this.data.size() == 0) {
+            this.el.disable();
+            this.cpEls.each(function(el) {
+                el = $(el);
+                el.hide();
+                Form.Element.disable(el);
+            });
+        }
+        else {
+            this.el.enable();
+            this.cpEls.each(function(el) {
+                el = $(el);
+                el.show();
+                Form.Element.enable(el);
+            });
+        }
+        // remove previous options
+        this.el.immediateDescendants().invoke('remove');
+        // default (blank) option
+        this.el.insert({
+            'bottom': Builder.node('option', {
+                'value': ''
+            },
+            '< >')
+        });
+        // add html options
+        this.data.each(function(item) {
+            this.el.insert({
+                'bottom': Builder.node('option', {
+                    'value': this.optionValue.evaluate(item),
+                    'title': this.optionTitle.evaluate(item)
+                },
+                this.optionText.evaluate(item))
+            });
+        }
+        .bind(this));
+        if (selEntryBackup && !selEntryBackup.value.blank()) {
+            // only restore an entry if one had been selected...
+            this.selectEntry(selEntryBackup.value, true);
+        }
+    },
+
+    selectEntry: function(value, silentChange) {
+        $A(this.el.options).each(function(option) {
+            if (option.value == value) {
+                option.selected = true;
+                if (!silentChange) {
+                    if (this.el && this.el.onchange) {
+                        this.el.onchange();
+                    }
+                    if (this.submitEl && this.submitEl.onclick) {
+                        this.submitEl.onclick();
+                    }
+                }
+            }
+        }
+        .bind(this));
+    }
+
+});
+
+var FilesGauge = Class.create(ListView, {
+
+    path: 'data',
+
+    initialize: function($super, el) {
+        this.gaugeSize = parseFloat($('sessionLimit').value);
+        $super(el);
+    },
+
+    build: function() {
+        // loading the data from the model
+        this.level = 0.0;
+        if (this.data) {
+            this.data.each(function(item) {
+                this.level = this.level + parseFloat(item.value.size);
+            }
+            .bind(this));
+        }
+        this.usage = this.level / this.gaugeSize * 100;
+        this.el.style.width = this.usage + '%';
+    }
+
+});
+
+var User = Class.create({
+
+    initialize: function() {
+        this.workspace = new Feed('session_workspace.py');
+        this.updateFromCookie();
+    },
+
+    setEmail: function(email) {
+        editCookieValue('email', email);
+        this.updateFromCookie();
+    },
+
+    updateFromCookie: function() {
+        if (document.cookie.toString() == '') {
+            $('nocookie').show();
+        }
+        else {
+            $('nocookie').hide();
+        }
+        var sessionKey = readCookieValue('sessionKey');
+        if (sessionKey != this.sessionKey) {
+            this.sessionKey = sessionKey;
+            document.fire('user:sessionKey');
+            this.workspace.get();
+            if (typeof _gaq != 'undefined') { // GA set session key
+              _gaq.push(['_setCustomVar',
+                    1,
+                    'session_key',
+                    this.sessionKey,
+                    3
+                 ]);
+            }
+        }
+        var email = readCookieValue('email');
+        if (email != this.email) {
+            this.email = email;
+            document.fire('user:email');
+            $('userEmail').innerHTML = this.email;
+            if (typeof _gaq != 'undefined') { // GA set e-mail
+              _gaq.push(['_setCustomVar',
+                    2,
+                    'e-mail',
+                    this.email,
+                    3
+                 ]);
+            }
+        }
+        var activated = (readCookieValue('activated') == 'True');
+        if (activated != this.activated) {
+            this.activated = activated;
+            document.fire('user:activated');
+            if (!this.activated) {
+                $$("[href='#user::activate']").each(function(aEl) {aEl.up().show();});
+                $('userEmail').addClassName('inactive');
+                $('userStatus').addClassName('inactive');
+            }
+            else {
+                $$("[href='#user::activate']").each(function(aEl) {aEl.up().hide();});
+                $('userEmail').removeClassName('inactive');
+                $('userStatus').removeClassName('inactive');
+            }
+            if (typeof _gaq != 'undefined') { // GA set session activation state
+              _gaq.push(['_setCustomVar',
+                    3,
+                    'session_activation_state',
+                    this.activated,
+                    3
+                 ]);
+            }
+        }
+        var authenticated = (readCookieValue('authenticated') == 'True');
+        if (authenticated != this.authenticated) {
+            this.authenticated = authenticated;
+            document.fire('user:authenticated');
+            if (this.authenticated) {
+                $$("[href='#user::signin']").each(function(aEl) {aEl.up().hide();});
+                $('userStatus').innerHTML = '(registered)';
+                if ($('userOpenIdsignInOpen')) {
+                    $('userOpenIdsignInOpen').up().hide();
+                }
+            }
+            else {
+                $$("[href='#user::signin']").each(function(aEl) {aEl.up().show();});
+                $('userStatus').innerHTML = options['anonymousSession'] ? '(guest)' : '(register to submit)';
+                if ($('userOpenIdsignInOpen')) {
+                    $('userOpenIdsignInOpen').up().show();
+                }
+            }
+            if (typeof _gaq != 'undefined') { // GA set session type
+              _gaq.push(['_setCustomVar',
+                    4,
+                    'session_type',
+                    this.authenticated ? 'registered' : 'guest',
+                    3
+                 ]);
+            }
+        }
+    }
+
+});
+
+var methods = $H({
+    'forms': new Template('form.py?id=#{id}'),
+    'jobs': new Template('job_view.py?id=#{id}'),
+    'data': new Template('data_view.py?id=#{id}'),
+    'tutorials': new Template('#{id}'),
+    'viewer': new Template('viewer_view.py?#{parameters}')
+});
+
+// list of non-databox types
+var simpleDatatypes = ['Choice', 'MultipleChoice', 'Boolean', 'Integer', 'Float', 'String', 'Filename', 'MultipleText'];
+
+var Databox = Class.create({
+
+    initialize: function(paramEl) {
+        this.paramEl = paramEl;
+        this.form = paramEl.up('form');
+        this.name = paramEl.readAttribute('data-parametername');
+        this.input = paramEl.select('[name="' + paramEl.readAttribute('data-parametername') + '"]')[0];
+        this.idx = 0;
+        this.datatype = paramEl.readAttribute('data-datatype');
+        this.datatypeSuperClass = paramEl.readAttribute('data-datatype-superclass');
+        if (paramEl.readAttribute('data-ismultiple') == 'true') {
+          var existing_databoxes = this.existingDataboxes();
+          if (existing_databoxes.size() != 0) {
+            prvIdx = parseInt(existing_databoxes.collect(function(databoxId) {return databoxId.split('[')[1].split(']')[0];}).max());
+            this.idx = prvIdx + 1;
+            this.input = paramEl.select('[name="' + this.name + '[' + prvIdx.toPaddedString(4) + ']"]')[0];
+          }
+          this.name += '[' + this.idx.toPaddedString(4) + ']';
+          this.datatype = this.datatype.replace('Multiple', '');
+          this.datatypeSuperClass = this.datatypeSuperClass.replace('Multiple', '');
+        }else {
+        }
+        this.program = this.form.readAttribute('id');
+        this.biotype = paramEl.readAttribute('data-biotype').strip().split(' ').without('');
+        this.formats = paramEl.readAttribute('data-formats').strip().split(' ').without('');
+        this.control = this.form[this.name];
+        this.databoxType = this.input.type && this.input.type == 'file' ? 'binary' : 'text';
+        if (this.databoxType == 'binary') {
+            this.input.type = 'text';
+        }
+        var ip = this.input.clone();
+        ip.value = this.input.value;
+        ip.setAttribute('class', 'databox_paste');
+        ip.setAttribute('name', this.name);
+        ip.observe('change', onFormParameterModification);
+        var id = this.input.clone();
+        //id.disable();
+        id.setAttribute('readonly', 'readonly');
+        id.setAttribute('class', 'databox_db');
+        id.setAttribute('name', this.name);
+        var iu = this.input.clone();
+        iu.disable();
+        //iu.setAttribute('readonly','readonly');
+        iu.setAttribute('class', 'databox_upload');
+        iu.setAttribute('name', this.name);
+        var ir = this.input.clone();
+        ir.disable();
+        //ir.setAttribute('readonly','readonly');
+        ir.setAttribute('class', 'databox_result');
+        ir.setAttribute('name', this.name);
+        var irp = Builder.node('input', {
+            type: 'hidden',
+            className: 'bookmark_ref databox_paste',
+            name: this.name + '.ref'
+        });
+        irp.store('databox', this);
+        var ird = irp.clone();
+        ird.setAttribute('class', 'bookmark_ref databox_db');
+        ird.store('databox', this);
+        var iru = irp.clone();
+        iru.setAttribute('class', 'bookmark_ref databox_upload');
+        iru.store('databox', this);
+        var irr = irp.clone();
+        irr.setAttribute('class', 'bookmark_ref databox_result');
+        irr.store('databox', this);
+        var inp = Builder.node('input', {
+            type: 'hidden',
+            name: this.name + '.name'
+        });
+        var ind = inp.clone();
+        var inu = inp.clone();
+        var inr = inp.clone();
+        var hsp = Builder.historySelect(this, 'paste', {
+            'ref': irp,
+            'value': ip,
+            'name': inp
+        });
+        var hsd = Builder.historySelect(this, 'db', {
+            'ref': ird,
+            'value': id,
+            'name': ind
+        });
+        var hsu = Builder.historySelect(this, 'upload', {
+            'ref': iru,
+            'value': iu,
+            'name': inu
+        });
+        var hsr = Builder.historySelect(this, 'result', {
+            'ref': irr,
+            'value': ir,
+            'name': inr
+        });
+        var dbs = Builder.dbSelect(this, {
+            'ref': ird,
+            'value': id,
+            'name': ind
+        });
+        var upu = Builder.uploadClient(this, {
+            'ref': iru,
+            'value': iu,
+            'name': inu
+        });
+        var pastePanel = Builder.node('div', [Builder.node('table', {
+            style: 'width: 100%;'
+        },
+        Builder.node('tbody', [Builder.node('tr', [Builder.node('td', ['Enter your data below:']), Builder.node('td', {
+            },
+        hsp)])])), ip, irp, inp]);
+        var dbPanel = Builder.node('div', [Builder.node('table', {
+            style: 'width: 100%;'
+        },
+        Builder.node('tbody', [Builder.node('tr', [Builder.node('td', dbs), Builder.node('td', {
+            },
+        hsd)])])), id, ird, ind]);
+        var uploadPanel = Builder.node('div', [Builder.node('table', {
+            style: 'width: 100%;'
+        },
+        Builder.node('tbody', [Builder.node('tr', [Builder.node('td', upu), Builder.node('td', {
+            },
+        hsu)])])), iu, iru, inu]);
+        var resultPanel = Builder.node('div', [Builder.node('table', {
+            style: 'width: 100%;'
+        },
+        Builder.node('tbody', [Builder.node('tr', [Builder.node('td', {
+            },
+        hsr)])])), ir, irr, inr]);
+        var choices = $H({
+            'paste': pastePanel,
+            'db': dbPanel,
+            'upload': uploadPanel,
+            'result': resultPanel
+        });
+        var def = 'paste';
+        if (this.databoxType == 'binary') {
+            def = 'upload';
+            choices.unset('paste');
+        }
+        var pairs = $H(choices).collect(function(choice) {
+            var inputModeAttrs = {
+                    type: 'radio',
+                    name: this.name + '.mode',
+                    value: choice.key
+              };
+            if (choice.key == def) {
+              inputModeAttrs.checked = true;
+            }
+            var pair = {
+                'choice': Builder.node('li',
+                            {className: 'menu ' + (choice.key == def ? 'selected' : '')},
+                            Builder.node('label', [choice.key, Builder.node('input', inputModeAttrs)])),
+                'content': Builder.node('div', {
+                             style: choice.key == def ? 'display:visible' : 'display:none'
+                             },
+                           choice.value)
+            };
+            pair.choice.writeAttribute('data-choicefor', pair.content.identify());
+            var build = function() {
+                // "build" disables a choice if it contains only invisible or disabled inputs
+                if (pair.content.select('input', 'select', 'textarea').detect(function(el) {
+                    return ((el.style.visibility == 'visible' || !el.style.visibility) && !el.disabled && !el.readOnly && !(el.type == 'hidden'));
+                })) {
+                    pair.choice.show();
+                }
+                else {
+                    pair.choice.hide();
+                }
+            };
+            build();
+            pair.content.observe('component:build', build);
+            return pair;
+        }.bind(this));
+        if (this.databoxType == 'binary') {
+            this.ebtn = '';
+        } else {
+            this.ebtn = Builder.node('button', 'EDIT');
+            // edit button
+            this.ebtn.observe('click',
+            function(e) {
+                var value = this.el.select('[name="' + this.name + '"]').pluck('value').join('');
+                pairs[0].choice.fire('databox:switch');
+                ip.value = value;
+            }.bind(this));
+        }
+        this.cbtn = Builder.node('button', 'CLEAR');
+        // clear button
+        this.cbtn.observe('click',
+        function(e) {
+            e.stop();
+            this.clear();
+        }.bind(this));
+        this.rbtn = '';
+        if (paramEl.readAttribute('data-ismultiple') == 'true') {
+          this.rbtn = Builder.node('button', {className: 'databox_remove_button'},'REMOVE');
+          this.rbtn.observe('click', function(e) {
+            e.stop();
+            this.remove();
+          }.bind(this));
+
+          this.paramEl.observe('multiple_parameter:add_remove', function(e) {
+            this.manageRbtn();
+          }.bind(this));
+        }
+        this.paramEl.observe('form:reset', function(e) {
+          if (this.existingDataboxes().size() > 1) {
+            this.remove();
+          }else {
+            this.clear();
+          }
+        }.bind(this));
+        var handles = Builder.node('ul', {
+            className: 'handlesList'
+        },
+        [pairs.pluck('choice'), Builder.node('li', {
+            className: 'ctrl menu'
+        },
+        [this.ebtn, this.cbtn, this.rbtn])]);
+        handles.observe('click', this.choose.bind(this));
+        handles.observe('databox:switch', this.choose.bind(this));
+        var panels = Builder.node('div', {
+            className: 'panelsList'
+        },
+        pairs.pluck('content'));
+        databoxContents = [handles, panels];
+        this.el = Builder.node('div', {'data-databoxname': this.name, 'data-databoxforparametername': this.paramEl.readAttribute('data-parametername'), 'data-databoxindex': this.idx}, databoxContents);
+        this.el.identify();
+    },
+
+    existingDataboxes: function() {
+      // get the list of databox elements corresponding to the same parameter
+      return this.paramEl.select('[data-databoxname]').invoke('readAttribute', 'data-databoxname');
+    },
+
+    manageRbtn: function() {
+      if (this.existingDataboxes().size() == 1) {
+        this.rbtn.disable();
+      }else {
+        this.rbtn.enable();
+      }
+    },
+
+    remove: function() {
+      // remove the current databox (unless it's the last one for this parameter)
+      if (this.existingDataboxes().size() > 1) {
+        this.el.remove();
+      }
+      this.paramEl.fire('multiple_parameter:add_remove');
+    },
+
+    choose: function(e) {
+        var h = e.findElement().hasAttribute('data-choicefor') ? e.findElement() : e.findElement().up('li[data-choicefor]');
+        if (h) {
+            var p = $(h.readAttribute('data-choicefor'));
+            // "choose" shows a choice and hides and resets values of all the others
+            h.down('input').checked = 'true';
+            h.addClassName('selected');
+            h.siblings().invoke('removeClassName', 'selected');
+            p.show();
+            p.siblings().invoke('hide');
+            this.clear();
+        }
+        e.stop();
+    },
+
+    clear: function() {
+        var els = this.el.select('li[data-choicefor]');
+        var targets = els.collect(function(el) {return $(el.readAttribute('data-choicefor'));});
+        targets.invoke('select', 'select', 'input', 'textarea').flatten().each(function(input) {
+            if (!input.hasClassName('protected')) {
+                input.value = input.defaultValue;
+                onFormParameterModification(input);
+            }
+        });
+        targets.invoke('select', 'input[type="hidden"]').flatten().each(function(input) {
+            if (!input.hasClassName('protected')) {
+                input.value = '';
+                //hidden inputs do not have a significant default value, we assume it is blank...
+            }
+        });
+        if ($(this.ebtn)) {
+          this.ebtn.disabled = false;
+        }
+        //this.paramEl.removeClassName('modified');
+    },
+
+    json_load: function(target, responseText) {
+        target.value.up('div').removeAllMessages();
+        var txt = responseText.strip();
+        if (txt != '') {
+            if (txt.isJSON()) {
+                user.workspace.get();
+                response = txt.evalJSON();
+                if (response.content != null) {
+                    this.clear();
+                    if ($(this.ebtn) && response.headFlag == 'HEAD') {
+                      this.ebtn.disabled = true;
+                    }else {
+                      this.ebtn.disabled = false;
+                    }
+                    if (this.databoxType == 'binary') {
+                      target.value.value = response.userName;
+                    } else {
+                      target.value.value = response.content;
+                    }
+                    if(target && response.safeFileName) {target.ref.value = response.safeFileName;}
+                    if(target && response.userName) { target.name.value = response.userName;}
+                    this.paramEl.addClassName('modified');
+                    // fire event that tells "simple form display" may have to adapt
+                    this.paramEl.fire('form:parameter_load');
+                }
+                if (response.errormsg) {
+                    //target.ref.retrieve('databox').el.setMessage(response.errormsg);
+                    target.value.up('div').setMessage(response.errormsg);
+                }
+            }
+            else {
+                target.value.up('div').setMessage('request failed, please check your data');
+            }
+        }
+    }
+
+});
+
+var Filebox = Class.create({
+
+    initialize: function(dataEl, options) {
+        this.options = options ? options : {
+                'position': 'after',
+                'components': ['FullScreen']
+            };
+        this.dataEl = dataEl;
+        this.metaEl = dataEl.up('[data-datatype]') || this.dataEl;
+        this.name = this.metaEl.readAttribute('data-parametername');
+        this.datatype = this.metaEl.readAttribute('data-datatype');
+        this.datatypeSuperclass = this.metaEl.readAttribute('data-datatype-superclass');
+        this.biotypes = this.metaEl.readAttribute('data-biotype').split(' ').without('');
+        this.format = this.metaEl.readAttribute('data-format').split(' ');
+        this.inputModes = this.metaEl.readAttribute('data-inputmodes').split(' ');
+        this.src = this.dataEl.readAttribute('data-src');
+        this.fileName = this.dataEl.readAttribute('data-filename');
+        this.components = $A();
+        // components requested in options
+        this.options.components.each(
+          function(componentName) {
+            this.components.push(this['build' + componentName]());
+          }.bind(this));
+        var iD = $H();
+        iD.set(this.options.position, Builder.node('div', {
+            className: 'controlbox'
+        },
+        [this.components]));
+        this.dataEl.insert(iD.toObject());
+    },
+
+    buildFullScreen: function() {
+      // return full screen button
+      return Builder.node('button', {onclick: 'window.open("' + this.src + '","_blank")'}, ['full screen']);
+    },
+
+    buildWidgets: function() {
+      // return the list of suitable widgets (viewers)
+        var dataSrc = this.src;
+        var view = new Data.View(portal.properties, null, Data.extract('viewer_inputs', this.pipeFilter.bind(this)));
+        return Builder.list(view, Builder.node('span'),
+          function(item) {
+              var vHref = '#viewer::program=' + item.value.programPID + '&parameter=' + item.value.name + '|' + dataSrc;
+              return Builder.node('a', {
+                  href: vHref,
+                  className: 'modalLink',
+                  'data-viewername': item.value.programPID
+              },
+              [Builder.node('button', [item.value.programName])]);
+          });
+    },
+
+    buildBookmarkPipeCommon: function() {
+      // return bookmark and pipe common elements button
+      var inputModeInputs = this.inputModes.collect(
+    	function(im) {
+        	return Builder.node('input', {type: 'hidden', name: 'inputModes', value: im});
+    	});
+      var r = [Builder.node('input', {type: 'hidden', name: 'safeFileName', value: this.fileName
+        }), inputModeInputs, Builder.node('input', {type: 'hidden', name: 'parameter', value: this.name
+        }), Builder.node('input', {type: 'hidden', name: 'callType', value: 'bookmark'
+        }), Builder.node('input', {type: 'hidden', name: 'datatype_class', value: this.datatype
+        }), Builder.node('input', {type: 'hidden', name: 'datatype_superclass', value: this.datatypeSuperclass
+        }),
+        this.biotypes.collect(function(str) {
+          return Builder.node('input', {
+              type: 'hidden',
+              name: 'biotype',
+              value: str
+          });
+        }),
+        this.format.collect(function(str) {
+          return Builder.node('input', {
+              type: 'hidden',
+              name: 'format',
+              value: str
+          });
+        })];
+      var jobEl = this.dataEl.up('.job');
+      if (jobEl) {
+          var jobid = jobEl.readAttribute('data-jobid');
+        r.push(Builder.node('input', {type: 'hidden', name: 'job', value: jobid}));
+      }
+      return r;
+    },
+
+    buildBookmark: function() {
+      // return the bookmark form
+        this.pipesSelect = Builder.pipesSelect(this.datatype, this.biotypes, this.format, this.insertElement);
+        return Builder.node('form', {
+                action: 'data_bookmark.py',
+                className: 'bookmark'
+            },
+            [this.buildBookmarkPipeCommon(),
+            Builder.node('input', {
+                type: 'submit',
+                value: 'bookmark'
+            }), ' as ', Builder.node('input', {
+                type: 'text',
+                name: 'userName',
+                size: '8',
+                value: this.fileName
+            })]);
+    },
+
+    buildPipes: function() {
+      // return the pipes selection form
+        this.pipesSelect = Builder.pipesSelect(this.datatype, this.biotypes, this.format, this.insertElement);
+        return Builder.node('form', {
+                className: 'bookmark further'
+            },
+            [this.buildBookmarkPipeCommon(),
+             this.pipesSelect]);
+    },
+
+    buildBookmarkPlusPipes: function() {
+      // return the bookmark and/or pipes selection controls in a single form
+        this.pipesSelect = Builder.pipesSelect(this.datatype, this.biotypes, this.format, this.insertElement);
+        return Builder.node('form', {
+                action: 'data_bookmark.py',
+                className: 'bookmark further'
+            },
+            [this.buildBookmarkPipeCommon(),
+            Builder.node('input', {
+                type: 'submit',
+                value: 'bookmark'
+            }), ' as ', Builder.node('input', {
+                type: 'text',
+                name: 'userName',
+                size: '8',
+                value: this.fileName
+            }), ' or ',
+            this.pipesSelect]);
+    },
+
+    buildWidgetBookmarkPlusPipes: function() {
+      var userFileName = this.fileName;
+      if (this.metaEl.readAttribute('data-extension')) {
+        userFileName += '.' + this.metaEl.readAttribute('data-extension');
+      }
+      // return the bookmark and/or pipes selection controls in a single form
+        this.pipesSelect = Builder.pipesSelect(this.datatype, this.biotypes, this.format, this.insertElement);
+        var bn = Builder.node('form', {
+                className: 'bookmark further',
+                action: 'data_upload.py'
+            },
+            [
+            Builder.node('em', [this.metaEl.readAttribute('data-prompt')]),
+            Builder.node('textarea', {'style': 'display: none;', 'name': 'data_input_upload'}),
+            this.buildBookmarkPipeCommon(),
+            Builder.node('input', {
+                type: 'submit',
+                value: 'bookmark'
+            }), Builder.node('input', {
+                type: 'hidden',
+                name: 'base64encoded',
+                value: this.metaEl.readAttribute('data-base64encoded')
+            }), ' as ', Builder.node('input', {
+                type: 'text',
+                name: 'data_input_upload.name',
+                size: '8',
+                value: userFileName
+            }), ' or ',
+            this.pipesSelect]);
+        bn.onOk = function(data) {
+          portal.stopWaiting();
+          var msgText = 'Your data ('+ data.userName + ") is now accessible <a href='#data::" + data.safeFileName + "'><strong>here</strong></a>";
+          bn.insert({'before': msgText});
+        }
+        bn.observe('submit',
+          function() {
+            bn.data_input_upload.value = eval(this.metaEl.readAttribute('data-portcode'));
+          }.bindAsEventListener(this));
+        return bn;
+    },
+
+    buildRemoveBookmark: function() {
+      // return the remove bookmark button
+      return Builder.node('a', {href: '#user::bookmarkremove::' + this.fileName, className: 'modalLink'},
+                Builder.node('button', {type: 'button'}, ['remove bookmark'])
+              );
+    },
+
+    buildRenameBookmark: function() {
+      // return the rename bookmark ??
+    },
+
+    build: function() {
+        //this.pipesSelect = Builder.pipesSelect(this.datatype, this.biotypes, this.format, this.insertElement);
+    },
+
+    pipeFilter: function(input) {
+        var input = input.value;
+        return filterPipe(this.datatype, this.format, this.biotypes, input.dataTypeClass, input.dataTypeFormats, input.bioTypes, false);
+    }
+
+});
+
+var JobStatus = Class.create({
+
+    initialize: function(initMessage) {
+      this.lastMessage = initMessage;
+      this.pid = initMessage.pid;
+      this.id = initMessage.id;
+      this.delay = 2.5;
+      this.times = 0;
+      this.maxTimes = 4;
+      portal.startWaiting();
+      portal.hideAllModal();
+      dhtmlHistory.add('jobs::' + this.pid);
+      // setting the dhtmlHistory here is hacky, but it allows to directly display
+      // the job status upon refresh.
+      if (this.running()) {
+        portal.startWaiting();
+        this.trigger();
+      }else {
+        this.show();
+      }
+    },
+
+    running: function() {
+      return ['submitted', 'pending', 'running'].include(this.lastMessage.status);
+    },
+
+    trigger: function() {
+      if (this.times == this.maxTimes) {
+        this.show();
+      }else {
+        setTimeout(this.check.bind(this), this.delay * 1000);
+        this.times += 1;
+      }
+    },
+
+    show: function() {
+      user.workspace.get();
+      portal.go('jobs::' + this.pid);
+    },
+
+    check: function() {
+      new Ajax.Request(
+        'job_status.py', {
+          method: 'GET',
+          parameters: $H({'jobId': this.id}),
+          onSuccess: function(resp) {
+            this.lastMessage = resp.responseText.evalJSON();
+            if (this.running()) {
+              this.trigger();
+            }else {
+              this.show();
+            }
+          }.bind(this)}
+      );
+    }
+
+});
+
+/**
+ * tabMethods is the set of methods that manage tabbed panels manipulation
+ */
+var tabMethods = {
+
+    /**
+   *
+   * @param {Element}
+   *            element container.
+   * @param {Data}
+   * insert a tabs container inside any given container
+   *            ajaxData [optional] the list to which this tabsContainer registers as a view.
+   */
+    insertTabsContainer: function(element) {
+        if (!$(element).down('.handlesList')) {
+            $(element).insert({
+                'bottom': "<ul class='handlesList'></ul><div class='panelsList'></ul>"
+            });
+        }
+    },
+
+    /**
+   * insert a tab in a given tabs container.
+   *
+   * @param {Element}
+   *            element the element containing the tabs (handles+panels).
+   * @param {String}
+   *            id the id of the tab to be created.
+   * @param {String}
+   *            label the label of the tab to be created (displayed in the
+   *            handle).
+   * @param {boolean}
+   *            close indicates wether or not a close control should be
+   *            created.
+   */
+    insertTab: function(element, id, label, close, handleClassName) {
+        if (! ($(element).down('ul.handlesList') &&
+        $(element).down('div.panelsList'))) {
+            $(element).insertTabsContainer();
+        }
+        // create tab html if it does not exist
+        if (!$(id)) {
+            // insert handle
+            $(element).down('ul.handlesList').__insertHandle(id, label, close, handleClassName);
+            // insert panel
+            return $(element).down('div.panelsList').__insertPanel(id);
+        }else {
+          return $(id);
+        }
+        //$('ms-' + id).showTab();
+    },
+
+    /**
+   * insert the html for a tabbed panel
+   *
+   * @param {Element}
+   *            element the tab panels container.
+   * @param {String}
+   *            id the id of the created tab.
+   */
+    __insertPanel: function(element, id) {
+        return $(element).insert(Builder.node('div', {
+            className: 'tabPanel',
+            id: 'ms-' + id
+        }));
+    },
+
+    /**
+   * insert the html for a tab handle
+   *
+   * @param {Element}
+   *            element the tab handles container.
+   * @param {String}
+   *            id the id of the tab to be created.
+   * @param {String}
+   *            label the label of the tab to be created (displayed in the
+   *            handle).
+   * @param {boolean}
+   *            close indicates wether or not a close control should be
+   *            created.
+   * @param {className}
+   *            className is the value of the class HTML attribute for the handle.
+   */
+    __insertHandle: function(element, id, label, close, className) {
+        var closeLink = close ? Builder.node('a', {
+            href: '#' + id,
+            className: 'closeTab',
+            title: 'close this tab'
+        },
+        'x') : '';
+        var handle = Builder.node('li', {className: 'menu ' + className},
+        [Builder.node('a', {
+            href: '#' + id,
+            className: 'link'
+        },
+        label), closeLink]);
+        return $(element).insert(handle);
+    },
+
+    updateHandleLabel: function(element, id, label) {
+        $$('li a.link[href="#' + id + '"]').invoke('update', label);
+    },
+
+    /**
+   * display a tab
+   *
+   * @param {Element}
+   *            element the panel of the element that should be displayed.
+   */
+    showTab: function(element) {
+        if ($(element).hasClassName('tabPanel')) {
+            var handleId = $$(
+            '.handlesList .link[href="#' + element.id.substr(3) + '"]',
+            '.handlesList .link[href="' + location.href.split('#').first() + '#' + element.id.substr(3) + '"]'
+            //this line is for ie7 compatibility
+            )[0];
+            var element = $(handleId);
+        }
+        else {
+            throw Error('please ask for a tab (' + element.id + ')');
+        }
+        //element.style.backgroundImage="url(/portal/css/../images/add_stat.gif)";
+        //if (element.next()) {element.next().style.backgroundImage="url(/portal/css/../images/add_stat.gif)";}
+        var liEl = $(element.up('li'));
+        liEl.siblings().each(function(el) {
+            if (el.down('a.link')) {
+                //el.down('a').style.backgroundImage="";
+                //if (el.down('a').next()) {el.down('a').next().style.backgroundImage="";}
+                el.removeClassName('selected');
+                var panelId = el.down('a').readAttribute('href').split('#').last();
+                $('ms-' + panelId).hide();
+            }
+        });
+        liEl.addClassName('selected');
+        var panel = $('ms-' + element.readAttribute('href').split('#').last());
+        panel.show();
+        panel.setStyle({borderTopColor: element.up('li').getStyle('backgroundColor')});
+        // recursively show the parent tabs of the shown tab
+        if ($(element).up('.tabPanel') && !panel.up('.tabPanel').visible()) {
+            $(element).up('.tabPanel').showTab();
+        }
+        if (panel.down('ul.handlesList') && panel.down('ul.handlesList').down('li')) {
+            if (!panel.down('ul.handlesList').down('li.selected')) {
+                panel.down('.tabPanel').showTab();
+            }
+        }
+    },
+
+    /**
+   * hides an element this method overrides prototypejs', because display:none
+   * only sometimes is not always sufficient to hide a panel in IE
+   *
+   * @param {Element}
+   *            element the element to hide.
+   */
+    hide: function(element) {
+        $(element).style.display = 'none';
+        $(element).style.visibility = 'hidden';
+        return element;
+    },
+
+    /**
+   * show an element this method overrides prototypejs', because display:none only
+   * sometimes is not always sufficient to hide a panel in IE
+   *
+   * @param {Element}
+   *            element the element to show.
+   */
+    show: function(element) {
+        $(element).style.display = '';
+        $(element).style.visibility = '';
+        return element;
+    },
+
+    setLoading: function(element) {
+        $(element).addClassName('loading');
+    },
+
+    unsetLoading: function(element) {
+        $(element).removeClassName('loading');
+    },
+
+    /**
+   * remove a tab
+   *
+   * @param {String}
+   *            id the id of the element that should be removed.
+   */
+    removeTab: function(element) {
+        var id = $(element).id;
+        var tab = $$(
+        '.handlesList a[href="#' + id.substr(3) + '"]',
+        '.handlesList a[href="' + location.href.split('#').first() + '#' + id.substr(3) + '"]'
+        ).first().up('li');
+        if (tab.match('.selected')) {
+            var nextTab = tab.next() ? tab.next() : tab.previous();
+            if (nextTab) {
+                var nextId = nextTab.down('a.link').readAttribute('href').substr(1);
+                portal.go(nextId);
+            }
+        }
+        element.remove();
+        tab.remove();
+    }
+
+};
+
+/**
+ * formMethods is the set of methods that manage form-related operations
+ */
+var formMethods = {
+    /**
+   * sets an error message corresponding to the element
+   *
+   * @param {element}
+   *            element the DOM element.
+   * @param {message}
+   *            message the message to display.
+   */
+    setMessage: function(element, message, formSummary) {
+        var id = element.identify();
+        var m = Builder.node('div', {
+            className: 'errormsg',
+            'data-messagefor': id
+        },
+        message);
+        if ((element.tagName == 'FORM')) {
+          element.insert({'top': m});
+        }else {
+          element.insert({'before': m});
+        }
+        if (formSummary) {
+            var s = Builder.node('div', {
+                className: 'errormsg'
+            },
+            [element.readAttribute('data-parametername'),
+            ':',
+            message.clone(true)]);
+            element.up('form').insert({
+                'top': s
+            });
+        }
+    },
+
+    /**
+   * removes every error message corresponding to the control
+   *
+   * @param {element}
+   *            element the DOM element.
+   */
+    removeMessage: function(element) {
+        if (element.id) {
+            $$('[data-messagefor="#' + element.id + '"]').invoke('remove');
+        }
+    },
+
+    /**
+     * removes every error message contained in the element's descendants
+     *
+     * @param {element}
+     *            element the DOM element.
+     */
+    removeAllMessages: function(element) {
+        element.select('.errormsg').invoke('remove');
+    },
+
+    /**
+     * submits the form using the AJAX wrapper
+     *
+     * @param {form}
+     *            element the DOM form element.
+     */
+    _submit: function(form) {
+        var action = form.readAttribute('action');
+        if (form.hasClassName('program')) {
+            form.onOk = function(data) {
+                if (data.emailNeeded) {
+                    document.observe('user:email',
+                    function(e) {
+                        document.stopObserving('user:email');
+                        this._submit();
+                    }.bind(form));
+                    document.observe('user:email:cancel',
+                    function(e) {
+                        document.stopObserving('user:email');
+                    }.bind(form));
+                    portal.go('user::email');
+                    return;
+                }
+                if (data.activationNeeded) {
+                    document.observe('user:activated',
+                    function(e) {
+                        document.stopObserving('user:activated');
+                        this._submit();
+                    }.bind(form));
+                    portal.go('user::activate');
+                    return;
+                }
+                new JobStatus(data);
+            }
+        }
+        if (form.callType && form.callType.value == 'further') {
+            if (form.next && !form.next.value.blank()) {
+                form.onOk = function(response) {
+                	form.callType.value = 'bookmark';
+                    var program = form.next.value.split('|')[0];
+                    var param = form.next.value.split('|')[1];
+                    var callback = function() {
+                        var getFormAndLoadHistory = function() {
+                            try {
+                                var paramEl = $(program).select('[data-parametername="' + param + '"]')[0];
+                                var isMultiple = paramEl.readAttribute('data-ismultiple') == 'true';
+                                paramEl.fire('parameter:pipe', response);
+                            } catch (e) {
+                                logError(e);
+                            }
+                        }
+                        user.workspace.get(getFormAndLoadHistory);
+                    }
+                    portal.go('forms::' + program, callback);
+                }
+            }
+        }
+        if (form.hasClassName('help')) {
+           form.onOk = function(response) {
+             //portal.go('welcome');
+             var response = response;
+             var display_sent_message = function() {
+               $('help_request_confirmation_message').innerHTML = response.msg;
+             }
+             portal.go('user::helpsent', display_sent_message);
+           }
+        }
+        if (action != null) {
+            new Ajax.Request(action, {
+                method: form.getAMethod(),
+                parameters: form.serialize({hash: true}),
+                onCreate: function(resp) {
+                    this.removeAllMessages();
+                }.bind(form),
+                onFailure: function(resp) {
+                    portal.stopWaiting();
+                  logError(resp);
+                }.bind(form),
+                onSuccess: function(resp) {
+                  var okCallbackEnroute = false;
+                    if (resp.responseText && resp.responseText.isJSON()) {
+                        data = resp.responseText.evalJSON();
+                        if (data.errormsg) {
+                            if (data.errorparam && this.down('[data-parametername="' + data.errorparam + '"]')) {
+                                var error_msg = Builder.node('span', [data.errormsg,
+                                    Builder.node('a', {className: 'modalLink', href: '#user::help::' + data.id + '::' + data.errormsg + '::' + data.errorparam}, [
+                                        Builder.node('button', 'get help')])]);
+                                this.down('[data-parametername="' + data.errorparam + '"]').setMessage(error_msg, true);
+                            } else {
+                                this.setMessage(data.errormsg);
+                            }
+                        }
+                        else {
+                            if (form.hasClassName('modal')) {
+                              portal.hideAllModal();
+                            }
+                            if (this.onOk) {
+                                this.onOk(data);
+                                okCallbackEnroute = true;
+                            }
+                            this.onOk = null;
+                        }
+                    }
+                  if (!okCallbackEnroute) portal.stopWaiting();
+                    user.updateFromCookie();
+                    // GA event tracking
+                    try {
+                      var gCategory = ''; gAction = ''; gLabel = '';
+                      switch (action) {
+                        case 'data_upload.py':
+                          gCategory = 'bookmark';
+                          gAction = 'upload';
+                          break;
+                        case 'session_job_submit.py':
+                          gCategory = 'service';
+                          gAction = 'run';
+                          gLabel = form.readAttribute('id');
+                          break;
+                        case 'programs_list.py':
+                          gCategory = 'services_list';
+                          gAction = 'display';
+                          gLabel = form.serialize();
+                          break;
+                        case 'data_remove.py':
+                          gCategory = 'bookmark';
+                          gAction = 'remove';
+                          break;
+                        case 'session_job_remove.py':
+                          gCategory = 'job';
+                          gAction = 'remove';
+                          break;
+                        case 'data_bookmark.py':
+                          gCategory = 'bookmark';
+                          gAction = 'create_from_result';
+                          break;
+                        case 'session_setemail.py':
+                          gCategory = 'session';
+                          gAction = 'set_email';
+                          gLabel = user.email;
+                          break;
+                        case 'session_signin.py':
+                          gCategory = 'session';
+                          gAction = 'signin';
+                          gLabel = user.sessionKey;
+                          break;
+                        case 'session_signout.py':
+                          gCategory = 'session';
+                          gAction = 'signout';
+                          gLabel = user.sessionKey;
+                          break;
+                        case 'session_register.py':
+                          gCategory = 'session';
+                          gAction = 'register';
+                          gLabel = user.sessionKey;
+                          break;
+                        case 'session_activate.py':
+                          gCategory = 'session';
+                          gAction = 'activate';
+                          gLabel = user.sessionKey;
+                          break;
+                        case 'help_request.py':
+                          gCategory = 'session';
+                          gAction = 'ask_help';
+                          gLabel = form.serialize();
+                          break;
+                      }
+                      gTrack(gCategory, gAction, gLabel);
+                    }catch (e) {
+                      console.log('error while tracking form submission');
+                    }
+                }.bind(form)
+            });
+        }
+        else {
+            if (form.onOk) {
+              form.onOk(form.serialize({
+                  hash: true
+              }));
+              form.onOk = null;
+            }
+        }
+    },
+
+    /**
+     * get the real HTTP method which will be used for AJAX calls
+     *
+     * @param {form}
+     *            element the DOM form element.
+     */
+    'getAMethod': function(form) {
+      //  in Mobyle,
+      // where POST is the default
+      var man = form.getAttributeNode('method');
+      return (man && man.specified && man.value != '') ? man.value : 'POST';
+    }
+
+};
+
+Element.addMethods(tabMethods);
+Element.addMethods(formMethods);
+
+var Portal = Class.create({
+
+    initialize: function(el) {
+        this.el = $(el);
+        this.properties = portalProperties;
+        this.filesList = new(Class.create(BookmarkLinksList, {
+            feed: user.workspace
+        }))('filesListUl');
+        this.userWorkflowsList = new(Class.create(WorkflowsLinksList, {
+            feed: user.workspace
+        }))('userWorkflowsListUl');
+        this.forms = $H();
+        // setting up the data management page
+        this.dataManagement = new(Class.create(BookmarksTable, {
+            feed: user.workspace
+        }))('dataManagement');
+        this.dataGauge = new(Class.create(FilesGauge, {
+            feed: user.workspace
+        }))('sessionUsage');
+        this.jobsList = new(Class.create(JobLinksList, {
+            feed: user.workspace
+        }))('jobsListUl');
+        this.jobsManagement = new(Class.create(JobsTable, {
+            feed: user.workspace
+        }))('jobsManagement');
+        new ToolTipManager();
+        if ($('autoRefreshFreq').value > 0) {
+            new PeriodicalExecuter(function() {
+                user.workspace.get();
+            },
+            $('autoRefreshFreq').value);
+        }
+        // in the case of jobs, set up a workspace listener which will update or remove the job tab
+        // if its status has changed or if it has been removed
+        document.observe('feed:update/' + user.workspace.url,
+        function() {
+            $$('.job').each(function(el) {
+                var jobPid = el.readAttribute('data-jobpid');
+                var jobStatus = el.readAttribute('data-jobstatus');
+                var jobMessage = el.readAttribute('data-jobmessage');
+                var id = 'jobs::' + jobPid;
+                if (!user.workspace.data.jobs[jobPid]) {
+                    // remove if job no longer in the workspace
+                    this.closeTab(id);
+                } else {
+                    if (user.workspace.previousData &&
+                    user.workspace.previousData.jobs &&
+                    user.workspace.previousData.jobs[jobPid] &&
+                    (user.workspace.data.jobs[jobPid].status != user.workspace.previousData.jobs[jobPid].status ||
+                    user.workspace.data.jobs[jobPid].status != jobStatus ||
+                    user.workspace.data.jobs[jobPid].status_message != user.workspace.previousData.jobs[jobPid].status_message ||
+                    user.workspace.data.jobs[jobPid].status_message != jobMessage)) {
+                      var id = 'jobs::' + jobPid;
+                      // update job if its status has changed
+                      this._get(id, null, true);
+                    }
+                }
+            }.bind(this));
+        }.bind(this));
+        // in the case of data, set up a workspace listener which will remove the data tab if it has been removed
+        document.observe('feed:update/' + user.workspace.url,
+        function() {
+            $$('.bookmark').each(function(el) {
+                var dataPid = el.readAttribute('data-bookmarkpid');
+                var id = 'data::' + dataPid;
+                if (!user.workspace.data.data[dataPid]) {
+                    // remove if data no longer in the workspace
+                    this.closeTab(id);
+                } else {
+                    var format = el.readAttribute('data-format');
+                    var datatype = el.readAttribute('data-datatype');
+                    var biotype = el.readAttribute('data-biotype');
+                    var inputmodes = el.readAttribute('data-inputmodes');
+                    var username = el.readAttribute('data-username');
+                    if (user.workspace.data.data[dataPid].format != format ||
+                    user.workspace.data.data[dataPid].dataType != datatype ||
+                    user.workspace.data.data[dataPid].bioTypes != biotype ||
+                    user.workspace.data.data[dataPid].userModes != inputmodes ||
+                    user.workspace.data.data[dataPid].userName != username
+                    ) {
+                        // update bookmark if one of its properties has changed
+                        this._get(id, null, true);
+                    }
+                }
+            }.bind(this));
+            $$('.bookmark_ref[value!=""]').each(function(el) {
+                var dataPid = el.readAttribute('value');
+                if (!user.workspace.data.data[dataPid]) {
+                    // clear loaded databox value if it is no longer in the workspace
+                    el.retrieve('databox').clear();
+                }
+            });
+        }.bind(this));
+        // here we process preloaded form values to load them into the requested forms
+        var preload_forms = $('mobyle_load').select('input').invoke('readAttribute', 'data-servicename').uniq();
+        preload_forms.each(function(service_name) {
+          this.go('forms::' + service_name, function() {
+            var parameter_names = $('mobyle_load').select('input[data-servicename="' + service_name + '"]').invoke('readAttribute', 'data-parametername').uniq();
+            parameter_names.each(function(parameter_name) {
+              var parameter_values = $('mobyle_load').select('input[data-servicename="' + service_name + '"][data-parametername="' + parameter_name + '"]').invoke('readAttribute', 'value');
+              var formParam = $(service_name)[parameter_name];
+              if (formParam && formParam.value != null) {
+                //non-databox parameter
+                formParam.value = parameter_values;
+              }else {
+                //databox parameter
+                // switch the databox to "paste" mode
+                $(service_name).select('[name="' + parameter_name + '.mode"][value="paste"]')[0].fire('databox:switch');
+                // load data
+                $(service_name).select('[name="' + parameter_name + '"][class="databox_paste"]')[0].value = parameter_values;
+              }
+            });
+          });
+        }.bind(this));
+    },
+
+    closeTab: function(id) {
+        if ($('ms-' + id)) {
+            $('ms-' + id).removeTab();
+            dhtmlHistory.add(where());
+        }
+    },
+
+    _get: function(id, callback, update) {
+        var path = id.strip().split('::');
+        var request = {
+            'method': 'get',
+            parameters: {},
+            update: update,
+            id: id
+        };
+        switch (path[0]) {
+        case 'user':
+            request.insert = function(response, request) {
+                $('userPopupOverlay').insert(Builder.node('span', {
+                    id: 'modalId'
+                }));
+                $('modalId').insert(response.responseText);
+                if (Prototype.Browser['IE'] == true) {
+                  $('modalId').down('.modal').insert(Builder.node('iframe', {}));
+                }
+            }
+            switch (path[1]) {
+            case 'signin':
+                request.url = 'session_signin_form.py';
+                break;
+            case 'signout':
+                request.url = 'session_signout_form.py';
+                break;
+            case 'register':
+                request.url = 'session_register_form.py';
+                break;
+            case 'activate':
+                request.url = 'session_activate_form.py';
+                break;
+            case 'email':
+                request.url = 'session_email_form.py';
+                break;
+            case 'help':
+                request.url = 'session_job_help_form.py';
+                request.parameters['id'] = path[2];
+                request.parameters['message'] = path[3];
+                request.parameters['param'] = path[4];
+                break;
+            case 'helpsent':
+                request.url = htDir + 'session_job_help_confirm.html';
+                request.parameters['message'] = path[2];
+                break;
+            case 'jobremove':
+                request.url = 'session_job_remove_form.py';
+                request.parameters['id'] = path[2];
+                break;
+            case 'bookmarkremove':
+                request.url = 'data_remove_form.py';
+                request.parameters['id'] = path[2];
+                break;
+            }
+            break;
+        case 'welcome':
+        case 'forms':
+        case 'data':
+        case 'jobs':
+        case 'tutorials':
+            request.parent = $('ms-' + path.slice(0, -1).join('::'));
+            request.parameters['id'] = path.slice(1).join('::');
+            request.insert = function(response, request) {
+                // if an error is sent back display it as a modal window ...
+                if (response.headerJSON && response.headerJSON.error) {
+                    portal.stopWaiting();
+                  $('userPopupOverlay').show();
+                    $('userPopupOverlay').insert(Builder.node('span', {
+                      id: 'modalId'
+                    }));
+                    $('modalId').insert(response.responseText);
+                    return;
+                }
+                // do not insert new HTML if another XMLHTTPRequest has
+                // already inserted the code (sometimes race conditions
+                // are created by Firefox/RSH event handling)
+                if($('ms-' + id)!=null && !update){
+                    return;
+                }
+                // ... otherwise go on
+                if (response.headerJSON && response.headerJSON.title) {
+                    var title = response.headerJSON.title;
+                }
+                else if ($$('*[href=#"' + id + '"]')) {
+                    var title = $$('*[href="#' + id + '"]')[0].title;
+                }
+                else {
+                    var title = id;
+                }
+                var contents = null;
+                if (! request.update) {
+                  contentsId = 'ms-' + id + '-contents';
+                  request.parent.insertTab(id, title, true, path[0]);
+                  $('ms-' + id).insert({top: Builder.node('div', {id: contentsId})});
+                  contents = $(contentsId);
+                }else {
+                  request.parent.updateHandleLabel(id, title);
+                  contents = $$('[data-pid="' + request.parameters['id'] + '"]').first();
+                }
+                contents.update(response.responseText);
+                contents.select('textarea').each(
+                // empty textareas which contain only a whitespace (XSL limitation)
+                function(el) {
+                    if(el.value == ' ') {el.value = '';}
+                }
+                );
+                // workflow graph events handling
+                contents.select('.workflow_graph').each(function(objectEl) {
+                    objectEl.onload = function(e) {
+                      var svgdoc = e.element().contentDocument;
+                      svgdoc.onclick = function(e) {
+                        e = Event.extend(e);
+                        var el = Event.element(e);
+                        var ancs = Element.ancestors(el);
+                        var el = ancs.find(function(el) {
+                          if (el.tagName == 'svg:a') {
+                            return el;
+                          }
+                        });
+                        if (el) {
+                          var destination = el.getAttributeNS('http://www.w3.org/1999/xlink', 'href').substr(1);
+                          var jobPid = destination.split('::').last();
+                          portal.go(id + '::' + jobPid);
+                        }
+                        Event.stop(e);
+                      };
+                    }.bind(this);
+                }.bind(this));
+            }
+            switch (path[0]) {
+            case 'forms':
+                request.url = 'form.py';
+                request.callback = function(id) {
+                    if (!$('ms-' + id)) return;
+                    // when a form is loaded, we create the databoxes dynamically
+                    new FormParametersManager($('ms-' + id + '-contents').down('form'));
+                    if (portal.properties.simple_forms_active) {
+                      new FormSimpleParametersToggleManager($('ms-' + id + '-contents').down('form'));
+                    }
+                }
+                break;
+            case 'jobs':
+                request.url = 'job_view.py';
+                request.parameters['pid'] = path.slice(1).join('::');
+                request.callback = function(id) {
+                    if (!$('ms-' + id + '-contents')) return;
+                    // when a job is loaded, we create the resultboxes dynamically
+                    $('ms-' + id + '-contents').select('.job_results [data-filename]').each(function(el) {
+                        new Filebox(el, {
+                            'position': 'after',
+                            'components': ['FullScreen', 'Widgets', 'BookmarkPlusPipes']
+                        });
+                    });
+                    updateProgressReports();
+                    // if the job is "imported" by opening a portal link, refresh workspace after
+                    // job view added the job to it.
+                    if (!user.workspace.data.jobs || !user.workspace.data.jobs[request.parameters['pid']]) {
+                      user.workspace.get();
+                    }
+                }
+                break;
+            case 'data':
+                request.url = 'data_view.py';
+                request.callback = function(id) {
+                    // when a data bookmark is loaded, we create the bookmarkboxes dynamically
+                    $('ms-' + id + '-contents').select('[data-filename]').each(function(el) {
+                        new Filebox(el, {
+                            'position': 'before',
+                            'components': ['FullScreen', 'RemoveBookmark', 'Widgets', 'Pipes']
+                        });
+                    });
+                }
+                break;
+            case 'tutorials':
+                request.url = 'tutorial.py';
+                break;
+            }
+            break;
+        case 'viewer':
+            request.url = 'viewer_view.py';
+            request.callback = function(id) {
+                // when a data bookmark is loaded, we create the bookmarkboxes dynamically
+                $('modalId').select('[data-viewerport]').each(function(el) {
+                    new Filebox(el, {
+                        'position': 'after',
+                        'components': ['WidgetBookmarkPlusPipes']
+                    });
+                });
+            }
+            request.parameters = path[1].toQueryParams();
+            request.insert = function(response, request) {
+                var box = Builder.node('div', {
+                    className: 'modal viewer'
+                });
+                var vHref = request.url + '?' + $H(request.parameters).toQueryString();
+                box.insert(Builder.node('div', {
+                    className: 'controls',
+                    style: 'text-align: right; padding-top: 2px; z-index:100001;'
+                },
+                [
+                Builder.node('a', {
+                    href: vHref,
+                    title: 'open in a new tab',
+                    target: '_blank',
+                    className: 'detachModal'
+                },
+                ['>']), Builder.node('a', {
+                    className: 'closeModal',
+                    title: 'close this view',
+                    href: '#'
+                },
+                ['X'])]));
+                box.insert(response.responseText);
+                $('userPopupOverlay').insert(Builder.node('span', {
+                    id: 'modalId'
+                }));
+                $('modalId').insert(box);
+            }
+            break;
+        }
+        if (callback) {
+            if (request.callback) {
+                request.callback = request.callback.wrap(function(callOriginal, id) {
+                    callOriginal(id);
+                    callback(id);
+                });
+            } else {
+                request.callback = callback;
+            }
+        }
+        if (request.url == null) {
+          gTrack('error', 'report', request);
+          portal.go('welcome');
+          return;
+        }
+        new Ajax.Request(request.url, {
+            method: request.method,
+            parameters: request.parameters,
+            onSuccess: function(response) {
+                request.insert(response, request);
+                if (request.callback) {
+                    request.callback(request.id);
+                }
+            },
+            onFailure: function(response) {
+              portal.stopWaiting();
+              callback = dhtmlHistory.add(where());
+            },
+            onException: function(response) {
+              portal.stopWaiting();
+              callback = dhtmlHistory.add(where());
+            }
+        });
+    },
+
+    go: function(id,callback, update) {
+        var info = id.split('::');
+        gTrack(info[0], 'display', info[1]);
+        this.hideAllModal();
+        var path = id.strip().split('::');
+        var idlist = path.inject($A(), function(list, value, index) {
+          if (list.length == 0) {
+            list[list.length] = value;
+          }else {
+            list[list.length] = list[list.length - 1] + '::' + value;
+          }
+          return list;
+        });
+        idlist = idlist.without('user', 'viewer');
+        idlist.asyncEach(function(id, resume) {this._go(id, resume);}.bind(this), callback);
+    },
+
+    _go: function(id, callback, update) {
+          if (update || !this._exists(id)) {
+              portal.startWaiting();
+              if (callback) {
+                  callback = callback.wrap(function(callOriginal, id) {
+                      callOriginal(id);
+                      portal._show(id);
+                  });
+              } else {
+                  callback = portal._show.bind(this, id);
+              }
+              this._get(id, callback, update);
+          }else {
+              this._show(id);
+              if (callback) {
+                callback(id);
+              }
+          }
+    },
+
+    _exists: function(id, callback) {
+        switch (id) {
+        case 'user':
+        case 'viewer':
+            return false;
+        default:
+            return $('ms-' + id);
+        }
+    },
+
+    _show: function(id, callback) {
+        var path = id.split('::').without('');
+        portal.stopWaiting();
+        switch (path[0]) {
+        case 'user':
+        case 'viewer':
+            $('userPopupOverlay').show();
+            process_autofocus();
+            return;
+        default:
+            if ($('ms-' + id)) {
+              $('ms-' + id).showTab();
+            }
+        }
+        dhtmlHistory.add(where());
+    },
+
+    hideAllModal: function(element) {
+        if($('modalId')) {$('modalId').remove();}
+        $('userPopupOverlay').hide();
+    },
+
+    startWaiting: function() {
+      $('waitOverlay').show();
+    },
+
+    stopWaiting: function() {
+      $('waitOverlay').hide();
+    }
+
+});
+
+var where = function() {
+    if ($('userPopupOverlay').visible()) {
+        var el = $('userPopupOverlay').select('modal').detect(function(el) {
+            return el.visible();
+        });
+        return el && el.id ? el.id.substr(3) : null;
+    }
+    else {
+        var stateIds = $A(['user', 'welcome', 'forms', 'data', 'jobs', 'tutorials']);
+        var els = $('portalMain').select('.tabPanel').findAll(function(el) {
+            var parent = el.id.substr(3).split('::').first();
+            return (((el.offsetHeight + el.offsetWidth) > 0) && stateIds.include(parent));
+        });
+        var id = els.pluck('id').sortBy(function(s) {
+            return s.length;
+        }).last();
+        return id.substr(3);
+    }
+};
+
+var ResizeHandle = Class.create({
+
+    initialize: function(container, handle, type) {
+        this.container = $(container);
+        var handle = $(handle);
+        /* Add property to container to store position variables */
+        this.container.moveposition = {
+            x: 0,
+            y: 0
+        };
+        this.moveListener = function(event) {
+            /* Calculate how far the mouse moved */
+            var moved = {
+                x: (event.pointerX() - this.container.moveposition.x),
+                y: (event.pointerY() - this.container.moveposition.y)
+            };
+            /* Reset container's x/y utility property */
+            this.container.moveposition = {
+                x: event.pointerX(),
+                y: event.pointerY()
+            };
+
+            /* Update container's size */
+            var height = parseFloat(this.container.getStyle('height').gsub('px', ''));
+            var width = parseFloat(this.container.getStyle('width').gsub('px', ''));
+            var new_h = (!type || type == 'y') ? height + moved.y : height;
+            var new_w = (!type || type == 'x') ? width + moved.x : width;
+            this.container.setStyle({
+                height: new_h + 'px',
+                width: new_w + 'px'
+            });
+        }.bindAsEventListener(this);
+
+        /* Listen for 'mouse down' on handle to start the move listener */
+        handle.observe('mousedown',
+        function(event) {
+            /* Set starting x/y */
+            this.container.moveposition = {
+                x: event.pointerX(),
+                y: event.pointerY()
+            };
+            /* Start listening for mouse move on body */
+            Event.observe(document.body, 'mousemove', this.moveListener);
+        }.bindAsEventListener(this));
+
+        /* Listen for 'mouse up' to cancel 'move' listener */
+        Event.observe(document.body, 'mouseup',
+        function(event) {
+            Event.stopObserving(document.body, 'mousemove', this.moveListener);
+        }.bindAsEventListener(this));
+    }
+});
+
+var ToolTipManager = Class.create({
+
+    initialize: function() {
+        this.initializeBehaviour();
+    },
+
+    initializeBehaviour: function(el) {
+        document.observe('mouseover',
+        function(e) {
+            var element = e.element();
+            // if a tooltip exists for this program, hide it and show it only
+            // when mouse is over it
+            if (element && typeof element.next == 'function' &&
+            element.next() &&
+            element.next().hasClassName('tooltip')) {
+                var tooltipEl = element.next();
+                this.showTooltip(this, tooltipEl);
+                element.onmouseover = this.showTooltip.bindAsEventListener(this, tooltipEl);
+                element.onmouseout = this.hideTooltip.bindAsEventListener(this, tooltipEl, element);
+                tooltipEl.onmouseout = this.hideTooltip.bindAsEventListener(this, tooltipEl, element);
+                element.onmousemove = this.moveTooltip.bindAsEventListener(this, tooltipEl);
+                tooltipEl.onclick = element.onclick;
+            }
+        }
+        .bind(this));
+    },
+
+    showTooltip: function(e, tooltipEl) {
+        if (this.timeOutTooltipId) {
+            clearTimeout(this.timeOutTooltipId);
+        }
+        this.timeOutTooltipId = setTimeout((function() {
+            Element.show(tooltipEl);
+        }).bind(this), 500);
+    },
+
+    hideTooltip: function(e, tooltipEl, element) {
+        if (e.relatedTarget != tooltipEl && e.relatedTarget != element) {
+            // do not accept this event if we leave to enter the tooltip element
+            if (this.timeOutTooltipId) {
+                clearTimeout(this.timeOutTooltipId);
+            }
+            Element.hide(tooltipEl);
+        }
+    },
+
+    moveTooltip: function(e, tooltipEl) {
+        if (e.currentTarget != null) {
+            Element.setStyle(tooltipEl, {
+                position: 'absolute',
+                top: (Event.pointerY(e) - e.currentTarget.offsetParent.offsetTop) +
+                'px',
+                left: (Event.pointerX(e) - e.currentTarget.offsetParent.offsetLeft) +
+                'px'
+            });
+        }
+    }
+});
+
+function readCookieValue(key) {
+    var regex = new RegExp(key + '\s*="?(.*?)"?(;|$)');
+    var cookie = document.cookie.toString();
+    var match = cookie.match(regex);
+    return match ? unescape(match[1]) : '';
+}
+
+function editCookieValue(key, value) {
+    var regex = new RegExp(key + '\s*="?(.*?)"?(;|$)');
+    var repl = key + '=' + encodeURIComponent(value) + '';
+    var cookie = document.cookie.toString();
+    document.cookie = repl;
+    return;
+}
+
+function readUrlParameterValue(name) {
+    name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
+    var regexS = '[\\?&]' + name + '=([^&#]*)';
+    var regex = new RegExp(regexS);
+    var results = regex.exec(window.location.href);
+    if (results == null)
+    return '';
+    else
+    return results[1];
+}
+
+//firebug logging
+function logError(err) {
+    console.group('Mobyle error');
+    console.dir(err);
+    console.trace();
+    console.groupEnd();
+    gTrack('error', 'report', err);
+}
+
+Ajax.Responders.register({
+    // this hack forces to avoid caching throughout every ajax request,
+    // because IE tends to force cache when using GET requests.
+    onCreate: function(o) {
+        o.options.requestHeaders = $H({
+            'Cache-Control': 'post-check=1,pre-check=2,no-cache,must-revalidate',
+            'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT',
+            'X-MobylePortalName': portalProperties.portal_name
+        });
+    },
+    // this traces any ajax exception that might be raised
+    onException: function(req, err) {
+        logError(err);
+    },
+    onComplete: function(response) {
+        // because IE does not include dynamically loaded html in its observed code,
+        // we need to re-register the form event capture each time HTML is loaded.
+        init_observers();
+        // refresh user space when modifying session contents...
+          switch (response.url) {
+          case 'data_bookmark.py':
+          case 'data_remove.py':
+          case 'data_rename.py':
+          case 'data_upload.py':
+          case 'session_activate.py':
+          case 'session_captcha_check.py':
+          case 'session_job_submit.py':
+          case 'session_job_remove.py':
+          case 'session_job_rename.py':
+          case 'session_register.py':
+          case 'session_setemail.py':
+          case 'session_signin.py':
+          case 'session_signout.py':
+            user.workspace.get();
+            break;
+        }
+    }
+});
+
+var process_autofocus = function() {
+  // this function provides a patch that simulates the autofocus for browsers that do not support HTML5 autofocus attribute.
+  //if (!("autofocus" in document.createElement("input"))){
+      var autofoc_els = $$('*[style~="z-index:"] *[autofocus]');
+      if (autofoc_els.length == 0) {
+          autofoc_els = $$('*[autofocus]');
+      }
+       autofoc_els.first().focus();
+  //}
+};
+
+// this wraps any event handling function to catch raised exceptions
+Event.observe = Event.observe.wrap(function(proceed, element, eventName, handler) {
+    handler = handler.wrap(function(proceed, e) {
+        try {
+            return proceed(e);
+        } catch (err) {
+            logError(err);
+        }
+    });
+    return proceed(element, eventName, handler);
+});
+
+Event.observe(window, 'load',
+function() {
+
+    options = {
+        'anonymousSession': ($F('anonymousSession') == 'True'),
+        'authenticatedSession': ($F('authenticatedSession') == 'True')
+    };
+
+    user = new User();
+
+    portal = new Portal('mainContainer');
+    if (dhtmlHistory.isSafari) {
+      dhtmlHistory.add = function(hash) {console.log(hash);};
+      // this desactivates rsh altogether in Safari, to avoid the bugs in this specific browser
+    }else {
+      dhtmlHistory.initialize(historyListener);
+    }
+
+    var initialState = document.location.hash.substr(1);
+    initialState = initialState.blank() ? 'welcome' : initialState;
+    portal.go(initialState);
+    new ProgramsListForm();
+
+    init_observers();
+    process_autofocus();
+
+    new ResizeHandle('navbar', 'navbarResize', 'x');
+});
+
+var ProgramsListForm = Class.create({
+
+    initialize: function(dataEl) {
+        this.form = $('programSearch');
+        this.displayList();
+        this.form.observe('submit',
+        function(e) {
+            this.displayList();
+            e.stop();
+        }.bind(this));
+        $('listAllSubmit').observe('click', $('searchString').clear.bind($('searchString')));
+        var eventtowatch = Prototype.Browser['IE'] == true ? 'click' : 'change';
+        $A(this.form.serviceTypeSort).each(function(el) {
+            el.observe(eventtowatch, this.displayList.bind(this));
+        }.bind(this));
+        $A(this.form.classifyBy).each(function(el) {
+            el.observe(eventtowatch, this.displayList.bind(this));
+        }.bind(this));
+    },
+
+    displayList: function() {
+        this.form.request({
+            onCreate: function(response) {
+                this.form.disable();
+            }.bind(this),
+            onSuccess: function(response) {
+                $('programs_list_channel').update(response.responseText);
+                var el = $('listAllSubmitBlock');
+                if (this.form.searchString.value.blank()) {
+                    el.style.visibility = 'hidden';
+                }
+                else {
+                    el.style.visibility = 'visible';
+                }
+            }.bind(this),
+            onComplete: function(response) {
+                this.form.enable();
+            }.bind(this)
+        });
+    }
+
+
+});
+
+var observers = $H({
+    'links': {
+        'event': 'click',
+        'selector': 'body',
+        'fn': function(e) {
+            var linkEl = e.element();
+            var aas = linkEl.ancestors().concat([linkEl]);
+            var l = Selector.findElement(aas, '.link, .modalLink');
+            if (l && l.hash) {
+                portal.go(l.hash.substr(1));
+                e.stop();
+                return;
+            }
+            var l = Selector.findElement(aas, '.closeTab');
+            if (l && l.hash) {
+                $('ms-' + l.hash.substr(1)).removeTab();
+                e.stop();
+                dhtmlHistory.add(where());
+                return;
+            }
+            var l = Selector.findElement(aas, '.closeModal');
+            if (l) {
+                portal.hideAllModal();
+                e.stop();
+                dhtmlHistory.add(where());
+                return;
+            }
+            var l = Selector.findElement(aas, '.detachModal');
+            if (l) {
+                portal.hideAllModal();
+                dhtmlHistory.add(where());
+                return;
+            }
+        }
+    },
+    'clear_messages': {
+        'event': 'change',
+        'selector': 'body',
+        'fn': function(e) {
+            e.element().removeMessage();
+        }
+    },
+    'blinds, examples, refreshs, minimizers': {
+        'event': 'click',
+        'selector': 'body',
+        'fn': function(e) {
+            var el = e.element();
+            // managing blinds for comments, drawers, programs tree, etc.
+            if (el.hasClassName('blindLink')) {
+                var tg = $(e.element().readAttribute('href').split('#').last());
+                if (tg.visible()) {
+                    el.addClassName('closed');
+                }
+                else {
+                    el.removeClassName('closed');
+                }
+                Effect[Element.visible(tg) ? 'BlindUp' : 'BlindDown'](tg);
+                e.stop();
+            }
+            // managing examples
+            if (el.hasClassName('exampleLink')) {
+                var tg = $(e.element().readAttribute('href').substr(1));
+                var program = tg.up('form').id;
+                var param = tg.readAttribute('data-forparameter');
+                var formParam = tg.up('form')[tg.readAttribute('data-forparameter')];
+                e.stop(); // stop the event before, just in case anything goes wrong...
+                if (formParam.value) {
+                  //non-databox parameter
+                  formParam.value = tg.down('pre').innerHTML.unescapeHTML();
+                }else {
+                  //databox parameter
+                  // switch the databox to "paste" mode
+                  $(program).select('[name="' + param + '.mode"][value="paste"]')[0].fire('databox:switch');
+                  // load data
+                  $(program).select('[name="' + param + '"][class="databox_paste"]')[0].value = tg.down('pre').innerHTML.unescapeHTML();
+                }
+            }
+            // managing refresh links
+            if (el.hasClassName('refresh_link') || el.up('a.refresh_link')) {
+                user.workspace.get();
+                e.stop();
+            }
+            // managing minimizable fieldsets
+            if (el.tagName == 'LEGEND' && el.up('fieldset').hasClassName('minimizable')) {
+                var tgA = el.nextSiblings().select(function(el) {el.hasClassName('commentText')});
+                tgA.each(function(tg) {
+                    Effect[el.up('fieldset').hasClassName('minimized') ? 'BlindDown' : 'BlindUp'](tg, {
+                        queue: 'end'
+                    });
+                });
+                Element[el.up('fieldset').hasClassName('minimized') ? 'removeClassName' : 'addClassName'](el.up('fieldset'), 'minimized');
+                e.stop();
+            }
+        }
+    },
+    'submit': {
+        'event': 'submit',
+        'selector': 'form',
+        'fn': function(e) {
+            var form = e.element();
+            e.stop();
+            if (form.getAMethod() != 'get') {
+                portal.startWaiting();
+            }
+            form._submit();
+        }
+    },
+    'click_download': {
+        'event': 'click',
+        'selector': 'a button',
+        'fn': function(e) {
+            // this handler fires the onclick event not triggered in IE when a
+            // button is clicked inside an anchor element
+            var a = e.element().up('a');
+            if (a && a.click) {
+                e.element().up('a').click();
+                e.stop();
+            }
+        }
+    }
+});
+
+var init_observers = function() {
+    observers.each(function(pair) {
+        $$(pair.value.selector).each(function(el) {
+            el.stopObserving(pair.value.event, pair.value.fn);
+            el.observe(pair.value.event, pair.value.fn);
+        });
+    });
+};
+
+// RSH initialization
+window.dhtmlHistory.create({
+    toJSON: function(o) {
+        return Object.toJSON(o);
+    },
+    fromJSON: function(s) {
+        return s.evalJSON();
+    },
+    'blankURL': portalProperties.htbase + 'js/blank.html'
+});
+
+var historyListener = function(newLocation, historyData) {
+    if (newLocation == 'undefined' || newLocation == 'null' || newLocation.blank()) return; // safari bug
+    portal.go(newLocation);
+};
+
+Form.Element.disable = Form.Element.disable.wrap(function(proceed, el) {
+    el = proceed(el);
+    if (el.match('.tabPanel')) {
+        var cpnts = el.select('input', 'select', 'textarea').reject(function(el) {
+            return el.disabled == true;
+        });
+        if (cpnts.size() == 0) {
+            el.hide();
+            $$('.handlesList .link[href="#' + el.id.substr(3) + '"]')[0].up('li').hide();
+        }
+    }
+});
+
+Form.Element.enable = Form.Element.enable.wrap(function(proceed, el) {
+    el = proceed(el);
+    if (el.match('.tabPanel') && !(Form.serialize(el, false).blank())) {
+        $$('.handlesList .link[href="#' + el.id.substr(3) + '"]')[0].up('li').show();
+    }
+});
+
+//GA event tracking wrapper function
+var gTrack = function(category,action,label) {
+  if (typeof _gaq != 'undefined') {
+     _gaq.push(['_trackEvent', category, action, label]);
+  }
+};
+
+var ResizeHandle = Class.create({
+
+    initialize: function(container, handle, type) {
+        this.container = $(container);
+        var handle = $(handle);
+        /* Add property to container to store position variables */
+        this.container.moveposition = {
+            x: 0,
+            y: 0
+        };
+        this.moveListener = function(event) {
+            /* Calculate how far the mouse moved */
+            var moved = {
+                x: (event.pointerX() - this.container.moveposition.x),
+                y: (event.pointerY() - this.container.moveposition.y)
+            };
+            /* Reset container's x/y utility property */
+            this.container.moveposition = {
+                x: event.pointerX(),
+                y: event.pointerY()
+            };
+
+            /* Update container's size */
+            var height = parseFloat(this.container.getStyle('height').gsub('px', ''));
+            var width = parseFloat(this.container.getStyle('width').gsub('px', ''));
+            var new_h = (!type || type == 'y') ? height + moved.y : height;
+            var new_w = (!type || type == 'x') ? width + moved.x : width;
+            this.container.setStyle({
+                height: new_h + 'px',
+                width: new_w + 'px'
+            });
+        }.bindAsEventListener(this);
+
+        /* Listen for 'mouse down' on handle to start the move listener */
+        handle.observe('mousedown',
+        function(event) {
+            /* Set starting x/y */
+            this.container.moveposition = {
+                x: event.pointerX(),
+                y: event.pointerY()
+            };
+            /* Start listening for mouse move on body */
+            Event.observe(document.body, 'mousemove', this.moveListener);
+        }.bindAsEventListener(this));
+
+        /* Listen for 'mouse up' to cancel 'move' listener */
+        Event.observe(document.body, 'mouseup',
+        function(event) {
+            Event.stopObserving(document.body, 'mousemove');
+        }.bindAsEventListener(this));
+    }
+});
+
+var FormSimpleParametersToggleManager = Class.create({
+
+    initialize: function(formEl) {
+      var simpleParamEls = formEl.select('.parameter[data-issimple="true"]');
+      var allParamEls = formEl.select('.parameter');
+      if (simpleParamEls.size() > 0 && allParamEls.size() != simpleParamEls.size()) {
+        this.simple = null; //"simple form" mode toggle
+        this.formEl = formEl;
+        this.showAdvanced = Builder.node('button', ['advanced options']);
+        this.showAdvanced.observe('click',
+          function(ev) {
+            this.showAll();
+            ev.stop();
+          }.bindAsEventListener(this));
+        this.formEl.select('.formCtrl').first().insert(this.showAdvanced);
+        this.showSimple = Builder.node('button', ['only simple options']);
+        this.showSimple.observe('click',
+          function(ev) {
+            this.showOnlySimple();
+            ev.stop();
+          }.bindAsEventListener(this));
+        this.formEl.observe('form:parameter_load', function(event) {
+          if (this.simple) {
+            // if simple form toggle, reinitialize simple form display
+            // to display modified parameters
+            this.showAll();
+            this.showOnlySimple();
+          }
+        }.bind(this));
+        this.formEl.select('.formCtrl').first().insert(this.showSimple);
+        this.showOnlySimple();
+      }
+    },
+
+    showOnlySimple: function(event) {
+      // hide advanced parameters unless value!=default
+      this.formEl.select('.parameter').each(
+        function(pEl) {
+          if (pEl.readAttribute('data-issimple') != 'true' && !pEl.hasClassName('modified')) {
+            pEl.hide();
+          }
+        }.bind(this)
+      );
+      // hide "hidden parameters" container paragraphs
+      this.formEl.select('[data-paragraphname]').each(
+        function(pgEl) {
+          if (pgEl.select('.parameter.modified').size() == 0 && pgEl.select('.parameter[data-issimple="true"]').size() == 0) {
+            pgEl.hide();
+          }
+        }
+      );
+      this.showSimple.hide();
+      this.showAdvanced.show();
+      this.simple = true;
+    },
+
+    showAll: function() {
+      this.formEl.select('.parameter').each(
+        function(pEl) {
+          if (pEl.readAttribute('data-issimple') != 'true') {
+            pEl.show();
+          }
+        }
+      );
+      this.formEl.select('[data-paragraphname]').each(
+        function(pgEl) {
+          if (pgEl.select('.parameter[data-issimple="true"]').size() == 0) {
+            pgEl.show();
+          }
+        }
+      );
+      this.showAdvanced.hide();
+      this.showSimple.show();
+      this.simple = false;
+    }
+});
+
+var FormParametersManager = Class.create({
+
+    initialize: function(formEl) {
+      this.formEl = formEl;
+      this.formEl.select('.parameter').each(
+        function(pEl) {
+          this.createParameter(pEl);
+        }.bind(this)
+      );
+      if(portal.properties.email_results){
+          this.email_results_ctrl = new Element('div').update('Email me the results of this job <select name="_email_notify"><option value="auto">if it takes a long time</option><option value="true">yes</option><option value="false">no</option></select>');
+          this.formEl.down('.header').insert(this.email_results_ctrl);
+          this.updateEmailCtrl();
+          document.observe('user:email',this.updateEmailCtrl.bind(this));
+      }
+      this.formEl.observe('reset',
+        function(event) {
+          this.formEl.select('.modified').invoke('removeClassName', 'modified');
+          this.formEl.select('[data-ismultiple="true"]').invoke('fire', 'form:reset');
+      }.bind(this));
+    },
+    
+    updateEmailCtrl: function(){
+    	// set visibility for results mail settings
+    	if(user.email){
+            this.email_results_ctrl.show();
+    	}else{
+            this.email_results_ctrl.hide();
+    	}
+    },
+
+    createParameter: function(el, isClone,value) {
+      // create a parameter for the form
+      // el: "parameter"-level element in the form
+      // isClone: boolean that mentions if the parameter already contains a Databox
+      var datatype = el.readAttribute('data-datatype');
+      var name = el.readAttribute('data-parametername');
+      var isMultiple = el.readAttribute('data-ismultiple') == 'true';
+      var parameter = null; // parameter is either a Databox object or an input control element
+      if (!datatype.blank() && simpleDatatypes.indexOf(datatype) == -1) {
+        parameter = this.createDatabox(el, name, isMultiple, value);
+      }else {
+        parameter = this.createSimpleParameter(el, isMultiple, value);
+      }
+      el.fire('multiple_parameter:add_remove');
+      if (isMultiple & !isClone) {
+        var addValues = Builder.node('button', {className: 'databox_add_button', title: 'click here to add new values for this parameter'}, ['+']);
+        el.insert({top: addValues});
+        addValues.observe('click', function(ev) {
+          ev.stop();
+          this.createParameter(el, true);
+        }.bind(this));
+      }
+      el.select('input,select,textarea').each(
+      function(ctrlEl) {
+          ctrlEl.observe('reset', onFormParameterModification);
+      });
+      if (!isClone) {
+        el.observe('parameter:pipe', function(e) {
+          try {
+          var inputMode = e.memo.inputModes.isArray ? e.memo.inputModes[0] : e.memo.inputModes;
+          var resultId = e.memo.safeFileName;
+          var databoxToPipe = null;
+          var name = el.readAttribute('data-parametername');
+          if (isMultiple) {
+            var databoxes = this.formEl.select('[data-databoxforparametername="' + name + '"]');
+            var firstEmptyDatabox = databoxes.detect(function(dbEl) {
+              var dbName = dbEl.readAttribute('data-databoxname');
+              var value = dbEl.select('[name="' + dbName + '"]').invoke('getValue').without('') + dbEl.select('[name="' + dbName + '.ref"]').invoke('getValue').without('');
+              return value == '';
+            });
+            if (firstEmptyDatabox) {
+              name = name + '['+ firstEmptyDatabox.readAttribute('data-databoxindex') + ']';
+            }else {
+              name = this.createParameter(el, true).name;
+            }
+          }
+          // switch the databox to "result" mode
+          this.formEl.select('[name="' + name + '.mode"][value="' + inputMode + '"]')[0].fire('databox:switch');
+          // select appropriate value in results history to load in the databox
+          var program = this.formEl.id;
+          $(program + '::' + name + '::' + inputMode + '::history').value = resultId;
+          // load data
+          $(program + '::' + name + '::' + inputMode + '::history').next('input[type="button"]').fire('databox:history_load');
+          }catch (e) {console.log(e)}
+        }.bind(this));
+      }
+      return parameter;
+    },
+
+    createDatabox: function(el,name,isMultiple,value) {
+      var inputs = el.select('[name=' + name + ']');
+      var db = new Databox(el);
+      el.insert({
+          bottom: db.el
+      });
+      inputs.invoke('remove');
+      return db;
+    },
+
+    createSimpleParameter: function(el,isMultiple,value) {
+      el.select('input,select,textarea').each(
+        function(ctrlEl) {
+          ctrlEl.observe('change', onFormParameterModification);
+      }.bind(this));
+      return el;
+    }
+});
+
+var onFormParameterModification = function(source) {
+  // this function tests wether a control value is different from its corresponding
+  // parameter value (based on data-defaultvalue attribute), and depending on the
+  // result adds or removes the "modified" css class.
+  var el = source.findElement ? source.findElement() : source;
+  var form = el.up('form');
+  var parEl = el.up('[data-parametername]');
+  var formNameToTest = parEl.readAttribute('data-parametername');
+  //if the parameter is a databox
+  if (parEl.select('[data-databoxforparametername]')) {
+    if (parEl.readAttribute('data-ismultiple') == 'true') {
+      var databoxes = parEl.select('[data-databoxforparametername]');
+      var firstNonEmptyDatabox = databoxes.detect(function(dbEl) {
+        var dbName = dbEl.readAttribute('data-databoxname');
+        var value = dbEl.select('[name="' + dbName + '"]').invoke('getValue').without('') + dbEl.select('[name="' + dbName + '.ref"]').invoke('getValue').without('');
+        return value != '';
+      });
+      if (firstNonEmptyDatabox) {
+        formNameToTest = firstNonEmptyDatabox.readAttribute('data-databoxname');
+      }
+    }
+  }
+  var value = form[formNameToTest].value || $A(form[formNameToTest]).pluck('value').reject(function(val) {return val == ''});
+  value = (value == '') ? undefined : value;
+  var defaultValue = parEl.readAttribute('data-default-value');
+  defaultValue = (defaultValue == '') ? undefined : defaultValue;
+  if (value != defaultValue) {
+    parEl.addClassName('modified');
+  }else {
+    parEl.removeClassName('modified');
+  }
+};
diff --git a/Src/Portal/htdocs/MobylePortal/js/mobyle_ga.js b/Src/Portal/htdocs/MobylePortal/js/mobyle_ga.js
new file mode 100644
index 0000000..ce56492
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/mobyle_ga.js
@@ -0,0 +1,16 @@
+/**
+* Google Analytics statistics
+*
+*/
+var _gaq = _gaq || [];
+
+Event.observe(window, 'load', function() {
+_gaq.push(['_setAccount', $('ga_code').value]);
+_gaq.push(['_trackPageview']);
+
+(function() {
+  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
+})();
+});
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/js/openid/openid.js b/Src/Portal/htdocs/MobylePortal/js/openid/openid.js
new file mode 100644
index 0000000..b6206d6
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/openid/openid.js
@@ -0,0 +1,44 @@
+jQuery.noConflict();
+
+function bindOpenIdForm() {
+  jQuery('#openid_form').hide();
+  jQuery('#openid_loader').show();
+  jQuery('#openid_user_bar').load(
+    jQuery('#openid_form').attr('action'),
+    { 'openidurl': jQuery("input[id$='_openid_url']").val() },
+    function() {
+      jQuery('#openid_form').unbind();
+      jQuery('#openid_form').submit(function() {return bindOpenIdForm();});
+      if (typeof(transiting) == 'undefined' || !transiting) {
+        jQuery('#openid_loader').hide();
+      }
+      else {
+        jQuery('#openid_loader').show();
+        jQuery('#openid_user_bar').hide();
+        transiting = false;
+      }
+    }
+  );
+  return false;
+}
+
+function changeServer(url, selectStart, selectLength) {
+  jQuery("input[id$='_openid_url']").val(url);
+  
+  // IE
+  if (jQuery("input[id$='_openid_url']")[0].createTextRange) {
+    var range = jQuery("input[id$='_openid_url']")[0].createTextRange();
+    range.collapse(true);
+    range.moveEnd('character', selectStart+selectLength);
+    range.moveStart('character', selectStart);
+    range.select();
+  // real browsers :)
+  } else if (jQuery("input[id$='_openid_url']")[0].setSelectionRange) {
+    jQuery("input[id$='_openid_url']")[0].focus();
+    jQuery("input[id$='_openid_url']")[0].setSelectionRange(selectStart, selectStart+selectLength);
+  }
+    
+  return false;
+}
+
+  
diff --git a/Src/Portal/htdocs/MobylePortal/js/rsh.js b/Src/Portal/htdocs/MobylePortal/js/rsh.js
new file mode 100755
index 0000000..abc4b1b
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/js/rsh.js
@@ -0,0 +1,780 @@
+/*
+Copyright (c) 2007 Brian Dillard and Brad Neuberg:
+Brian Dillard | Project Lead | bdillard at pathf.com | http://blogs.pathf.com/agileajax/
+Brad Neuberg | Original Project Creator | http://codinginparadise.org
+   
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/*
+	dhtmlHistory: An object that provides history, history data, and bookmarking for DHTML and Ajax applications.
+	
+	dependencies:
+		* the historyStorage object included in this file.
+
+*/
+window.dhtmlHistory = {
+	
+	/*Public: User-agent booleans*/
+	isIE: false,
+	isOpera: false,
+	isSafari: false,
+	isKonquerer: false,
+	isGecko: false,
+	isSupported: false,
+	
+	/*Public: Create the DHTML history infrastructure*/
+	create: function(options) {
+		
+		/*
+			options - object to store initialization parameters
+			options.blankURL - string to override the default location of blank.html. Must end in "?"
+			options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
+			options.toJSON - function to override default JSON stringifier
+			options.fromJSON - function to override default JSON parser
+			options.baseTitle - pattern for title changes; example: "Armchair DJ [@@@]" - @@@ will be replaced 
+		*/
+
+		var that = this;
+		
+		/*Set up the historyStorage object; pass in options bundle*/
+		window.historyStorage.setup(options);
+		
+		/*Set up our base title if one is passed in*/
+		if (options && options.baseTitle) {
+			if (options.baseTitle.indexOf("@@@") < 0 && historyStorage.debugMode) {
+				throw new Error("Programmer error: options.baseTitle must contain the replacement parameter"
+				+ " '@@@' to be useful.");
+			}
+			this.baseTitle = options.baseTitle;
+		}
+		
+		/*set user-agent flags*/
+		var UA = navigator.userAgent.toLowerCase();
+		var platform = navigator.platform.toLowerCase();
+		var vendor = navigator.vendor || "";
+		if (vendor === "KDE") {
+			this.isKonqueror = true;
+			this.isSupported = false;
+		} else if (typeof window.opera !== "undefined") {
+			this.isOpera = true;
+			this.isSupported = true;
+		} else if (typeof document.all !== "undefined") {
+			this.isIE = true;
+			this.isSupported = true;
+		} else if (vendor.indexOf("Apple Computer, Inc.") > -1) {
+			this.isSafari = true;
+			this.isSupported = (platform.indexOf("mac") > -1);
+		} else if (UA.indexOf("gecko") != -1) {
+			this.isGecko = true;
+			this.isSupported = true;
+		}
+
+		/*Create Safari/Opera-specific code*/
+		if (this.isSafari) {
+			this.createSafari();
+		} else if (this.isOpera) {
+			this.createOpera();
+		}
+		
+		/*Get our initial location*/
+		var initialHash = this.getCurrentLocation();
+
+		/*Save it as our current location*/
+		this.currentLocation = initialHash;
+
+		/*Now that we have a hash, create IE-specific code*/
+		if (this.isIE) {
+			/*Optionally override the URL of IE's blank HTML file*/
+			if (options && options.blankURL) {
+				var u = options.blankURL;
+				/*assign the value, adding the trailing ? if it's not passed in*/
+				this.blankURL = (u.indexOf("?") != u.length - 1
+					? u + "?"
+					: u
+				);
+			}
+			this.createIE(initialHash);
+		}
+
+		/*Add an unload listener for the page; this is needed for FF 1.5+ because this browser caches all dynamic updates to the
+		page, which can break some of our logic related to testing whether this is the first instance a page has loaded or whether
+		it is being pulled from the cache*/
+
+		var unloadHandler = function() {
+			that.firstLoad = null;
+		};
+		
+		this.addEventListener(window,'unload',unloadHandler);		
+
+		/*Determine if this is our first page load; for IE, we do this in this.iframeLoaded(), which is fired on pageload. We do it
+		there because we have no historyStorage at this point, which only exists after the page is finished loading in IE*/
+		if (this.isIE) {
+			/*The iframe will get loaded on page load, and we want to ignore this fact*/
+			this.ignoreLocationChange = true;
+		} else {
+			if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+				/*This is our first page load, so ignore the location change and add our special history entry*/
+				this.ignoreLocationChange = true;
+				this.firstLoad = true;
+				historyStorage.put(this.PAGELOADEDSTRING, true);
+			} else {
+				/*This isn't our first page load, so indicate that we want to pay attention to this location change*/
+				this.ignoreLocationChange = false;
+				this.firstLoad = false;
+				/*For browsers other than IE, fire a history change event; on IE, the event will be thrown automatically when its
+				hidden iframe reloads on page load. Unfortunately, we don't have any listeners yet; indicate that we want to fire
+				an event when a listener is added.*/
+				this.fireOnNewListener = true;
+			}
+		}
+
+		/*Other browsers can use a location handler that checks at regular intervals as their primary mechanism; we use it for IE as
+		well to handle an important edge case; see checkLocation() for details*/
+		var locationHandler = function() {
+			that.checkLocation();
+		};
+		setInterval(locationHandler, 100);
+	},	
+	
+	/*Public: Initialize our DHTML history. You must call this after the page is finished loading. Optionally, you can pass your listener in
+	here so you don't need to make a separate call to addListener*/
+	initialize: function(listener) {
+
+		/*save original document title to plug in when we hit a null-key history point*/
+		this.originalTitle = document.title;
+		
+		/*IE needs to be explicitly initialized. IE doesn't autofill form data until the page is finished loading, so we have to wait*/
+		if (this.isIE) {
+			/*If this is the first time this page has loaded*/
+			if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+				/*For IE, we do this in initialize(); for other browsers, we do it in create()*/
+				this.fireOnNewListener = false;
+				this.firstLoad = true;
+				historyStorage.put(this.PAGELOADEDSTRING, true);
+			}
+			/*Else if this is a fake onload event*/
+			else {
+				this.fireOnNewListener = true;
+				this.firstLoad = false;   
+			}
+		}
+		/*optional convenience to save a separate call to addListener*/
+		if (listener) {
+			this.addListener(listener);
+		}
+	},
+
+	/*Public: Adds a history change listener. Only one listener is supported at this time.*/
+	addListener: function(listener) {
+		this.listener = listener;
+		/*If the page was just loaded and we should not ignore it, fire an event to our new listener now*/
+		if (this.fireOnNewListener) {
+			this.fireHistoryEvent(this.currentLocation);
+			this.fireOnNewListener = false;
+		}
+	},
+	
+	/*Public: Change the current HTML title*/
+	changeTitle: function(historyData) {
+		var winTitle = (historyData && historyData.newTitle
+			/*Plug the new title into the pattern*/
+			? this.baseTitle.replace('@@@', historyData.newTitle)
+			/*Otherwise, if there is no new title, use the original document title. This is useful when some
+			history changes have title changes and some don't; we can automatically return to the original
+			title rather than leaving a misleading title in the title bar. The same goes for our "virgin"
+			(hashless) page state.*/
+			: this.originalTitle
+		);
+		/*No need to do anything if the title isn't changing*/
+		if (document.title == winTitle) {
+			return;
+		}
+
+		/*Now change the DOM*/
+		document.title = winTitle;
+		/*Change it in the iframe, too, for IE*/
+		if (this.isIE) {
+			this.iframe.contentWindow.document.title = winTitle;
+		}
+		
+		/*If non-IE, reload the hash so the new title "sticks" in the browser history object*/
+		if (!this.isIE && !this.isOpera) {
+			var hash = decodeURIComponent(document.location.hash);
+			if (hash != "") {
+				var encodedHash = encodeURIComponent(this.removeHash(hash));
+				document.location.hash = encodedHash;
+			} else {
+				//document.location.hash = "#";
+			}
+		}
+	},
+	
+	/*Public: Add a history point. Parameters available:
+	* newLocation (required):
+		This will be the #hash value in the URL. Users can bookmark it. It will persist across sessions, so
+		your application should be able to restore itself to a specific state based on just this value. It
+		should be either a simple keyword for a viewstate or else a pseudo-querystring.
+	* historyData (optional):
+		This is for complex data that is relevant only to the current browsing session. It will be available
+		to your application until the browser is closed. If the user comes back to a bookmarked history point
+		during a later session, this data will no longer be available. Don't rely on it for application
+		re-initialization from a bookmark.
+	* historyData.newTitle (optional):
+		This will swap out the html <title> attribute with a new value. If you have set a baseTitle using the
+		options bundle, the value will be plugged into the baseTitle by swapping out the @@@ replacement param.
+	*/
+	add: function(newLocation, historyData) {
+		
+		var that = this;
+		
+		/*Escape the location and remove any leading hash symbols*/
+		//var encodedLocation = encodeURIComponent(this.removeHash(newLocation));
+		var encodedLocation = this.removeHash(newLocation);
+		
+		if (this.isSafari) {
+
+			/*Store the history data into history storage - pass in unencoded newLocation since
+			historyStorage does its own encoding*/
+			historyStorage.put(newLocation, historyData);
+
+			/*Save this as our current location*/
+			this.currentLocation = encodedLocation;
+	
+			/*Change the browser location*/
+			window.location.hash = encodedLocation;
+		
+			/*Save this to the Safari form field*/
+			this.putSafariState(encodedLocation);
+
+			this.changeTitle(historyData);
+
+		} else {
+			
+			/*Most browsers require that we wait a certain amount of time before changing the location, such
+			as 200 MS; rather than forcing external callers to use window.setTimeout to account for this,
+			we internally handle it by putting requests in a queue.*/
+			var addImpl = function() {
+				
+				/*Indicate that the current wait time is now less*/
+				if (that.currentWaitTime > 0) {
+					that.currentWaitTime = that.currentWaitTime - that.waitTime;
+				}
+
+				/*IE has a strange bug; if the encodedLocation is the same as _any_ preexisting id in the
+				document, then the history action gets recorded twice; throw a programmer exception if
+				there is an element with this ID*/
+				if (document.getElementById(encodedLocation) && that.debugMode) {
+					var e = "Exception: History locations can not have the same value as _any_ IDs that might be in the document,"
+					+ " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
+					+ " IDs in this document. The following ID is already taken and cannot be a location: " + newLocation;
+					throw new Error(e); 
+				}
+
+				/*Store the history data into history storage - pass in unencoded newLocation since
+				historyStorage does its own encoding*/
+				historyStorage.put(newLocation, historyData);
+
+				/*Indicate to the browser to ignore this upcomming location change since we're making it programmatically*/
+				that.ignoreLocationChange = true;
+
+				/*Indicate to IE that this is an atomic location change block*/
+				that.ieAtomicLocationChange = true;
+
+				/*Save this as our current location*/
+				that.currentLocation = encodedLocation;
+				
+				/*Change the browser location*/
+				window.location.hash = encodedLocation;
+
+				/*Change the hidden iframe's location if on IE*/
+				if (that.isIE) {
+					that.iframe.src = that.blankURL + encodedLocation;
+				}
+
+				/*End of atomic location change block for IE*/
+				that.ieAtomicLocationChange = false;
+				
+				that.changeTitle(historyData);
+				
+			};
+
+			/*Now queue up this add request*/
+			window.setTimeout(addImpl, this.currentWaitTime);
+
+			/*Indicate that the next request will have to wait for awhile*/
+			this.currentWaitTime = this.currentWaitTime + this.waitTime;
+		}
+	},
+
+	/*Public*/
+	isFirstLoad: function() {
+		return this.firstLoad;
+	},
+
+	/*Public*/
+	getVersion: function() {
+		return this.VERSIONNUMBER;
+	},
+	
+	/*- - - - - - - - - - - -*/
+	
+	/*Private: Constant for our own internal history event called when the page is loaded*/
+	PAGELOADEDSTRING: "DhtmlHistory_pageLoaded",
+	
+	VERSIONNUMBER: "0.8",
+	
+	/*
+		Private: Pattern for title changes. Example: "Armchair DJ [@@@]" where @@@ will be relaced by values passed to add();
+		Default is just the title itself, hence "@@@"
+	*/
+	baseTitle: "@@@",
+	
+	/*Private: Placeholder variable for the original document title; will be set in ititialize()*/
+	originalTitle: null,
+	
+	/*Private: URL for the blank html file we use for IE; can be overridden via the options bundle. Otherwise it must be served
+	in same directory as this library*/
+	blankURL: "blank.html?",
+	
+	/*Private: Our history change listener.*/
+	listener: null,
+
+	/*Private: MS to wait between add requests - will be reset for certain browsers*/
+	waitTime: 200,
+	
+	/*Private: MS before an add request can execute*/
+	currentWaitTime: 0,
+
+	/*Private: Our current hash location, without the "#" symbol.*/
+	currentLocation: null,
+
+	/*Private: Hidden iframe used to IE to detect history changes*/
+	iframe: null,
+
+	/*Private: Flags and DOM references used only by Safari*/
+	safariHistoryStartPoint: null,
+	safariStack: null,
+	safariLength: null,
+
+	/*Private: Flag used to keep checkLocation() from doing anything when it discovers location changes we've made ourselves
+	programmatically with the add() method. Basically, add() sets this to true. When checkLocation() discovers it's true,
+	it refrains from firing our listener, then resets the flag to false for next cycle. That way, our listener only gets fired on
+	history change events triggered by the user via back/forward buttons and manual hash changes. This flag also helps us set up
+	IE's special iframe-based method of handling history changes.*/
+	ignoreLocationChange: null,
+
+	/*Private: A flag that indicates that we should fire a history change event when we are ready, i.e. after we are initialized and
+	we have a history change listener. This is needed due to an edge case in browsers other than IE; if you leave a page entirely
+	then return, we must fire this as a history change event. Unfortunately, we have lost all references to listeners from earlier,
+	because JavaScript clears out.*/
+	fireOnNewListener: null,
+
+	/*Private: A variable that indicates whether this is the first time this page has been loaded. If you go to a web page, leave it
+	for another one, and then return, the page's onload listener fires again. We need a way to differentiate between the first page
+	load and subsequent ones. This variable works hand in hand with the pageLoaded variable we store into historyStorage.*/
+	firstLoad: null,
+
+	/*Private: A variable to handle an important edge case in IE. In IE, if a user manually types an address into their browser's
+	location bar, we must intercept this by calling checkLocation() at regular intervals. However, if we are programmatically
+	changing the location bar ourselves using the add() method, we need to ignore these changes in checkLocation(). Unfortunately,
+	these changes take several lines of code to complete, so for the duration of those lines of code, we set this variable to true.
+	That signals to checkLocation() to ignore the change-in-progress. Once we're done with our chunk of location-change code in
+	add(), we set this back to false. We'll do the same thing when capturing user-entered address changes in checkLocation itself.*/
+	ieAtomicLocationChange: null,
+	
+	/*Private: Generic utility function for attaching events*/
+	addEventListener: function(o,e,l) {
+		if (o.addEventListener) {
+			o.addEventListener(e,l,false);
+		} else if (o.attachEvent) {
+			o.attachEvent('on'+e,function() {
+				l(window.event);
+			});
+		}
+	},
+
+	/*Private: Create IE-specific DOM nodes and overrides*/
+	createIE: function(initialHash) {
+		/*write out a hidden iframe for IE and set the amount of time to wait between add() requests*/
+		this.waitTime = 400;/*IE needs longer between history updates*/
+		var styles = (historyStorage.debugMode
+			? 'width: 800px;height:80px;border:1px solid black;'
+			: historyStorage.hideStyles
+		);
+		var iframeID = "rshHistoryFrame";
+		var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="' + this.blankURL + initialHash + '"></iframe>';
+		document.write(iframeHTML);
+		this.iframe = document.getElementById(iframeID);
+	},
+	
+	/*Private: Create Opera-specific DOM nodes and overrides*/
+	createOpera: function() {
+		this.waitTime = 400;/*Opera needs longer between history updates*/
+		var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
+		document.write(imgHTML);
+	},
+	
+	/*Private: Create Safari-specific DOM nodes and overrides*/
+	createSafari: function() {
+		var formID = "rshSafariForm";
+		var stackID = "rshSafariStack";
+		var lengthID = "rshSafariLength";
+		var formStyles = historyStorage.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
+		var stackStyles = (historyStorage.debugMode
+			? 'width: 800px;height:80px;border:1px solid black;'
+			: historyStorage.hideStyles
+		);
+		var lengthStyles = (historyStorage.debugMode
+			? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
+			: historyStorage.hideStyles
+		);
+		var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+			+ '<textarea style="' + stackStyles + '" id="' + stackID + '">[]</textarea>'
+			+ '<input type="text" style="' + lengthStyles + '" id="' + lengthID + '" value=""/>'
+		+ '</form>';
+		document.write(safariHTML);
+		this.safariStack = document.getElementById(stackID);
+		this.safariLength = document.getElementById(lengthID);
+		if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+			this.safariHistoryStartPoint = history.length;
+			this.safariLength.value = this.safariHistoryStartPoint;
+		} else {
+			this.safariHistoryStartPoint = this.safariLength.value;
+		}
+	},
+	
+	/*TODO: make this public again?*/
+	/*Private: Get browser's current hash location; for Safari, read value from a hidden form field*/
+	getCurrentLocation: function() {
+		var r = (this.isSafari
+			? this.getSafariState()
+			: this.getCurrentHash()
+		);
+		return r;
+	},
+	
+	/*TODO: make this public again?*/
+	/*Private: Manually parse the current url for a hash; tip of the hat to YUI*/
+    getCurrentHash: function() {
+		var r = window.location.href;
+		var i = r.indexOf("#");
+		return (i >= 0
+			? r.substr(i+1)
+			: ""
+		);
+    },
+	
+	/*Private: Safari method to read the history stack from a hidden form field*/
+	getSafariStack: function() {
+		var r = this.safariStack.value;
+		return historyStorage.fromJSON(r);
+	},
+	/*Private: Safari method to read from the history stack*/
+	getSafariState: function() {
+		var stack = this.getSafariStack();
+		var state = stack[history.length - this.safariHistoryStartPoint - 1];
+		return state;
+	},			
+	/*Private: Safari method to write the history stack to a hidden form field*/
+	putSafariState: function(newLocation) {
+	    var stack = this.getSafariStack();
+	    stack[history.length - this.safariHistoryStartPoint] = newLocation;
+	    this.safariStack.value = historyStorage.toJSON(stack);
+	},
+
+	/*Private: Notify the listener of new history changes.*/
+	fireHistoryEvent: function(newHash) {
+		var decodedHash = decodeURIComponent(newHash)
+		/*extract the value from our history storage for this hash*/
+		var historyData = historyStorage.get(decodedHash);
+		this.changeTitle(historyData);
+		/*call our listener*/
+		this.listener.call(null, decodedHash, historyData);
+	},
+	
+	/*Private: See if the browser has changed location. This is the primary history mechanism for Firefox. For IE, we use this to
+	handle an important edge case: if a user manually types in a new hash value into their IE location bar and press enter, we want to
+	to intercept this and notify any history listener.*/
+	checkLocation: function() {
+		
+		/*Ignore any location changes that we made ourselves for browsers other than IE*/
+		if (!this.isIE && this.ignoreLocationChange) {
+			this.ignoreLocationChange = false;
+			return;
+		}
+
+		/*If we are dealing with IE and we are in the middle of making a location change from an iframe, ignore it*/
+		if (!this.isIE && this.ieAtomicLocationChange) {
+			return;
+		}
+		
+		/*Get hash location*/
+		var hash = this.getCurrentLocation();
+		
+		/*Do nothing if there's been no change*/
+		if (hash == this.currentLocation) {
+			return;
+		}
+		
+		/*In IE, users manually entering locations into the browser; we do this by comparing the browser's location against the
+		iframe's location; if they differ, we are dealing with a manual event and need to place it inside our history, otherwise
+		we can return*/
+		this.ieAtomicLocationChange = true;
+
+		if (this.isIE && this.getIframeHash() != hash) {
+			this.iframe.src = this.blankURL + hash;
+		}
+		else if (this.isIE) {
+			/*the iframe is unchanged*/
+			return;
+		}
+
+		/*Save this new location*/
+		this.currentLocation = hash;
+
+		this.ieAtomicLocationChange = false;
+
+		/*Notify listeners of the change*/
+		this.fireHistoryEvent(hash);
+	},
+
+	/*Private: Get the current location of IE's hidden iframe.*/
+	getIframeHash: function() {
+		var doc = this.iframe.contentWindow.document;
+		var hash = String(doc.location.search);
+		if (hash.length == 1 && hash.charAt(0) == "?") {
+			hash = "";
+		}
+		else if (hash.length >= 2 && hash.charAt(0) == "?") {
+			hash = hash.substring(1);
+		}
+		return hash;
+	},
+
+	/*Private: Remove any leading hash that might be on a location.*/
+	removeHash: function(hashValue) {
+		var r;
+		if (hashValue === null || hashValue === undefined) {
+			r = null;
+		}
+		else if (hashValue === "") {
+			r = "";
+		}
+		else if (hashValue.length == 1 && hashValue.charAt(0) == "#") {
+			r = "";
+		}
+		else if (hashValue.length > 1 && hashValue.charAt(0) == "#") {
+			r = hashValue.substring(1);
+		}
+		else {
+			r = hashValue;
+		}
+		return r;
+	},
+
+	/*Private: For IE, tell when the hidden iframe has finished loading.*/
+	iframeLoaded: function(newLocation) {
+		/*ignore any location changes that we made ourselves*/
+		if (this.ignoreLocationChange) {
+			this.ignoreLocationChange = false;
+			return;
+		}
+
+		/*Get the new location*/
+		var hash = String(newLocation.search);
+		if (hash.length == 1 && hash.charAt(0) == "?") {
+			hash = "";
+		}
+		else if (hash.length >= 2 && hash.charAt(0) == "?") {
+			hash = hash.substring(1);
+		}
+		/*Keep the browser location bar in sync with the iframe hash*/
+		window.location.hash = hash;
+
+		/*Notify listeners of the change*/
+		this.fireHistoryEvent(hash);
+	}
+
+};
+
+/*
+	historyStorage: An object that uses a hidden form to store history state across page loads. The mechanism for doing so relies on
+	the fact that browsers save the text in form data for the life of the browser session, which means the text is still there when
+	the user navigates back to the page. This object can be used independently of the dhtmlHistory object for caching of Ajax
+	session information.
+	
+	dependencies: 
+		* json2007.js (included in a separate file) or alternate JSON methods passed in through an options bundle.
+*/
+window.historyStorage = {
+	
+	/*Public: Set up our historyStorage object for use by dhtmlHistory or other objects*/
+	setup: function(options) {
+		
+		/*
+			options - object to store initialization parameters - passed in from dhtmlHistory or directly into historyStorage
+			options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
+			options.toJSON - function to override default JSON stringifier
+			options.fromJSON - function to override default JSON parser
+		*/
+		
+		/*process init parameters*/
+		if (typeof options !== "undefined") {
+			if (options.debugMode) {
+				this.debugMode = options.debugMode;
+			}
+			if (options.toJSON) {
+				this.toJSON = options.toJSON;
+			}
+			if (options.fromJSON) {
+				this.fromJSON = options.fromJSON;
+			}
+		}		
+		
+		/*write a hidden form and textarea into the page; we'll stow our history stack here*/
+		var formID = "rshStorageForm";
+		var textareaID = "rshStorageField";
+		var formStyles = this.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
+		var textareaStyles = (historyStorage.debugMode
+			? 'width: 800px;height:80px;border:1px solid black;'
+			: historyStorage.hideStyles
+		);
+		var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+			+ '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
+		+ '</form>';
+		document.write(textareaHTML);
+		this.storageField = document.getElementById(textareaID);
+		if (typeof window.opera !== "undefined") {
+			this.storageField.focus();/*Opera needs to focus this element before persisting values in it*/
+		}
+	},
+	
+	/*Public*/
+	put: function(key, value) {
+		
+		var encodedKey = encodeURIComponent(key);
+		
+		this.assertValidKey(encodedKey);
+		/*if we already have a value for this, remove the value before adding the new one*/
+		if (this.hasKey(key)) {
+			this.remove(key);
+		}
+		/*store this new key*/
+		this.storageHash[encodedKey] = value;
+		/*save and serialize the hashtable into the form*/
+		this.saveHashTable();
+	},
+
+	/*Public*/
+	get: function(key) {
+
+		var encodedKey = encodeURIComponent(key);
+		
+		this.assertValidKey(encodedKey);
+		/*make sure the hash table has been loaded from the form*/
+		this.loadHashTable();
+		var value = this.storageHash[encodedKey];
+		if (value === undefined) {
+			value = null;
+		}
+		return value;
+	},
+
+	/*Public*/
+	remove: function(key) {
+		
+		var encodedKey = encodeURIComponent(key);
+
+		this.assertValidKey(encodedKey);
+		/*make sure the hash table has been loaded from the form*/
+		this.loadHashTable();
+		/*delete the value*/
+		delete this.storageHash[encodedKey];
+		/*serialize and save the hash table into the form*/
+		this.saveHashTable();
+	},
+
+	/*Public: Clears out all saved data.*/
+	reset: function() {
+		this.storageField.value = "";
+		this.storageHash = {};
+	},
+
+	/*Public*/
+	hasKey: function(key) {
+		
+		var encodedKey = encodeURIComponent(key);
+
+		this.assertValidKey(encodedKey);
+		/*make sure the hash table has been loaded from the form*/
+		this.loadHashTable();
+		return (typeof this.storageHash[encodedKey] !== "undefined");
+	},
+
+	/*Public*/
+	isValidKey: function(key) {
+		return (typeof key === "string");
+		//TODO - should we ban hash signs and other special characters?
+	},
+	
+	/*- - - - - - - - - - - -*/
+
+	/*Private - CSS strings utilized by both objects to hide or show behind-the-scenes DOM elements*/
+	showStyles: 'border:0;margin:0;padding:0;',
+	hideStyles: 'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
+	
+	/*Private - debug mode flag*/
+	debugMode: false,
+
+	/*Private: Our hash of key name/values.*/
+	storageHash: {},
+
+	/*Private: If true, we have loaded our hash table out of the storage form.*/
+	hashLoaded: false, 
+
+	/*Private: DOM reference to our history field*/
+	storageField: null,
+
+	/*Private: Assert that a key is valid; throw an exception if it not.*/
+	assertValidKey: function(key) {
+		var isValid = this.isValidKey(key);
+		if (!isValid && this.debugMode) {
+			throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
+		}
+	},
+
+	/*Private: Load the hash table up from the form.*/
+	loadHashTable: function() {
+		if (!this.hashLoaded) {	
+			var serializedHashTable = this.storageField.value;
+			if (serializedHashTable !== "" && serializedHashTable !== null) {
+				this.storageHash = this.fromJSON(serializedHashTable);
+				this.hashLoaded = true;
+			}
+		}
+	},
+	/*Private: Save the hash table into the form.*/
+	saveHashTable: function() {
+		this.loadHashTable();
+		var serializedHashTable = this.toJSON(this.storageHash);
+		this.storageField.value = serializedHashTable;
+	},
+	/*Private: Bridges for our JSON implementations - both rely on 2007 JSON.org library - can be overridden by options bundle*/
+	toJSON: function(o) {
+		return o.toJSONString();
+	},
+	fromJSON: function(s) {
+		return s.parseJSON();
+	}
+};
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/annotate.xsl b/Src/Portal/htdocs/MobylePortal/xsl/annotate.xsl
new file mode 100644
index 0000000..6d5e54d
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/annotate.xsl
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+	annotate.xsl stylesheet
+	Authors: Herv� M�nager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+
+	<xsl:include href="ident.xsl" />
+
+	<!-- processing parameters to annotate them with necessary classes and transform nesting labels into fieldsets --> 
+	<xsl:template match="xhtml:label[not(xhtml:label) and (not(xhtml:input[@type='radio']))]">
+		<xsl:variable name="param-name" select="current()//xhtml:*/@name" />
+		<xsl:element name="label" use-attribute-sets="param">
+			<xsl:apply-templates select="@*" />
+			<xsl:apply-templates select="text()" />
+			<xsl:apply-templates select="//parameter[(name/text()=$param-name)]/type" mode="label"/>
+			<xsl:apply-templates select="//parameter[(name/text()=$param-name)]/comment" mode="ajaxLink"/>			
+			<xsl:apply-templates select="//parameter[(name/text()=$param-name)]/example" mode="ajaxLink"/>			
+			<xsl:apply-templates select="*" />
+			<xsl:apply-templates select="//parameter[(name/text()=$param-name)]/comment" mode="ajaxTarget"/>
+			<xsl:apply-templates select="//parameter[(name/text()=$param-name)]/example" mode="ajaxTarget"/>
+		</xsl:element>
+	</xsl:template>	
+
+	<xsl:template match="xhtml:label[xhtml:label]">
+		<xsl:element name="fieldset" use-attribute-sets="param">
+			<xsl:apply-templates select="@*" />
+			<legend>
+				<xsl:value-of select="text()"/>
+				<xsl:apply-templates select="//parameter[(name/text()=current()//xhtml:*/@name)]/type" mode="label"/>
+				<xsl:apply-templates select="//parameter[(name/text()=current()//xhtml:*/@name)]/comment" mode="ajaxLink"/>			
+			</legend>
+			<xsl:apply-templates select="//parameter[(name/text()=current()//xhtml:*/@name)]/comment" mode="ajaxTarget" />			
+			<xsl:apply-templates select="*" />
+		</xsl:element>
+	</xsl:template>
+
+	<xsl:template match="xhtml:fieldset[@data-paragraphname]">
+		<xsl:element name="fieldset">
+			<xsl:apply-templates select="@*" />
+			<xsl:apply-templates select="xhtml:legend" />
+			<xsl:apply-templates select="//paragraph[(name/text()=current()/@data-paragraphname)]/comment" mode="ajaxTarget"/>
+			<xsl:apply-templates select="//paragraph[(name/text()=current()/@data-paragraphname)]/example" mode="ajaxTarget"/>
+			<xsl:apply-templates select="*[local-name()!='legend']" />
+		</xsl:element>
+	</xsl:template>
+	
+	<xsl:template match="xhtml:fieldset[@data-parametername]">
+		<xsl:element name="fieldset" use-attribute-sets="param">
+			<xsl:apply-templates select="@*" />
+			<xsl:apply-templates select="xhtml:legend" />
+			<xsl:apply-templates select="//parameter[(name/text()=current()/@data-parametername)]/comment" mode="ajaxTarget"/>          
+			<xsl:apply-templates select="//parameter[(name/text()=current()/@data-parametername)]/example" mode="ajaxTarget"/>          
+			<xsl:apply-templates select="*[local-name()!='legend']" />
+		</xsl:element>
+	</xsl:template> 
+	
+	<xsl:template match="xhtml:fieldset[@data-paragraphname]/xhtml:legend">
+		<legend>
+			<xsl:apply-templates select="@*|node()|text()" />
+			<xsl:apply-templates select="//paragraph[(name/text()=current()/../@data-paragraphname)]/comment" mode="ajaxLink"/>         
+			<xsl:apply-templates select="//paragraph[(name/text()=current()/../@data-paragraphname)]/example" mode="ajaxLink"/>         
+		</legend>       
+	</xsl:template>     
+	
+	<xsl:template match="xhtml:fieldset[@data-parametername]/xhtml:legend">
+		<legend>
+			<xsl:apply-templates select="@*|node()|text()" />
+			<xsl:apply-templates select="//parameter[(name/text()=current()/../@data-parametername)]/comment" mode="ajaxLink"/>         
+			<xsl:apply-templates select="//parameter[(name/text()=current()/../@data-parametername)]/example" mode="ajaxLink"/>         
+		</legend>       
+	</xsl:template> 
+	
+	<xsl:template match="xhtml:div[@data-parametername]">
+		<xsl:element name="div" use-attribute-sets="param">
+			<xsl:apply-templates select="@*|node()|text()" />
+			<xsl:apply-templates select="//parameter[(name/text()=current()/@data-parametername)]/type" mode="label"/>
+			<xsl:apply-templates select="//parameter[(name/text()=current()/@data-parametername)]/comment" mode="ajaxLink"/>          
+			<xsl:apply-templates select="//parameter[(name/text()=current()/@data-parametername)]/comment" mode="ajaxTargetJob"/>
+		</xsl:element>
+	</xsl:template>	
+
+	<xsl:template match="example" mode="ajaxLink">
+		<a href="#{generate-id(..)}::example" class="exampleLink" title="click to prefill with an example">[use example data]</a>		
+		<xsl:if test="not(../comment)">
+			<a href="#{generate-id(..)}::comment" class="blindLink commentToggle" title="click to expand/collapse contextual help">?</a>		
+		</xsl:if>
+	</xsl:template>	
+	
+	<xsl:template match="example" mode="ajaxTarget">
+		<div class="example" id="{generate-id(..)}::example" data-forparameter="{../name/text()}" style="display:none">
+			<pre><xsl:apply-templates select="node()" mode="escape"/></pre>
+		</div>
+		<xsl:if test="not(../comment)">
+			<div id="{generate-id(..)}::comment" class="commentText" style="display:none" mode="ajaxTarget">
+				<xsl:apply-templates select="../example" mode="exampleInclude"/>
+			</div>			
+		</xsl:if>
+	</xsl:template>	
+
+	<xsl:template match="example" mode="exampleInclude">
+		<div class="example" id="{generate-id(..)}::example" data-forparameter="{../name/text()}">Example data <i>(click on <a>[use example data]</a> to load)</i>:
+			<pre><xsl:apply-templates select="node()" mode="escape"/></pre>
+		</div>
+	</xsl:template> 
+<!--
+    XML escaping code, shamelessly copied and adapted from http://stackoverflow.com/questions/1162352/converting-xml-to-escaped-text-in-xslt
+-->
+    <xsl:template match="*" mode="escape">
+        <!-- Begin opening tag -->
+        <xsl:text><</xsl:text>
+        <xsl:value-of select="name()"/>
+        <!-- Namespaces -->
+        <xsl:for-each select="namespace::*">
+            <xsl:if test="name() != 'xml'">
+                <xsl:text> xmlns</xsl:text>
+                <xsl:text>:</xsl:text>
+                <xsl:value-of select="name()"/>
+                <xsl:text>='</xsl:text>
+                <xsl:call-template name="escape-xml">
+                    <xsl:with-param name="text" select="."/>
+                </xsl:call-template>
+                <xsl:text>'</xsl:text>
+            </xsl:if>
+        </xsl:for-each>
+
+        <!-- Attributes -->
+        <xsl:for-each select="@*">
+            <xsl:text> </xsl:text>
+            <xsl:value-of select="name()"/>
+            <xsl:text>='</xsl:text>
+            <xsl:call-template name="escape-xml">
+                <xsl:with-param name="text" select="."/>
+            </xsl:call-template>
+            <xsl:text>'</xsl:text>
+        </xsl:for-each>
+
+        <!-- End opening tag -->
+        <xsl:text>></xsl:text>
+
+        <!-- Content (child elements, text nodes, and PIs) -->
+        <xsl:apply-templates select="node()" mode="escape" />
+
+        <!-- Closing tag -->
+        <xsl:text></</xsl:text>
+        <xsl:value-of select="name()"/>
+        <xsl:text>></xsl:text>
+    </xsl:template>
+
+    <xsl:template match="text()" mode="escape">
+        <xsl:call-template name="escape-xml">
+            <xsl:with-param name="text" select="."/>
+        </xsl:call-template>
+    </xsl:template>
+
+    <xsl:template match="processing-instruction()" mode="escape">
+        <xsl:text><?</xsl:text>
+        <xsl:value-of select="name()"/>
+        <xsl:text> </xsl:text>
+        <xsl:call-template name="escape-xml">
+            <xsl:with-param name="text" select="."/>
+        </xsl:call-template>
+        <xsl:text>?></xsl:text>
+    </xsl:template>
+
+    <xsl:template name="escape-xml">
+        <xsl:param name="text"/>
+        <xsl:if test="$text != ''">
+            <xsl:variable name="head" select="substring($text, 1, 1)"/>
+            <xsl:variable name="tail" select="substring($text, 2)"/>
+            <xsl:choose>
+                <xsl:when test="$head = '&'">&amp;</xsl:when>
+                <xsl:when test="$head = '<'">&lt;</xsl:when>
+                <xsl:when test="$head = '>'">&gt;</xsl:when>
+                <xsl:when test="$head = '"'">&quot;</xsl:when>
+                <xsl:when test="$head = "'"">&apos;</xsl:when>
+                <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise>
+            </xsl:choose>
+            <xsl:call-template name="escape-xml">
+                <xsl:with-param name="text" select="$tail"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/atom.xsl b/Src/Portal/htdocs/MobylePortal/xsl/atom.xsl
new file mode 100644
index 0000000..476c162
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/atom.xsl
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:a="http://www.w3.org/2005/Atom"
+  xmlns:xhtml="http://www.w3.org/1999/xhtml" 
+  xmlns="http://www.w3.org/1999/xhtml" 
+  exclude-result-prefixes="a xhtml"
+  xmlns:str="http://exslt.org/strings"
+  >
+
+  <xsl:output method="xml" encoding="utf-8"
+              doctype-public="-//W3C//DTD XHTML 1.1//EN"
+              doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"/>
+
+  <xsl:param name="htdocsDir" />  
+
+  <xsl:template match="a:*" />
+
+  <xsl:template match="@*|node()|text()" priority="-1">
+    <xsl:call-template name="nodeCopy" />
+  </xsl:template>
+  
+  <xsl:template name="nodeCopy">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()|text()" />
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="a:feed">
+        <h4><xsl:apply-templates select="a:title" mode="text-construct"/></h4>
+        <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="a:summary">
+    <div class="summary">
+      <xsl:apply-templates select="." mode="text-construct"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="a:content">
+    <div class="content">
+        <xsl:value-of select="string(.)" disable-output-escaping="yes"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="a:entry">
+    <div class="entry">
+      <h5><xsl:apply-templates select="a:title" mode="text-construct"/></h5>
+      <xsl:apply-templates/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="a:link" mode="links">
+    <a href="{@href}">
+      <xsl:value-of select="@rel"/>
+      <xsl:if test="not(@rel)">[generic link]</xsl:if>
+      <xsl:if test="@type">
+	<xsl:text> (</xsl:text><xsl:value-of select="@type"/><xsl:text>): </xsl:text>
+      </xsl:if>
+      <xsl:value-of select="@title"/>
+    </a>
+    <xsl:if test="position() != last()">
+      <xsl:text> | </xsl:text>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="a:category" mode="categories">
+    <xsl:value-of select="@term"/>
+    <xsl:if test="position() != last()">
+      <xsl:text> | </xsl:text>
+    </xsl:if>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/bookmark.xsl b/Src/Portal/htdocs/MobylePortal/xsl/bookmark.xsl
new file mode 100644
index 0000000..7543aa9
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/bookmark.xsl
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+  
+  <xsl:include href="job.xsl" />
+  
+  <xsl:param name="dataId" />
+
+  <xsl:param name="sessionUrl" />  
+
+  <xsl:param name="inputModes" />  
+  
+  <xsl:template match="/">
+    <xsl:apply-templates select="//data[@id=$dataId]" />
+  </xsl:template>
+
+  <xsl:template match="data">
+    <fieldset class="bookmark" data-bookmarkpid="{@id}"
+      data-pid="{@id}"
+      data-format="{type/dataFormat/text()}" 
+      data-datatype="{type/datatype/class/text()}" 
+      data-biotype="{type/biotype/text()}" 
+      data-card="{type/card/text()}" 
+      data-inputmodes="{$inputModes}"
+      data-username="{userName/text()}">
+      <legend>
+        <xsl:value-of select="userName/text()"/>
+        <xsl:if test="type/dataFormat/text()">
+          (<xsl:value-of select="type/dataFormat/text()"/>)
+        </xsl:if>
+        <a target="_blank" class="saveFileLink" title="save this file" alt="save this file" href="{$sessionUrl}/{@id}?save"> save </a>
+      </legend>
+      <div class="parameter" 
+        data-parametername="" 
+        data-datatype-superclass="" 
+        data-format="{type/dataFormat/text()}" 
+        data-datatype="{type/datatype/class/text()}" 
+        data-biotype="{type/biotype/text()}" 
+        data-card="{type/card/text()}" data-inputmodes="{$inputModes}">
+        <span data-filename="{@id}" data-src="{$sessionUrl}/{@id}">
+          <xsl:choose>  
+            <xsl:when test="@size<=$previewDataLimit">
+                <xsl:text disable-output-escaping="yes"><![if !IE]></xsl:text>
+              <object data="{$sessionUrl}/{@id}">
+                This file cannot be displayed in your browser. Click on the "save" link to download it.                  
+              </object>
+              <xsl:text disable-output-escaping="yes"><![endif]></xsl:text>              
+              <xsl:text disable-output-escaping="yes"><!--[if IE]></xsl:text>
+              <iframe src="{$sessionUrl}/{@id}">
+                This file cannot be displayed in your browser. Click on the "save" link to download it.                  
+              </iframe>
+              <xsl:text disable-output-escaping="yes"><![endif]--></xsl:text>              
+            </xsl:when>
+            <xsl:otherwise>
+              <p class="commentText">
+                The file is too big to be safely displayed here (<xsl:value-of select="round(number(@size) div 1024)"/> KiB).
+                <a target="_blank" href="{$sessionUrl}/{@id}">Click here to display this result in a separate window.</a>
+              </p>
+            </xsl:otherwise>
+          </xsl:choose>
+        </span>
+      </div>
+    </fieldset>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/clean_import.xsl b/Src/Portal/htdocs/MobylePortal/xsl/clean_import.xsl
new file mode 100644
index 0000000..8b37f2e
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/clean_import.xsl
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	ident.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	<!-- This stylesheet contains the XSL Identity Transform used by many other stylesheets -->
+
+	<xsl:include href="ident.xsl" />
+	
+	<xsl:template match="/">
+		<xsl:apply-templates select="@*|node()|text()" />
+	</xsl:template>
+
+	<xsl:template match="interface[@generated='true']" />
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/form.xsl b/Src/Portal/htdocs/MobylePortal/xsl/form.xsl
new file mode 100644
index 0000000..72bc7ba
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/form.xsl
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	form.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+
+	<!-- this uri is the identifier of the program xml, i.e., the url of the xml definition on its execution server --> 
+	<xsl:param name="programUri" />	
+
+	<!-- this uri is the identifier of the program xml, i.e., the url of the xml definition on its execution server --> 
+	<xsl:param name="programPID" /> 
+
+	<xsl:variable name="server" select="substring-before($programPID,'.')" />
+	
+	<xsl:include href="ident.xsl" />
+
+	<xsl:template match="/|comment()|processing-instruction()">
+		<xsl:copy>
+			<!-- go process children (applies to root node only) -->
+			<xsl:apply-templates/>
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="*">
+		<xsl:element name="{local-name()}">
+			<!-- go process attributes and children -->
+			<xsl:apply-templates select="@*|node()"/>
+		</xsl:element>
+	</xsl:template>
+	
+	<xsl:template match="@*">
+		<xsl:attribute name="{local-name()}">
+			<xsl:value-of select="."/>
+		</xsl:attribute>
+	</xsl:template>	
+
+	<xsl:template match="/">
+		<form action="session_job_submit.py" id="{$programPID}" class="program">
+			<table class="header">
+				<tr>
+					<td style="width: 60%">
+						<xsl:apply-templates select="*/head" mode="serviceHeader"/>						
+					</td>
+					<td>
+						<xsl:apply-templates select="*/head" mode="formHeader"/>						
+					</td>
+				</tr>
+			</table>
+			<xsl:apply-templates select="*/head/doc/comment" mode="ajaxTarget"/>
+			<xsl:apply-templates select="*/flow"/>
+			<xsl:apply-templates select="*/head/interface[@type='form']/*" />
+			<xsl:apply-templates select="*/head" mode="serviceFooter"/>
+		</form>
+	</xsl:template>
+
+	<xsl:template match="flow">
+		<xsl:text disable-output-escaping="yes"><![if !IE]></xsl:text>
+			<fieldset class="minimizable">
+				<legend>Workflow details</legend>
+				<center>
+					<object data="workflow_layout.py?id={$programPID}">
+						<iframe width="100%" src="workflow_layout.py?id={$programPID}" />
+					</object>
+				</center>
+			</fieldset>
+		<xsl:text disable-output-escaping="yes"><![endif]></xsl:text>
+	</xsl:template>
+
+	<xsl:template match="head" mode="formHeader">
+		<span class="formCtrl">
+			<input type="submit" value="Run" />
+			<input type="reset" value="Reset" />
+			<xsl:if test="/program">
+				<input type="hidden" name="programName" value="{$programUri}" />
+			</xsl:if>
+			<xsl:if test="/workflow">
+				<input type="hidden" name="workflowUrl" value="{$programUri}" />
+			</xsl:if>
+		</span>
+	</xsl:template>	
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/form_graph.xsl b/Src/Portal/htdocs/MobylePortal/xsl/form_graph.xsl
new file mode 100644
index 0000000..c54b670
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/form_graph.xsl
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	form_graph.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
+
+	<xsl:include href="ident.xsl" />
+
+	<xsl:template match="/">
+		<xsl:apply-templates select="workflow/head/interface[@type='graph']/svg:svg"/>
+	</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/graphviz_simplify.xsl b/Src/Portal/htdocs/MobylePortal/xsl/graphviz_simplify.xsl
new file mode 100644
index 0000000..aea06b6
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/graphviz_simplify.xsl
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	form_graph.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
+
+	<xsl:include href="ident.xsl" />
+
+	<xsl:template match="svg:*/@style" />
+	<xsl:template match="svg:*/@stroke" />
+	<xsl:template match="svg:*/@font-family" />
+	<xsl:template match="svg:*/@font-size" />
+	<xsl:template match="svg:svg/@width" />
+	<xsl:template match="svg:svg/@height" />	
+	
+	<xsl:template match="interface[@type='graph']/svg:svg">
+		<xsl:copy>
+			<xsl:apply-templates select="@*" />
+			<xsl:variable name="w"><xsl:value-of select="number(substring-before(@width,'pt'))"/></xsl:variable>
+			<xsl:variable name="h"><xsl:value-of select="number(substring-before(@height,'pt'))"/></xsl:variable>
+			<xsl:variable name="width">
+				<xsl:choose>
+					<xsl:when test="$w < 800">800</xsl:when>
+					<xsl:otherwise><xsl:value-of select="$w"/></xsl:otherwise>
+				</xsl:choose>
+			</xsl:variable>
+			<xsl:attribute name="viewBox"><xsl:text>0 0 </xsl:text><xsl:value-of select="$width" /><xsl:text> </xsl:text><xsl:value-of select="$h" /></xsl:attribute>
+			<style>
+				g{
+					fill: white; stroke:black; font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px;
+				}
+
+				text{
+					fill: black; stroke: none;
+				}				
+
+                .jobstatus.building text{
+                    fill: darkorange;
+                }
+                
+                .jobstatus.submitted text, .jobstatus.pending text, .jobstatus.running text, .jobstatus.hold text{
+                    fill: darkorange;
+                }
+                
+                .jobstatus.error text, .jobstatus.killed text{
+                    fill: red;
+                }
+                
+                .jobstatus.finished text{
+                    fill: green;
+                }
+                
+                a.jobstatus:hover{
+                	stroke-width: 2px;
+                }
+            </style>
+			<xsl:apply-templates select="node()|text()" />
+		</xsl:copy> 
+	</xsl:template>	
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/ident.xsl b/Src/Portal/htdocs/MobylePortal/xsl/ident.xsl
new file mode 100644
index 0000000..4d4f578
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/ident.xsl
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	ident.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	<!-- This stylesheet contains the XSL Identity Transform used by many other stylesheets -->
+
+	<xsl:template match="@*|node()|text()" priority="-1">
+		<xsl:call-template name="nodeCopy" />
+	</xsl:template>
+
+	<xsl:template name="nodeCopy">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()|text()" />
+		</xsl:copy>
+	</xsl:template>
+
+	<xsl:template match="type" mode="label">
+		<!-- processing parameter type to display it in the parameter label if relevant (i.e., "bio-related" type) -->
+		<xsl:variable name ="dataType">
+			<xsl:choose>
+				<xsl:when test="datatype/superclass"><xsl:value-of select="datatype/superclass"/></xsl:when>
+				<xsl:otherwise><xsl:value-of select="datatype/class"/></xsl:otherwise>
+			</xsl:choose>
+		</xsl:variable>		
+		<xsl:if test="not(($dataType = 'Choice') or ($dataType = 'MultipleChoice') or ($dataType = 'Boolean') or ($dataType = 'Integer') or ($dataType = 'Float') or ($dataType = 'String') or ($dataType = 'Filename') or ($dataType = 'Binary'))">
+			<xhtml:i>
+				(<xsl:if test="biotype">
+					<xsl:for-each select="biotype">
+						<xsl:value-of select="text()" />
+						<xsl:if test="position()!= last()"><xsl:text> or </xsl:text></xsl:if>
+					</xsl:for-each>
+					<xsl:text> </xsl:text>
+				</xsl:if>
+				<xsl:value-of select="datatype/class/text()" />)
+			</xhtml:i>
+		</xsl:if>
+	</xsl:template>	
+
+	<xsl:attribute-set name="param">
+		<!-- 
+			<xsl:attribute name="id"><xsl:value-of select="//parameter[(name/text()=current()/@data-parametername)]/name/text()"/></xsl:attribute>
+		-->
+		<xsl:attribute name="class">
+			<!-- "parameter" class defines the outer element for a parameter that groups a set of controls -->
+			<xsl:text>parameter </xsl:text>
+			<xsl:if test="//parameter[(name/text()=current()/@data-parametername) and @ismandatory and (@ismandatory='1' or @ismandatory='true')]">
+				mandatory
+				<xsl:if test="//parameter[(name/text()=current()/@data-parametername) and ancestor-or-self::*/precond]"><xsl:text> conditional</xsl:text></xsl:if>
+			</xsl:if>
+		</xsl:attribute>
+		<xsl:attribute name="title">
+			<!-- help text for mandatory parameters -->
+			<xsl:if test="//parameter[(name/text()=current()/@data-parametername) and @ismandatory and (@ismandatory='1' or @ismandatory='true')]">
+				<xsl:text>This parameter is mandatory</xsl:text><xsl:if test="//parameter[(name/text()=current()/@data-parametername) and ancestor-or-self::*/precond]"><xsl:text> under certain conditions.</xsl:text></xsl:if>
+			</xsl:if>
+		</xsl:attribute>
+		<xsl:attribute name="data-issimple"><xsl:value-of select="boolean(//parameter[name/text()=current()/@data-parametername and (@issimple='1' or @issimple='true')])" /></xsl:attribute>
+		<xsl:attribute name="data-ismultiple"><xsl:value-of select="starts-with(//parameter[name/text()=current()/@data-parametername]/type/datatype/class/text(),'Multiple')" /></xsl:attribute>
+		<xsl:attribute name="data-default-value"><xsl:value-of select="//parameter[name/text()=current()/@data-parametername]/vdef/value/text()" /></xsl:attribute>			
+		<xsl:attribute name="data-datatype"><xsl:value-of select="//parameter[name/text()=current()/@data-parametername]/type/datatype/class/text()" /></xsl:attribute>
+		<xsl:attribute name="data-datatype-superclass"><xsl:value-of select="//parameter[name/text()=current()/@data-parametername]/type/datatype/superclass/text()" /></xsl:attribute>		
+		<xsl:attribute name="data-biotype">
+			<xsl:for-each select="//parameter[name/text()=current()/@data-parametername]/type/biotype">
+				<xsl:value-of select="text()" /><xsl:text> </xsl:text>
+			</xsl:for-each>
+		</xsl:attribute>
+		<xsl:attribute name="data-card">
+			<xsl:value-of select="//parameter[name/text()=current()/@data-parametername]/type/card/text()" />
+		</xsl:attribute>
+		<xsl:attribute name="data-formats">
+			<xsl:for-each select="//parameter[name/text()=current()/@data-parametername]/type//dataFormat">
+				<xsl:value-of select="text()" /><xsl:text> </xsl:text>
+			</xsl:for-each>
+		</xsl:attribute>
+	</xsl:attribute-set>
+	
+	<xsl:template match="head" mode="serviceHeader">
+			<xhtml:h1>
+				<xsl:if test="package/doc/title/text()">
+					<xsl:value-of select="package/doc/title/text()" />
+					<xsl:if test="package/version">
+						<xsl:text> </xsl:text>
+						<xsl:value-of select="package/version/text()"/>
+					</xsl:if>:
+				</xsl:if>
+				<xsl:value-of select="doc/title/text()" />
+				<xsl:if test="$server">@<xsl:value-of select="$server"/></xsl:if>
+				<xsl:if test="version">
+					<xsl:text> </xsl:text>
+					<xsl:value-of select="version/text()"/>
+				</xsl:if>
+				<xsl:apply-templates select="doc/comment" mode="ajaxLink"/>
+			</xhtml:h1>
+			<!-- Description -->
+			<xhtml:h2>
+				<xsl:copy-of select="doc/description/text/text()" />
+			</xhtml:h2>
+	</xsl:template>
+
+	<xsl:template match="head" mode="serviceFooter">
+		<xhtml:div class="info">
+			<xsl:if test="package/doc/description/text/text()">
+				<xhtml:h4><xsl:copy-of select="package/doc/description/text/text()" /></xhtml:h4>
+			</xsl:if>
+			<xsl:apply-templates select="package/doc/reference" mode="serviceFooter"/>
+			<xsl:apply-templates select="doc/reference" mode="serviceFooter"/>
+			<xsl:apply-templates select="package/doc/authors" mode="serviceFooter"/>
+			<xsl:apply-templates select="doc/authors" mode="serviceFooter"/>
+			<xsl:apply-templates select="package/doc/doclink" mode="serviceFooter"/>
+			<xsl:apply-templates select="doc/doclink" mode="serviceFooter"/>
+		</xhtml:div>
+	</xsl:template>
+	
+	<xsl:template match="reference" mode="serviceFooter">
+		<!-- Reference Link -->
+		<xhtml:div class="reference">
+			<xsl:choose>
+				<xsl:when test="@url or @doi">
+					<xhtml:a target="_blank">
+						<xsl:choose>
+							<xsl:when test="@url">
+								<xsl:attribute name="href"><xsl:value-of select="@url"/></xsl:attribute>
+							</xsl:when>
+							<xsl:when test="@doi">
+								<xsl:attribute name="href">http://dx.doi.org/<xsl:value-of select="@doi"/></xsl:attribute>
+							</xsl:when>
+						</xsl:choose>
+						<xsl:copy-of select="child::node()" />
+					</xhtml:a>
+				</xsl:when>
+				<xsl:otherwise>
+					<xsl:copy-of select="child::node()" />					
+				</xsl:otherwise>
+			</xsl:choose>
+		</xhtml:div>		
+	</xsl:template>
+	
+	<xsl:template match="authors" mode="serviceFooter">
+		<xhtml:div class="authors"><xsl:copy-of select="child::node()" /></xhtml:div>
+	</xsl:template>
+	
+	<xsl:template match="doclink" mode="serviceFooter">
+		<xhtml:a target="_blank">
+			<xsl:attribute name="href">
+				<xsl:value-of select="." />
+			</xsl:attribute>
+			<xsl:value-of select="." />
+		</xhtml:a>
+	</xsl:template>
+
+	<xsl:template match="comment" mode="ajaxLink">
+		<xhtml:a href="#{generate-id(..)}::comment" class="blindLink commentToggle" onclick="if (typeof portal=='undefined'){{var target=document.getElementById(this.getAttribute('href').substr(1)); target.style.display=(target.style.display=='none') ? '':'none';}}" title="click to expand/collapse contextual help">?</xhtml:a>        
+	</xsl:template>
+	
+	<xsl:template match="comment" mode="ajaxTarget">
+		<xsl:choose>
+			<xsl:when test="text">
+				<xhtml:div id="{generate-id(..)}::comment" class="commentText" style="display:none" mode="ajaxTarget">
+					<xsl:apply-templates select="text" mode="ajaxTarget"/>
+					<xsl:apply-templates select="../example" mode="exampleInclude"/>
+				</xhtml:div>
+			</xsl:when>
+			<xsl:otherwise>
+				<xhtml:div id="{generate-id(..)}::comment" class="commentText" style="display:none" mode="ajaxTarget">
+					<xsl:copy-of select="xhtml:*|text()" />     
+					<xsl:apply-templates select="../example" mode="exampleInclude"/>
+				</xhtml:div>
+			</xsl:otherwise>            
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template match="comment" mode="ajaxTargetJob">
+		<xsl:choose>
+			<xsl:when test="text">
+				<xhtml:div id="{generate-id(..)}::comment" class="commentText" style="display:none" mode="ajaxTarget">
+					<xsl:apply-templates select="text" mode="ajaxTarget"/>
+				</xhtml:div>
+			</xsl:when>
+			<xsl:otherwise>
+				<xhtml:div id="{generate-id(..)}::comment" class="commentText" style="display:none" mode="ajaxTarget">
+					<xsl:copy-of select="xhtml:*|text()" />     
+				</xhtml:div>
+			</xsl:otherwise>            
+		</xsl:choose>
+	</xsl:template>	
+
+	<xsl:template match="comment/text" mode="ajaxTarget">
+		<xhtml:div><xsl:apply-templates select="text()" /></xhtml:div>
+	</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/job.xsl b/Src/Portal/htdocs/MobylePortal/xsl/job.xsl
new file mode 100644
index 0000000..0ef70a4
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/job.xsl
@@ -0,0 +1,459 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+  
+  <xsl:output method="html" indent="yes" />	
+
+  <xsl:include href="remove_ns.xsl" />
+
+  <xsl:param name="jobPID" ></xsl:param>  
+
+  <xsl:param name="servicePID" ></xsl:param>  
+
+  <xsl:param name="isIE" >False</xsl:param>  
+
+  <xsl:param name="previewDataLimit" >99999999999999999999999999</xsl:param>  
+  
+  <xsl:variable name="job" select="/"/>
+  
+  <xsl:variable name="jobId" select="$job/jobState/id"/>
+
+  <xsl:variable name="statusValue">
+    <xsl:choose>
+      <xsl:when test="/jobState">
+        <xsl:choose>
+          <xsl:when test="/jobState/status">
+            <xsl:value-of select="/jobState/status/value/text()" />
+          </xsl:when>      
+          <xsl:otherwise>
+            <xsl:value-of select="document(concat($job/jobState/id,'/mobyle_status.xml'))/status/value/text()" />        
+          </xsl:otherwise>
+        </xsl:choose>        
+      </xsl:when>
+      <xsl:otherwise></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="statusMessage">
+    <xsl:choose>
+      <xsl:when test="/jobState">
+        <xsl:choose>
+          <xsl:when test="/jobState/status">
+            <xsl:value-of select="/jobState/status/message/text()" />
+          </xsl:when>      
+          <xsl:otherwise>
+            <xsl:value-of select="document(concat($job/jobState/id,'/mobyle_status.xml'))/status/message/text()" />        
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>  
+
+  <xsl:template match="/">
+    <xsl:choose>
+      <xsl:when test="$jobPID!=''">
+        <xsl:apply-templates select="/jobState/program|/jobState/workflow" />              
+      </xsl:when>
+      <xsl:otherwise>
+        <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+          <!-- TODO update all CSS links -->
+          <head>
+          <!-- here we compute the path to the css stylesheets dir, based on the href pseudo-attribute of the XSL processing instruction -->
+          <xsl:variable name="xslUri" select="translate(substring-before(substring-after(processing-instruction('xml-stylesheet'), 'href='), ' '),'"','')" />
+          <xsl:variable name="cssBase" select="concat(substring-before($xslUri, '/xsl'),'/css/')" />
+          <title>Mobyle job report for <xsl:value-of select="$jobId"/></title>
+          <style type="text/css">
+            @import "<xsl:value-of select='$cssBase' />mobyle.css"; 
+            @import "mobyle.css"; 
+            .minimizable.minimized  > legend{
+                background-image: none;
+                display:inherit;
+            }
+
+            .minimizable.minimized > *{
+                display:block;
+            }
+          </style>
+          </head>
+          <body>
+            <xsl:apply-templates select="/jobState/program|/jobState/workflow" />              
+          </body>
+        </html>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>    
+    
+
+  <xsl:template match="/jobState/program|/jobState/workflow">    
+    <div class="job"
+      data-jobid="{$jobId}"
+      data-jobpid="{$jobPID}"
+      data-pid="{$jobPID}"
+      data-servicepid="{$servicePID}"
+      data-servicename="{$job/jobState/name/text()}" 
+      data-servicelocalname="{/jobState/*/head/name/text()}" 
+      data-jobdate="{$job/jobState/date/text()}" 
+      data-jobstatus="{$statusValue}"
+      data-jobmessage="{$statusMessage}">
+        <fieldset class="job_controls">
+          <legend class="jobstatus {$statusValue}" title="status: {$statusValue}">
+            <xsl:value-of select="$jobId" />
+          </legend>
+          <xsl:if test="$statusMessage!=''">
+            <div class="info" style="white-space:pre-wrap;">
+              <xsl:value-of select="$statusMessage" />
+            </div>            
+          </xsl:if>
+          <xsl:if test="$jobPID!=''"><!-- only display if jobPID available, i.e., we are working within the portal -->
+            <xsl:if test="not($job/jobState/workflowID)">
+              <xsl:if test="$statusValue!='finished' and $statusValue!='error' and $statusValue!='killed'">
+                <a href="#" class="refresh_link"><button type="button">update</button></a>          
+              </xsl:if>
+              <a href="#user::help::{$jobId}" class="modalLink"><button type="button">get help</button></a>               
+              <a href="#forms::{$servicePID}" class="link"><button type="button">back to form</button></a>                
+              <a href="#user::jobremove::{$jobPID}" class="modalLink"><button type="button">remove job</button></a>                
+            </xsl:if>
+            <xsl:if test="$statusValue='finished' or $statusValue='error' or $statusValue='killed'">
+              <a href="{$jobId}/{/jobState/*/head/name/text()}_{substring-after($jobId,concat(/jobState/*/head/name/text(),'/'))}.zip">
+                <button type="button">download</button>
+              </a>
+            </xsl:if>
+          </xsl:if>
+          <xsl:if test="/jobState/program/head/progressReport">
+            <fieldset class="minimizable">
+              <legend>
+                <xsl:choose>
+                  <xsl:when test="/jobState/program/head/progressReport/@prompt">
+                    <xsl:value-of select="/jobState/program/head/progressReport/@prompt"/>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    job progress report
+                  </xsl:otherwise>
+                </xsl:choose>
+              </legend>
+              <textarea class="progressReport" readonly="readonly" data-url="{$job/jobState/id}/{/jobState/program/head/progressReport/text()}" />
+            </fieldset>
+          </xsl:if>
+        </fieldset>
+      <xsl:if test="$job/jobState/data/output">
+        <fieldset class="job_results">
+          <legend>results</legend>
+          <xsl:apply-templates select="/jobState/*/head/interface[@type='job_output']/*"/> 
+        </fieldset>
+      </xsl:if>
+      <fieldset class="job_inputs">
+        <legend>parameters</legend>
+        <xsl:apply-templates select="/jobState/*/head/interface[@type='job_input']/*"/>
+      </fieldset>
+      <xsl:if test="$job/jobState/commandLine or $job/jobState/paramFiles/file">
+        <fieldset class="job_details minimizable minimized">
+          <legend>job execution</legend>
+          <div>
+          <xsl:apply-templates select="$job/jobState/commandLine"/>          
+          <xsl:apply-templates select="$job/jobState/paramFiles/file"/>
+          </div>
+        </fieldset>
+      </xsl:if>
+      <xsl:if test="$job/jobState/jobLink">
+        <xsl:apply-templates select="/jobState/workflow/flow"/>     
+      </xsl:if>   
+    </div>
+  </xsl:template>
+
+  <xsl:template match="flow">
+    <xsl:if test="$jobPID!=''">
+      <xsl:text disable-output-escaping="yes"><![if !IE]></xsl:text>
+        <fieldset class="job_details minimizable">
+          <legend>workflow details</legend>
+          <div>
+            <center>
+              <object class="workflow_graph" data="workflow_job_layout.py?id={$jobPID}">
+                <iframe width="100%" class="workflow_graph" src="workflow_job_layout.py?id={$jobPID}" />
+              </object>
+            </center>
+          </div>
+        </fieldset>
+      <xsl:text disable-output-escaping="yes"><![endif]></xsl:text>              
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="*[@data-paragraphname]">
+    <!-- do not display a paragraph unless there are data to be displayed -->
+    <xsl:if test="$job/jobState/data//*[name/text()=current()//@data-parametername]">
+      <xsl:element name="{local-name(.)}">
+        <xsl:apply-templates select="@*|node()" />
+      </xsl:element>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="*[@data-parametername]">
+    <!-- do not display a parameter unless there are data to be displayed -->
+    <xsl:variable name='parametername' select="@data-parametername"/>
+    <xsl:if test="$job/jobState/data/*[parameter/name=$parametername]">
+      <xsl:element name="{local-name(.)}" use-attribute-sets="param">
+        <!-- override data-format for dynamically-specified version -->
+        <xsl:attribute name="data-inputmodes">result</xsl:attribute>
+        <xsl:attribute name="data-format">
+          <xsl:apply-templates select="//parameter[name/text()=$parametername]/type/dataFormat" mode="dataFormats" />
+        </xsl:attribute> 
+        <xsl:attribute name="title">
+          <xsl:value-of select="concat(//parameter[name/text()=$parametername]/type/biotype/text(), ' ',//parameter[name/text()=$parametername]/type/datatype/class)"/>
+        </xsl:attribute>
+        <xsl:apply-templates select="@*" />
+	<span class="prompt"><xsl:value-of select="//parameter[name/text()=$parametername]/prompt/text()" /></span>
+        <xsl:apply-templates select="xhtml:*" mode="pre"/>
+        <!-- this part handles the display of results or parameter data which have no predefined custom <interface> tag -->        
+        <xsl:apply-templates select="$job/jobState/data/*[parameter/name=$parametername]/file" mode="dataProcessing"/>
+        <xsl:apply-templates select="$job/jobState/data/*[parameter/name=$parametername]/formattedFile" mode="dataProcessing"/>
+        <xsl:apply-templates select="$job/jobState/data/*[parameter/name=$parametername]/value" mode="dataProcessing"/>          
+        </xsl:element>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="xhtml:*[not(@*[ancestor::*[@data-parametername] and (contains(.,'data-url') or contains(.,'data-value'))]|text()[ancestor::*[@data-parametername] and (contains(.,'data-url') or contains(.,'data-value'))])]|@*|text()" mode="pre">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()|text()" mode="pre" />
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="*[@*[ancestor::*[@data-parametername] and (contains(.,'data-url') or contains(.,'data-value'))]|text()[ancestor::*[@data-parametername] and (contains(.,'data-url') or contains(.,'data-value'))]]" mode="customInterface">
+    <!-- custom <interface> tag handling: setting the parameter name as a variable and then looping over all the values -->
+    <xsl:variable name='parametername' select="ancestor-or-self::*[@data-parametername]/@data-parametername"/>
+    <xsl:variable name='current' select="."/>
+    <xsl:for-each select="$job/jobState/data/*[parameter/name=$parametername]/file/text()">
+      <xsl:variable name="result-value" select="."/>      
+      <xsl:element name="{local-name($current)}">
+        <xsl:apply-templates select="$current/@*|$current/node()|$current/text()" >
+          <xsl:with-param name="parametername" select="$parametername" />
+          <xsl:with-param name="result-value" select="$result-value" />
+        </xsl:apply-templates>
+      </xsl:element>
+      <a target="_blank" class="saveFileLink" title="save this file" alt="save this file" href="{concat($jobId,'/',$result-value)}?save"> save </a>
+    </xsl:for-each>
+    <xsl:for-each select="$job/jobState/data/*[parameter/name=$parametername]/value/text()">
+      <xsl:element name="{local-name($current)}">
+        <xsl:variable name="result-value" select="."/>      
+        <xsl:apply-templates select="$current//@*|$current//node()|$current//text()" >
+          <xsl:with-param name="parametername" select="$parametername" />
+          <xsl:with-param name="result-value" select="$result-value" />
+        </xsl:apply-templates>
+      </xsl:element>
+    </xsl:for-each>
+  </xsl:template>
+
+  <xsl:template match="@*[contains(.,'data-url') or contains(.,'data-value')]" mode="customInterface">
+    <!-- custom <interface> tag handling in attribute values: replacing 'data-url' with the actual url of the data and 'data-value' with its actual value (=file name for files) -->
+    <xsl:param name="parametername" />
+    <xsl:param name="result-value" />
+    <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
+      <xsl:choose>
+        <xsl:when test="contains(.,'data-url')">
+          <xsl:value-of select="substring-before(.,'data-url')"/>
+          <xsl:value-of select="$jobId" />/<xsl:value-of select="$result-value" />
+          <xsl:value-of select="substring-after(.,'data-url')"/>          
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="substring-before(.,'data-value')"/>
+          <xsl:value-of select="$result-value"/>
+          <xsl:value-of select="substring-after(.,'data-value')"/>                    
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:attribute>          
+  </xsl:template>
+
+  <xsl:template match="text()[contains(.,'data-url') or contains(.,'data-value')]" mode="customInterface">
+    <!-- custom <interface> tag handling in text: replacing 'data-url' with the actual url of the data and 'data-value' with its actual value (=file name for files) -->
+    <xsl:param name="parametername" />
+    <xsl:param name="result-value" />
+    <xsl:choose>
+      <xsl:when test="contains(.,'data-url')">
+        <xsl:value-of select="substring-before(.,'data-url')"/>
+        <xsl:value-of select="$jobId" />/<xsl:value-of select="$result-value" />
+        <xsl:value-of select="substring-after(.,'data-url')"/>          
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="substring-before(.,'data-value')"/>
+        <xsl:value-of select="$result-value"/>
+        <xsl:value-of select="substring-after(.,'data-value')"/>                    
+      </xsl:otherwise>
+    </xsl:choose>         
+  </xsl:template>
+
+	<xsl:template match="@*|node()|text()" priority="-1" mode="customInterface">
+	    <xsl:param name="parametername" />
+	    <xsl:param name="result-value" />
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()|text()" mode="customInterface">
+			  <xsl:with-param name="parametername" select="$parametername" />
+              <xsl:with-param name="result-value" select="$result-value" />
+			</xsl:apply-templates>
+		</xsl:copy>
+	</xsl:template>
+	
+  <xsl:template match="dataFormat/text()" mode="dataFormats">
+    <xsl:copy-of select="normalize-space(.)"/>
+  </xsl:template>
+
+  <xsl:template match="dataFormat/ref" mode="dataFormats">
+    <xsl:variable name="ref"><xsl:value-of select="@param"/></xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$job/jobState/data/*[parameter/name=$ref]/value">
+        <xsl:value-of select="normalize-space($job/jobState/data/*[parameter/name=$ref]/value)"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="normalize-space(//parameter[name/text()=$ref]/vdef/value)"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="dataFormat/test" mode="dataFormats">
+    <xsl:variable name="ref"><xsl:value-of select="@param"/></xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$job/jobState/data/*[parameter/name=$ref]/value">
+        <xsl:variable name="value" select="$job/jobState/data/*[parameter/name=$ref]/value"/>
+        <xsl:choose>
+          <xsl:when test="@eq and @eq=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@ne and @ne!=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@lt and @lt<$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@le and @le<=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@gt and @gt>$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@ge and @ge>=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="value" select="//parameter[name/text()=$ref]/vdef/value"/>
+        <xsl:choose>
+          <xsl:when test="@eq and @eq=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@ne and @ne!=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@lt and @lt<$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@le and @le<=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@gt and @gt>$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+          <xsl:when test="@ge and @ge>=$value"><xsl:apply-templates select="child::node()" mode="dataFormats"/></xsl:when>
+        </xsl:choose>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="value[//parameter[name/text()=current()/../parameter/name/text()]/type/datatype/class[text()='Choice' or text()='MultipleChoice']]" mode="dataProcessing">
+    <xsl:text> </xsl:text><span class="parameter_value"><xsl:value-of select="//parameter[name/text()=current()/../parameter/name/text()]//label[../value/text()=current()/text()]/text()"/></span>
+  </xsl:template>
+
+  <xsl:template match="value" mode="dataProcessing">
+    <xsl:text> </xsl:text><span class="parameter_value"><xsl:value-of select="text()"/></span>
+  </xsl:template>	
+
+  <xsl:template match="file/fmtProgram" mode="dataProcessing"></xsl:template>
+
+  <xsl:template name="fileDisplay">
+    <xsl:variable name='resultId' select="concat($jobId,'/',text())"/>
+    <fieldset>
+      <xsl:attribute name="class">
+        minimizable <xsl:if test="name(..)='input'">minimized</xsl:if>
+      </xsl:attribute>
+      <legend>
+        <xsl:if test="local-name(.)='formattedFile'">
+          reformatted file produced by <xsl:value-of select="../fmtProgram/text()"></xsl:value-of><xsl:text>: </xsl:text>
+        </xsl:if>
+        <xsl:value-of select="text()"/>
+        <xsl:choose>
+          <!-- data format is displayed to the user -->
+          <xsl:when test="@fmt">
+            (<xsl:value-of select="@fmt"/>)            
+          </xsl:when>
+          <xsl:when test="not(@fmt) and //parameters/parameter[name/text()=current()/../parameter/name/text()]/type/dataFormat">
+            (<xsl:apply-templates select="//parameters//parameter[name/text()=current()/../parameter/name/text()]/type/dataFormat" mode="dataFormats" />)            
+          </xsl:when>
+        </xsl:choose>
+        <a target="_blank" class="saveFileLink" title="save this file" alt="save this file" href="{$resultId}?save"> save </a>
+      </legend>
+      <span>      
+        <span data-filename="{text()}" data-src="{$resultId}">
+          <xsl:choose>
+            <xsl:when test="//parameter[not(ancestor-or-self::jobState) and name/text()=current()/../parameter/name]/interface">
+              <xsl:apply-templates select="//parameter[not(ancestor-or-self::jobState) and name/text()=current()/../parameter/name]/interface" />
+            </xsl:when>     
+            <xsl:when test="count(//file)+count(//formattedFile)>=20">
+              <p class="commentText">
+                This file cannot be displayed because too many have been produced by this job.
+                <a target="_blank" href="{$resultId}">Click here to display this result in a separate window.</a>
+              </p>
+            </xsl:when>
+            <xsl:when test="@size<=$previewDataLimit">
+              <xsl:choose>
+	            <xsl:when test="//parameter[name/text()=current()/../parameter/name]/interface[@type='job_output' and not(@generated='true')]">
+	              <xsl:apply-templates select="//parameter[name/text()=current()/../parameter/name]/interface[@type='job_output']/*" mode="customInterface">
+			      <xsl:with-param name="parametername" select="current()/../parameter/name/text()" />
+		              <xsl:with-param name="result-value" select="text()" />
+	              </xsl:apply-templates>
+	            </xsl:when>     
+                <xsl:when test="system-property('xsl:vendor')='Microsoft' or $isIE='True'">
+                  <iframe src="{$resultId}">
+                    This file cannot be displayed in your browser. Click on the "save" link to download it.                  
+                  </iframe>
+                </xsl:when>
+                <xsl:otherwise>
+                  <object data="{$resultId}">
+                    This file cannot be displayed in your browser. Click on the "save" link to download it.                  
+                  </object>
+                </xsl:otherwise>
+              </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+              <p class="commentText">
+                The file is too big to be safely displayed here (<xsl:value-of select="round(number(@size) div 1024)"/> KiB).
+                <a target="_blank" href="{$resultId}">Click here to display this result in a separate window.</a>
+              </p>
+            </xsl:otherwise>
+          </xsl:choose>
+        </span>
+      </span>
+    </fieldset>    
+  </xsl:template>
+
+  <xsl:template match="raw[parent::file]|formattedFile[parent::file]" mode="dataProcessing">
+    <xsl:call-template name="fileDisplay" />
+  </xsl:template>
+
+  <xsl:template match="file[not(raw or formattedFile)]" mode="dataProcessing">
+    <xsl:call-template name="fileDisplay" />
+  </xsl:template>
+
+  <xsl:template match="commandLine">
+    <fieldset>
+      <legend>Command line</legend>
+      <div><xsl:value-of select="text()" /></div>
+    </fieldset>
+  </xsl:template>
+  
+  <xsl:template match="commandLine">
+    <fieldset>
+      <legend>Command line</legend>
+      <div><xsl:value-of select="text()" /></div>
+    </fieldset>
+  </xsl:template>
+  
+  <xsl:template match="paramFiles/file">
+    <fieldset>
+      <legend>Parameters file:</legend>
+      <a href="{$jobId}/{text()}" target="_blank"><xsl:value-of select="text()"/></a>
+    </fieldset>
+  </xsl:template>
+
+  <xsl:template match="*[@class='commentText']">
+    <xsl:copy>
+      <xsl:apply-templates select="@*" />
+      <xsl:attribute name="id"><xsl:value-of select="concat(@id,'.',$jobPID)" /></xsl:attribute> 
+      <xsl:apply-templates select="node()|text()" />
+    </xsl:copy>    
+  </xsl:template>  
+
+  <xsl:template match="*[@class='blindLink commentToggle']">
+    <xsl:copy>
+      <xsl:apply-templates select="@*" />
+      <xsl:attribute name="href"><xsl:value-of select="concat(@href,'.',$jobPID)" /></xsl:attribute> 
+      <xsl:apply-templates select="node()|text()" />
+    </xsl:copy>    
+  </xsl:template>  
+  
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/job_graph.xsl b/Src/Portal/htdocs/MobylePortal/xsl/job_graph.xsl
new file mode 100644
index 0000000..4a7dd10
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/job_graph.xsl
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	job_graph.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" 
+	xmlns:xhtml="http://www.w3.org/1999/xhtml" 
+	xmlns:svg="http://www.w3.org/2000/svg"
+	xmlns:xlink="http://www.w3.org/1999/xlink">
+
+	<xsl:include href="ident.xsl" />
+
+	<xsl:param name="jobPID" />	
+
+	<xsl:template match="processing-instruction()">
+	</xsl:template>
+	
+	<xsl:template match="/jobState">
+		<xsl:apply-templates select="workflow/head/interface[@type='graph']/svg:svg"/>
+	</xsl:template>
+
+	<xsl:template match="svg:g[svg:title/text()=/jobState/jobLink/@taskRef]">
+		<xsl:variable name="subJobId" select="/jobState/jobLink[@taskRef=current()/svg:title/text()]/@jobId" />
+		<xsl:variable name="subJobDoc" select="document(concat($subJobId,'/index.xml'))" />
+		<xsl:variable name="subJobServer" select="/jobState/workflow/flow/task[@id=current()/svg:title/text()]/@server" />		
+		<xsl:variable name="subTaskPIDSuff" select="$subJobDoc/jobState/*/head/name/text()" />
+		<xsl:variable name="subTaskPID">
+			<xsl:if test="$subJobServer!=''"><xsl:value-of select="$subJobServer"/>.</xsl:if>
+			<xsl:value-of select="$subTaskPIDSuff"/>
+		</xsl:variable>
+		<xsl:variable name="subJobPID" select="concat($subTaskPID,'.',substring-after($subJobId,concat($subTaskPIDSuff,'/')))" />
+		<svg:a target="_top" xlink:href="#jobs::{$jobPID}::{$subJobPID}">
+			<xsl:variable name="statusValue">
+				<xsl:choose>
+					<xsl:when test="$subJobDoc/jobState/status">
+						<xsl:value-of select="/jobState/status/value/text()" />
+					</xsl:when>      
+					<xsl:otherwise>
+						<xsl:value-of select="document(concat($subJobId,'/mobyle_status.xml'))/status/value/text()" />        
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:variable>			
+			<xsl:attribute name="class">
+				<xsl:text>jobstatus </xsl:text>
+				<xsl:value-of select="$statusValue" />
+			</xsl:attribute>
+			<xsl:call-template name="nodeCopy" />
+		</svg:a>
+	</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/layout.xsl b/Src/Portal/htdocs/MobylePortal/xsl/layout.xsl
new file mode 100644
index 0000000..2488ded
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/layout.xsl
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+	layout.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"  xmlns:mb="http://www.mobyle.fr">
+
+	<xsl:namespace-alias stylesheet-prefix="mb" result-prefix="#default"/>	
+
+	<xsl:param name="type" />
+
+	<xsl:include href="ident.xsl" />	
+
+	<xsl:template match="head">
+		<xsl:if test="not(interface[@type=$type])">
+			<xsl:copy>
+				<xsl:apply-templates select="@*|node()" />
+				<xsl:if test="//interface[@type=$type]">
+					<mb:interface type="{$type}" generated="true">
+						<xsl:choose>
+							<xsl:when test="layout[@type='$type']">
+								<xsl:apply-templates select="layout[@type='$type']" mode="interfaceBuild" />
+							</xsl:when>
+							<xsl:otherwise>
+								<xsl:apply-templates select="/*/parameters/*" mode="interfaceBuild" />
+							</xsl:otherwise>
+						</xsl:choose>
+					</mb:interface>					
+				</xsl:if>
+			</xsl:copy>
+		</xsl:if>
+		<xsl:if test="interface[@type=$type]">
+			<xsl:copy-of select="."/>>
+		</xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="paragraph" mode="interfaceBuild">
+		<xsl:if test=".//interface[@type=$type]" >
+			<xhtml:fieldset class="minimizable">
+				<xsl:attribute name="data-paragraphname"><xsl:value-of select="name/text()"/></xsl:attribute>
+				<xhtml:legend><xsl:value-of select="prompt/text()" /></xhtml:legend>
+				<xhtml:div>
+				<xsl:choose>
+					<xsl:when test="layout">
+						<xsl:apply-templates select="layout" mode="interfaceBuild" />
+					</xsl:when>
+					<xsl:otherwise>
+						<xsl:apply-templates select="parameters" mode="interfaceBuild" />
+					</xsl:otherwise>
+				</xsl:choose>
+				</xhtml:div>
+			</xhtml:fieldset>
+		</xsl:if>
+	</xsl:template>
+
+	<xsl:template match="parameter" mode="interfaceBuild">
+		<xsl:apply-templates select="interface" mode="interfaceBuild" />
+	</xsl:template>	
+
+	<xsl:template match="interface/xhtml:*" mode="interfaceBuild">
+		<xsl:if test="../@type[.=$type]">
+			<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}" use-attribute-sets="parametername" >
+				<xsl:copy-of select="@*|*|text()"/>
+			</xsl:element>
+		</xsl:if>
+	</xsl:template>	
+
+	<xsl:template match="layout">
+	<xsl:copy-of select="." />
+	</xsl:template>
+	
+	<xsl:template match="layout" mode="interfaceBuild">
+		<xsl:choose>
+			<xsl:when test="name(..)='hbox'">
+				<xhtml:td>
+					<xhtml:table>
+						<xsl:apply-templates select="*" mode="interfaceBuild" />
+					</xhtml:table>
+				</xhtml:td>        
+			</xsl:when>
+			<xsl:when test="name(..)='vbox'">
+				<xhtml:tr>
+					<xhtml:td>
+						<xhtml:table>
+							<xsl:apply-templates select="*" mode="interfaceBuild" />
+						</xhtml:table>
+					</xhtml:td>        
+				</xhtml:tr>
+			</xsl:when>
+			<xsl:otherwise>
+				<xhtml:table>
+					<xsl:apply-templates select="*" mode="interfaceBuild" />
+				</xhtml:table>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:template match="hbox" mode="interfaceBuild">
+		<xhtml:tr>
+			<xsl:apply-templates select="*" mode="interfaceBuild" />
+		</xhtml:tr>
+	</xsl:template>
+	
+	<xsl:template match="vbox">
+		<xsl:apply-templates select="*" mode="interfaceBuild" />
+	</xsl:template>
+	
+	
+	<xsl:template match="box" mode="interfaceBuild">
+		<xsl:choose>
+			<xsl:when test="name(..)='hbox'">
+				<xhtml:td>
+					<xsl:apply-templates select="//parameter[name/text()=current()/text()]" mode="interfaceBuild" />
+				</xhtml:td>        
+			</xsl:when>
+			<xsl:when test="name(..)='vbox'">
+				<xhtml:tr>
+					<xhtml:td>
+						<xsl:apply-templates select="//parameter[name/text()=current()/text()]" mode="interfaceBuild" />
+					</xhtml:td>        
+				</xhtml:tr>
+			</xsl:when>      
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:attribute-set name="parametername">
+		<xsl:attribute name="data-{local-name(current()/../..)}name">
+			<xsl:value-of select="../../name/text()"/>
+		</xsl:attribute>
+	</xsl:attribute-set>	
+	
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/localize.xsl b/Src/Portal/htdocs/MobylePortal/xsl/localize.xsl
new file mode 100644
index 0000000..1ae50e7
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/localize.xsl
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+	localize.xsl stylesheet
+	Authors: Herv� M�nager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml"  xmlns:mb="http://www.mobyle.fr">
+
+	<xsl:namespace-alias stylesheet-prefix="mb" result-prefix="#default"/>	
+
+	<xsl:param name="localCategoriesUrl" />
+	
+	<xsl:variable name="localCategories" select="document($localCategoriesUrl)"/>
+
+	<xsl:include href="ident.xsl" />	
+
+	<xsl:template match="head|package">
+		<xsl:variable name="level">
+			<xsl:if test="local-name()='head'">program</xsl:if>
+			<xsl:if test="local-name()='package'">package</xsl:if>			
+		</xsl:variable>
+		<xsl:element name="{local-name()}">
+		<xsl:apply-templates select="@*|node()[local-name()!='category']|text()" />		
+		<xsl:if test="$localCategories//category[@name=current()/name/text() and @class=$level]">
+			<xsl:apply-templates select="$localCategories//category[@name=current()/name/text() and @class=$level]" />
+		</xsl:if>
+			<xsl:if test="not($localCategories//category[@name=current()/name/text() and @class=$level])">
+			<xsl:apply-templates select="category" />
+		</xsl:if>
+		</xsl:element>
+	</xsl:template>
+
+	<!-- remove localize.xml category element attributes so that the resulting file validates -->
+	<xsl:template match="category/@*"></xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/parameters.xsl b/Src/Portal/htdocs/MobylePortal/xsl/parameters.xsl
new file mode 100644
index 0000000..bee87b4
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/parameters.xsl
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- 
+	form_parameters.xsl stylesheet
+	Authors: Herv� M�nager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	
+	<xsl:include href="ident.xsl" />
+	
+	<xsl:template match="parameter[not(@isout) and not(@isstdout) and not(@ishidden) and not(ancestor-or-self::*[interface/@type='form'])]">
+		<xsl:copy>
+			<xsl:variable name ="dataType">
+				<xsl:choose>
+					<xsl:when test="type/datatype/superclass"><xsl:value-of select="type/datatype/superclass"/></xsl:when>
+					<xsl:otherwise><xsl:value-of select="type/datatype/class"/></xsl:otherwise>
+				</xsl:choose>
+			</xsl:variable>
+			<xsl:apply-templates select="@*|node()" />
+			<interface type='form' generated="true">
+				<xsl:choose>
+					<xsl:when test="($dataType = 'Choice') or ($dataType = 'MultipleChoice') or ($dataType = 'Boolean') or ($dataType = 'Integer') or ($dataType = 'Float') or ($dataType = 'String') or ($dataType = 'Filename')">
+						<xhtml:label>
+							<xsl:value-of select="prompt" />
+							<xsl:apply-templates select="." mode="controlChoice"/>
+						</xhtml:label>						
+					</xsl:when>
+					<xsl:otherwise>
+						<xhtml:fieldset>
+							<xhtml:legend><xsl:value-of select="prompt" /></xhtml:legend>
+							<xsl:apply-templates select="." mode="controlChoice"/>
+						</xhtml:fieldset>												
+					</xsl:otherwise>
+				</xsl:choose>
+			</interface>
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="parameter/name" mode="controlChoice">
+		<xsl:attribute name="name"><xsl:value-of select="text()"/></xsl:attribute>
+	</xsl:template>	
+
+	<xsl:template match="parameter" mode="controlChoice">
+		<!-- Textarea or Input controls -->
+		<xsl:variable name ="dataType">
+			<xsl:choose>
+				<xsl:when test="type/datatype/superclass"><xsl:value-of select="type/datatype/superclass"/></xsl:when>
+				<xsl:otherwise><xsl:value-of select="type/datatype/class"/></xsl:otherwise>
+			</xsl:choose>
+		</xsl:variable>
+		<xsl:variable name="inputElement">
+			<xsl:choose>
+				<xsl:when test="($dataType = 'Integer') or ($dataType = 'Float') or ($dataType = 'String') or ($dataType = 'Filename') or ($dataType = 'Binary')">input</xsl:when>
+				<xsl:otherwise>textarea</xsl:otherwise>
+			</xsl:choose>
+		</xsl:variable>
+		<xsl:variable name="inputType">
+			<xsl:choose>
+				<xsl:when test="($dataType = 'Integer') or ($dataType = 'Float') or ($dataType = 'String') or ($dataType = 'Filename')">text</xsl:when>
+				<xsl:when test="$dataType = 'Binary'">file</xsl:when>
+			</xsl:choose>			
+		</xsl:variable>
+		<xsl:element name="xhtml:{$inputElement}">
+			<xsl:apply-templates select="name" mode="controlChoice"/>
+			<xsl:if test="$inputElement='input'"><xsl:attribute name="type"><xsl:value-of select="$inputType"/></xsl:attribute></xsl:if>
+			<xsl:if test="$inputElement='textarea'"><xsl:attribute name="cols">60</xsl:attribute><xsl:attribute name="rows">7</xsl:attribute></xsl:if>
+			<xsl:choose>
+				<!-- Default value -->
+				<xsl:when test="$inputElement='input' and $inputType!='checkbox'"><xsl:attribute name="value"><xsl:value-of select="vdef/value/text()"/></xsl:attribute></xsl:when>
+				<xsl:when test="$inputElement='input' and $inputType='checkbox' and vdef/value/text()='1'"><xsl:attribute name="checked">checked</xsl:attribute></xsl:when>
+				<xsl:when test="$inputElement='textarea'">
+					<xsl:choose>
+						<xsl:when test="vdef and vdef/value/text()">
+							<xsl:value-of select="vdef/value/text()"/>
+						</xsl:when>
+						<xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
+					</xsl:choose>					
+				</xsl:when>
+			</xsl:choose>
+		</xsl:element>
+	</xsl:template>	
+
+	<xsl:template match="parameter[*[substring(local-name(.),2,4)='list'] or (type/datatype/superclass/text()='Boolean' or type/datatype/class/text()='Boolean')]" mode="controlChoice">
+		<!-- Select control for choices and booleans -->
+		<xhtml:select>
+			<xsl:if test="type/datatype/class='MultipleChoice'">
+				<xsl:attribute name="multiple">multiple</xsl:attribute>
+			</xsl:if>			
+			<xsl:apply-templates select="name" mode="controlChoice"/>
+			<xsl:apply-templates select="*[substring(local-name(.),2,4)='list']//value" mode="controlChoice"/>	
+			<xsl:if test="type/datatype/superclass/text()='Boolean' or type/datatype/class/text()='Boolean'">
+				<xsl:call-template name="selectOption">
+					<xsl:with-param name="value" select="1" />
+					<xsl:with-param name="label" >Yes</xsl:with-param>
+					<xsl:with-param name="isDefault" select="vdef/value/text()='1'" />
+				</xsl:call-template>
+				<xsl:call-template name="selectOption">
+					<xsl:with-param name="value" select="0" />
+					<xsl:with-param name="label" >No</xsl:with-param>
+					<xsl:with-param name="isDefault" select="vdef/value/text()='0'" />
+				</xsl:call-template>				
+			</xsl:if>
+		</xhtml:select>
+	</xsl:template>	
+
+	<xsl:template match="parameter/*[substring(local-name(.),2,4)='list']//value" mode="controlChoice">
+		<!-- Option sub-control for choices and booleans -->
+		<xsl:call-template name="selectOption">
+			<xsl:with-param name="value" select="text()" />
+			<xsl:with-param name="label" >
+				<xsl:choose>
+					<xsl:when test="../label/text()">
+						<xsl:value-of select="../label/text()"/>                
+					</xsl:when>
+					<xsl:otherwise></xsl:otherwise>
+				</xsl:choose>
+			</xsl:with-param>
+			<xsl:with-param name="isDefault" select="text()=ancestor-or-self::parameter/vdef/value/text()" />
+		</xsl:call-template>
+	</xsl:template>	
+
+	<xsl:template name="selectOption">
+		<xsl:param name="value" />
+		<xsl:param name="label" />
+		<xsl:param name="isDefault" />
+		<xhtml:option value="{$value}">
+			<xsl:if test="$isDefault">
+				<!-- Default value -->
+				<xsl:attribute name="selected" >selected</xsl:attribute>
+			</xsl:if>
+			<xsl:value-of select="$label"/>                
+		</xhtml:option>
+	</xsl:template>		
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/remove_ns.xsl b/Src/Portal/htdocs/MobylePortal/xsl/remove_ns.xsl
new file mode 100644
index 0000000..191708d
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/remove_ns.xsl
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+  
+  <xsl:output method="html" indent="yes" />	
+
+  <xsl:include href="ident.xsl" />
+
+  <xsl:template match="xhtml:*">
+    <xsl:element name="{local-name(.)}">
+      <xsl:apply-templates select="@*|node()|text()" />
+    </xsl:element>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/results.xsl b/Src/Portal/htdocs/MobylePortal/xsl/results.xsl
new file mode 100644
index 0000000..06529a1
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/results.xsl
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	form_parameters.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+	
+	<xsl:include href="ident.xsl" />
+
+	<xsl:param name="jobUrl" />
+	
+	<xsl:template match="parameters">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()" />
+			<xsl:if test="(/program) and not(ancestor::paragraph) and not(//parameter[(@isstdout='true' or @isstdout='1')])">
+				<!-- Adding stdout parameter if not specified in the program description -->
+				<parameter isstdout="true">
+					<name>stdout</name>
+					<prompt>Standard output</prompt>
+					<type>
+						<datatype>
+							<class>Report</class>
+						</datatype>
+					</type>
+					<interface type="job_output" generated="true">
+						<xhtml:div data-parametername="stdout">
+						</xhtml:div>
+					</interface>
+					<filenames>
+						<code proglang="perl">"<xsl:value-of select="/program/head/name/text()" />.out"</code>
+						<code proglang="python">"<xsl:value-of select="/program/head/name/text()" />.out"</code>
+					</filenames>
+				</parameter>
+			</xsl:if>
+			<!-- Adding stderr parameter -->
+			<xsl:if test="(/program) and not(ancestor::paragraph)">
+				<parameter isout="true">
+					<name>stderr</name>
+					<prompt>Standard error</prompt>
+					<type>
+						<datatype>
+							<class>Report</class>
+						</datatype>
+					</type>
+					<interface type="job_output" generated="true">
+						<xhtml:div data-parametername="stderr">
+						</xhtml:div>
+					</interface>
+					<filenames>
+						<code proglang="perl">"<xsl:value-of select="/program/head/name/text()" />.err"</code>
+						<code proglang="python">"<xsl:value-of select="/program/head/name/text()" />.err"</code>
+					</filenames>
+				</parameter>
+			</xsl:if>
+		</xsl:copy>
+	</xsl:template>	
+
+	<xsl:template match="parameter[(@isout or @isstdout) and count(ancestor-or-self::*/interface[@type='job_output'])=0]">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()" />
+			<interface generated="true" type="job_output">
+				<div xmlns="http://www.w3.org/1999/xhtml" data-parametername="{name/text()}">
+				</div>
+			</interface>
+		</xsl:copy>
+	</xsl:template>
+
+	<xsl:template match="parameter[not(@isout or @isstdout) and count(ancestor-or-self::*/interface[@type='job_input'])=0]">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()" />
+			<interface generated="true" type="job_input">
+				<div xmlns="http://www.w3.org/1999/xhtml" data-parametername="{name/text()}">
+				</div>
+			</interface>
+		</xsl:copy>
+	</xsl:template>		
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/schema_flatten.xsl b/Src/Portal/htdocs/MobylePortal/xsl/schema_flatten.xsl
new file mode 100644
index 0000000..2fb13e2
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/schema_flatten.xsl
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	ident.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:rng="http://relaxng.org/ns/structure/1.0" >
+	<!-- This stylesheet contains the XSL Identity Transform used by many other stylesheets -->
+
+	<xsl:template match="@*|node()|text()" priority="-1">
+		<xsl:call-template name="nodeCopy" />
+	</xsl:template>
+
+	<xsl:template name="nodeCopy">
+		<xsl:copy>
+			<xsl:apply-templates select="@*|node()|text()" />
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="rng:include">
+		<xsl:copy-of select="document(@href)/rng:grammar/*"/>
+	</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/tutorial.xsl b/Src/Portal/htdocs/MobylePortal/xsl/tutorial.xsl
new file mode 100644
index 0000000..3ce4485
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/tutorial.xsl
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+	tutorial.xsl stylesheet
+	Authors: Hervé Ménager
+-->
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+
+	<!-- this uri is the identifier of the tutorial xml, i.e., the url of the xml definition on its execution server --> 
+	<xsl:param name="programUri" />	
+
+	<!-- this uri is the identifier of the tutorial xml, i.e., the url of the xml definition on its execution server --> 
+	<xsl:param name="programPID" /> 
+
+	<xsl:variable name="server" select="substring-before($programPID,'.')" />
+	
+	<xsl:param name="codebase">/data/</xsl:param>
+	
+	<xsl:include href="ident.xsl" />
+
+	<xsl:template match="/|comment()|processing-instruction()">
+		<xsl:copy>
+			<!-- go process children (applies to root node only) -->
+			<xsl:apply-templates/>
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="*">
+		<xsl:element name="{local-name()}">
+			<!-- go process attributes and children -->
+			<xsl:apply-templates select="@*|node()"/>
+		</xsl:element>
+	</xsl:template>
+	
+	<xsl:template match="@*">
+		<xsl:choose>
+			<xsl:when test="contains(.,'tutorial-codebase')">
+				<xsl:attribute name="{name()}" namespace="{namespace-uri()}"><xsl:value-of select="substring-before(.,'tutorial-codebase')"/><xsl:value-of select="concat($codebase,'services/servers/local/tutorials/',/*/head/name/text())"/><xsl:value-of select="substring-after(.,'tutorial-codebase')"/></xsl:attribute>        
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:attribute name="{local-name()}">
+					<xsl:value-of select="."/>
+				</xsl:attribute>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+
+	<xsl:template match="/">
+			<table class="header">
+				<tr>
+					<td style="width: 60%">
+						<xsl:apply-templates select="*/head" mode="serviceHeader"/>						
+					</td>
+				</tr>
+			</table>
+			<xsl:apply-templates select="*/head/doc/comment" mode="ajaxTarget"/>
+			<xsl:apply-templates select="*/flow"/>
+			<xsl:apply-templates select="*/head/interface[@type='tutorial']/*" />
+			<xsl:apply-templates select="*/head" mode="serviceFooter"/>
+	</xsl:template>
+
+</xsl:stylesheet>
diff --git a/Src/Portal/htdocs/MobylePortal/xsl/viewer.xsl b/Src/Portal/htdocs/MobylePortal/xsl/viewer.xsl
new file mode 100644
index 0000000..c47ccf5
--- /dev/null
+++ b/Src/Portal/htdocs/MobylePortal/xsl/viewer.xsl
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml">
+  
+  <xsl:output method="html" indent="yes" />	
+
+  <xsl:include href="ident.xsl" />
+  
+  <xsl:param name="jobUrl" />
+
+  <xsl:param name="previewDataLimit">1048576</xsl:param>  
+
+  <xsl:param name="htdocs">/portal/</xsl:param>
+  
+  <xsl:param name="codebase">/data/</xsl:param>  
+
+  <xsl:variable name="job" select="document($jobUrl)"/>
+    
+  <xsl:variable name="server" select="''" />  
+
+  <xsl:template match="*">
+    <xsl:element name="{local-name()}">
+      <!-- go process attributes and children -->
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:element>
+  </xsl:template>
+  
+  <xsl:template match="/viewer">
+    <html>
+      <head>
+        <title>Mobyle - <xsl:value-of select="head/name/text()"/></title>
+        <link rel="stylesheet" href="{$htdocs}css/mobyle.css" type="text/css" media="screen" />
+        <link rel="stylesheet" href="{$htdocs}css/local.css" type="text/css" media="screen" />
+      </head>
+      <body>
+          <div class="viewerRelContainer">
+            <div class="header">
+              <xsl:apply-templates select="head" mode="serviceHeader"/>
+              <xsl:apply-templates select="head/doc/comment" mode="ajaxTarget"/>
+            </div>      
+            <xsl:if test="$jobUrl">
+              <div class="data_view">
+                <xsl:apply-templates select="head/interface[@type='viewer']"/>          
+              </div>
+            </xsl:if>
+            <div class="footer">
+              <xsl:apply-templates select="//parameter[@isout]" />        
+              <xsl:apply-templates select="head" mode="serviceFooter"/>
+            </div>
+          </div>
+      </body>
+      </html>
+  </xsl:template>
+
+  <xsl:template match="head/interface[@type='viewer']">
+        <xsl:apply-templates select="*"/>        
+  </xsl:template>    
+
+  <xsl:template match="parameter[@isout]">
+    <div style="display: none;" data-viewerport="{name/text()}" data-parametername="name/text()" data-datatype="{type/datatype/class/text()}" 
+      data-datatype-superclass="{type/datatype/superclass/text()}" data-format="{type/dataFormat/text()}" data-inputmodes="result" data-filename="{name/text()}"
+      data-portcode="{format/code/text()}" data-base64encoded="{format/@base64encoded}" data-extension="{format/@extension}" data-prompt="{prompt/text()}">
+      <xsl:attribute name="data-biotype">
+        <xsl:for-each select="type/biotype">
+          <xsl:value-of select="text()" /><xsl:text> </xsl:text>
+        </xsl:for-each>
+      </xsl:attribute>
+<!--
+      <button>
+        <xsl:attribute name="data-test">
+          <xsl:value-of select="format/code/text()"/>
+        </xsl:attribute>
+        <xsl:attribute name="onclick">
+          if(portal){bookmark_upload(<xsl:value-of select="format/code/text()"/>,$("<xsl:value-of select="name/text()"/>.username").value,'<xsl:value-of select="type/datatype/class/text()"/>','<xsl:value-of select="type/datatype/superclass/text()"/>',
+          '<xsl:value-of select="type/dataFormat/text()"/>','<xsl:value-of select="type/biotype/text()"/>',$("viewerFlash"),<xsl:value-of select="format/@base64encoded"/>);};
+        </xsl:attribute>
+        bookmark 
+        <xsl:value-of select="prompt"/>
+      </button>
+      <xsl:text> as </xsl:text>
+      <input type="text" value="{prompt}" size="8" name="{name}.username" id="{name}.username"/>
+-->
+    </div>
+  </xsl:template>
+    
+  <xsl:template match="xhtml:*[@data-paragraphname]">
+    <!-- do not display a paragraph unless there are data to be displayed -->
+    <xsl:if test="$job/jobState/data//*[name/text()=current()//@data-parametername]">
+      <xsl:element name="{local-name(.)}">
+        <xsl:apply-templates select="@*|node()" />
+      </xsl:element>
+    </xsl:if>
+  </xsl:template>
+
+ 
+  <xsl:template match="@*">
+    <xsl:choose>
+      <xsl:when test="../@data-parametername and contains(.,'data-url')">
+        <xsl:variable name='parametername' select="../@data-parametername"/>
+        <xsl:variable name="data-url" select="$job/jobState/data[parameter/name/text()=$parametername]/file/text()"/>            
+        <xsl:attribute name="{name()}" namespace="{namespace-uri()}"><xsl:value-of select="substring-before(.,'data-url')"/><xsl:value-of select="$data-url"/><xsl:value-of select="substring-after(.,'data-url')"/></xsl:attribute>        
+      </xsl:when>
+      <xsl:when test="contains(.,'viewer-codebase')">
+        <xsl:attribute name="{name()}" namespace="{namespace-uri()}"><xsl:value-of select="substring-before(.,'viewer-codebase')"/><xsl:value-of select="concat($codebase,'services/servers/local/viewers/',/*/head/name/text())"/><xsl:value-of select="substring-after(.,'viewer-codebase')"/></xsl:attribute>        
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy-of select="."/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/Tools/README b/Tools/README
new file mode 100644
index 0000000..d99c900
--- /dev/null
+++ b/Tools/README
@@ -0,0 +1,268 @@
+==========================================================================
+This file describe briefly different useful tools to administrate Mobyle.
+==========================================================================
+
+In this document we refer to MOBYLEHOME as the folder which contains the Mobyle installation core files.
+ 
+1- mobdeploy : to deploy the programs and viewers interfaces
+=============================================================
+
+Mobyle allows administrators to deploy programs that are either *local* or *imported* and now viewers that are *local*. 
+The `mobdeploy` script is used to *deploy* them on the server, making them available to the public. 
+This script also handles their *indexing*.
+Note: In the following, we will talk about Services which means the Programs & the Viewers.
+
+Overview
+:::::::::
+
+Once a Mobyle server has been installed in a **MOBYLEHOME** directory on the system, the program definitions (i.e., the xml files that describe them to the Mobyle system) are stored in two directories:
+        - the MOBYLEHOME/Services/ directory which contains program (Programs/) and viewer (Viewers/) definitions that the administrator has downloaded from distributions,
+        - and the MOBYLEHOME/Local/Programs directory which contains custom program definitions that have either been created de novo by the server owner, or that he modified to fit his own needs.
+
+In order to deploy the services, the service definitions are first tested to check that these definitions are valid. 
+Then, there is a predeployment step: the valid service definitions are copied to a temporary directory (tmpservices) the time necessary to generate the index.
+(In case of validation problem, the previous service definition installed is temporary kept, if it exists.)
+Finally, the whole content of this directory is transferred to a web-accessible directory defined by the server configuration in the **DOCUMENT_ROOT/PROGRAMS_URL** in case of Programs 
+or **DOCUMENT_ROOT/VIEWERS_URL** for Viewers.
+Therefore, these files will be directly accessible from the Web on their corresponding url. This document refers to this directory as **HTDOCS**.
+
+The imported program definitions are downloaded *via* the Web from the **HTDOCS** directory of the remote servers. 
+These program definitions are stored in **HTDOCS/programs/imports** directory.
+
+The programs and viewers index are stored in the **HTDOCS**/index directory.
+
+
+Configuration
+::::::::::::::
+
+* PROGRAMS *
+
+The configuration of published/imported services is located in the Local/Config/Config.py file.
+
+The deployment of local services (programs, workflows, viewers ) is configured by the **LOCAL_DEPLOY_INCLUDE**, and **LOCAL_DEPLOY_EXCLUDE** directives. 
+ LOCAL_DEPLOY_INCLUDE and LOCAL_DEPLOY_EXCLUDE are python dictionaries with 3 keys programs, workflows and viewers associated to 3 lists of names.
+ these defined repectively the programs, workflows and viewers and can include jokers.
+
+e.g.::
+  LOCAL_DEPLOY_INCLUDE = { 'programs' : [ 'golden', 'clustalw*', 'dnadist', 'fitch' ] ,
+                           'workflows': [ '*' ] ,
+                           'viewers'  : [ 'jalview' , 'varna' ]
+                           }
+  LOCAL_DEPLOY_EXCLUDE={'programs' : [ 'clustalw-profile' ] ,
+                        'workflows': [ '*' ] ,
+                        'viewers'  : [ 'jalview' , 'varna' ]
+                        }
+* VIEWERS *
+
+The important rule about viewer is that the viewer dependencies have to be available within a directory named like the
+viewer xml description in MOBYLEHOME/Services/Viewers/.
+For instance, for a viewer called 'jalview', MOBYLEHOME/Services/Viewers/ directory has to contain a 'jalview.xml' file and
+a 'jalview' directory where are the corresponding dependencies: the archive 'jalviewApplet.jar'.
+If the dependency directory is missing, the viewer won't be deployed.
+
+
+
+The exportation of services to other portals is defined by the **EXPORTED_SERVICES** directive, which lists these services.
+
+e.g.::
+  EXPORTED_SERVICES = [ 'golden', 'abiview' ]
+
+The importation of services from other portals is defined by the **PORTALS** directive, 
+a dictionary that defines external portals the following way:
+    - the key is the nickname of the portal, as it will appear in the programs list of the portal
+    - for each portal:
+      + url is the path to the directory that contains all the cgis (such as job_submit.py).
+      + help is the e-mail of the remote portal maintainer, that receives help requests about his job.
+      + repository is the path to the directory that contains all the published programs.
+      + services is a dictionary with 2 keys
+        - 'programs' associated to the list of imported programs.
+        - 'workflows' associated to the list of imported workflows.
+
+e.g.::
+  PORTALS={'portal1': {
+            'url': 'http://otherdomain.fr:port/cgi-bin/MobylePortal',
+            'help' : 'help at otherdomain.fr',
+            'repository': 'http://otherdomain.fr:port/MobyleData/',
+            services: { 'programs': ['clustalw-multialign'],
+                        'workflows' : ['workflow1_of_portal1']
+                      }
+            }
+        }
+
+
+
+        
+Usage and options
+:::::::::::::::::
+
+The ``mobdeploy`` script is located in the **$MOBYLEHOME/Tools** directory. 
+Before to run it, you should make sure that your current user has the permissions to read and write the files that belong to 
+the Mobyle user (e.g., apache user).
+
+Here are the available commands:
+           deploy   : deploy services      available options [-s --server, -p --programs , -w --workflows , -v --viewers , -f --force , -V --verbose]
+           clean   : clean the repository  available options [-s --server, -p --programs , -w --workflows , -v --viewers , -f --force , -V --verbose]
+           index   : generate indexes      available options [-s --server, -p --programs , -w --workflows , -v --viewers, -V --verbose ]
+And options:
+           -h --help
+           -p --programs : specify the names (comma separated list) of the programs on which the command is applied.
+                           The keyword 'all' mean all programs as defined in Config.py
+           -w --workflows: specify the names (comma separated list) of the workflows on which the command is applied.
+                           The keyword 'all' mean all workflows as defined in Config.py
+           -v --viewers  : specify the names (comma separated list) of the viewers on which the command is applied.
+                           The keyword 'all' mean all viewers as defined in Config.py
+           -s --server :   specify the names (comma separated list) of the servers on which the command is applied.
+                           The keyword 'all' mean all servers as defined in Config.py
+           -f --force : force to deploy a program even it is not imported. 
+                           This option is for debugging purpose only and shall not be used otherwise. 
+           -V --verbose : increase the verbosity (there is 3 level of verbosity: warning , info , debug . default is warning)
+
+Below are a few examples of how you can use the mobdeploy utility:
+
+  mobdeploy  deploy                           install all programs, workflows and viewers from all servers according to the configuration.
+  mobdeploy -p blast2 deploy                  deploy the *blast2* program from all servers.
+  mobdeploy -p blast2 clean                   remove the *blast2* from all servers.
+  mobdeploy -s portal1 deploy                 deploy all the program(s) and workflows imported from *portal1* server according to its configuration.
+  mobdeploy --server portal1 -p all deploy    deploy all the program(s) imported from *portal1* server according to its configuration.
+  mobdeploy -s local -w pretty_tree deploy    deploy the *pretty_tree* workflow from the local server.
+  mobdeploy -s local -p blast2 deploy         deploy the *blast2* program from the *local* server.
+  mobdeploy -s mylab -p seqret,blast2 deploy  deploy the remote program *seqret* and *blast2* from the *mylab* server.
+  mobdeploy -v all deploy                         deploy every viewers and its dependencies.
+  mobdeploy -v jalview,jarna deploy 	      deploy the *jalview* and *jarna* viewers and its dependencies
+  mobdeploy -p index                          only update the program index (in case the index file has been lost for instance)
+
+
+
+2- mobjobw : to list the running job 
+=====================================
+
+mobjob permit to monitor quickly the jobs which are on your batch system.
+the informations displayed are:
+ - the key of the job
+ - the batch system used ( several system could be mixed in Mobyle e.g. SGE and system )
+ - the queue name if it defined
+ - the status of the job : 
+         - running : the job is running,  
+         - pending : the job has been queued by the batch system
+         - hold    : the job has been hold by the administrator
+ - the jobName
+ - the email of the user 
+ - the submission date
+ - the IP of the user 
+ - the full qualified name of the user
+ - the command line 
+
+--------------------------------------------------------------------------------
+jobkey -- batch system/queue -- status
+jobName -- user email -- submission date -- requestor IP\full qualified name
+unix command line
+--------------------------------------------------------------------------------
+
+e.g.::
+--------------------------------------------------------------------------------
+K07333355412998 -- SGE/None -- running
+phyml -- email at domain.gov -- 01/13/09 18:59:51 -- 111.111.11.11\name.domain.ext
+phyml -i infile_data.phylipi -d aa -b 1000 -m JTT -a e
+--------------------------------------------------------------------------------
+O31005884758996 -- SGE/short -- running
+drawtree -- email2 at domain.edu -- 01/15/09 12:47:30 -- 250.250.250.250\UNKNOWN
+ln -s protpars.outtree intree && drawtree <drawtree.params && ln -s plotfile plotfile.ps
+
+
+3- mobkill : to kill a job. 
+===========================
+
+mobkill take one or more jobKeys and kill the corresponding jobs 
+e.g.::
+mobkill N32454430408001 V00863529172897
+
+
+4- mobclean : to clean the job or/and anonymous session tree
+=============================================================
+
+mobclean is a tool to clean your job result and anonymous session directories. By default, it cleans
+your directories(job & a. session) depending on your configuration parameters (the ones of your config.py).
+The tool allows you to make the cleaning separately and to run it on simulation mode (-n).
+
+Here are the available options:
+        -J          Clean Job Results.
+        -S          Clean anonymous sessions.
+        -d <val>    Delete directories older than <val> days ( positive integer value ).
+        -h          Display this help and exit.
+        -v          With error messages (verbose).
+        -n          In simulation mode (dry_run), nothing is cleaned.
+        -l <val>    Path to the Logfile where put the logs.
+   
+And some examples:
+        mobclean 	--> By default, the cleaning of jobs & sessions is done
+        mobclean -S -d 2 to clean the anonymous sessions older than 2 days
+        mobclean -J -S that is equivalent to the default run
+        mobclean -l /var/log/mobyle/cleaner_log : put the logs (error and warning) of the mobclean 
+   run into this log file.
+        mobclean --help
+        
+The cleaning of old jobs can be automated with a cron using mobclean.
+
+
+5- mobtypes : analyse the xml types
+====================================
+
+mobtypes help to ensure the coherence in xml typing between the programs xml definitions, which is
+ of the utmost importance for the chaining. 
+ mobtypes take programs xml file list as argument. If mobtypes is used without option , it build 
+a repository with all "xml types" (see doc about typing in mobyle) found and their corresponding 
+python classes. -if a repository is specify ( -r option ) mobtypes display the differences between 
+this repository and the xml types.
+
+usage: mobtypes < --ref path to a datatype repository > xmlpaths
+        options:
+            -h or --help  ... Print this message and exit.
+            -r or --ref   ... the path to reference
+e.g :: 
+mobtypes MOBYLEHOME/*.xml  
+python class    | xml class                      | description
+AbstractText    | AceAssembly                    | 
+AbstractText    | AncestorsFile                  | 
+AbstractText    | BambeTree                      | 
+...
+
+mobtypes -r path_to_a_repository MOBYLEHOME/*.xml
+    
+--- ../Programs/mobyle_xml_types.txt
++++ xml list
+- Binary          | AbiTraceFile                   | binary trace file produce by abi sequencer  
++ Binary          | HmmBinProfile                  | 
+  
+- Binary          | AbiTraceFile                   | binary trace file produce by abi sequencer 
+means that this line is in the repository and this AbiTraceFile/Binary is not in the xml programs analysed
+
++ Binary          | HmmBinProfile                  |
+means that HmmBinProfile/Binary is found in programs xml analysed but is not present in repository
+
+
+
+6- session0_9_updater.py : to update sessions from mobyle 0.9 to mobyle 0.95.1
+==============================================================================
+
+update the sessions located in Config  USER_SESSIONS_PATH from a Mobyle v0.9 
+to be compliant with the Mobyle v0.95.1
+option :
+   -f ( --force ) to force the update even an xml version of the session already exists.
+   -n ( --dry-run ) translate the session.dump in xml, but do not write them.
+        
+e.g ::
+session0_9_updater 2&> update.log
+
+7- mobvalid: validate program definition xml files
+==================================================
+
+mobvalid is a utility that automatically validates a program description, with 
+respect to the Mobyle format and constraints defined in the Relax-NG and 
+Schematron definitions. When creating or editing any program definition, 
+remember to validate it with this tool before installing it.
+
+Usage: mobvalid [OPTION]... FILE... 
+        Validate Mobyle program description files, each FILE being the path to the file.
+    
+        option:
+            -h or --help  ... Print this message and exit.
diff --git a/Tools/job_updater.py b/Tools/job_updater.py
new file mode 100755
index 0000000..5b10ae5
--- /dev/null
+++ b/Tools/job_updater.py
@@ -0,0 +1,268 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron               #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+
+import os , sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit( 'MOBYLEHOME must be defined in your environment' )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+import shutil
+import re
+from lxml import etree
+
+from Mobyle.ConfigManager import Config
+from Mobyle.JobState import JobState
+from Mobyle.Registry import registry
+from Mobyle.MobyleError import MobyleError
+
+
+class JobUpdater(object):
+    
+    def __init__(self , config , logger  ):
+        self.cfg = config
+        self.log = logger
+        self.skipped_jobs = []
+        self.skipped_services = []
+        self.jobs_url = self.cfg.results_url()
+        
+    def _on_error(self , err ):
+        self.log.warning( "%s : %s" ( err.filename , err ))
+        
+    def update( self , repository , force = False , dry_run = False , status_only = False ):
+        skipped_jobs = []
+        jobs_paths = os.walk( repository , onerror = self._on_error )
+        current_service = ''
+        for path in jobs_paths:
+            job_path = path[0] 
+            if 'index.xml' in path[2]:
+                if not '.admin' in path[2]:
+                    self.log.warning( "the directory %s have an index.xlm but not .admin may be not a job : skip" %path[2] )
+                    skipped_jobs.append( job_path )
+                    continue
+                service_name , job_key =  os.path.split( path[0] )
+                service_name = os.path.basename( service_name )
+                if service_name != current_service:
+                    current_service = service_name
+                    self.log.info( "-------- enter service %s --------" % service_name)
+                self.log.info( "====================" )
+                self.log.info( "updating job %s" %job_key )
+                job_dir_mtime = os.path.getmtime( job_path )
+                job_dir_atime = os.path.getatime( job_path )
+                index_mtime = os.path.getmtime( os.path.join( job_path , 'index.xml') )
+                index_atime = os.path.getatime( os.path.join( job_path , 'index.xml') )
+                index_path = os.path.join( job_path , 'index.xml')
+                try:
+                    os.utime( job_path , ( job_dir_atime , job_dir_mtime ) )
+                except Exception , err :
+                    self.log.warning( "cannot modify the mtime of %s : %s : fix this problem before rerun updater" %(job_path , err ) )
+                    continue
+
+                bckp_path = self.back_up(job_path, index_path, dry_run)
+                try:
+                    if not status_only:
+                        try:
+                            self.fix_definition( job_path , service_name , job_key , index_path , bckp_path , force = force , dry_run = dry_run )
+                        except Exception, err:
+                            self.roll_back(bckp_path, index_path)
+                    try:                 
+                        self.fix_status( job_path , index_path , force = force , dry_run = dry_run )
+                    except Exception, err:
+                        self.log.warning( "Error occured during status fixing : %s" % err, exc_info = True )
+                        self.roll_back(bckp_path, index_path)
+                finally:
+                    self.set_time(index_path, index_atime, index_mtime, job_path, job_dir_atime, job_dir_mtime)
+                
+                
+    def fix_definition( self , job_path , service_name , job_key ,index_path , bckp_path ,force = False ,dry_run = False):
+        self.log.info( "fixing %s job definition" % job_path )
+        try:
+            js = JobState( uri = job_path )
+        except Exception , err:
+            self.log.warning( "cannot load job %s : %s : skipped" %( job_path , err ) )
+            return
+        if js.getDefinition() is not None and not force:
+            self.log.warning("the job %s has already a definition: skipped " % job_path )
+            return
+        new_program_def_url = registry.getProgramUrl( service_name , 'local' )
+        self.log.info( "registry.getProgramUrl( %s , 'local' ) = %s "% (service_name , new_program_def_url ) )
+        new_host = self.cfg.root_url()
+        self.log.info( "new host = %s "% new_host )
+        job_id = js.getID()
+        new_job_id = "%s/%s" %(self.jobs_url , re.search( '.*/(.*/.*)$' , job_id ).group(1) )
+        self.log.info( "new job id = %s "% new_job_id )
+        if not dry_run:
+            js.setName( new_program_def_url )
+            try:
+                js.setDefinition( new_program_def_url)
+            except MobyleError:
+                self.log.warning( "the service : local.%s is not deployed to update job %s, deploy %s before" %(service_name , 
+                                                                                                                job_path,
+                                                                                                                service_name) )
+                return
+            js.setHost( new_host )
+            js.setID( new_job_id )
+            try:
+                js.commit()
+            except Exception ,err :
+                self.log.warning( "cannot commit updated job %s : %s " %( job_path , err ) )
+                self.roll_back(bckp_path, index_path)
+        else:
+            pass
+        
+    def fix_status(self , job_path , index_path , force = False , dry_run = False  ):
+        self.log.info( "fixing %s job status" % job_path )
+        status_path = os.path.join( job_path , 'mobyle_status.xml')
+        if os.path.exists( status_path ) and not force:
+            self.log.info("%s already exist : skip" % status_path )
+            return
+        else:
+            doc = etree.parse( os.path.join( job_path , 'index.xml') )
+            root = doc.getroot()
+            status_node = root.find( 'status' )
+            status = status_node.find( 'value' ).text
+            if status in ( "submitted", "pending", "running", "hold"):
+                return
+            try:
+                status_file = open(  status_path , 'w' )
+                status_file.write( etree.tostring( status_node , pretty_print = True , encoding='UTF-8' )) 
+            except Exception , err :
+                self.log.error( 'cannot make mobyle_status.xml : %s' % err )
+            finally:
+                try:
+                    status_file.close()
+                except:
+                    pass
+            root.remove( status_node )
+            try:
+                tmp_file = open( os.path.join( job_path ,'tmp_index.xml' ) , 'w' )
+                tmp_file.write( etree.tostring( doc , pretty_print = True , encoding='UTF-8' )) 
+                tmp_file.close()
+                os.rename( tmp_file.name , index_path )
+            except Exception , err :
+                self.log.error( 'cannot make index.xml : %s' % err )
+              
+                
+    def back_up(self , job_path , index_path , dry_run = False ):
+        bckp_path = os.path.join( job_path , '.index.xml.bckp')
+        if not os.path.exists( bckp_path ):
+            self.log.debug( "make  %s back up" % job_path )
+        if not dry_run:
+            try:
+                shutil.copy( index_path , bckp_path )
+            except Exception , err:
+                self.log.warning( "cannot make %s back up " % index_path)
+        return bckp_path
+        
+        
+    def roll_back(self , bckp_path , index_path ):
+        try:
+            shutil.copy( bckp_path , index_path )
+        except Exception:
+            self.log.warning( "cannot roll back ")
+            return
+        try:
+            os.unlink( bckp_path )
+        except Exception:
+            self.log.warning( "cannot remove back up index % " % bckp_path )
+            return                
+        self.log.warning( "job %s has been rolled back " %( os.path.dirname( index_path ) ) )
+        return
+  
+    def set_time(self , index_path , index_atime , index_mtime , job_path , job_dir_atime , job_dir_mtime ):
+        os.utime( index_path , ( index_atime , index_mtime ) )
+        os.utime( job_path , ( job_dir_atime , job_dir_mtime ) )
+
+
+
+if __name__ == "__main__":
+    
+    from optparse import OptionParser
+    
+
+    parser = OptionParser( )
+    parser.add_option( "-s" , "--services",
+                       action="store", 
+                       type = 'string', 
+                       dest="services",
+                       help="comma separated list of SERVICES to update."                       
+                       )
+    parser.add_option( "-l" , "--log",
+                       action="store", 
+                       type = 'string', 
+                       dest = "log_file",
+                       help= "Path to the Logfile where put the logs.")
+    
+    parser.add_option( "-n" , "--dry-run",
+                       action="store_true", 
+                       dest = "dry_run",
+                       default = False ,
+                       help= "don't actually do anything.")
+    
+    parser.add_option( "-f" , "--force",
+                       action="store_true", 
+                       dest = "force",
+                       default = False ,
+                       help= "force to update job even if it was already updated.")
+    
+    parser.add_option("-v", "--verbose",
+                      action="count", 
+                      dest="verbosity", 
+                      default= 0,
+                      help="increase the verbosity level. There is 4 levels: Error messages (default), Warning (-v), Info (-vv) and Debug.(-vvv)") 
+    
+    parser.add_option("--status-only",
+                      action="store_true", 
+                      dest="status_only", 
+                      default= False,
+                      help="fix the job status only") 
+    
+    options, args = parser.parse_args()
+    
+    
+    import logging 
+    logger = logging.getLogger( 'jobUpdater' ) 
+    if options.log_file is None:
+        handler = logging.StreamHandler(  sys.stderr  )
+    else:
+        try:
+            handler = logging.FileHandler( options.log_file , 'w' )
+        except(IOError , OSError) , err:
+            print >> sys.stderr , "cannot log messages in %s: %s"%( options.log_file , err )
+            sys.exit(1)
+    if options.verbosity < 2:
+        formatter = logging.Formatter( '%(filename)-10s : %(levelname)-8s : %(asctime)s : %(message)s' , '%a, %d %b %Y %H:%M:%S' )
+    else:
+        formatter =   logging.Formatter( '%(filename)-10s : %(levelname)-8s : L %(lineno)d : %(asctime)s : %(message)s' , '%a, %d %b %Y %H:%M:%S' )        
+    handler.setFormatter( formatter )
+    logger.addHandler( handler)
+    if options.verbosity == 0:
+        logger.setLevel( logging.WARNING )
+    elif options.verbosity == 1:
+        logger.setLevel( logging.INFO )
+    else:
+        logger.setLevel( logging.DEBUG )
+        
+    config = Config()
+    job_updater = JobUpdater( config , logger )
+    jobs_repository = config.results_path()
+    if options.services is None:
+        job_updater.update( jobs_repository , force = options.force , dry_run = options.dry_run , status_only = options.status_only )
+    else:
+        services = options.services.split( ',' )
+        for service in services:
+            service_path = os.path.join( jobs_repository , service )
+            job_updater.update( service_path , force = options.force , dry_run = options.dry_run , status_only = options.status_only )
+    
diff --git a/Tools/mob_log_rotate b/Tools/mob_log_rotate
new file mode 100644
index 0000000..4046c16
--- /dev/null
+++ b/Tools/mob_log_rotate
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+########################################################################################
+#                                                                                      #
+#   Author: Bertrand Neron,                                                            #
+#   Organization:'Biological Software and Databases' Group, Institut Pasteur, Paris.   #  
+#   Distributed under GPLv2 Licence. Please refer to the COPYING.LIB document.         #
+#                                                                                      #
+########################################################################################
+
+import gzip
+import time
+import stat
+import logging
+import logging.handlers
+ 
+import os , sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit( 'MOBYLEHOME must be defined in your environment' )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+        
+
+from Mobyle.ConfigManager import Config
+config = Config()
+ 
+
+log = logging.getLogger('mobrotate' )
+log_path = config.log_dir()
+defaultFormatter = logging.Formatter(
+                '%(name)-10s : %(levelname)-8s : %(filename)-10s: L %(lineno)d : %(asctime)s : %(message)s' ,
+                '%a, %d %b %Y %H:%M:%S'
+                )
+mailHandler = logging.handlers.SMTPHandler( config.mailhost(),
+                                            config.sender() ,
+                                            config.maintainer()  ,
+                                            '[ %s ] Mobyle problem' % config.root_url()
+                                            )
+mailHandler.setFormatter(defaultFormatter)
+log.addHandler(mailHandler)
+
+
+
+target_files=['access_log', 'account_log', 'cleaner_log', 'error_log', 'child_log']
+
+try:
+    os.chdir(log_path)
+except Exception ,err:
+    sys.exit(err)
+
+try:
+    with open('.month') as month_f:
+        rev = month_f.readline()
+except IOError, err:
+    if err.errno == 2: 
+        #the file does not exists
+        year =  time.strftime('%Y')
+        month = time.strftime('%m')
+        month = int(month) - 1
+        if month == 0:
+            month = 12
+            year = int(year) -1
+        rev = "{0}{1:02d}".format(year, month)
+    else:
+        log.critical('cannot rotate logs: {0}'.format(err))
+        sys.exit(2)
+except Exception, err:
+    log.critical('cannot rotate logs: {0}'.format(err))
+    sys.exit(2)
+
+
+for _file in target_files:
+    try:
+        os.rename(_file, "{0}.{1}".format(_file, rev) ) 
+        f = open(_file, 'w')
+        f.close()
+        os.chmod(_file, stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IROTH) # 644
+        log.debug("rotate {0}".format(_file))
+    except Exception, err:
+        log.critical('cannot rotate {0}: {1}'.format(_file, err))
+        continue
+        
+for _file in target_files:
+    file_to_gzip = "{0}.{1}".format(_file, rev)
+    if os.path.exists(file_to_gzip):
+        try:
+            f_in = open(file_to_gzip, 'rb')
+            f_out = gzip.open(file_to_gzip + '.gz', 'wb')
+            f_out.writelines(f_in)
+            os.unlink(file_to_gzip)
+        except Exception, err:
+            log.error("cannot compress log {0}: {1}".format(file_to_gzip, err))
+        finally:
+            try:
+                f_in.close()
+            except:
+                pass
+            try:
+                f_out.close()
+            except:
+                pass    
+with open(".month", 'w') as _f:
+    _f.write(time.strftime('%Y%m'))
+    
+    
diff --git a/Tools/mob_stat b/Tools/mob_stat
new file mode 100644
index 0000000..118b11c
--- /dev/null
+++ b/Tools/mob_stat
@@ -0,0 +1,234 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from collections import namedtuple, defaultdict
+import gzip
+import os
+import sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment if you want to send statistics by email')
+if (os.path.join(MOBYLEHOME, 'Src')) not in sys.path:
+    sys.path.append(os.path.join(MOBYLEHOME, 'Src'))
+    
+###############
+#  Utilities  #
+###############
+
+def memoize(func):
+    cache ={}
+    def wrapper(*args):
+        if args in cache:
+            return cache[args]
+        res = func(*args)
+        cache[args] = res
+        return res
+    return wrapper
+
+def group_by(logs, func):
+    chunks = defaultdict(list)
+    for log in logs:
+        key = func(log)
+        chunks[key].append(log)
+    return chunks
+
+def count(logs, key):
+    counts = {}
+    for log in logs:
+        k = getattr(log, key)
+        counts[k] = counts[k] + 1 if k in counts else 1
+    counts = list(counts.items())
+    counts.sort(key = lambda x : x[1])
+    return counts
+###############
+#    core     #
+###############
+
+log = namedtuple( 'log' , "day_of_week day_of_month month year hour prog email ip portal")
+
+def parse(log_paths):
+    logs= []
+    for log_path in log_paths:
+        
+        if log_path.endswith('.gz'):
+            log_file = gzip.open(log_path, 'r')
+        else:
+            log_file = open(log_path, 'r')
+        with log_file:
+            for line in log_file:
+                fields = line.strip().split()
+                email = fields[7]
+                portal = fields[9] if len(fields) == 10 else 'UNKNOW_PORTAL' 
+                l = log(fields[0], 
+                        int(fields[1]),
+                        fields[2],
+                        int(fields[3]),
+                        fields[4],
+                        fields[5],
+                        email,
+                        fields[8],
+                        portal)
+                logs.append(l)
+    return  logs
+
+ 
+def jobs_per_user(logs):
+    users = {}
+    for log in logs:
+        users[log.email] = users[log.email] + 1 if log.email in users else 1
+    users = list(users.items())
+    users.sort(key = lambda x : x[1])
+    return users
+
+def user_dict_2_list(d):
+    x_data = []
+    y_data = []
+    for x, user in enumerate(d, start = 1):
+        #print(user)
+        x_data.append(x)
+        y_data.append(math.log(user[1])) 
+    return x_data, y_data
+
+def is_pasteurien(log):
+    return log.email.endswith('pasteur.mg')
+
+def progs_sorter(log):
+    return log.prog
+
+def day_of_week_sorter(log):
+    return log.day_of_week
+
+def month_sorter(log):
+    return log.month
+
+def year_sorter(log):
+    return log.year
+
+def by_user(log):
+    return log.email
+
+def by_day(log): 
+    return (log.day_of_month, log.month, log.year)
+
+
+if __name__ == "__main__":
+    
+    import argparse
+    parser = argparse.ArgumentParser(description = """parse mobyle access log 
+and generate a report
+""")
+    parser.add_argument("logs",
+                        nargs = '+',
+                        help = "the access files log in gz format")
+    parser.add_argument("-o", "--output",
+                        dest = "output",
+                        action = "store",
+                        default = "mob_stat.out",
+                        help = "the output file"
+                       )
+    parser.add_argument("-m", "--email",
+                        dest = "email",
+                        action = "store_true",
+                        help = "send the results by email (by default to the Mobyle maintainers)"
+                       )
+    parser.add_argument("--to",
+                        dest = "to",
+                        nargs = '+',
+                        action = "store",
+                        default = None,
+                        help = "replace the dest of the email (the -m option must be set)"
+                       ) 
+    args = parser.parse_args()
+    logs = parse(args.logs)
+    
+
+    TOTAL_JOBS = len(logs)
+    UNIC_USERS = count(logs, 'email')
+
+    ##################
+    #  programs used #
+    ##################
+    prog_used = group_by(logs, progs_sorter)
+    PROG_USED = len(prog_used)
+    prog_used = count(logs, 'prog')
+    PROG_USED_TOP_10 = prog_used[-10:]
+
+    #####################################
+    # how many user use a given program #
+    #####################################
+    progs = []
+    by_progs = group_by(logs, progs_sorter) 
+    for prog in by_progs:
+        users = count(by_progs[prog], 'email')
+        progs.append((prog, len(users)))
+    progs.sort(key= lambda x : x[1])
+    NB_OF_USER_BY_PROG_TOP10 = progs[-10:]
+    
+    users = jobs_per_user(logs)
+    NB_OF_JOBS_BY_USER_TOP10 = users[-10:]
+
+    ##########
+    # Report #
+    ##########
+    with open(args.output, 'w') as report:
+        if TOTAL_JOBS:
+            report.write('Total number of jobs = {}\n'.format(TOTAL_JOBS))
+            nb_of_days = len(group_by(logs, by_day))
+            report.write('nb of jobs / days = {:.0f}\n'.format(TOTAL_JOBS / nb_of_days))
+            report.write("number of users = {0}\n".format(len(UNIC_USERS)))
+         
+            title = '\n{0} programs used (top 10)\n'.format(PROG_USED)
+            report.write(title)
+            report.write('{}\n'.format('=' * (len(title)-2)))
+            report.write('\tprogram : nb jobs\n')
+            report.write('\t{}\n'.format('-'*17))
+            PROG_USED_TOP_10.reverse()
+            for prg in PROG_USED_TOP_10:
+                report.write('\t{} : {}\n'.format(*prg))
+            
+            title = "\nNumber of users by program (top 10)\n"
+            report.write(title)
+            report.write('{}\n'.format('=' * (len(title)-2)))
+            report.write('\tprogram : users\n')
+            report.write('\t{}\n'.format('-'*15))
+            NB_OF_USER_BY_PROG_TOP10.reverse()
+            for prg in NB_OF_USER_BY_PROG_TOP10:        
+                report.write( '\t{} : {}\n'.format(*prg)) 
+             
+            title = "\nNumber of jobs by user (top 10)\n"
+            report.write(title)
+            report.write('{}\n'.format('=' * (len(title)-2)))
+            report.write('\tuser : jobs\n')
+            NB_OF_JOBS_BY_USER_TOP10.reverse()
+            report.write('\t{}\n'.format('-'*11))    
+            for usr in NB_OF_JOBS_BY_USER_TOP10:        
+                report.write( '\t{} : {}\n'.format(*usr))
+        else:
+            report.write('No jobs')
+            
+    ##########################################
+    # email the report to mobyle maintainers #
+    ##########################################
+    if args.email:
+        from Mobyle.ConfigManager import Config
+        config = Config()
+        from Mobyle.Net import EmailAddress , Email
+        
+        if args.to:
+            email_addr = EmailAddress(args.to)
+        else:
+            email_addr = EmailAddress(config.maintainer())
+            
+        email_checked = email_addr.check()
+        if not email_checked:
+            msg = email_addr.getMessage()
+            print >> sys.stderr , msg
+            sys.exit(2)       
+        
+        mail =  Email( email_addr )    
+        mail.send('STAT', {'SENDER'     : config.sender() ,
+                           'HELP'       : config.mailHelp() ,
+                           'SERVER_NAME': config.portal_url()}, 
+                  files = [args.output])
\ No newline at end of file
diff --git a/Tools/mobclean b/Tools/mobclean
new file mode 100644
index 0000000..88f535c
--- /dev/null
+++ b/Tools/mobclean
@@ -0,0 +1,464 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Sandrine Larroude ,Bertrand Neron               #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os , sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit( 'MOBYLEHOME must be defined in your environment' )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+        
+    
+from time import time
+from shutil import rmtree
+
+from Mobyle.ConfigManager import Config
+config = Config()
+
+from Mobyle.Session import Session
+from Mobyle.MobyleError import MobyleError, SessionError ,URLError, HTTPError, JobError
+from Mobyle.Admin import Admin
+from Mobyle.Utils import isExecuting
+from Mobyle.JobState import JobState
+from Mobyle.StatusManager import StatusManager
+
+def day2second( day ):
+    """
+    @param day: the number of day to convert in second
+    @type day: integer or float
+    @return: convert a number of days in second
+    @rtype: integer
+    """
+    return int( day * 24 * 60 * 60 ) 
+
+
+def clean_jobs( config , start_time , delay , logger , dry_run = False ):
+    """
+    remove job directory if job is finished and older than the delay.
+    @param config: the Mobyle Configuration    
+    @type config: L{Config} instance
+    @param start_time: a time in seconds which represent the date of the beginig of cleaning since Epoch.
+    @type start_time: foat
+    @param delay: the delay in days to remove sessons older than start_time + delay
+    @type delay: float    
+    @param logger: the logger to log informations
+    @type logger: logging.logger instance
+    @param dry_run: 
+    @type dry_run: boolean
+    """
+    
+    jobs_repository = config.results_path()
+    if not os.path.isdir( jobs_repository ):
+        logger.critical( "Check your Mobyle configuration file, the jobs repository :'%s' does not exist")
+        sys.exit(1)
+    try:
+        service_names = os.listdir( jobs_repository )
+        service_names.remove( 'ADMINDIR' )
+    except Exception, err:
+        logger.critical( "The jobs directory is not accessible: %s" %err , exc_info = True )
+        sys.exit(1) 
+    for service_name in service_names:
+        logger.info( "-------- cleanning %s --------" % service_name)
+        service_path = os.path.join( jobs_repository ,  service_name )
+        if not os.path.isdir( service_path ):
+            logger.info( "ignore the file %s" %service_path )
+            continue
+        try:
+            jobs_keys = os.listdir( service_path )
+        except Exception, err:
+            logger.error( "The jobs directory for service %s is not accessible: %s" %( service_name , err ) )
+            continue
+        
+        for job_key in jobs_keys:
+            logger.info( "cleaning job %s" %job_key )
+            job_path = os.path.join( service_path , job_key )
+            if not os.path.isdir( job_path ):
+                logger.debug( "%s is not a directory." %job_path )
+                continue
+            try:
+                admin = Admin( job_path )
+                job_state = JobState( job_path )
+                if job_state.isWorkflow():
+                    clean_workflow_job( jobs_repository , service_name , job_key , job_path , start_time , delay , admin , job_state , logger , dry_run = dry_run )
+                else:
+                    clean_program_job( jobs_repository , service_name , job_key , job_path , start_time , delay , admin , job_state , logger , dry_run = dry_run )
+                del job_state._refs[ job_path ]
+            except MobyleError , err:
+                logger.error( "cannot remove the job : %s : %s" %( job_path , err ) )    
+            
+            
+def clean_program_job( jobs_repository , service_name , job_key , job_path , 
+                       start_time , delay , admin , job_state , logger , dry_run = False):
+    
+    delay_sec = day2second( delay )
+    sm = StatusManager()
+    job_status = sm.getStatus( job_path )
+    if not job_status.isKnown():
+        logger.warning( "Unkown status for job %s"% job_path )
+        return
+    is_in_admindir = os.access( os.path.join( jobs_repository, 'ADMINDIR', "%s.%s" %( service_name , job_key) )
+                                , os.F_OK)
+    last_modification_time = os.path.getmtime( job_path )
+    old_job = int( start_time - last_modification_time ) > delay_sec
+    if job_status.isEnded() and old_job :
+        logger.debug( "the job is ended and old ( %d > %d )"%( int( start_time - last_modification_time ) , delay_sec) )
+        if is_in_admindir:
+            logger.error("the job %s has the status %s and is still in ADMINDIR." %( job_path , job_status ) )
+        else:
+            workflowID = admin.getWorkflowID()
+            logger.debug( "workflowID = %s "%workflowID )
+            if workflowID:
+                logger.debug( "the job %s belongs to the workflow %s" %(job_path ,  workflowID ))
+                try:
+                    wf_state = JobState( uri = workflowID )
+                except URLError , err :
+                    #the portal is down?
+                    logger.warning( "The job %s belongs to the workflow %s and the portal does respond (%s). The job is not removed."% (job_path , 
+                                                                                                                                        workflowID , 
+                                                                                                                                        err))
+                except HTTPError , err:
+                    #the job does not exists anymore
+                    if err.code == 404:
+                        logger.info( "The job %s belongs to the workflow %s which is not exist any more (%s). The job is removed." % (job_path ,
+                                                                                                                                      workflowID , 
+                                                                                                                                      err))
+                        if not dry_run:
+                            try:
+                                rmtree( job_path )
+                            except Exception, err:
+                                logger.error( "cannot remove job %s : %s ." %(job_path , err ) )
+                    else:
+                        logger.info( "The job %s belongs to the workflow %s which is not reachable (%s). The job is not removed." % (job_path , 
+                                                                                                                                     workflowID , 
+                                                                                                                                     err))
+                except JobError , err:
+                    from errno import ENOENT
+                    if err.errno == ENOENT:
+                        logger.debug( "The job %s belongs to the workflow %s. which is not exist any more. The job is removed."% (job_path , 
+                                                                                                                                  workflowID 
+                                                                                                                                  ))
+                        if not dry_run:
+                            try:
+                                rmtree( job_path )
+                            except Exception, err:
+                                logger.error( "cannot remove job %s : %s ." %(job_path , err ) )
+                    else:
+                        logger.error( "the workflow job %s cannot be loaded: %s : the job %s is not removed."%( workflowID ,
+                                                                                                             err ,
+                                                                                                             job_path
+                                                                                                            ) )
+                except Exception ,err:
+                    logger.error( "an error occured during %s workflow job loading: %s : the job %s is not removed."%( workflowID ,
+                                                                                                                       err ,
+                                                                                                                       job_path
+                                                                                                                       ) )
+                else:
+                    workflow_status = sm.getStatus( wf_state.getDir() )
+                    logger.debug( "the job %s belongs to the workflow %s which has %s status. The job is not removed."%( job_path , 
+                                                                                                                         workflowID , 
+                                                                                                                         workflow_status))
+            else:
+                logger.info( "remove job %s" % job_path )
+                if not dry_run:
+                    try:
+                        rmtree( job_path )
+                    except Exception, err:
+                        logger.error( "cannot remove job %s : %s ." %(job_path , err ) )
+    elif job_status.isQueryable() and old_job:
+        if not is_in_admindir:
+            logger.error("The job %s has the status %s since more than %d days and is not anymore in ADMINDIR." % ( job_path, job_status, delay))
+        else:
+            try:
+                if not isExecuting( job_path ):
+                    logger.error( "The job %s has the status %s even if it is not executing." % ( job_path, job_status ) )
+            except MobyleError ,err:
+                logger.error( "Probblem during quering the satus of the job %s: %s" %( job_path ,err ) )
+    elif old_job :#not ended not queryable, building??
+        logger.error( "The job %s has the %s status since more than the delay" % (job_path, job_status) )
+    else:
+        logger.info( "The job %s is too young to be cleaned" %job_path )
+
+
+def clean_workflow_job(jobs_repository , service_name , job_key , job_path , 
+                       start_time , delay , admin , job_state , logger , dry_run = False):
+    delay_sec = day2second( delay )
+    sm = StatusManager()
+    job_status = sm.getStatus( job_path )
+    if not job_status.isKnown():
+        logger.warning( "Unkown status for job %s"% job_path )
+        return
+    last_modification_time = os.path.getmtime( job_path )
+    old_job = int( start_time - last_modification_time ) > delay_sec
+    if job_status.isEnded() and old_job :
+        logger.debug( "the workflow is ended and old ( %d > %d )"%( int( start_time - last_modification_time ) , delay_sec) )
+        workflowID = admin.getWorkflowID()
+        logger.debug( "workflowID = %s "%workflowID )
+        if workflowID:#this workflow is a subtask of an other workflow
+            logger.debug( "the workflow %s belongs to the workflow %s" %(job_path ,  workflowID ))
+            try:
+                wf_state = JobState( uri = workflowID )
+            except URLError , err :
+                #the portal is down?
+                logger.warning( "The workflow %s belongs to the workflow %s and the portal does respond (%s). The workflow is not removed."% (job_path , 
+                                                                                                                                    workflowID , 
+                                                                                                                                    err))
+            except HTTPError , err:
+                #the workflow does not exists anymore
+                if err.code == 404:
+                    logger.info( "The workflow %s belongs to the workflow %s which is not exist any more (%s). The workflow is removed." % (job_path ,
+                                                                                                                                  workflowID , 
+                                                                                                                                  err))
+                    if not dry_run:
+                        try:
+                            rmtree( job_path )
+                        except Exception, err:
+                            logger.error( "cannot remove workflow %s : %s ." %(job_path , err ) )
+                else:
+                    logger.info( "The workflow %s belongs to the workflow %s which is not reachable (%s). The workflow is not removed." % (job_path , 
+                                                                                                                                 workflowID , 
+                                                                                                                                 err))
+            except JobError , err:
+                from errno import ENOENT
+                if err.errno == ENOENT:
+                    logger.debug( "The job %s belongs to the workflow %s. which is not exist any more. The job is removed."% (job_path , 
+                                                                                                                              workflowID 
+                                                                                                                              ))
+                    if not dry_run:
+                        try:
+                            rmtree( job_path )
+                        except Exception, err:
+                            logger.error( "cannot remove job %s : %s ." %( job_path , err ) )
+                else:
+                    logger.error( "the workflow job %s cannot be loaded: %s : the workflow %s is not removed."%( workflowID ,
+                                                                                                         err ,
+                                                                                                         job_path
+                                                                                                        ) )
+            except Exception ,err:
+                logger.error( "an error occured during %s workflow job loading: %s : the job %s is not removed."%( workflowID ,
+                                                                                                                   err ,
+                                                                                                                   job_path
+                                                                                                                   ) )
+            else:
+                workflow_status = sm.getStatus( wf_state.getDir() )
+                logger.debug( "the workflow %s belongs to the workflow %s which has %s status. The workflow is not removed."%( job_path , 
+                                                                                                                     workflowID , 
+                                                                                                                     workflow_status))
+        else: #this is a "top level" workflow
+            logger.info( "remove workflow %s" % job_path )
+            if not dry_run:
+                try:
+                    rmtree( job_path )
+                except Exception, err:
+                    logger.error( "cannot remove workflow %s : %s ." %( job_path , err ) )
+                    
+    elif old_job :
+        logger.info( "The workflow %s has the %s status and is older than %d days" % (job_path, job_status , delay) )
+    else:
+        logger.info( "The workflow %s is too young to be cleaned" %job_path )
+                    
+                    
+def clean_sessions( config , start_time , delay , logger , dry_run = False ):
+    """
+    remove annonymous sessions if the sessions does not point toward any jobs
+    @param config: the Mobyle Configuration    
+    @type config: L{Config} instance
+    @param start_time: a time in seconds which represent the date of the beginig of cleaning since Epoch.
+    @type start_time: foat
+    @param delay: the delay in days to remove sessons older than start_time + delay
+    @type delay: float    
+    @param logger: the logger to log informations
+    @type logger: logging.logger instance
+    @param dry_run: 
+    @type dry_run: boolean
+    """
+    delay = day2second( delay )
+    sessions_repository = os.path.join( config.user_sessions_path() , 'anonymous' )
+    if not os.path.isdir( sessions_repository ):
+        logger.critical( "Check your Mobyle configuration file, the annonymous sessions directory:'%s' does not exist" %sessions_repository , exc_info= True)
+        sys.exit(1)
+    try:
+        sessions_keys = os.listdir( sessions_repository )
+    except Exception, err:
+        logger.critical( "The anonymous sessions directory is not accessible: %s" %err ,  exc_info= True )
+        sys.exit(1)                 
+    for session_key in sessions_keys :
+        try:
+            session_path = os.path.join( sessions_repository , session_key )
+            if not os.path.isdir( session_path ):
+                logger.debug( "%s is not a directory." %session_path)
+                continue
+            last_modification_time = os.path.getmtime( session_path )
+            session = Session( session_path , session_key , config )
+            try:
+                jobs = session.getAllJobs()
+                if not jobs and int( start_time - last_modification_time ) > delay :
+                    logger.info("removing session %s" %session_path)
+                    if not dry_run:
+                        try:
+                            rmtree( session_path )
+                        except Exception , err:
+                            logger.error( "cannot remove the session %s : %s" %( session_path , err ))
+                            continue
+            except SessionError, err:  
+                if not os.access( os.path.join( session_path,'.session.xml'), os.F_OK):
+                    logger.warning( "no .session.xml in the session %s , remove it anyway" % session_path )
+                    try:
+                        rmtree( session_path )
+                    except Exception , err:
+                        logger.error( "cannot remove the session %s : %s" %( session_path , err ))
+                        continue    
+                else:
+                    logger.error( "Error during session %s loading: %s" % ( session_path , err ) )
+                    continue
+        except MobyleError, me:
+            logger.error( "Error during session %s loading: %s" % ( session_path , me ) )
+            continue
+            
+        
+        
+        
+        
+        
+if __name__ == "__main__":
+    import atexit
+    now = time()
+    from optparse import OptionParser
+
+    parser = OptionParser( )
+    parser.add_option( "-j" , "--jobs",
+                       action = "store_true", 
+                       dest = "jobs",
+                       default = False ,
+                       help = "Clean jobs (programs and workflows).")
+    
+    parser.add_option( "-s" , "--sessions",
+                       action = "store_true", 
+                       dest = "sessions",
+                       default = False ,
+                       help = "Clean anonymous sessions.")
+    
+    parser.add_option( "-d" , "--delay",
+                       action = "store", 
+                       type = 'int', 
+                       dest = "delay",
+                       default = config.remainResults() ,
+                       help = "Delete jobs/sessions older than <DELAY> days ( positive integer value ).")
+    
+    parser.add_option( "-l" , "--log",
+                       action = "store", 
+                       type = 'string', 
+                       dest = "log_file",
+                       help = "Path to the Logfile where put the logs.")
+    
+    parser.add_option( "-n" , "--dry-run",
+                       action = "store_true", 
+                       dest = "dry_run",
+                       help = "don't actually do anything.")
+    
+    parser.add_option( "-v" , "--verbose",
+                      action= "count", 
+                      dest = "verbosity", 
+                      default = 0 ,
+                      help = "increase the verbosity level. There is 4 levels: Error messages (default), Warning (-v), Info (-vv) and Debug.(-vvv)") 
+    
+    parser.add_option( "-q" , "--quiet" ,
+                      action= "store_true" , 
+                      dest = "quiet" , 
+                      default = False ,
+                      help = "disable messages on standard error") 
+    
+    options, args = parser.parse_args()
+    
+    
+    
+    if not options.jobs and not options.sessions :
+        options.jobs = True
+        options.sessions =True
+
+    import logging  
+
+    cleaner_handlers = []
+    
+    if options.quiet :
+        cleaner_handlers.append( logging.FileHandler( '/dev/null' , 'a' ) )
+    if options.log_file :
+        try:
+            cleaner_handlers.append( logging.FileHandler( options.log_file , 'a' ) )
+        except(IOError , OSError) , err:
+            print >> sys.stderr , "cannot log messages in %s: %s" % ( options.log_file , err )
+            sys.exit(1)
+    else:
+        cleaner_handlers.append( logging.StreamHandler( sys.stderr ) )
+                
+    if options.verbosity == 0:
+        for h in cleaner_handlers: h.setLevel( logging.ERROR )
+    elif options.verbosity == 1:
+        for h in cleaner_handlers: h.setLevel( logging.WARNING )
+    elif options.verbosity == 2:
+        for h in cleaner_handlers: h.setLevel( logging.INFO )
+    elif options.verbosity == 3:
+        for h in cleaner_handlers: h.setLevel( logging.DEBUG )
+        
+    if options.verbosity < 3:
+        cleaner_formatter = logging.Formatter( '%(filename)-10s : %(levelname)-8s : %(asctime)s : %(message)s', '%a, %d %b %Y %H:%M:%S' )
+    else:
+        cleaner_formatter = logging.Formatter( '%(filename)-10s : %(levelname)-8s : L %(lineno)d : %(asctime)s : %(message)s', '%a, %d %b %Y %H:%M:%S' )        
+    for h in cleaner_handlers: 
+        h.setFormatter( cleaner_formatter )
+    logger = logging.getLogger( 'cleaner' )
+    for h in cleaner_handlers:
+        logger.addHandler( h )
+    
+    mail_handler = logging.handlers.SMTPHandler( config.mailhost(),
+                                                 config.sender() ,
+                                                 config.maintainer()  ,
+                                                 '[ %s ] Mobyle cleaner problem' % config.root_url()
+                                                )
+    mail_handler.setLevel( logging.CRITICAL )
+    mail_handler.setFormatter( logging.Formatter( '%(filename)-10s : %(levelname)-8s : L %(lineno)d : %(asctime)s : %(message)s', '%a, %d %b %Y %H:%M:%S' ) )
+    logger.addHandler( mail_handler )
+    
+    lock_file = os.path.join( config.results_path() , '.mobclean_lock' )
+    def remove_lock():
+        if os.path.exists( lock_file ):
+            try:
+                os.unlink( lock_file )
+            except Exception, err:
+                logger.critical( "cannot remove lock %s" %lock_file )
+    
+    if os.path.exists( lock_file ):
+        try:
+            f= file( lock_file , 'r')
+            pid = f.readline()
+            f.close()
+        except Exception, err:
+            pid = "UNKNOWN"
+        logger.critical( "a mobclean is already running (pid = %s), abort this one "%pid )
+        sys.exit(1)
+    else:
+        try:
+            f= file( lock_file , 'w')
+            f.write( str( os.getpid() ) )
+            f.close()
+        except Exception , err:
+            logger.critical( "cannot put a lock file, abort mobclean: %s" %err )
+            sys.exit(1)
+        atexit.register( remove_lock )
+
+    if options.jobs :
+        clean_jobs( config , now , options.delay , logger, dry_run = options.dry_run )
+    if options.sessions :
+        clean_sessions( config , now , options.delay , logger, dry_run = options.dry_run )
+        
+    
\ No newline at end of file
diff --git a/Tools/mobdeploy b/Tools/mobdeploy
new file mode 100755
index 0000000..cc71c78
--- /dev/null
+++ b/Tools/mobdeploy
@@ -0,0 +1,1134 @@
+#! /usr/bin/env python
+
+  
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron, Sandrine Larroude               #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+"""
+mobdeploy
+
+This script is used to update the list of deployed and imported services
+on the mobyle server, as well as the various index files.
+"""
+
+import sys, os, urllib2
+import simplejson
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+import shutil
+from lxml import etree
+from glob import glob
+import logging
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+from Mobyle.Registry import Registry, ServerDef, ProgramDef, WorkflowDef, ViewerDef, TutorialDef,registry as registry_from_existing
+from Mobyle import MobyleLogger, InterfacePreprocessor
+from Mobyle.Validator import Validator
+from Mobyle.SearchIndex import SearchIndex
+from Mobyle.ClassificationIndex import ClassificationIndex
+from Mobyle.DataInputsIndex import DataInputsIndex
+from Mobyle.DescriptionsIndex import DescriptionsIndex
+from Mobyle.DataTypeValidator import DataTypeValidator
+
+net_enabled_parser = etree.XMLParser(no_network=False)
+
+class ServicesDeployer(object):
+    
+    def __init__(self , verbosity = 0, useJing=False ):
+        self.useJing = useJing
+        self.tmp_dir = os.path.join( _cfg.repository_path() , 'tmp_deployment' )
+        self.tmp_servers_dir = os.path.join( self.tmp_dir , 'servers' )
+        self.tmp_indexes =  os.path.join( self.tmp_dir , 'index')
+        MobyleLogger.MLogger()
+        self.log = logging.getLogger('mobyle.registry' )
+        console = logging.StreamHandler(sys.stderr)
+        if verbosity == 0 :
+            self.log.setLevel( logging.WARNING )
+            formatter = logging.Formatter('%(levelname)-8s %(message)s')
+        elif verbosity == 1:
+            self.log.setLevel( logging.INFO )
+            formatter = logging.Formatter('%(levelname)-8s %(message)s')
+        else :
+            self.log.setLevel( logging.DEBUG )
+            formatter = logging.Formatter('L %(lineno)d : %(levelname)-8s %(message)s')
+        console.setFormatter(formatter)
+        self.log.addHandler(console)
+        self.config_registry = self.get_registry_from_config()
+        self.tpl_preprocessor=InterfacePreprocessor.InterfacePreprocessor()
+        self.dtv = DataTypeValidator()
+    
+    def makeTmp( self ):
+        """
+        create a temporary architecture to build new services, viewers and indexes ...
+        """
+        self.log.info( "creating temporary architecture for services pre-deployment")
+        if os.path.exists( self.tmp_dir ):
+            self.log.critical("%s already exists. Reasons for this can be that \n(1) another mobdeploy is running,\n(2) a previous mobdeploy crashed.\nRemove it before to run mobdeploy again"% self.tmp_dir )
+            sys.exit(1)
+        os.mkdir( self.tmp_dir )
+        os.mkdir( self.tmp_servers_dir )
+        os.mkdir( self.tmp_indexes )
+     
+        
+    def switchDeployement( self ):
+        """
+        replace deployed services, viewers, indexes by the new one 
+        """
+        self.log.info( "switching from pre-deployed to deployed" )
+        effective_services_path = _cfg.services_path()
+        old_deployement_path = os.path.join( _cfg.repository_path() , 'old_deployement' )
+        if os.path.exists( old_deployement_path ) :
+            shutil.rmtree( old_deployement_path )
+        if os.path.exists( effective_services_path ):
+            os.rename( effective_services_path , old_deployement_path )
+        os.rename( self.tmp_dir , effective_services_path )
+    
+    
+    def check_workflow_consistency( self , predeploy_registry ):
+        """
+        check if each task of local workflow is deployed
+        """
+        self.log.info( "check local workflows consitency" )
+        if predeploy_registry.serversByName.get('local') is not None:
+            local_server = predeploy_registry.serversByName[ 'local' ]
+            for workflow_def in local_server.workflows :
+                doc = etree.parse( workflow_def.path )
+                tasks_node = doc.findall( 'flow/task')
+                for task_node in tasks_node:
+                    service_task = task_node.get( 'service')
+                    server_task = task_node.get( 'server' , 'local' )
+                    try:
+                        predeploy_registry.serversByName[ server_task ].programsByName[ service_task ]
+                        continue
+                    except KeyError:
+                        try:
+                            predeploy_registry.serversByName[ server_task ].workflowsByName[ service_task ]
+                            continue
+                        except KeyError:
+                            self.log.warning( "the service %s.%s required by workflow %s is missing." %(
+                                                                                                          server_task ,
+                                                                                                          service_task ,
+                                                                                                          workflow_def.name
+                                                                                                          ) )
+                            self.log.warning( "workflow %s is removed until service %s.%s is deployed" %(workflow_def.name ,
+                                                                                                               server_task ,
+                                                                                                               service_task ,
+                                                                                                               ) )
+                            predeploy_registry.pruneService( workflow_def )
+                            try:
+                                os.unlink( workflow_def.path )
+                            except Exception, err:
+                                self.log.error( "unable to remove illegitimate workflow %s : %s" %(workflow_def.name , err ) )
+                            
+                        
+    def doCommand( self , cmd , server_names , programs_name , workflows_names , viewers_name , tutorials_names, force = False ):
+        """
+    
+        """
+        self.makeTmp()
+        if cmd != 'index':
+            cmd_registry = self.get_registry_from_args( server_names , programs_name , workflows_names , viewers_name, tutorials_names)
+            
+            self._recoverFromExisting( cmd_registry, server_names, programs_name, workflows_names, viewers_name, tutorials_names)
+            self._do_cmd( cmd , cmd_registry , force = force )
+        idx_registry= Registry()
+        idx_registry.servers_path = self.tmp_servers_dir
+        idx_registry.load()
+        self.check_workflow_consistency( idx_registry )
+        self.make_indexes(idx_registry)
+        self.switchDeployement()
+        
+    def get_registry_from_args(self, server_names, programs_names, workflows_names , viewers_names, tutorials_names):
+        """
+        build a registry based on the arguments 
+        @param server_names: the portals name as defined in the configuration where the services can be found, 
+          - 'local' mean this portal
+          - 'all' mean all the server describe in the config
+        @type server_names: list of string.
+        @param programs_names: the names of the programs, 
+          - 'all' mean all the programs for a server describe in the config 
+        @type programs_names: list of string
+        @param workflows_names: the names of the workflows, 
+          - 'all' mean all the workflows for a server describe in the config 
+        @type workflows_names: list of string
+        @param viewers_names: the names of the viewers, 
+          - 'all' mean all the viewers (only local server has viewers ). 
+        @type viewers_names: list of string
+        @param tutorials_names: the names of the tutorials, 
+          - 'all' mean all the tutorials (only local server has tutorials ). 
+        @type tutorials_names: list of string
+        @return: a registry builded from the arguments
+        @rtype: L{Mobyle.Registry} instance
+        """
+        args_registry = Registry()
+        if server_names == ['all']:
+            server_names = _cfg.portals().keys()
+            server_names.append( 'local' )
+        for server_name in server_names:
+            try:
+                server_from_conf = self.config_registry.serversByName[ server_name ]
+            except KeyError:
+                self.log.warning( "the server %s is not defined in the Mobyle Config, it will be skipped" %server_name )
+                continue
+            server = ServerDef( name = server_from_conf.name ,
+                                url  =  server_from_conf.url ,
+                                help = server_from_conf.help ,
+                                repository = server_from_conf.repository ,
+                                jobsBase = server_from_conf.jobsBase
+                               )
+            args_registry.addServer( server )
+            if programs_names == ['all']:
+                programs_names_for_this_server = [ p.name for p in server_from_conf.programs ]
+            else:
+                programs_names_for_this_server = programs_names
+            for name in programs_names_for_this_server:
+                if server.name == 'local':
+                    try:
+                        url = server_from_conf.programsByName[ name ].url
+                        path = server_from_conf.programsByName[ name ].path
+                    except KeyError:
+                        custom_path = os.path.join( _cfg.mobylehome() , 'Local' , 'Services' , 'Programs' , name +'.xml' )
+                        if os.path.exists( custom_path ):
+                            url = "file://%s" %custom_path
+                            path = custom_path
+                        else:
+                            #I put the public_path in service even this service is not exist
+                            #this test is performed by is_available
+                            #and if cmd is deploy a warnig is raised and service skipped
+                            #    if cmd is removed the service will be removed form the next deployement
+                            #fix bug #438
+                            public_path = os.path.join( _cfg.mobylehome() , 'Services' , 'Programs' , name +'.xml' )
+                            url = "file://%s" %public_path
+                            path =  public_path
+                else:
+                    url = args_registry.getProgramUrl( name, server.name )
+                    path = None
+                service = ProgramDef( name = name,
+                                        url = url,
+                                        path = path ,
+                                        server = server
+                                    )
+                args_registry.addProgram( service )
+            
+            if workflows_names == ['all']:
+                workflows_names_for_this_server = [ s.name for s in server_from_conf.workflows ]
+            else:
+                workflows_names_for_this_server = workflows_names
+            for name in workflows_names_for_this_server:
+                if server.name == 'local':
+                    try:
+                        url = server_from_conf.workflowsByName[ name ].url
+                        path = server_from_conf.workflowsByName[ name ].path
+                    except KeyError:
+                        custom_path = os.path.join( _cfg.mobylehome() , 'Local' , 'Services' , 'Workflows' , name +'.xml' )
+                        if os.path.exists( custom_path ):
+                            url = "file://%s" %custom_path
+                            path = custom_path
+                        else:
+                            public_path = os.path.join( _cfg.mobylehome() , 'Services' , 'Workflows' , name +'.xml' )
+                            url = "file://%s" %public_path
+                            path =  public_path
+                else:
+                    url = args_registry.getWorkflowUrl( name, server.name)
+                    path = None
+
+                workflow = WorkflowDef( name = name,
+                                        url = url,
+                                        path = path,
+                                        server = server
+                                    )
+                args_registry.addWorkflow( workflow )
+            
+            if server_name == 'local':
+                if viewers_names == ['all']:
+                    viewers_names = [ v.name for v in server_from_conf.viewers ]
+                for name in viewers_names:
+                    try:
+                        url = server_from_conf.viewersByName[ name ].url
+                        path = server_from_conf.viewersByName[ name ].path
+                    except KeyError:
+                        custom_path = os.path.join( _cfg.mobylehome() , 'Local' , 'Services' , 'Viewers' , name +'.xml' )
+                        if os.path.exists( custom_path ):
+                            url = "file://%s" %custom_path
+                            path = custom_path
+                        else:
+                            public_path = os.path.join( _cfg.mobylehome() , 'Services' , 'Viewers' , name +'.xml' )
+                            url = "file://%s" %public_path
+                            path = public_path
+                    viewer = ViewerDef( name = name ,
+                                        url = url ,
+                                        path = path ,
+                                        server = server
+                                        )
+                    args_registry.addViewer( viewer ) 
+                    
+                if tutorials_names == ['all']:
+                    tutorials_names = [ t.name for t in server_from_conf.tutorials ]
+                for name in tutorials_names:
+                    try:
+                        url = server_from_conf.tutorialsByName[ name ].url
+                        path = server_from_conf.tutorialsByName[ name ].path
+                    except KeyError:
+                        custom_path = os.path.join( _cfg.mobylehome() , 'Local' , 'Services' , 'Tutorials' , name +'.xml' )
+                        if os.path.exists( custom_path ):
+                            url = "file://%s" %custom_path
+                            path = custom_path
+                        else:
+                            public_path = os.path.join( _cfg.mobylehome() , 'Services' , 'Tutorials' , name +'.xml' )
+                            url = "file://%s" %public_path
+                            path = public_path
+                    tutorial = TutorialDef( name = name ,
+                                        url = url ,
+                                        path = path ,
+                                        server = server
+                                        )
+                    args_registry.addTutorial( tutorial )   
+        return args_registry
+
+
+    def get_registry_from_config(self ):
+        """
+        build a registry based on the configuration ( as described by 
+        LOCAL_DEPLOY_INCLUDE , LOCAL_DEPLOY_EXCLUDE and PORTALS )
+        @return: a registry builded from the configuartion
+        @rtype: L{Mobyle.Registry} instance
+        """
+        config_reg = Registry()
+        customPrefix = os.path.join ( _cfg.mobylehome() , "Local" , "Services" )
+        publicPrefix = os.path.join ( _cfg.mobylehome() , "Services" )
+        
+        def _include( services , service_type ):
+            """
+            @param services: a container to store service name and path
+            @type services: dict
+            @param service_type: the type of the service (programs, workflows, viewer ,... )
+            @type service_type: string
+            @return: the list of local (from this server) service file paths include in deploy
+            @rtype: list of strings
+            """
+            for mask in _cfg.services_deployment_include( service_type ):
+                for path in _uniq( glob( os.path.join( publicPrefix , service_type[0].upper() + service_type[1:] , mask + '.xml' )) , 
+                                        glob( os.path.join( customPrefix , service_type[0].upper() + service_type[1:] ,mask + '.xml' ))
+                                        ):
+                    services[ os.path.basename( path )[:-4] ] = path
+            return services
+            
+        def _exclude( services , service_type ):
+            """
+            @param services: a container to store service name and path
+            @type services: dict
+            @param service_type: the type of the service (programs, workflows, viewer ,... )
+            @type service_type: string
+            @return: the list of local (from this server) service file paths to exclude from deploy
+            @rtype: list of strings
+            """
+            for mask in _cfg.services_deployment_exclude( service_type ):
+                for path in _uniq( glob( os.path.join( publicPrefix , service_type[0].upper() + service_type[1:], mask + '.xml' )) ,
+                                        glob( os.path.join( customPrefix , service_type[0].upper() + service_type[1:] , mask + '.xml' ))
+                                        ):
+                    try:
+                        del( services[ os.path.basename( path )[:-4] ] )
+                    except KeyError:
+                        pass
+            return services         
+        
+        def _uniq( publicPath , customPath ):
+            """
+            @param publicPath: the list of service definition in the MOBYLEHOME/Services/xx
+            @type publicPath: list of strings
+            @param customPath: the list of service definition in the MOBYLEHOME/Local/Services/xx
+            @type customPath: list of strings
+            @return: a list containing one xml uri per service with the priority to the custom xml   
+            @rtype: list of string
+            """
+            result = {}
+            for path in publicPath:
+                result[ os.path.basename( path )[:-4] ] = path
+            for path in customPath:
+                result[ os.path.basename( path )[:-4] ] = path
+            return result.values()
+        
+        local_server = ServerDef( name = 'local',
+                                  url = _cfg.cgi_url() ,
+                                  help = 'foo at bar',
+                                  repository = _cfg.repository_url() ,
+                                  jobsBase = _cfg.results_url()
+                                  )
+        config_reg.addServer(local_server)
+        
+        #for method in _cfg.services_deployment_order():
+        #    getattr( self.get_registry_from_config , '_' + method )( local_services )
+        #    eval( '_%s( local_services )' %method ) 
+        
+        ##############################
+        #
+        # local services
+        #
+        ################################
+        local_programs = {}    
+        local_programs = _include( local_programs , 'programs' )
+        local_programs = _exclude( local_programs , 'programs' )
+        #program    
+        for program_name , program_path in local_programs.items():
+            program = ProgramDef( name = program_name,
+                                  url = "file://%s" %program_path ,
+                                  path = program_path,
+                                  server = local_server
+                                )  
+            config_reg.addProgram( program )    
+        
+        #workflow
+        local_workflows = {}
+        local_workflows = _include( local_workflows , 'workflows' )
+        local_workflows = _exclude( local_workflows , 'workflows' )
+        
+        for workflow_name , workflow_path in local_workflows.items():
+            workflow = WorkflowDef( name = workflow_name,
+                                  url = "file://%s" %workflow_path ,
+                                  path = workflow_path,
+                                  server = local_server
+                                )  
+            config_reg.addWorkflow( workflow )   
+        
+        #viewer  
+        local_viewers = {}
+        local_viewers = _include( local_viewers , 'viewers' )
+        local_viewers = _exclude( local_viewers , 'viewers' )
+        for viewer_name , viewer_path in local_viewers.items():
+            viewer = ViewerDef( name = viewer_name,
+                                 url = "file://%s" %viewer_path ,
+                                  path = viewer_path,
+                                  server = local_server
+                                )  
+            config_reg.addViewer( viewer )   
+       
+        #tutorials  
+        local_tutorials = {}
+        local_tutorials = _include( local_tutorials , 'tutorials' )
+        local_tutorials = _exclude( local_tutorials , 'tutorials' )
+        for tutorial_name , tutorial_path in local_tutorials.items():
+            tutorial = TutorialDef( name = tutorial_name,
+                                  url = "file://%s" %tutorial_path ,
+                                  path = tutorial_path,
+                                  server = local_server
+                                )  
+            config_reg.addTutorial( tutorial ) 
+        ##############################
+        #
+        # imported services
+        #
+        ################################    
+        effective_servers_path =  _cfg.servers_path()   
+        for server_name, properties in _cfg.portals().items():
+            server = ServerDef( name = server_name,
+                                url = properties['url'] ,
+                                help = properties['help'],
+                                repository = properties['repository'],
+                                jobsBase = "%s/data/jobs" %properties['repository']
+                               )
+            config_reg.addServer(server)
+            if 'services' in properties:
+                #program
+                if 'programs' in properties[ 'services' ]:
+                    for prg_name in properties[ 'services' ][ 'programs' ]:
+                        url = config_reg.getProgramUrl( prg_name, server.name )
+                        program = ProgramDef( name = prg_name,
+                                              url = url,
+                                              path = os.path.join( effective_servers_path , server.name , ProgramDef.directory_basename , prg_name , '.xml') ,
+                                              server = server
+                                            )
+                        config_reg.addProgram(program)
+                #workflow    
+                if 'workflows' in properties[ 'services' ]:
+                    for wf_name in properties[ 'services' ][ 'workflows' ]:
+                        url = config_reg.getWorkflowUrl( wf_name, server.name)
+                        workflow = WorkflowDef( name = wf_name,
+                                              url = url,
+                                              path = os.path.join( effective_servers_path , server.name , WorkflowDef.directory_basename , wf_name , '.xml') ,
+                                              server = server
+                                            )
+                        config_reg.addWorkflow( workflow )
+        return config_reg
+    
+                   
+        
+    def _recoverFromExisting(self, args_registry, server_names, programs_name, workflows_names, viewers_name, tutorials_names):
+        """
+        based on the informations from the registry_from_existing recover the services definitions
+        which are not specified on the command line and recover for the new deployement.
+        @param args_registry: the registry build from the command line options.
+        @type args_registry: a L{Mobyle.Registry} instance
+        """
+        for server in registry_from_existing.servers if not 'all' in server_names else ():
+            if server.programs or server.workflows or server.viewers or server.tutorials :
+                tmp_server_path = os.path.join( self.tmp_servers_dir , server.name )
+                try:
+                    if os.path.isdir( tmp_server_path ):
+                        shutil.rmtree( tmp_server_path )
+                    os.mkdir( tmp_server_path , 0775 )
+                    if server.programs:
+                        os.mkdir( os.path.join( tmp_server_path , ProgramDef.directory_basename ) , 0775 ) 
+                    if server.workflows:
+                        os.mkdir( os.path.join( tmp_server_path , WorkflowDef.directory_basename ) , 0775 ) 
+                    if server.viewers:
+                        os.mkdir( os.path.join( tmp_server_path , ViewerDef.directory_basename ) , 0775 ) 
+                    if server.tutorials:
+                        os.mkdir( os.path.join( tmp_server_path , TutorialDef.directory_basename ) , 0775 ) 
+                    
+                except (OSError , IOError ), err :
+                    self.log.critical( "cannot create temporary directories : %s" %err )
+                    sys.exit(1)
+                if server.name in server_names:
+                    programs_2_recover = server.programs if not 'all' in programs_name else ()
+                else:
+                    programs_2_recover = server.programs
+                for program in programs_2_recover:
+                    if not program in args_registry.programs:
+                        try:
+                            self._getOldXmlAsIs( program )
+                        except Exception , err:
+                            self.log.error("cannot retrieve xml corresponding to program %s.%s from the deployed version : %s" %(server.name,
+                                                                                                                     program.name , 
+                                                                                                                      err,
+                                                                                                                      ) ,
+                                                                                                                      exc_info = True
+                            )
+                if server.name in server_names:
+                    workflows_2_recover = server.workflows if not 'all' in workflows_names else ()
+                else:
+                    workflows_2_recover = server.workflows
+                for workflow in workflows_2_recover:
+                    if not workflow in args_registry.workflows :
+                        try:
+                            self._getOldXmlAsIs( workflow )
+                        except Exception , err:
+                            self.log.error("cannot retrieve xml corresponding to workflow %s.%s from the deployed version  : %s" %(server.name,
+                                                                                                                      workflow.name ,
+                                                                                                                      err
+                                                                                                                      ))
+                if server.name in server_names:
+                    viewers_2_recover = server.viewers if not 'all' in viewers_name else ()
+                else:
+                    viewers_2_recover = server.viewers
+                for viewer in viewers_2_recover:            
+                    if not viewer in args_registry.viewers :
+                        try:
+                            self._getOldXmlAsIs( viewer )
+                        except Exception , err:
+                            self.log.error("cannot retrieve xml corresponding to viewer %s.%s from the deployed version  : %s" %(server.name,
+                                                                                                                      viewer.name ,
+                                                                                                                      err
+                                                                                                                      ))
+                if server.name in server_names:
+                    tutorials_2_recover = server.tutorials if not 'all' in tutorials_names else ()
+                else:
+                    tutorials_2_recover = server.tutorials
+                for tutorial in tutorials_2_recover:            
+                    if not tutorial in args_registry.tutorials :
+                        try:
+                            self._getOldXmlAsIs( tutorial )
+                        except Exception , err:
+                            self.log.error("cannot retrieve xml corresponding to tutorial %s.%s from the deployed version  : %s" %(server.name,
+                                                                                                                      tutorial.name ,
+                                                                                                                      err
+                                                                                                                      ))
+                
+    def _do_cmd( self , cmd , cmd_registry , force = False ):
+        """
+        execute the command cmd on the services defined in the cmd_registry
+        @param cmd: the command to execute on the services to prepare a new deployment:
+          -deploy to deploy a service
+          -clean to remove a service
+        @type cmd: string 
+        @param cmd_registry: the registry build based on the arguments specified on the command line
+        @type cmd_registry: a L{Mobyle.Registry} instance
+        @param force: force the execution of the command even the service  is not exported
+        """
+        for server in cmd_registry.servers:
+            if server.name!='local':
+                try:
+                    req = urllib2.Request(server.url+"/net_services.py")
+                    handle = urllib2.urlopen(req)
+                    resp = handle.read()
+                    server.services_availability_properties =  simplejson.loads(resp)
+                except Exception, e:
+                    self.log.error( "error retrieving server properties for server %s, no service will be imported from it" % server.name )
+                    continue
+            tmp_server_path = os.path.join( self.tmp_servers_dir , server.name )
+            if not os.path.isdir( tmp_server_path ):
+                try:
+                    os.mkdir( tmp_server_path , 0775 )
+                except (OSError , IOError ), err :
+                    self.log.critical( "cannot create temporary directories %s : %s" %( tmp_server_path , err) )
+                    sys.exit(1)
+            if server.programs:    
+                tmp_programs_path = os.path.join( tmp_server_path , ProgramDef.directory_basename )
+                if not os.path.isdir( tmp_programs_path ):
+                    try:
+                        os.mkdir( tmp_programs_path , 0775 )
+                    except (OSError , IOError ), err :
+                        self.log.critical( "cannot create temporary directories %s : %s" %( tmp_programs_path , err) )
+                        sys.exit(1)
+            if server.workflows:    
+                tmp_workflows_path = os.path.join( tmp_server_path , WorkflowDef.directory_basename )
+                if not os.path.isdir( tmp_workflows_path ):
+                    try:
+                        os.mkdir( tmp_workflows_path , 0775 )
+                    except (OSError , IOError ), err :
+                        self.log.critical( "cannot create temporary directories %s : %s" %( tmp_workflows_path , err) )
+                        sys.exit(1)  
+            if server.viewers:    
+                tmp_viewers_path = os.path.join( tmp_server_path , ViewerDef.directory_basename )
+                if not os.path.isdir( tmp_viewers_path ):
+                    try:
+                        os.mkdir( tmp_viewers_path , 0775 ) 
+                    except (OSError , IOError ), err :
+                        self.log.critical( "cannot create temporary directories %s : %s" %( tmp_viewers_path , err) )
+                        sys.exit(1)           
+
+            if server.tutorials:    
+                tmp_tutorials_path = os.path.join( tmp_server_path , TutorialDef.directory_basename )
+                if not os.path.isdir( tmp_tutorials_path ):
+                    try:
+                        os.mkdir( tmp_tutorials_path , 0775 ) 
+                    except (OSError , IOError ), err :
+                        self.log.critical( "cannot create temporary directories %s : %s" %( tmp_tutorials_path , err) )
+                        sys.exit(1) 
+            for service in server.programs + server.workflows + server.viewers + server.tutorials  :
+                if cmd == 'deploy':
+                    tmp_service_path = os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" )
+                    if not self.is_available( service ):
+                        if server.name == 'local':
+                            self.log.warning( "the server local does not have %s %s.  This service will not be deployed" %(service.type, service.name) )
+                            continue
+                        else:
+                            if force:
+                                self.log.warning( "the server %s does not export %s %s." %(server.name , service.type, service.name ) )
+                            else:
+                                self.log.warning( "the server %s does not export %s %s. This service will not be deployed" %(service.type,
+                                                                                                                             server.name ,
+                                                                                                                             service.name 
+                                                                                                                             ) )
+                                continue
+                    if not self.config_registry.has_service(service) :
+                        self.log.warning( "The %s %s.%s is not in the Config. When you want to clean this service, you must do it explicitly or add it to the Config." %( service.type,
+                                                                                                                                                                          server.name ,
+                                                                                                                                                                          service.name
+                                                                                                                                                                       ))
+                    try:
+                        self.log.debug("copying %s to %s" % (service.url, tmp_service_path))
+                        self.getXml( service.url , tmp_service_path )
+                    except Exception , err :
+                        self.log.error("the %s %s.%s cannot be retrieved: %s"%( service.type, server.name , service.name , err ), exc_info=True)
+                        if registry_from_existing.has_service( service ):
+                            try:
+                                self._getOldXmlAsIs( registry_from_existing.serversByName[ service.server.name ].programsByName[ service.name ] )
+                                self.log.error( "the %s %s.%s has been recovered from the already deployed version"%( service.type, server.name, service.name ))
+                                continue
+                            except Exception, err:
+                                self.log.error( str(err) ,exc_info = True )
+                                continue
+                        else:
+                            cmd_registry.pruneService( service )
+                            self.log.error( "the %s %s.%s cannot be deployed "%( service.type, server.name, service.name ) ) 
+                            continue 
+                    
+                    #validate the xml
+                    try:
+                        validate = self._validateOrDie( service )
+                        if not(validate):
+                            raise Exception("service validation failed")
+                        if isinstance( service , ViewerDef ) or isinstance( service , TutorialDef ):
+                            try:
+                                src = service.path[:-4]
+                                dst = os.path.join( tmp_server_path , service.directory_basename , service.name )
+                                if os.path.exists(src):
+                                    shutil.copytree( src , dst)
+                                else:
+                                    if isinstance( service , ViewerDef ):
+                                        raise Exception( "there is no directory corresponding to viewer %s "%service.name )
+                            except Exception , err:
+                                self.log.error( "the archive corresponding to the %s %s cannot be retrieved : %s" %( service.type ,
+                                                                                                                     service.name , 
+                                                                                                                       err ))
+                                #remove the new xml before to recover the old one
+                                #because the xml is 0444 and it cannot be replace by the old one
+                                os.unlink( tmp_service_path ) 
+                                try:
+                                    self._getOldXmlAsIs( registry_from_existing.serversByName[ service.server.name ].programsByName[ service.name ] )
+                                    self.log.warning( "the %s %s.%s has been recovered from the already deployed version"%( service.type, server.name , service.name ),
+                                                    exc_info = True )
+                                    continue #the xml has been already processed
+                                except Exception, err:
+                                    #the message is already logged in _getOldXmlAsIs
+                                    try:
+                                        os.unlink( tmp_service_path )
+                                    except:
+                                        pass
+                                    try:
+                                        os.unlink( dst )
+                                    except :
+                                        pass
+                                    continue
+                    except Exception, err:
+                        self.log.error( "the service %s.%s does not validate : %s"%( server.name , service.name , err ))
+                        self.log.error( "the service %s.%s will not be deployed : %s"%( server.name , service.name , err ))
+                        os.unlink( tmp_service_path )
+                        if isinstance( service , ViewerDef ):
+                            path = os.path.join( tmp_server_path , service.directory_basename , service.name )
+                            shutil.rmtree( path )
+                        if registry_from_existing.has_service( service ):
+                            try:
+                                self._getOldXmlAsIs( registry_from_existing.serversByName[ service.server.name ].programsByName[ service.name ] )
+                                self.log.error( "the service %s.%s has been recovered from the already deployed version"%( server.name , service.name ))
+                                continue
+                            except Exception, err:
+                                self.log.error( err )
+                                continue
+                        else:
+                            cmd_registry.pruneService( service )
+                            self.log.error( "the service %s.%s cannot be deployed "%( server.name , service.name ) ) 
+                            continue 
+                          
+                    #process the xml
+                    try:
+                        self.log.debug('processing service at url %s' % service.url)
+                        self.process_service( tmp_service_path )                        
+                    except Exception , err :
+                        self.log.error( "the %s %s.%s cannot be transformed : %s"%( service.type, server.name , service.name , err ), exc_info=True)
+                        os.unlink( tmp_service_path )
+                        if isinstance( service , ViewerDef ):
+                            path = os.path.join( tmp_server_path , service.directory_basename , service.name )
+                            shutil.rmtree( path )
+                        if registry_from_existing.has_service(service):
+                            try:
+                                self._getOldXmlAsIs( registry_from_existing.serversByName[ service.server.name ].programsByName[ service.name ] )
+                                self.log.error( "the %s %s.%s has been recovered from the already deployed version"%( service.type, server.name, service.name ))
+                                continue
+                            except Exception, err:
+                                cmd_registry.pruneService( service )
+                                self.log.error( "the %s %s.%s cannot be deployed "%( service.type, server.name , service.name ) ) 
+                                continue
+                        else:
+                            cmd_registry.pruneService( service )
+                            self.log.error( "the %s %s.%s cannot be deployed "%( service.type, server.name , service.name ) ) 
+                            continue   
+                                       
+                elif cmd == 'clean':
+                    cmd_registry.pruneService( service )
+                    if self.config_registry.has_service( service ) :
+                        self.log.warning( "the %s %s.%s is in the Config. If you want to delete  it permanently from your portal remove it from the Config." %( service.type, server.name , service.name) )
+                        continue
+                    
+                    
+            ###############################
+            # clean the empty directories #
+            ###############################
+            tmp_viewers_path = os.path.join( tmp_server_path , ViewerDef.directory_basename )
+            if os.path.isdir( tmp_viewers_path ) and not os.listdir( tmp_viewers_path ):
+                try:
+                    self.log.debug( "cleaning %s.viewers dir" %server.name )
+                    os.rmdir( tmp_viewers_path )
+                except (OSError , IOError ), err :
+                    self.log.debug( "ERROR during cleaning %s.viewers : %s "%(server.name , err) )
+            
+            tmp_tutorials_path = os.path.join( tmp_server_path , ViewerDef.directory_basename )
+            if os.path.isdir( tmp_tutorials_path ) and not os.listdir( tmp_tutorials_path ):
+                try:
+                    self.log.debug( "cleaning %s.tutorials dir" %server.name )
+                    os.rmdir( tmp_tutorials_path )
+                except (OSError , IOError ), err :
+                    self.log.debug( "ERROR during cleaning %s.tutorials : %s "%(server.name , err) )        
+            
+            tmp_workflows_path = os.path.join( tmp_server_path , WorkflowDef.directory_basename )
+            if os.path.isdir( tmp_workflows_path ) and not os.listdir( tmp_workflows_path ) :
+                try:
+                    self.log.debug( "cleaning %s.workflows dir" %server.name )
+                    os.rmdir( tmp_workflows_path )
+                except (OSError , IOError ), err :
+                    self.log.debug( "ERROR during cleaning %s.workflows : %s "%(server.name , err) )
+            
+            tmp_programs_path = os.path.join( tmp_server_path , ProgramDef.directory_basename )
+            if os.path.isdir( tmp_programs_path ) and not os.listdir( tmp_programs_path ) :
+                try:
+                    self.log.debug( "cleaning %s.programs dir" %server.name )
+                    os.rmdir( tmp_programs_path )
+                except (OSError , IOError ), err :
+                    self.log.debug( "ERROR during cleaning %s.programs : %s "%(server.name , err) )
+            
+            if os.path.isdir( tmp_server_path ) and not os.listdir( tmp_server_path ) :
+                try:
+                    self.log.debug( "cleaning %s dir" %server.name )
+                    os.rmdir( tmp_server_path )
+                except (OSError , IOError ), err :
+                    self.log.debug( "ERROR during cleaning %s : %s "%(server.name , err) )
+                    
+                    
+                    
+    def is_available(self , service ):
+        """
+        @param service:
+        @type service:
+        @return: 
+        @rtype: boolean
+        """
+        if service.server.name == 'local':
+            if os.path.exists( service.path ):
+                return True
+            else:
+                return False
+        else:
+            ap = service.server.services_availability_properties.get(service.name)
+            av = True
+            if ap is None:
+                self.log.error( "import error: service %s is not present on server %s, import cancelled."%(service.name , service.server.name) )
+                av = False
+            else:
+                #remote service exists, continue
+                if ap['disabled']:
+                    self.log.warning( "import error: service %s is disabled on server %s, import cancelled."%(service.name , service.server.name) )
+                    av = False
+                #remote service enabled, continue
+                if not(ap['authorized']):
+                    self.log.warning( "import error: service %s is not authorized for you on server %s, import cancelled."%(service.name , service.server.name) )
+                    av = False
+                if not(ap['exported']):
+                    self.log.warning( "import error: service %s is not exported on server %s, import cancelled."%(service.name , service.server.name) )
+                    av = False
+            return av
+        
+    def _getOldXmlAsIs(self , service ):
+        """
+        get the already deployed xml and copy it (hard link) in tmp directory 
+        @param service: the service 
+        @type service: a Registry.ProgramDef or Registry.WorkflowDef instance
+        """
+        self.log.info( "> RECOVER %s.%s definition from the deployed version" %( service.server.name , service.name ) )
+        tmp_server_path = os.path.join( self.tmp_servers_dir , service.server.name )
+        try:
+            os.link(  service.path , os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ) )
+        except Exception, err:
+            #if the src and dest are not on the same device
+            #an OSError: [Errno 18] Invalid cross-device link , is raised
+            self.log.debug("_getOldXmlAsIs os.link( %s , %s ) failed : %s"%( service.path ,
+                                                                             os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ),
+                                                                             err))
+            try:
+                shutil.copy( service.path , os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ) )
+            except IOError ,err:
+                msg = "can't copy service definition from %s to %s : %s" %( service.path ,
+                                                              os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ) ,
+                                                              err )
+                raise IOError( msg )
+        
+        if isinstance( service , ViewerDef ):
+            src = service.path[:-4]
+            dst = os.path.join( tmp_server_path , service.directory_basename , service.name )
+            try:    
+                shutil.copytree( src , dst)
+            except Exception , err:
+                try:
+                    os.unlink( os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ) )
+                except:
+                    pass
+                raise Exception( "can't copy viewer directory from %s to %s: %s"%( src ,
+                                                                                       dst ,
+                                                                                       err))
+                
+        if isinstance( service , TutorialDef ):
+            src = service.path[:-4]
+            dst = os.path.join( tmp_server_path , service.directory_basename , service.name )
+            if os.path.exists(src):
+                try:    
+                    shutil.copytree( src , dst)
+                except Exception , err:
+                    try:
+                        os.unlink( os.path.join( tmp_server_path , service.directory_basename , service.name + ".xml" ) )
+                    except:
+                        pass
+                    raise Exception( "can't copy viewer directory from %s to %s: %s"%( src ,
+                                                                                           dst ,
+                                                                                           err))
+                                    
+         
+        
+    def getXml(self , uri , dest_path ):
+        """
+        get the XML corresponding to uri , resolve the Xinclude and write the 
+        resulting XML to dest_path
+        @param uri: the uri of the xml 
+        @type uri: string
+        @param src_path: the path to write the xml after Xinclude resolution
+        @type src_path: string
+        """
+        doc_tree = etree.parse( uri, parser=net_enabled_parser)
+        doc_tree.xinclude()
+        dest_file = open( dest_path, "w" )
+        dest_file.write( etree.tostring( doc_tree ) )
+        dest_file.close()
+            
+    def template(self, programpath):
+        """
+        Pre-process the xsl on program xml definitions
+        @param programpath : xml path file to process
+        @type programpath : string
+        @return: if the processing run well or not
+        @rtype: boolean
+        """        
+        doc = etree.parse(programpath)
+        if doc.getroot().tag=='workflow':
+            from Mobyle.Workflow import Parser
+            from Mobyle.WorkflowLayout import layout
+            p = Parser()
+            w = p.parse(programpath)
+            i_l_s = layout(w) # graph layout as an svg string
+            i_l_e = etree.XML(i_l_s) # graph layout as an element
+            g_i = w.find("head/interface[@type='graph']")
+            if g_i is None:
+                g_i = etree.SubElement(w.find('head'),'interface') # graph interface container element
+            g_i.clear()
+            g_i.set("type","graph")
+            g_i.append(i_l_e)
+            fileResult = open(programpath,"w")
+            fileResult.write(etree.tostring(w, pretty_print=True))
+            fileResult.close()
+            doc = etree.parse(programpath)
+
+        for style, params in self.XslPipe:
+            for p in params.keys():
+                if p == 'programUri':
+                    params[p] = "'"+programpath+"'"
+                    self.log.debug('programUri=%s' % programpath)
+            result = style(doc, **params)
+            doc = result
+
+        #write the result on the xml itself
+        try:
+            fileResult = open(programpath,"w")
+            fileResult.write(str(doc))
+            fileResult.close()
+            return True
+        except IOError, ex:
+            self.log.error("Problem with the html generation: %s." % ex)
+            return False  
+          
+    def process_service(self , tmp_service_path ):
+        """
+        @param tmp_service_path: the absolute path to find the new service definition
+        @type tmp_service_path:  ServiceDef instance
+        """
+        self.log.info( "processing %s" %tmp_service_path )
+        self.tpl_preprocessor.process_interface(tmp_service_path)
+        #self.template(tmp_service_path)
+        os.chmod( tmp_service_path , 0444 )
+    
+    def _validateOrDie(self, service ):    
+        """
+        checks if the program on the specified path validates.
+        logs validation errors
+        if it does not validate, the file is removed
+        Warning: validation should be done in the target path, to avoid file loss.
+        @param path: path to the program file
+        @type path: string
+        @return: True if it validates, False otherwise
+        @rtype: list of strings
+        """
+        self.log.info( "validating %s.%s (published on %s" % (service.server.name , service.name, service.url) )
+        try:
+            if service.path is not None:
+                docPath=service.path
+            else:
+                docPath=service.url
+            val = Validator(type = service.type, docPath = docPath, publicURI = service.url,runRNG_Jing=self.useJing)
+            val.run()
+            if not(val.valOk):
+                self.log.error("Testing : Deployment of %s.%s ABORTED, it does NOT validate. If it exists, the previous xml version is temporary kept. Details follow:" % (service.server.name , 
+                                                                                                                                                                           service.name) )
+                if val.runRNG and val.rngOk == False:
+                    self.log.error("* relax ng validation failed - errors detail:")
+                    for re in val.rngErrors:
+                        self.log.error(" - %s" % re)
+                if val.runRNG_Jing and not(val.rng_JingOk):
+                    self.log.error("* relax ng JING validation failed - errors detail:")
+                    for line in val.rng_JingErrors:
+                         self.log.error("- %s" % (line))
+                if val.runSCH and len(val.schErrors) > 0:
+                    self.log.error("* schematron validation failed - errors detail:")
+                    for se in val.schErrors:
+                        self.log.error(" - %s" % se)       
+                return False
+            else:
+                # if it validates w.r.t. Validator rules, then try validating w.r.t the datatypes
+                errors = self.dtv.validateDataTypes(docPath=docPath)
+                #if len(errors)!=0:
+                #    self.log.error("* datatypes validation failed - errors detail:")
+                #    for error in errors:
+                #        self.log.error(" - %s" % error)
+                #    return False
+            return True
+        except Exception, err:
+            self.log.error("the service %s on server %s does not validate(url=%s): %s" % ( service.name, service.server.name, service.url, err ), exc_info=True)
+            return False    
+
+    
+    def make_indexes( self, registry ):
+        """
+        attention pour le moment j'ai enleve l'argument kind. a voir 
+        dans quelles conditions on fait quoi 
+        workflow, program , viewer ...
+        generate index files corresponding to the deployed service
+        @param kind: le type d'objet sur lequel faire les index ????
+        @type kind: string
+        """
+        self.log.info("Regenerating indexes:")
+        for kind in ["program","workflow","viewer", "tutorial"]:
+            self.log.info("Regenerating search index for %s..." % kind)
+            SearchIndex.generate(type=kind, dir=self.tmp_indexes, registry=registry)
+            self.log.info("Regenerating classification index for %s..." % kind)
+            ClassificationIndex.generate(type=kind, dir=self.tmp_indexes, registry=registry)
+            self.log.info("Regenerating descriptions index for %s..." % kind)
+            DescriptionsIndex.generate(type=kind, dir=self.tmp_indexes, registry=registry)
+            self.log.info("Regenerating inputs index for %s..." % kind)
+            DataInputsIndex.generate(type=kind, dir=self.tmp_indexes, registry=registry)
+             
+ 
+if __name__ == '__main__':
+    from optparse import OptionParser, OptionGroup
+    servers   = None
+    programs  = None
+    workflows = None
+    viewers   = None
+    tutorials = None
+    
+    usage = """usage: %prog [options] cmd
+cmds:
+    deploy   : deploy services
+    clean    : remove services
+    index    : build the indexes (deploy and clean imply index)
+
+If no options are provided the command is applied 
+on all services from all servers and all viewers.
+    """
+    parser = OptionParser( usage= usage )
+
+    service_group = OptionGroup(parser, "services related options" )
+
+    service_group.add_option("-p", "--programs",
+                      action="store", 
+                      type = 'string', 
+                      dest="programs",
+                      help="comma separated list of programs on which the command will be applied. The keyword 'all' can be used to install all programs defined in the Mobyle configuration. ")
+    
+    service_group.add_option("-w", "--workflows",
+                      action="store", 
+                      type = 'string', 
+                      dest="workflows",
+                      help="comma separated list of workflows on which the command will be applied. The keyword 'all' can be used to install all workflows defined in the Mobyle configuration. ")
+    
+    service_group.add_option("-s", "--servers",
+                      action="store", 
+                      type = 'string', 
+                      dest="servers",
+                      help="comma separated list of SERVERS on which the command will be applied. This option is meaningless with options -v -t. The keyword 'all' can be used to install all programs defined in the Mobyle configuration.")
+    
+    service_group.add_option("-f", "--force",
+                      action="store_true", 
+                      dest="force",
+                      default= False ,
+                      help="force the deployement of a service even the server does not export it. This option is reserved for debugging purpose.")
+    parser.add_option_group(service_group)
+
+    service_group.add_option("-j", "--jing",
+                      action="store_true", 
+                      dest="useJing",
+                      default= False ,
+                      help="Also validate using Jing, which should generate more helpful error messages. This option is reserved for debugging purpose.")
+    parser.add_option_group(service_group)
+    
+    viewer_group = OptionGroup(parser, "viewers related options" )
+    
+    viewer_group.add_option("-v", "--viewers",
+                      action="store", 
+                      type = 'string', 
+                      dest="viewers",
+                      help="comma separated list of VIEWERS on which the command will be applied. The keyword 'all' can be used to install all viewers defined in the Mobyle configuration.")
+    parser.add_option_group(viewer_group)
+
+    tutorial_group = OptionGroup(parser, "tutorials related options" )
+    service_group.add_option("-t", "--tutorials",
+                      action="store", 
+                      type = 'string', 
+                      dest="tutorials",
+                      help="comma separated list of TUTORIALS on which the command will be applied. The keyword 'all' can be used to install all viewers defined in the Mobyle configuration.")
+
+    general_group = OptionGroup(parser, "general options" )
+    general_group.add_option("-V", "--verbose",
+                      action="count", 
+                      dest="verbosity", 
+                      default= 0,
+                      help="increase the verbosity level. There is 3 levels: Warning (default) show Warning and Error messages , Info (-V) and Debug.(-VV)") 
+    parser.add_option_group(general_group)     
+    options, args = parser.parse_args()
+    if len( args ) != 1:
+        parser.print_help( sys.stderr )
+        sys.exit(1)
+    command = args[0]    
+    
+    
+    if options.servers is None :
+        options.servers   = 'all'
+    if options.programs is None and options.workflows is None and options.viewers is None and options.tutorials is None:
+        options.programs  = 'all'
+        options.viewers   = 'all'
+        options.workflows = 'all'
+        options.tutorials = 'all'
+        
+    if options.servers is not None:    
+        servers = options.servers.split( ',' )
+        if 'all' in servers:
+            servers = [ 'all' ]
+    if options.programs is not None:    
+        programs = options.programs.split( ',' )
+        if 'all' in programs:
+            programs =[ 'all' ]
+    else:
+        programs = []
+    if options.workflows is not None:    
+        workflows = options.workflows.split( ',' )
+        if 'all' in workflows:
+            workflows =[ 'all' ]  
+    else:
+        workflows = []
+    if options.viewers is not None:
+        viewers = options.viewers.split( ',' )
+        if 'all' in viewers:
+            viewers = [ 'all' ]
+    else:
+        viewers = []
+        
+    if options.tutorials is not None:
+        tutorials = options.tutorials.split( ',' )
+        if 'all' in tutorials:
+            tutorials = [ 'all' ]
+    else:
+        tutorials = []
+    deployer = ServicesDeployer( verbosity = options.verbosity, useJing=options.useJing )          
+    deployer.doCommand(command , servers, programs, workflows, viewers, tutorials, force= options.force )
\ No newline at end of file
diff --git a/Tools/mobemail b/Tools/mobemail
new file mode 100755
index 0000000..1615db4
--- /dev/null
+++ b/Tools/mobemail
@@ -0,0 +1,143 @@
+#! /bin/env python
+# -*- coding: utf-8 -*-
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron               #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os , sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit( 'MOBYLEHOME must be defined in your environment' )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+      
+      
+from hashlib import md5
+import os.path
+      
+from Mobyle.ConfigManager import Config  
+from Mobyle.Transaction import Transaction
+from Mobyle.Session import Session
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.Admin import Admin
+from Mobyle.JobState import JobState , url2path
+
+
+def get_session_key( email ):
+    """
+    """
+    newMd5 = md5()
+    newMd5.update( email )
+    return newMd5.hexdigest()
+
+
+
+if __name__ == '__main__':
+    from optparse import OptionParser
+    usage = """usage: %prog [options] old_email new_email
+    
+change authenticated session email and update jobs with the new session key
+    """
+    parser = OptionParser( usage= usage )
+    parser.add_option( "-v" , "--verbose",
+                      action= "count", 
+                      dest = "verbosity", 
+                      default = 0 ,
+                      help = "increase the verbosity level of the output.") 
+    options, args = parser.parse_args() 
+    if len(args) != 2:
+        parser.print_help( sys.stderr )
+        sys.exit(1)
+    import logging
+    sh_formatter = logging.Formatter("%(levelname)-8s : L %(lineno)d : %(message)s")
+    sh = logging.StreamHandler( sys.stderr ) 
+    sh.setFormatter(sh_formatter)
+    logger = logging.getLogger('mobemail')  
+    logger.addHandler( sh )
+    
+    if options.verbosity == 0:
+        logger.setLevel( logging.ERROR )
+    elif options.verbosity == 1:
+        logger.setLevel( logging.WARNING )
+    elif options.verbosity == 2:
+        logger.setLevel( logging.INFO )
+    elif options.verbosity == 3:
+        logger.setLevel( logging.DEBUG )
+    
+    old_email = args[0]
+    new_email = args[1]
+    
+    config = Config()
+    old_session_key = get_session_key( old_email )
+    new_session_key = get_session_key( new_email )
+    old_session_dir = os.path.normpath( os.path.join( config.user_sessions_path() , AuthenticatedSession.DIRNAME , old_session_key ))
+    new_session_dir = os.path.normpath( os.path.join( config.user_sessions_path() , AuthenticatedSession.DIRNAME , new_session_key ))   
+    if not os.path.exists( old_session_dir ): 
+        msg = "The session corresponding to %s does not exists" % old_email
+        logger.critical(msg)
+        sys.exit(2)
+    if os.path.exists( new_session_dir):
+        msg = "The session corresponding to %s already exists" % new_email
+        logger.critical(msg)
+        sys.exit(3)
+    transaction =  Transaction( os.path.normpath( os.path.join( old_session_dir , Session.FILENAME)), Transaction.WRITE )
+    tr_email =  transaction.getEmail()
+    if old_email != tr_email:
+        if new_email == tr_email:
+            logger.warning( "the email of session is already set to new email: continue")
+        else:
+            msg = "The email %s in the session %s does not match the provided email %s"%(old_email, old_session_dir , tr_email)
+            logger.critical(msg)
+            sys.exit(3)
+    logger.debug("set session email to %s" % new_email)        
+    transaction.setEmail( new_email )
+    transaction.setID( new_session_key )
+    transaction.commit()
+    transaction =  Transaction( os.path.normpath( os.path.join( old_session_dir , Session.FILENAME)), Transaction.READ )
+    all_jobs = transaction.getAllJobs()
+    logger.debug("get session jobs")  
+    transaction.commit()
+    for j in all_jobs:
+        job_path = url2path( j['jobID'] )
+        if not os.path.exists(job_path):
+            logger.debug("The job %s does not exists : continue" % job_path)
+            continue
+        index_path = os.path.join( job_path , 'index.xml')
+        job_dir_mtime = os.path.getmtime( job_path )
+        job_dir_atime = os.path.getatime( job_path )
+        index_mtime = os.path.getmtime( os.path.join( job_path , 'index.xml') )
+        index_atime = os.path.getatime( os.path.join( job_path , 'index.xml') )
+        job =  JobState( job_path )
+        job_email = job.getEmail()
+        if old_email != job_email:
+            if new_email == job_email:
+                logger.warning( "the email of job %s is already set to new email: continue" % job_path)
+                continue
+            msg = "The email in the job %s does not match the provided email %s"%(job_path , job_email)
+            logger.error(msg)
+            sys.exit(3)
+        logger.debug("job %s set email %s -> %s"% (job.getDir() , old_email, new_email )) 
+        job.setEmail( new_email )
+        logger.debug("job %s set session to %s"% (job.getDir(),new_session_key))
+        job.setSessionKey( new_session_key )
+        job.commit()
+        logger.debug("job admin %s set email %s -> %s"%(job.getDir() , old_email, new_email))
+        adm = Admin(job_path )
+        logger.debug("job admin %s set session to %s" % (job.getDir(), new_session_key))
+        adm.setEmail( new_email )
+        adm.setSession( new_session_key )
+        adm.commit()
+        os.utime( index_path , ( index_atime , index_mtime ) )
+        os.utime( job_path , ( job_dir_atime , job_dir_mtime ) )
+    logger.debug("rename session  %s -> %s"% (old_session_dir , new_session_dir)) 
+    os.rename(old_session_dir, new_session_dir)     
+                                                                    
\ No newline at end of file
diff --git a/Tools/mobjobw b/Tools/mobjobw
new file mode 100755
index 0000000..e7e4fbc
--- /dev/null
+++ b/Tools/mobjobw
@@ -0,0 +1,86 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Nicolas Joly , Bertrand Neron                   #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os, sys, time
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+  MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+  sys.exit('MOBYLEHOME must be defined in your environment')
+
+sys.path.append(os.path.join(MOBYLEHOME,'Src'))
+
+from Mobyle.ConfigManager import Config
+from Mobyle.MobyleLogger import MLogger
+MLogger()
+
+from Mobyle.Admin import Admin 
+from Mobyle.Utils import getStatus as utils_getStatus
+
+cfg = Config()
+adm = cfg.admindir()
+lst = os.listdir(adm)
+
+for job in lst:
+    fil = os.path.join(adm, job)
+    if not os.path.isfile(fil) or job[0] == '.': 
+        continue
+    admin = Admin(fil)
+    
+    print "%s" % ("-"*80)
+    
+    jid = admin.getJobID()
+    if jid == None:
+        print "Unknown job %s" % job
+        continue
+    key = jid.split(os.path.sep)[-1]
+    exc_alias = admin.getExecutionAlias()
+    que = admin.getQueue()
+    try:
+        sta = utils_getStatus(jid)
+    except Exception , err:
+        print >> sys.stderr , "ERROR : %s : getStatus : %s "%( jid , err )
+        continue
+    print "%s -- %s/%s -- %s" % (key, exc_alias , que, sta)
+    
+    nam = admin.getJobName()
+    ema = admin.getEmail()
+    tim = time.strftime("%x %X", admin.getDate())
+    ses = admin.getSession()
+    adr = admin.getRemote()
+    wfid = admin.getWorkflowID()
+    if wfid :
+        src = 'LOCAL'
+        wf_nam = '/'.join( wfid.split('/')[-2:] )
+    elif ses :
+        wf_nam = 'STANDALONE'
+        src = 'LOCAL'
+    else :
+        wf_nam = 'UNKNOWN'
+        src =  'REMOTE'
+        
+    print "%s -- %s -- %s -- %s -- %s -- %s " % (nam, ema, tim, src, wf_nam , adr)
+    fil = os.path.join(cfg.results_path(), nam, key, '.command')
+    try:
+        fh = open(fil)
+        cmd = fh.readline().strip()
+    except IOError, err:
+        cmd = "no command line"
+    finally:
+        try:
+            fh.close()
+        except:
+            pass
+    print "%s" % cmd
+
+sys.exit(0)
diff --git a/Tools/mobkill b/Tools/mobkill
new file mode 100755
index 0000000..0e5c7fd
--- /dev/null
+++ b/Tools/mobkill
@@ -0,0 +1,134 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron                                  #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os, sys, getopt
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+from string import ascii_uppercase
+import glob
+from Mobyle.MobyleLogger import MLogger
+MLogger()
+
+from Mobyle.JobFacade import JobFacade
+from Mobyle.MobyleError import MobyleError
+from Mobyle.ConfigManager import Config
+from Mobyle.Job import path2url
+
+def killJobs( keys ):
+    """
+    kill the job corresponding to the keys
+    @param keys: the keys of the job to kill
+    @type keys: list of strings
+    """
+    errors = []
+
+    for key in keys:
+
+        if len( key ) == 15  and key[0] in ascii_uppercase :
+            try:
+                int( key[1:] )
+            except ValueError:
+                errors.append( ( key , "invalid key" ) )
+                continue
+            else:
+                config = Config()
+                search = "%s/*.%s" % ( config.admindir() , key )
+                admins = glob.glob( search )
+                
+                if len( admins ) == 1 :
+                    adminPath = os.path.realpath( admins[0] )
+                    job_path = os.path.dirname( adminPath )
+                    
+                elif len( admins ) == 0:
+                    errors.append( ( key , "no running job with key : " + key  ) )
+                    continue
+                else:
+                    raise MobyleError , "there is more than 1 running job with the same key : " + key 
+                
+            try:
+                job_id = path2url( job_path )
+                job_name = job_path.split( os.path.sep )[-2 ]
+                job_facade = JobFacade.getFromJobId( job_id )
+            except MobyleError , err :
+                errors.append( ( key , "invalid key" ) )
+                continue
+            
+            if job_id is None:
+                # an error occured on this jobID but I continue to kill the other jobIDs
+                errors.append( ( "%s/%s"%( job_name, key) , 'no jobID for this job' ) )
+                continue
+            try:
+                
+                try:
+                    job_facade.killJob()
+                except Exception , err :
+                    errors.append( ( "%s/%s"%( job_name, key) , str( err )  ) )
+                    continue
+                    
+            except MobyleError , err :
+                errors.append( ( "%s/%s"%( job_name, key) , str( err ) ) )
+
+        else: # len(key) != 15
+            errors.append( ( key , "invalid key" ) )
+            continue
+
+    if errors:
+        msg = ''
+        for job , msgErr in errors :
+            msg = "%s Mkill( %s ) - %s\n" % ( msg , job , msgErr )
+
+        raise MobyleError , msg
+
+if __name__ == '__main__':
+
+    def usage():
+        print """
+        usage: mkill jobKeys  ... 
+    
+        option:
+            -h or --help  ... Print this message and exit.
+        """
+
+    # ===== command line parser
+    try:
+        opts, args = getopt.gnu_getopt( sys.argv[1:], "h", ["help"] )
+    
+        for option , value in opts: 
+    
+            if option in ( "-h","--help" ):
+                usage()
+                sys.exit( 0 )
+    
+        if not args:
+            usage()
+            sys.exit( 1 )
+            
+    except getopt.GetoptError:
+            print usage()
+            sys.exit( 1 )
+    
+    # ===== killer
+    try:
+        killJobs( args )
+    except MobyleError , err:
+        print >> sys.stderr , err
+        sys.exit( 2 )
diff --git a/Tools/mobpasswd b/Tools/mobpasswd
new file mode 100755
index 0000000..61fe1d1
--- /dev/null
+++ b/Tools/mobpasswd
@@ -0,0 +1,261 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron                                  #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import sys
+from hashlib import md5
+import os.path
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+
+from Mobyle.ConfigManager import Config
+from Mobyle.Session import Session , Transaction
+from Mobyle.AuthenticatedSession import AuthenticatedSession
+from Mobyle.MobyleError import MobyleError , SessionError 
+from Mobyle.Net import EmailAddress , Email
+
+
+    
+class SessionPasswd( Session ):
+    
+    PASS_MIN_LEN = 6
+    PASS_MAX_LEN = 12
+    STRENGTH = [ 'blank', 'Very Weak', 'Weak', 'Medium', 'Strong', 'Very Strong']
+    
+    def __init__( self , user_email  , cfg ):
+        self.newPasswd = None
+        self.cfg = cfg
+        self.user_email =  user_email     
+        sessionDir , key = self._findSession()
+        Session.__init__( self, sessionDir , key , self.cfg )
+        if str(self.user_email) != self.getEmail():
+            raise MobyleError , "the email %s in xml does not match the session Key %s" % ( self.getEmail() , key ) 
+    
+    def crypt(self , clearpasswd):
+        newMd5 = md5()
+        newMd5.update( clearpasswd )
+        newCryptPasswd = newMd5.hexdigest()
+        return newCryptPasswd
+    
+    
+    def setPasswd( self, clearPasswd ):
+        newCryptPasswd = self.crypt( clearPasswd )
+        transaction = self._getTransaction( Transaction.WRITE )
+        try:
+            transaction.setPasswd( newCryptPasswd )
+            self.newPasswd = ( clearPasswd , newCryptPasswd )
+            transaction.commit()
+        except Exception , err:
+            transaction.rollback()
+            raise err
+            
+    def sendNewPasswd(self):
+        if not self.newPasswd:
+            raise MobyleError , "password has not been set"
+
+        mail = Email( self.user_email ) 
+        mail.send( 'NEW_PASSWD' , {'SENDER'     : cfg.sender() ,
+                                   'HELP'       : cfg.mailHelp() ,
+                                   'SERVER_NAME': cfg.portal_url() ,
+                                   'PASSWD'     : self.newPasswd[0],          
+                                   }  )
+
+
+    def status(self):
+        """
+        """
+        email , authenticated , activated = self.getBaseInfo()
+        return """
+    email          %s
+    session        %s
+    authenticated  %s
+    activated      %s
+    """ %( email , self.key , authenticated , activated )
+    
+    
+    def inactivate(self):
+        transaction = self._getTransaction( Transaction.WRITE)
+        try:
+            transaction.inactivate()
+            transaction.commit()
+        except Exception , err :
+            transaction.rollback()
+            raise err
+            
+            
+    def activate(self):
+        transaction = self._getTransaction( Transaction.WRITE)
+        try:
+            transaction.activate()
+            transaction.commit()
+        except Exception , err :
+            transaction.rollback()
+            raise err
+    
+    def strenght_meter(self , passwd ):
+        score = 1
+        if not passwd:
+            # the password is empty
+            return 0
+        if len( passwd ) < self.PASS_MIN_LEN :
+            # a short password canot be stronger than 1
+            return 1
+        if len( passwd ) >= self.PASS_MIN_LEN :
+            score += 1
+        if passwd.lower() != passwd and passwd.upper() != passwd:
+            #the password mix upper and lower case
+            score += 1
+        for x in passwd:
+            #the password contains at least one digits
+            if x.isdigit(): 
+                score += 1
+                break
+        for x in passwd:
+            #the password contains at least one symbols (not digits not letters)
+            if not x.isalnum() :
+                score += 1
+                break
+        return score
+
+    def _findSession( self ):
+        newMd5 = md5()
+        newMd5.update( str( self.user_email ) )
+        key = newMd5.hexdigest()
+        sessionDir = os.path.join( cfg.user_sessions_path() , AuthenticatedSession.DIRNAME , key )
+        if os.path.exists( sessionDir ):
+            return sessionDir , key
+        else:
+            raise SessionError , "there no session corresonding to %s" % self.user_email
+        
+if __name__ == '__main__':
+    from getpass import getpass
+    from optparse import OptionParser
+    
+    usage = """usage: %prog [options] email
+    
+change session password for email
+    """
+    parser = OptionParser( usage= usage )
+    parser.add_option( "-s" , "--status",
+                       action = "store_true", 
+                       dest = "status",
+                       default = False ,
+                       help = "display session status.")
+    
+    parser.add_option( "-f" , "--force",
+                       action = "store_true", 
+                       dest = "force",
+                       default = False ,
+                       help = "force new password even if strenght is weak.")
+    
+    parser.add_option( "-m" , "--mail",
+                       action = "store_true", 
+                       dest = "mail",
+                       default = False ,
+                       help = "send new password by email.")
+    
+    parser.add_option( "-a" , "--activate",
+                       action = "store_true", 
+                       dest = "activate",
+                       default = False ,
+                       help = "activate session.")
+    
+    parser.add_option( "-i" , "--inactivate",
+                       action = "store_true", 
+                       dest = "inactivate",
+                       default = False ,
+                       help = "inactivate session.")
+    
+    options, args = parser.parse_args() 
+
+    if  options.activate and options.inactivate:
+        print >> sys.stderr , "options -i and -a are not compatble"
+        parser.print_help( sys.stderr )
+        sys.exit(1)
+    try:
+        email = args[0]
+    except IndexError:
+        print >> sys.stderr , "You must provide an email."
+        parser.print_help( sys.stderr )
+        sys.exit(1)
+    
+    cfg = Config()    
+    email = EmailAddress( email )
+    email_checked = email.check()
+    if not email_checked:
+        msg = email.getMessage()
+        print >> sys.stderr , msg
+        sys.exit(2)       
+    
+    if options.status:
+        try:
+            sPasswd = SessionPasswd( email , cfg )
+        except SessionError , err :
+            print >> sys.stderr , err
+            sys.exit(1)
+            
+        email , isAuthenticated , isActivated = sPasswd.getBaseInfo()
+        print >> sys.stdout , """
+email          %s
+session        %s
+authenticated  %s
+activated      %s        
+""" % ( email , sPasswd.key , isAuthenticated , isActivated )
+        sys.exit(0)
+         
+    if options.activate:
+        sPasswd = SessionPasswd( email , cfg )
+        sPasswd.activate()
+    elif options.inactivate:
+        sPasswd = SessionPasswd( email , cfg )
+        sPasswd.inactivate()
+    else:
+        print >> sys.stdout ,"Changing passowrd for %s" % email 
+        sPasswd = SessionPasswd( email , cfg )
+        passwd = getpass( "New password : " )
+        confirm = getpass( "Retype new password : " )
+        if passwd != confirm:
+            print >> sys.stderr , "Sorry, passwords do not match."
+            sys.exit(3)
+        strenght = sPasswd.strenght_meter( passwd )
+        if strenght < 4 and not options.force:
+            print >> sys.stderr , """BAD PASSWORD: password strenght is %s
+Choose a password 
+  - with %d min lenght
+  - mix upper and lower case
+  - use digits and symbols """ % ( sPasswd.STRENGTH[ strenght ] , sPasswd.PASS_MIN_LEN )
+        else:
+            try:
+                sPasswd.setPasswd( passwd )
+                print >> sys.stderr , "the password for user %s has been updated successfully" % email
+            except Exception ,err:
+                print >> sys.stderr , "changing password for user %s aborted :\n %s" %( email , err )
+                sys.exit(4)
+            if options.mail:
+                try:   
+                    sPasswd.sendNewPasswd()
+                    print >> sys.stderr , "the new password for user %s has been send successfully" % email
+                except Exception , err :
+                    print >> sys.stderr , "the new password cannot be send to %s :\n %s" %( email , err )
+                    sys.exit(5)
+                
+  
+
+        
+    
diff --git a/Tools/mobshell b/Tools/mobshell
new file mode 100644
index 0000000..a940aae
--- /dev/null
+++ b/Tools/mobshell
@@ -0,0 +1,103 @@
+#! /usr/bin/env python
+import sys, os, getopt
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+class Console(object):
+    # shamelessly stolen from https://code.djangoproject.com/browser/django/trunk/django/core/management/commands/shell.py!
+
+    shells = ['ipython','bpython','python']
+
+    def __init__(self,locals={}):
+        self.locals = locals
+        
+    def ipython(self):
+        try:
+            from IPython import embed
+            embed(user_ns=self.locals)
+        except ImportError:
+            # IPython < 0.11
+            # Explicitly pass an empty list as arguments, because otherwise
+            # IPython would use sys.argv from this script.
+            try:
+                from IPython.Shell import IPShell
+                shell = IPShell(argv=[])
+                shell.mainloop()
+            except ImportError:
+                # IPython not found at all, raise ImportError
+                raise
+    
+    def bpython(self):
+        import bpython
+        bpython.embed(locals_=self.locals)
+    
+    def run_shell(self, shells=[]):
+        for shell in shells or self.shells:
+            try:
+                return getattr(self, shell)()
+            except ImportError:
+                pass
+        raise ImportError
+    
+    def python(self):
+        import code
+        imported_objects = {}
+        try: # Try activating rlcompleter, because it's handy.
+            import readline
+        except ImportError:
+            pass
+        else:
+            # We don't have to wrap the following import in a 'try', because
+            # we already know 'readline' was imported successfully.
+            import rlcompleter
+            readline.set_completer(rlcompleter.Completer(imported_objects).complete)
+            readline.parse_and_bind("tab:complete")
+        code.interact(local=self.locals)
+
+if __name__ == '__main__':
+
+    def usage():
+        print """
+        Usage: mobshell [OPTION]... FILE... 
+        Run mobyle shell
+    
+        option:
+            -i or --ipython    force ipython usage
+            -b or --bpython    force ipython usage
+            -p or --python    force "simple" python usage
+            -h or --help  ... Print this message and exit.
+        """
+
+    c = Console(locals())
+
+    try:
+        opts, args = getopt.gnu_getopt( sys.argv[1:], "hibp", ["help", "ipython", "bpython", "python"] )
+    except getopt.GetoptError:
+        usage()
+        sys.exit( 1 )
+
+    shells = []
+    for option , value in opts:     
+        if option in ( "-h", "--help" ):
+            usage()
+            sys.exit( 0 )
+        if option in ( "-i", "--ipython"):
+            shells.append("ipython")
+        if option in ( "-b", "--bpython"):
+            shells.append("bpython")
+        if option in ( "-p", "--python"):
+            shells.append("python")
+
+    c.run_shell(shells)
\ No newline at end of file
diff --git a/Tools/mobtypes b/Tools/mobtypes
new file mode 100755
index 0000000..b023807
--- /dev/null
+++ b/Tools/mobtypes
@@ -0,0 +1,374 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron                                  #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os , sys
+import time
+from lxml import etree
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+from Mobyle.Classes.DataType import DataTypeFactory
+import Mobyle.Parser 
+import Mobyle.Classes.Core
+import Local.CustomClasses
+from Mobyle.MobyleError import ParserError
+
+
+
+class XmlDataTypeRepository:
+    TAIL    = '//'
+    SEP     = '|'
+    COMMENT = '#'
+    XML_HEADER = "%-30.30s %s %-15.15s %s %s" % ( " xml class" , SEP , "python class", SEP , "description" )
+    PYTHON_HEADER = " Mobyle python class used "
+    BIOTYPE_HEADER = " biotypes used " 
+    
+    def __init__(self, path):
+    
+        self._dataTypeFactory = DataTypeFactory()
+        self.descriptions = {}
+        self.biotypes = []    
+        self.xmlTypes = []  # [ xmlType, python Type ]  
+        self.pythonTypes = [] 
+        self.parse( path )
+        
+    def parse(self, xmlPaths):
+        self.name = 'xml list'
+        errors = []
+        biotypes = {}
+        for xmlPath in xmlPaths:
+            doc = etree.parse( "file://%s" % os.path.abspath( xmlPath ) )
+            parameters = doc.xpath( './/parameter' )
+            for parameter in parameters:
+                try:
+                    typeNode = parameter.xpath( 'type' )[0]
+                    mt = Mobyle.Parser.parseType( typeNode , self._dataTypeFactory )
+                    for biotype in mt.getBioTypes():
+                        biotypes[ biotype ] = None
+                except IndexError:
+                    parameterName = parameter.xpath( 'name/text()')[0]
+                    errors.append( "%s/%s haven't any type" % (xmlPath , parameterName ) ) 
+                except ParserError , err:
+                    parameterName = parameter.xpath( 'name/text()')[0]
+                    errors.append( "%s/%s : %s" % (xmlPath , parameterName , err ) )
+        if errors:
+            for error in errors:
+                print >> sys.stderr , "ERROR ", error
+            sys.exit(1)
+        
+        for dtName in self._dataTypeFactory.definedDataTypes.keys():
+            ancestors = self._dataTypeFactory.definedDataTypes[ dtName ].ancestors
+            mobyleClasses = dir( Mobyle.Classes )
+            localCustomClasses = dir( Local.CustomClasses )
+            if dtName in mobyleClasses:
+                self.pythonTypes.append( dtName )
+            elif dtName in localCustomClasses:
+                self.pythonTypes.append( dtName )
+            else:
+                self.xmlTypes.append( ( ancestors[0] , ancestors[1] ) )
+        
+        self.biotypes = biotypes.keys()
+        self.biotypes.sort()
+        self.pythonTypes.sort()
+        self.xmlTypes.sort()
+        
+    def mistakeDetector(self):
+        warnings = []
+        xmlCaseError = {}
+        for xmlType , pythonType in self.xmlTypes:
+            upXmlType = xmlType.upper()
+            if xmlCaseError.has_key( upXmlType ):
+                warnings.append( "found to similar xml datatype %s and %s" % ( xmlType , xmlCaseError[ upXmlType ] ))
+            else:
+                xmlCaseError[ upXmlType ] = xmlType
+        biotypeCaseError = {}
+        for biotype in self.biotypes:
+            upBiotype = biotype.upper()
+            if biotypeCaseError.has_key( upBiotype ):
+                warnings.append( "found to similar biotype %s and %s" % (biotype , biotypeCaseError[ upBiotype] ) )
+            else:
+                biotypeCaseError[ upBiotype ] = biotype
+        
+        return warnings
+            
+            
+    def __str__(self):
+        repr = ''
+        comment = '# mobyle datatype repositotry %s #' % time.strftime( '%x %X' , time.localtime() )
+        
+        pythonBody = '\n'.join( [ "%-25.25s %s " % ( pythonType , self.SEP ) for pythonType in self.pythonTypes ] )
+
+        repr = repr + "%(comment)s\n%(diese)s\n%(header)s\n%(diese)s\n%(body)s\n%(tail)s\n" % {"comment": comment ,
+                                                                                               "diese"  : '#'*26 ,
+                                                                                               "header" : self.PYTHON_HEADER ,
+                                                                                               "body"   : pythonBody ,
+                                                                                               "tail"   : self.TAIL
+                                                                                              }
+
+
+        xmlBody = '\n'.join( [ "%-30.30s %s %-15.15s %s " % ( xmlType , self.SEP , pythonType , self.SEP ) for xmlType , pythonType in self.xmlTypes ] )
+        repr = repr + "\n%(diese)s\n%(header)s\n%(diese)s\n%(body)s\n%(tail)s\n" % {"diese" : '#'*64 ,
+                                                                                    "header" : self.XML_HEADER ,
+                                                                                    "body"   : xmlBody ,
+                                                                                    "tail"   : self.TAIL 
+                                                                                   }
+        
+        biotypesBody = '\n'.join([ '%-15.15s %s' % ( biotype , self.SEP ) for biotype in self.biotypes ] )
+        repr = repr + "\n%(diese)s\n%(header)s\n%(diese)s\n%(body)s\n%(tail)s\n" % {"diese"  : '#'*15 ,
+                                                                                    "header" : self.BIOTYPE_HEADER ,
+                                                                                    "body"   : biotypesBody ,
+                                                                                    "tail"   : self.TAIL 
+                                                                                   }
+        return repr
+    
+    
+    
+    def diff(self , repository ):
+        import difflib
+        sm = difflib.SequenceMatcher()
+        pythonDiff = ''
+        xmlDiff = ''
+        bioDiff = ''
+        #sm.set_seqs( self.pythonTypes , repository.pythonTypes )
+        sm.set_seqs( repository.pythonTypes , self.pythonTypes )
+        blocks = sm.get_opcodes()
+        for block in blocks:
+            if block[0] == 'equal' :
+                continue
+            elif block[0] == 'insert' :
+                pythonDiff += '\n'.join( [ '+ '+ str( Type ) for Type in self.pythonTypes[ block[3] : block[4] ] ] )
+                pythonDiff += '\n'
+            elif block[0] == 'delete':
+                pythonDiff +=  '\n'.join( [ '- '+ str( Type ) for Type in repository.pythonTypes[ block[1] : block[2] ] ])
+                pythonDiff += '\n'
+        if pythonDiff:
+            pythonDiff =  "Mobyle python diff\n%s\n%s\n" % ( "="*20 , pythonDiff )
+        
+        sm.set_seqs( repository.xmlTypes , self.xmlTypes )    
+        blocks = sm.get_opcodes()
+        for block in blocks:
+            if block[0] == 'equal' :
+                continue
+            elif block[0] == 'insert' :
+                xmlDiff += '\n'.join( ["+ %-15.15s %s %-30.30s %s" % ( Type[0] , self.SEP , Type[1]  , self.SEP ) for Type in self.xmlTypes[ block[3] : block[4] ] ] )
+                xmlDiff += '\n'
+            elif block[0] == 'delete':
+                xmlDiff += '\n'.join( ["- %-15.15s %s %-30.30s %s" % ( Type[0] , self.SEP , Type[1]  , self.SEP ) for Type in repository.xmlTypes[ block[1] : block[2] ] ] ) 
+                xmlDiff += '\n'
+        if xmlDiff:
+            xmlDiff =  "xml class diff\n%s\n%s\n" % ( "="*15 , xmlDiff)
+            
+            
+        sm.set_seqs( repository.biotypes , self.biotypes )    
+        blocks = sm.get_opcodes()
+        for block in blocks:
+            if block[0] == 'equal' :
+                continue
+            elif block[0] == 'insert' :
+                bioDiff += '\n'.join( [ '+ '+ str( Type ) for Type in self.biotypes[ block[3] : block[4] ] ] )
+                pythonDiff += '\n'
+            elif block[0] == 'delete':
+                bioDiff +=  '\n'.join( [ '- '+ str( Type ) for Type in repository.biotypes[ block[1] : block[2] ] ])
+                bioDiff += '\n'
+        if bioDiff:
+            bioDiff =  "biotypes diff\n%s\n%s\n" % ( "="*14 , bioDiff )
+        
+        if pythonDiff or xmlDiff or bioDiff: 
+            return "--- %s\n+++ %s\n\n %s%s%s" % ( repository.name , self.name ,  pythonDiff , xmlDiff , bioDiff )
+    
+    
+class DataTypeReference( XmlDataTypeRepository ):
+    
+    
+    def parse(self , repositoryPath ):
+        self.name = repositoryPath
+        f = file( repositoryPath , 'r' )
+        header_found = False
+        tail_found = False
+        
+        for line in f:
+            line = line.strip()
+            if line.startswith( self.COMMENT ):
+                continue
+            elif line == self.PYTHON_HEADER.strip():
+                header_found = True
+                self.pythonTypes = self._parsePythonType( f )
+            elif line == self.XML_HEADER.strip():
+                header_found = True
+                self.xmlTypes , self.descriptions = self._parseXmlType( f )
+            elif line == self.BIOTYPE_HEADER.strip():
+                header_found = True
+                self.biotypes = self._parseBiotype( f )
+            elif line.startswith( self.TAIL ):
+                tail_found = True
+                break
+            else:
+                if not header_found:
+                    print >> sys.stderr , "ERROR bad file format for DataType Reference %s : No header found" % f.name
+                    sys.exit(2)
+                    
+    
+    def _parsePythonType(self , refFile ):
+        tail_found = False
+        pythonTypes = {} 
+        for line in refFile:
+            if line.startswith( self.COMMENT ):
+                continue
+            elif line.startswith( self.TAIL ):
+                tail_found = True
+                break
+            else:
+                fields = line.split( self.SEP )
+                fields = [ item.strip() for item in fields ]
+                if fields == ['']: # skip blank line
+                    continue
+                else:
+                    pythonType = fields[0]
+                    if pythonTypes.has_key( pythonType ):
+                        print >> sys.stderr , "the reference %s has 2 %s entries" % ( ref.name , pythonType )
+                        sys.exit(2)
+                    else:
+                        pythonTypes[ pythonType ] = None 
+                        
+        if not tail_found:
+            print >> sys.stderr , "ERROR bad file format for DataType Reference %s : Unexpected end of file " % refFile.name
+            sys.exit(2)
+        
+        pythonTypes = pythonTypes.keys()
+        pythonTypes.sort()
+        return pythonTypes
+        
+    def _parseXmlType(self, refFile ):
+        tail_found = False
+        descriptions = {}
+        for line in refFile:
+            if line.startswith( self.COMMENT ):
+                continue
+            elif line.startswith( self.TAIL ):
+                tail_found = True
+                break
+            else:
+                fields = line.split( self.SEP )
+                fields = [ item.strip() for item in fields ]
+                if fields == ['']: # skip blank line
+                    continue
+                else:
+                    xmlKlass , pythonKlass , description = fields
+                    dataType = self._dataTypeFactory.newDataType(  pythonKlass , xmlName = xmlKlass )
+                    descriptions[ xmlKlass ] = description
+        if not tail_found:
+            print >> sys.stderr , "ERROR bad file format for DataType Reference %s : Unexpected end of file " % refFile.name
+            sys.exit(2)
+        
+        xmlTypes = []
+        for dtName in self._dataTypeFactory.definedDataTypes.keys():
+            ancestors = self._dataTypeFactory.definedDataTypes[ dtName ].ancestors
+            mobyleClasses = dir( Mobyle.Classes )
+            localCustomClasses = dir( Local.CustomClasses )
+            if dtName in mobyleClasses:
+                pass
+            elif dtName in localCustomClasses:
+                pass
+            else:
+                xmlTypes.append( ( ancestors[0] , ancestors[1] ) )
+        
+        xmlTypes.sort()
+        return xmlTypes , descriptions
+        
+    def _parseBiotype(self, refFile ):
+        biotypes = {}
+        tail_found = False
+        for line in refFile:
+            if line.startswith( self.COMMENT ):
+                continue
+            elif line.startswith( self.TAIL ):
+                tail_found = True
+                break
+            else:
+                fields = line.split( self.SEP )
+                fields = [ item.strip() for item in fields ]
+                if fields == ['']: # skip blank line
+                    continue
+                else:
+                    biotype = fields[0]
+                    if biotypes.has_key( biotype ):
+                        print >> sys.stderr , "the reference %s has 2 %s entries" % ( ref.name , biotype )
+                        sys.exit(2)
+                    else:
+                        biotypes[ biotype ] = None 
+        
+        if not tail_found:
+            print >> sys.stderr , "ERROR bad file format for DataType Reference %s : Unexpected end of file " % refFile.name
+            sys.exit(2)
+        biotypes = biotypes.keys()
+        biotypes.sort()    
+        return  biotypes   
+        
+        
+if __name__ == '__main__':
+    from getopt import gnu_getopt , GetoptError
+    
+    def usage():
+        print """
+        generate on the standart output a mobyle types repository ( python , xml and biotype ).
+        if -ref is specified do a diff between the ref and the xml
+        
+        usage: mobtypes < --ref path to a datatype repository > < xmlpaths to analyze >
+        if no paths are provide as arguments, use the xmlpaths installed (local and imported) for this portal.
+        options:
+            -h or --help  ... Print this message and exit.
+            -r or --ref   ... the path to a datatype repository.
+        """
+
+    try:
+        opts, xmlPaths = gnu_getopt( sys.argv[1:], "hr:", [ "help" , "ref=" ] )
+        refPath = None
+        for option , value in opts: 
+            if option in ( "-h","--help" ):
+                usage()
+                sys.exit( 0 )
+            elif option in ( "-r" , "--ref=" ):
+                refPath = value
+                if not os.path.exists( value ):
+                    print >> sys.stderr , "ERROR : %s %s the No such file" % ( option , value )
+                    sys.exit(1)
+        
+        if not xmlPaths:
+            from Mobyle.Registry import registry
+            xmlPaths = [  p.path for p in registry.programs ]
+            
+    except GetoptError:
+            print usage()
+            sys.exit( 1 )
+
+    xmlRepository = XmlDataTypeRepository( xmlPaths )
+    if refPath:
+        ref = DataTypeReference( refPath )
+        print ref.diff( xmlRepository )
+    else:
+        print xmlRepository
+        w = xmlRepository.mistakeDetector()
+        if w:
+            print >> sys.stderr , " -- WARNINGS -- "
+            print >> sys.stderr , '\n'.join( w )
+
+
+    
diff --git a/Tools/mobvalid b/Tools/mobvalid
new file mode 100755
index 0000000..cac13a1
--- /dev/null
+++ b/Tools/mobvalid
@@ -0,0 +1,101 @@
+#! /usr/bin/env python
+import os , sys, getopt
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+from Mobyle.Validator import Validator
+from Mobyle.Registry import registry
+from Mobyle.DataTypeValidator import DataTypeValidator
+dtv = DataTypeValidator()
+
+if __name__ == '__main__':
+
+    def usage():
+        print """
+        Usage: mobvalid [OPTION]... FILE... 
+        Validate Mobyle service description files (program or viewer), each FILE being the path to the file.
+    
+        option:
+            -w or --workflow    Validate a workflow
+                              (Default validates a command line program)
+            -v or --viewer    Validate a viewer
+                              (Default validates a command line program)
+            -j or --jing      Also run Jing to validate (gives more accurate error messages than the usual validation
+                              mechanism)
+                              (Requires java and that the jing jarfiles are located in MOBYLEHOME/Tools/validation/jing)
+            -q or --quiet  ... Do not print any message if the file validates
+            -h or --help  ... Print this message and exit.
+        """
+
+    
+    # ===== command line parser
+    try:
+        opts, args = getopt.gnu_getopt( sys.argv[1:], "hvwtjq", ["help", "viewer", "workflow", "tutorial", "jing", "quiet"] )
+        type = "program"
+        useJing = False    
+        beQuiet = False
+        for option , value in opts:     
+            if option in ( "-h", "--help" ):
+                usage()
+                sys.exit( 0 )
+            if option in ( "-v", "--viewer"):
+                print option
+                type = "viewer"
+            if option in ( "-w", "--workflow"):
+                print option
+                type = "workflow"
+            if option in ( "-t", "--tutorial"):
+                print option
+                type = "tutorial"
+            if option in ( "-j", "--jing"):
+                useJing = True
+            if option in ( "-q", "--quiet"):
+                beQuiet = True
+        if not args:
+            usage()
+            sys.exit( 1 )
+            
+    except getopt.GetoptError:
+            usage()
+            sys.exit( 1 )
+    
+    for file in args :
+      if not(beQuiet):
+          print "Processing file %s..." % (str(file))
+      val = Validator(type, file,runRNG_Jing=useJing)
+      val.run()
+      try:
+          if val.valOk:
+              errors = dtv.validateDataTypes(docPath=file)
+              if len(errors)!=0:
+                  print "%s does NOT validate. Details follow:" % str(file)
+                  print "* datatypes validation failed - errors detail:"
+                  for error in errors:
+                      print " - %s" % error
+              elif not(beQuiet):
+                  print "%s validates." % str(file)
+          else:
+              print "%s does NOT validate. Details follow:" % str(file)
+          if val.runRNG and not(val.rngOk):
+              print "* relax ng validation failed - errors detail:"
+              for re in val.rngErrors:
+                   print " - line %s, column %s: %s" % (re.line, re.column, re.message)
+          if val.runRNG_Jing and not(val.rng_JingOk):
+              print "* relax ng JING validation failed - errors detail:"
+              for line in val.rng_JingErrors:
+                   print "- %s" % (line)
+          if val.runSCH and not(val.schOk):
+              print "* schematron validation failed - errors detail:"
+              for se in val.schErrors:
+                  print " - %s" % se
+      except Exception, e:
+          print "Error processing file %s: %s" % (str(file), str(e))
\ No newline at end of file
diff --git a/Tools/mobversion b/Tools/mobversion
new file mode 100755
index 0000000..2c6d393
--- /dev/null
+++ b/Tools/mobversion
@@ -0,0 +1,33 @@
+#! /usr/bin/env python
+
+  
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron                                  #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+import os , sys
+
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit('MOBYLEHOME must be defined in your environment')
+
+if ( MOBYLEHOME ) not in sys.path:
+    sys.path.append( MOBYLEHOME )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:    
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+
+
+from Mobyle.ConfigManager import Config
+_cfg = Config()
+
+
+print  _cfg.version()
+
diff --git a/Tools/session_updater.py b/Tools/session_updater.py
new file mode 100755
index 0000000..d909df4
--- /dev/null
+++ b/Tools/session_updater.py
@@ -0,0 +1,342 @@
+#! /usr/bin/env python
+
+#############################################################
+#                                                           #
+#   Author: Bertrand Neron               #
+#   Organization:'Biological Software and Databases' Group, #
+#                Institut Pasteur, Paris.                   #
+#   Distributed under GPLv2 Licence. Please refer to the    #
+#   COPYING.LIB document.                                   #
+#                                                           #
+#############################################################
+
+
+import os , sys
+MOBYLEHOME = None
+if os.environ.has_key('MOBYLEHOME'):
+    MOBYLEHOME = os.environ['MOBYLEHOME']
+if not MOBYLEHOME:
+    sys.exit( 'MOBYLEHOME must be defined in your environment' )
+if ( os.path.join( MOBYLEHOME , 'Src' ) ) not in sys.path:
+    sys.path.append( os.path.join( MOBYLEHOME , 'Src' ) )
+import shutil
+import re
+from random import randint
+from time import time
+        
+from Mobyle.ConfigManager import Config
+from Mobyle.MobyleError import MobyleError
+from Mobyle.Transaction import Transaction
+from lxml import etree
+
+
+class UpdateTransaction( Transaction ):
+    
+    def getAllJobsID(self):
+        return [ node.get( 'id') for node in self._root.findall( 'jobList/job' ) ]
+            
+    def setJobID( self , oldID , newID ):
+        """
+        set the ID of the job corresponding  oldID to newID
+        @param oldID: the ID of a job in this transaction
+        @type oldID: string
+        @param oldID: the new ID of a job in this transaction
+        @type oldID: string
+        """
+        jobNode = self._getJobNode( oldID )
+        jobNode.set( 'id' , newID )
+        self._setModified( True )
+
+    
+    def removeJob(self , jobID ):
+        """
+        remove the entry corresponding to this jobID 
+        @param jobID: the identifier of the job in the session ( the url without index.xml )
+        @type jobID: string
+        @return: the job corresponding to jobID as a dict
+        @rtype: dictionary
+        @raise ValueError: if the jobID does not match any entry in this session
+        """
+        jobNode = self._getJobNode(jobID)
+        jobListNode = jobNode.getparent()
+        jobListNode.remove( jobNode )
+        ## remove the ref of this job from the data
+        dataUsedNodes = jobNode.findall( 'dataUsed' )
+        for dataUsedNode in dataUsedNodes:
+            dataID = dataUsedNode.get( 'ref' )
+            try:
+                dataNode = self._getDataNode( dataID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    usedByNode = dataNode.xpath( 'usedBy[@ref="%s"]' %jobID )[0]
+                except IndexError:
+                    continue
+                else:
+                    dataNode.remove( usedByNode )
+            
+        dataProducedNodes = jobNode.findall( 'dataProduced' )
+        for dataProducedNode in dataProducedNodes:
+            dataID = dataProducedNode.get( 'ref' )
+            try:
+                dataNode = self._getDataNode( dataID )
+            except ValueError:
+                continue
+            else:
+                try:
+                    producedByNode = dataNode.xpath( 'producedBy[@ref="%s"]' %jobID )[0]
+                except IndexError:
+                    continue
+                else:
+                    dataNode.remove( producedByNode )
+        self._setModified( True ) 
+
+
+    def setTicket( self , ticket_id, exp_date ):
+        """
+        set the currently allocated ticket and its expiration date
+        @param ticket_id: the ticket
+        @type ticket_id: string
+        @param exp_date: the expiration date
+        @type exp_date: date
+        """
+        tck = self._root.find( 'ticket' )
+        if tck is not None:
+            self._setModified( False )
+        else:
+            ticketNode = etree.Element( "ticket" )
+            ticket_idNode = etree.Element( "id" )
+            ticket_idNode.text = str( ticket_id )
+            exp_dateNode = etree.Element( "exp_date" )
+            exp_dateNode.text = str( exp_date )
+            ticketNode.append( ticket_idNode )
+            ticketNode.append( exp_dateNode )
+            self._root.append( ticketNode )    
+            self._setModified( True )
+            
+    def updateData( self , old_jobs_repository , new_jobs_repository ):  
+        modified = False
+        usedBy_nodes = self._root.findall( 'dataList/data/usedBy' )
+        for usedBy_node in usedBy_nodes:
+            old_job_url = usedBy_node.get( 'ref' )
+            match =  re.search( '(.*)/(.*/.*)$' , old_job_url )
+            if match:
+                jobs_repository = match.group(1)
+                if jobs_repository == old_jobs_repository:
+                    job = match.group(2)
+                    new_job_url = "%s/%s" %( new_jobs_repository , job )
+                    usedBy_node.set( 'ref' , new_job_url )
+                    logger.debug( "usedBy node has been updated from %s to %s"%( old_job_url , new_job_url ))
+                else:
+                    dataNode = usedBy_node.getparent()
+                    dataNode.remove( usedBy_node )
+                    logger.debug( "usedBy node referencing job %s was removed" % old_job_url )
+                modified =True
+            else:
+                raise Exception( "usedBy node have a ref %s witch not match standard job url" %old_job_url)
+
+        producedBy_nodes = self._root.findall( 'dataList/data/producedBy' )
+        for producedBy_node in producedBy_nodes:
+            old_job_url = producedBy_node.get( 'ref' )
+            match =  re.search( '(.*)/(.*/.*)$' , old_job_url )
+            if match:
+                jobs_repository = match.group(1)
+                if jobs_repository == old_jobs_repository:
+                    job = match.group(2)
+                    new_job_url = "%s/%s" %( new_jobs_repository , job )
+                    producedBy_node.set( 'ref' , new_job_url )
+                    logger.debug( "producedBy as been updated from %s to %s"%( old_job_url , new_job_url ))
+                else:
+                    dataNode = producedBy_node.getparent()
+                    dataNode.remove( producedBy_node )
+                    logger.debug( "producedBy node referencing job %s was removed" % old_job_url )
+                modified =True
+            else:
+                raise Exception( "producedBy node have a ref %s witch not match standard job url" %old_job_url)
+            
+            self._setModified( modified )
+
+
+
+class SessionUpdater(object):
+    
+    def __init__(self , config , logger , old_jobs_repository_url ):
+        self.cfg = config
+        self.log = logger
+        self.old_jobs_repository_url = old_jobs_repository_url.rstrip( '/' )
+        self.new_jobs_repository_url = self.cfg.results_url()
+        self.skipped_sessions = []
+        
+    def _on_error(self , err ):
+        self.log.warning( "%s : %s" ( err.filename , err ))
+        
+    def update( self , repository , force = False ,dry_run = False):
+        sessions_paths = os.walk( repository , onerror = self._on_error )
+        for path in sessions_paths:
+            session_path = path[0] 
+            if '.session.xml' in path[2]:
+                session_key =  os.path.split( path[0] )[-1]
+                self.fix( session_path , session_key , force = force , dry_run = dry_run )
+    
+#    def rollback(self ):
+#        session_dir_mtime = os.path.getmtime( session_path )
+#        session_dir_atime = os.path.getatime( session_path )
+#        index_mtime = os.path.getmtime( os.path.join( session_path , '.session.xml') )
+#        index_atime = os.path.getatime( os.path.join( session_path , '.session.xml') )
+#        index_path = os.path.join( session_path , '.session.xml')
+#        
+#        bckp_path = os.path.join( session_path , '.session.xml.bckp') 
+#    
+    
+    def fix( self , session_path , session_key , force = False ,dry_run = False):
+        self.log.info( "====================" )
+        if session_path.find( '/authentified/') != -1 :
+            session_type = 'authentified'
+        else:
+            session_type = 'anonymous'
+            
+        self.log.info( "updating %s session %s" %( session_type , session_key ) )
+        session_dir_mtime = os.path.getmtime( session_path )
+        session_dir_atime = os.path.getatime( session_path )
+        index_mtime = os.path.getmtime( os.path.join( session_path , '.session.xml') )
+        index_atime = os.path.getatime( os.path.join( session_path , '.session.xml') )
+        index_path = os.path.join( session_path , '.session.xml')
+        try:
+            os.utime( session_path , ( session_dir_atime , session_dir_mtime ) )
+        except Exception , err :
+            self.log.warning( "cannot modify the mtime of %s : %s : fix this problem before rerun updater" %(session_path , err ) )
+            return
+        bckp_path = os.path.join( session_path , '.session.xml.bckp')
+        if not os.path.exists( bckp_path ):
+            self.log.debug( "make  %s back up" % session_path )
+            if not dry_run:
+                try:
+                    shutil.copy( index_path , bckp_path )
+                except Exception ,err:
+                    self.log.warning( "cannot make %s back up " % index_path)
+                else:
+                    os.utime( bckp_path , ( index_atime , index_mtime ) )
+                    os.utime( session_path , ( session_dir_atime , session_dir_mtime ) )
+            else:
+                pass
+            
+        try:
+            ut = UpdateTransaction( index_path , UpdateTransaction.WRITE ) 
+        except Exception , err:
+            self.log.warning( "cannot load session %s : %s : skipped" %( session_path , err ) )
+            return
+        if session_type == 'authentified' :
+            ticket_id = str(randint(0,1000000))
+            t = time() + 3600 
+            ut.setTicket( ticket_id, t )
+            self.log.debug('set ticket ticket_id = %s , time = %d' %(ticket_id , t ))
+        all_jobsID = ut.getAllJobsID()
+        for id in all_jobsID:
+            match =  re.search( '(.*)/(.*/.*)$' , id )
+            if match:
+                jobs_repository = match.group(1)
+                if jobs_repository == self.old_jobs_repository_url:
+                    job = match.group(2)
+                    new_job_url = "%s/%s" %( self.new_jobs_repository_url , job )
+                    self.log.debug( "update jobid %s by %s" %(id , new_job_url ) )
+                    ut.setJobID( id , new_job_url )
+                else:
+                    self.log.debug( "remove job %s" %id )
+                    if jobs_repository.find( 'http://mobyle.pasteur.fr/Mobyle/Results' ) != -1:
+                        self.log.warning( ' -- ATTENTION vielle session: %s -- ' % session_path )
+                    ut.removeJob( id )
+            else:
+                raise Exception( "job have an id %s witch not match standard job url" % id )
+            
+        ut.updateData( self.old_jobs_repository_url , self.cfg.results_url() )
+        ut.commit()
+       
+        os.utime( index_path , ( index_atime , index_mtime ) )
+        os.utime( session_path , ( session_dir_atime , session_dir_mtime ) )
+        
+        
+
+            
+if __name__ == "__main__":
+    
+    from optparse import OptionParser
+    
+    parser = OptionParser( )
+    parser.add_option( "-s" , "--sessions",
+                       action="store", 
+                       type = 'string', 
+                       dest="sessions",
+                       help="comma separated list of sessions to update."                       
+                       )
+    parser.add_option( "--old-jobs-repos",
+                       action="store", 
+                       type = 'string', 
+                       dest="old_jobs_repository",
+                       help="the previous mobyle jobs repository url."                       
+                       )
+    
+    parser.add_option( "-l" , "--log",
+                       action="store", 
+                       type = 'string', 
+                       dest = "log_file",
+                       help= "Path to the Logfile where put the logs.")
+    
+    parser.add_option( "-n" , "--dry-run",
+                       action="store_true", 
+                       dest = "dry_run",
+                       default = False ,
+                       help= "don't actually do anything.")
+    
+    parser.add_option( "-f" , "--force",
+                       action="store_true", 
+                       dest = "force",
+                       default = False ,
+                       help= "force to update session even if it was already updated.")
+    
+    parser.add_option("-v", "--verbose",
+                      action="count", 
+                      dest="verbosity", 
+                      default= 0,
+                      help="increase the verbosity level. There is 4 levels: Error messages (default), Warning (-v), Info (-vv) and Debug.(-vvv)") 
+    
+    options, args = parser.parse_args()
+#    if not options.old_host :
+#        parser.error("option --old-host is mandatory")
+    if not options.old_jobs_repository:
+        parser.error("option --old-jobs-repos is mandatory")
+    
+    import logging 
+    logger = logging.getLogger( 'sessionUpdater' ) 
+    if options.log_file is None:
+        handler = logging.StreamHandler(  sys.stderr  )
+    else:
+        try:
+            handler = logging.FileHandler( options.log_file , 'a' )
+        except(IOError , OSError) , err:
+            print >> sys.stderr , "cannot log messages in %s: %s"%( options.log_file , err )
+            sys.exit(1)
+    if options.verbosity < 2:
+        formatter = logging.Formatter( '%(filename)-10s : %(levelname)-8s : %(asctime)s : %(message)s' , '%a, %d %b %Y %H:%M:%S' )
+    else:
+        formatter =   logging.Formatter( '%(filename)-10s : %(levelname)-8s : L %(lineno)d : %(asctime)s : %(message)s' , '%a, %d %b %Y %H:%M:%S' )        
+    handler.setFormatter( formatter )
+    logger.addHandler( handler)
+    if options.verbosity == 0:
+        logger.setLevel( logging.WARNING )
+    elif options.verbosity == 1:
+        logger.setLevel( logging.INFO )
+    else:
+        logger.setLevel( logging.DEBUG )
+
+    config = Config()
+    session_updater = SessionUpdater( config , logger , options.old_jobs_repository)
+    session_repository = config.user_sessions_path()
+    if options.sessions is None:
+        session_updater.update( session_repository , force = options.force , dry_run = options.dry_run )
+    else:
+        sessions = options.sessions.split( ',' )
+        for session in sessions:
+            session_path = os.path.join( session_repository , session )
+            session_updater.update( session_path , force = options.force , dry_run = options.dry_run )
+    
diff --git a/Tools/setsid.c b/Tools/setsid.c
new file mode 100644
index 0000000..fd01ddd
--- /dev/null
+++ b/Tools/setsid.c
@@ -0,0 +1,12 @@
+
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+
+  /* Make this process a group leader */
+  (void)setsid();
+
+  /* Execute the real command */
+  execvp(argv[1], argv+1);
+
+  return 1; }
diff --git a/Tools/validation/iso_abstract_expand.xsl b/Tools/validation/iso_abstract_expand.xsl
new file mode 100644
index 0000000..e7c2b7f
--- /dev/null
+++ b/Tools/validation/iso_abstract_expand.xsl
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?><?xar XSLT?>
+
+<!-- 
+     OVERVIEW - iso_abstract_expand.xsl
+     
+	    This is a preprocessor for ISO Schematron, which implements abstract patterns. 
+	    It also 
+	       	* extracts a particular schema using an ID, where there are multiple 
+	    schemas, such as when they are embedded in the same NVDL script 
+	    	* experimentally, allows parameter recognition and substitution inside
+	    	text as well as @context, @test, & @select.
+		
+		
+		This should be used after iso-dsdl-include.xsl and before the skeleton or
+		meta-stylesheet (e.g. iso-svrl.xsl) . It only requires XSLT 1.
+		 
+		Each kind of inclusion can be turned off (or on) on the command line.
+		 
+-->
+<!-- 
+  VERSION INFORMATION
+  2008-09-18 RJ
+  		* move out param test from iso:schema template  to work with XSLT 1. (Noah Fontes)
+  		
+  2008-07-29 RJ 
+  		* Create.  Pull out as distinct XSL in its own namespace from old iso_pre_pro.xsl
+  		* Put everything in private namespace
+  		* Rewrite replace_substring named template so that copyright is clear
+  	
+  2008-07-24 RJ
+       * correct abstract patterns so for correct names: param/@name and
+     param/@value
+    
+  2007-01-12  RJ 
+     * Use ISO namespace
+     * Use pattern/@id not  pattern/@name 
+     * Add Oliver Becker's suggests from old Schematron-love-in list for <copy> 
+     * Add XT -ism?
+  2003 RJ
+     * Original written for old namespace
+     * http://www.topologi.com/resources/iso-pre-pro.xsl
+-->	
+<!--
+ LEGAL INFORMATION
+ 
+ Copyright (c) 2000-2008 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+<xslt:stylesheet version="1.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron"  
+    xmlns:nvdl="http://purl.oclc.org/dsdl/nvdl"  
+    
+  
+    xmlns:iae="http://www.schematron.com/namespace/iae" 
+     
+      >
+	
+	<xslt:param name="schema-id"></xslt:param>
+	
+	
+	<!-- Driver for the mode -->
+	<xsl:template match="/">
+  		<xsl:apply-templates select="." mode="iae:go" />
+	</xsl:template> 
+	
+	
+	<!-- ================================================================================== -->
+	<!-- Normal processing rules                                                            -->
+	<!-- ================================================================================== -->
+	<!-- Output only the selected schema --> 
+	<xslt:template match="iso:schema" >
+	    <xsl:if test="string-length($schema-id) =0 or @id= $schema-id ">
+	    	<xslt:copy>
+				<xslt:copy-of select="@*" />
+				<xslt:apply-templates  mode="iae:go" /> 
+			</xslt:copy>
+		</xsl:if>
+	</xslt:template>
+	
+ 
+	<!-- Strip out any foreign elements above the Schematron schema .
+		-->
+	<xslt:template match="*[not(ancestor-or-self::iso:*)]"     mode="iae:go"  >
+	   <xslt:apply-templates  mode="iae:go" />
+	</xslt:template>
+	   
+	
+	<!-- ================================================================================== -->
+	<!-- Handle Schematron abstract pattern preprocessing                                   -->
+	<!-- abstract-to-real calls
+			do-pattern calls 
+				macro-expand calls 
+					multi-macro-expand
+						replace-substring                                                   -->
+	<!-- ================================================================================== -->
+	
+	<!--
+		Abstract patterns allow you to say, for example
+		
+		<pattern name="htmlTable" is-a="table">
+			<param name="row" value="html:tr"/>
+			<param name="cell" value="html:td" />
+			<param name="table" value="html:table" />
+		</pattern>
+		
+		For a good introduction, see Uche Ogbujii's article for IBM DeveloperWorks
+		"Discover the flexibility of Schematron abstract patterns"
+		  http://www-128.ibm.com/developerworks/xml/library/x-stron.html
+		However, note that ISO Schematron uses @name and @value attributes on
+		the iso:param element, and @id not @name on the pattern element.
+		
+	-->
+	
+	<!-- Suppress declarations of abstract patterns -->
+	<xslt:template match="iso:pattern[@abstract='true']"  mode="iae:go"  >
+		<xslt:comment>Suppressed abstract pattern <xslt:value-of select="@id"/> was here</xslt:comment>	
+	</xslt:template> 
+	
+	
+	<!-- Suppress uses of abstract patterns -->
+	<xslt:template match="iso:pattern[@is-a]"  mode="iae:go" >
+		<xslt:comment>Start pattern based on abstract <xslt:value-of select="@is-a"/></xslt:comment>
+		
+		<xslt:call-template name="iae:abstract-to-real" >
+			<xslt:with-param name="caller" select="@id" />
+			<xslt:with-param name="is-a" select="@is-a" />
+		</xslt:call-template>
+			
+	</xslt:template>
+	 
+	 
+	
+	<!-- output everything else unchanged -->
+	<xslt:template match="*" priority="-1"  mode="iae:go" >
+	    <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="iae:go"/> 
+		</xslt:copy>
+	</xslt:template>
+	
+	<!-- Templates for macro expansion of abstract patterns -->
+	<!-- Sets up the initial conditions for the recursive call -->
+	<xslt:template name="iae:macro-expand">
+		<xslt:param name="caller"/>
+		<xslt:param name="text" />
+		<xslt:call-template name="iae:multi-macro-expand">
+			<xslt:with-param name="caller" select="$caller"/>
+			<xslt:with-param name="text" select="$text"/>
+			<xslt:with-param name="paramNumber" select="1"/>
+		</xslt:call-template>
+		
+	</xslt:template>
+	
+	<!-- Template to replace the current parameter and then
+	   recurse to replace subsequent parameters. -->
+	    
+	<xslt:template name="iae:multi-macro-expand">
+		<xslt:param name="caller"/>
+		<xslt:param name="text" />
+		<xslt:param name="paramNumber" />
+
+		
+		<xslt:choose>
+			<xslt:when test="//iso:pattern[@id=$caller]/iso:param[ $paramNumber]">
+
+				<xslt:call-template name="iae:multi-macro-expand">
+					<xslt:with-param name="caller" select="$caller"/>	
+					<xslt:with-param name="paramNumber" select="$paramNumber + 1"/>		
+					<xslt:with-param name="text" >
+						<xslt:call-template name="iae:replace-substring">
+							<xslt:with-param name="original" select="$text"/>
+							<xslt:with-param name="substring"
+							select="concat('$', //iso:pattern[@id=$caller]/iso:param[ $paramNumber ]/@name)"/>
+							<xslt:with-param name="replacement"
+								select="//iso:pattern[@id=$caller]/iso:param[ $paramNumber ]/@value"/>			
+						</xslt:call-template>
+					</xslt:with-param>						
+				</xslt:call-template>
+			</xslt:when>
+			<xslt:otherwise><xslt:value-of select="$text" /></xslt:otherwise>		
+		
+		</xslt:choose>
+	</xslt:template>
+	
+	
+	<!-- generate the real pattern from an abstract pattern + parameters-->
+	<xslt:template name="iae:abstract-to-real" >
+		<xslt:param name="caller"/>
+		<xslt:param name="is-a" />
+		<xslt:for-each select="//iso:pattern[@id= $is-a]">
+		<xslt:copy>
+		
+		    <xslt:choose>
+		      <xslt:when test=" string-length( $caller ) = 0">
+		      <xslt:attribute name="id"><xslt:value-of select="concat( generate-id(.) , $is-a)" /></xslt:attribute>
+		      </xslt:when>
+		      <xslt:otherwise>
+				<xslt:attribute name="id"><xslt:value-of select="$caller" /></xslt:attribute>
+		      </xslt:otherwise>
+		    </xslt:choose> 
+			
+			<xslt:apply-templates select="*|text()" mode="iae:do-pattern"    >
+				<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+			</xslt:apply-templates>	
+			
+		</xslt:copy>
+		</xslt:for-each>
+	</xslt:template>
+		
+	
+	<!-- Generate a non-abstract pattern -->
+	<xslt:template mode="iae:do-pattern" match="*">
+		<xslt:param name="caller"/>
+		<xslt:copy>
+			<xslt:for-each select="@*[name()='test' or name()='context' or name()='select']">
+				<xslt:attribute name="{name()}">
+				<xslt:call-template name="iae:macro-expand">
+						<xslt:with-param name="text"><xslt:value-of select="."/></xslt:with-param>
+						<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+					</xslt:call-template>
+				</xslt:attribute>
+			</xslt:for-each>	
+			<xslt:copy-of select="@*[name()!='test'][name()!='context'][name()!='select']" />
+			<xsl:for-each select="node()">
+				<xsl:choose>
+				    <!-- Experiment: replace macros in text as well, to allow parameterized assertions
+				        and so on, without having to have spurious <iso:value-of> calls and multiple
+				        delimiting -->
+					<xsl:when test="self::text()">	
+						<xslt:call-template name="iae:macro-expand">
+							<xslt:with-param name="text"><xslt:value-of select="."/></xslt:with-param>
+							<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+						</xslt:call-template>
+					</xsl:when>
+					<xsl:otherwise>
+						<xslt:apply-templates select="." mode="iae:do-pattern">
+							<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+						</xslt:apply-templates>		
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:for-each>			
+		</xslt:copy>
+	</xslt:template>
+	
+	<!-- UTILITIES --> 
+	<!-- Simple version of replace-substring function -->
+	<xslt:template name="iae:replace-substring">
+		<xslt:param name="original" />    
+		<xslt:param name="substring" />   
+		<xslt:param name="replacement" select="''"/>
+		
+  <xsl:choose>
+    <xsl:when test="not($original)" /> 
+    <xsl:when test="not(string($substring))">
+      <xsl:value-of select="$original" />
+    </xsl:when> 
+        <xsl:when test="contains($original, $substring)">
+          <xsl:variable name="before" select="substring-before($original, $substring)" />
+          <xsl:variable name="after" select="substring-after($original, $substring)" />
+          
+          <xsl:value-of select="$before" />
+          <xsl:value-of select="$replacement" />
+          <!-- recursion -->
+          <xsl:call-template name="iae:replace-substring">
+            <xsl:with-param name="original" select="$after" />
+            <xsl:with-param name="substring" select="$substring" />
+            <xsl:with-param name="replacement" select="$replacement" /> 
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+        	<!-- no substitution -->
+        	<xsl:value-of select="$original" />
+        </xsl:otherwise>
+      </xsl:choose> 
+</xslt:template>
+
+</xslt:stylesheet>
\ No newline at end of file
diff --git a/Tools/validation/iso_dsdl_include.xsl b/Tools/validation/iso_dsdl_include.xsl
new file mode 100644
index 0000000..2bcf626
--- /dev/null
+++ b/Tools/validation/iso_dsdl_include.xsl
@@ -0,0 +1,989 @@
+<?xml version="1.0" encoding="UTF-8"?><?xar XSLT?>
+
+<!-- 
+     OVERVIEW : iso_dsdl_include.xsl
+     
+	    This is an inclusion preprocessor for the non-smart text inclusions
+	    of ISO DSDL. It handles 
+	    	<relax:extRef> for ISO RELAX NG
+	    	<sch:include>  for ISO Schematron and Schematron 1.n
+	    	<xi:xinclude>  simple W3C XIncludes for ISO NVRL and DSRL 
+	    	<crdl:ref>     for draft ISO CRDL
+	    	<dtll:include> for draft ISO DTLL
+	    	<* @xlink:href> for simple W3C XLink 1.1 embedded links
+	    	
+		 
+		This should be the first in any chain of processing. It only requires
+		XSLT 1. Each kind of inclusion can be turned off (or on) on the command line.
+		
+		Ids in fragment identifiers or xpointers will be sought in the following
+		order:
+		    * @xml:id
+		    * id() for typed schemas (e.g. from DTD) [NOTE: XInclude does not support this]
+		    * untyped @id 
+		    
+		The proposed behaviour for the update to ISO Schematron has been implemented. If an
+		include points to an element with the same name as the parent, then that element's
+		contents will be included. This supports the merge style of inclusion.    
+		
+		When an inclusion is made, it is preceded by a PI with target DSDL_INCLUDE_START
+		and the href and closed by a PI with target DSDL_INCLUDE_START and the href. This is
+		to allow better location of problems, though only to the file level. 
+		    
+		Limitations:
+			* No rebasing: relative paths will be interpreted based on the initial document's
+			path, not the including document. (Severe limitation!)
+			* No checking for circular references
+			* Not full xpointers: only ID matching
+			* <relax:include> not implemented 
+			* XInclude handling of xml:base and xml:lang not implemented   
+-->
+<!-- 
+  VERSION INFORMATION
+  2008-09-18
+    * Remove new behaviour for include, because it conflicts with existing usage [KH]
+    * Add extends[@href] element with that merge functionality
+    * Generate PIs to notate source of inclusions for potential better diagnostics
+    
+  2008-09-16
+    * Fix for XSLT1
+    
+  2008-08-28
+  	* New behaviour for schematron includes: if the pointed to element is the same as the current,
+  	include the children.
+  	
+  2008-08-20
+  	* Fix bug: in XSLT1 cannot do $document/id('x') but need to use for-each
+  	
+  2008-08-04
+  	* Add support for inclusions in old namespace  
+  	
+  2008-08-03
+  	* Fix wrong param name include-relaxng & include-crdl (KH, PH)
+  	* Allow inclusion of XSLT and XHTML (KH)
+  	* Fix inclusion of fragments (KH)
+  	
+  2008-07-25
+  	* Add selectable input parameter
+  
+  2008-07-24  
+	* RJ New
+-->	
+<!--
+ LEGAL INFORMATION
+ 
+ Copyright (c) 2008 Rick Jelliffe 
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+<xslt:stylesheet version="1.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
+    xmlns:nvdl="http://purl.oclc.org/dsdl/nvdl" 
+	xmlns:xhtml="http://www.w3.org/1999/xhtml"
+    xmlns:schold="http://www.ascc.net/xml/schematron"
+    xmlns:crdl="http://purl.oclc.org/dsdl/crepdl/ns/structure/1.0"
+    xmlns:xi="http://www.w3.org/2001/XInclude"
+    xmlns:dtll="http://www.jenitennison.com/datatypes"
+    xmlns:dsdl="http://www.dsdl.org/namespace" 
+    xmlns:relax="http://relaxng.org/ns/structure/1.0"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+            > 
+	 <!-- Note: The URL for the dsdl namespace is not official -->
+	
+	
+<xsl:param name="include-schematron">true</xsl:param>
+<xsl:param name="include-crdl">true</xsl:param>
+<xsl:param name="include-xinclude">true</xsl:param>
+<xsl:param name="include-dtll">true</xsl:param>
+<xsl:param name="include-relaxng">true</xsl:param> 
+<xsl:param name="include-xlink">true</xsl:param> 
+	
+<xsl:template match="/">
+  <xsl:apply-templates select="." mode="dsdl:go" />
+</xsl:template>
+	
+	<!-- output everything else unchanged -->
+	<xslt:template match="node()" priority="-1" mode="dsdl:go"> 
+	    <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		</xslt:copy>
+	</xslt:template>
+
+	 
+			
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 2 - Regular grammar-based validation - RELAX NG        --> 
+	<!-- This only implements relax:extRef not relax:include which   -->
+	<!-- is complex.                                                 -->
+	<!-- =========================================================== -->
+	<xslt:template   match="relax:extRef" mode="dsdl:go">
+	 
+	
+	    <!-- Insert subschema -->
+       
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+       
+     	<xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+       <xsl:choose>
+       	<xsl:when test="not( $include-relaxng = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in RELAX NG extRef include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//*[@xml:id= $fragment-id ] | id( $fragment-id) | //*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+             
+               <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if> 
+			<!-- use a for-each so that the id() function works correctly on the external document -->
+			<xsl:for-each select="$theDocument">
+              <xsl:variable name="theFragment" select="$theDocument//*[@xml:id= $fragment-id ]        
+                  |  id( $fragment-id)          
+              | $theDocument//*[@id= $fragment-id ]" />
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:apply-templates select=" $theFragment[1]" mode="dsdl:go"/>
+             </xsl:for-each>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/*" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+ 
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+       		<xsl:apply-templates select="$theFragment " mode="dsdl:go"/>
+       	   </xsl:otherwise>
+       </xsl:choose> 
+       	
+       </xsl:otherwise>
+      </xsl:choose>
+      
+     	<xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xslt:template>
+	    
+	
+	
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 3 - Rule-based validation - Schematron                 --> 
+	<!-- =========================================================== -->
+	
+	
+   <!-- Extend the URI syntax to allow # references -->
+   <!-- Add experimental support for simple containers like  /xxx:xxx/iso:pattern to allow better includes -->
+   <xsl:template match="iso:include" mode="dsdl:go">
+    
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+       
+       
+     	<xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+     	
+    <xsl:choose>
+       	<xsl:when test="not( $include-schematron = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>    
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//iso:*[@xml:id= $fragment-id ] 
+              	 |id( $fragment-id)
+              	 | //iso:*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <!-- case where there is a fragment in another document (should be an iso: element) -->
+			<!-- There are three cases for includes with fragment:
+					0) No href file or no matching id - error!
+			    	1) REMOVED
+			    	      
+			        2) The linked-to element is sch:schema however the parent of the include
+			            is not a schema. In this case, it is an error. (Actually, it should
+			            be an error for other kinds of containment problems, but we won't
+			            check for them in this version.)
+			            
+			        3) Otherwise, include the pointed-to element
+			 -->  
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="originalParent" select=".." />
+              
+              <!-- case 0 -->
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			   </xsl:if>
+			   <!-- use for-each to rebase id() to external document -->
+			   <xsl:for-each select="$theDocument" >
+              		<xsl:variable name="theFragment" 
+              			select=" $theDocument//iso:*[@xml:id= $fragment-id ] |
+              	 		id($fragment-id) |
+              			$theDocument//iso:*[@id= $fragment-id ]" />
+              		 	
+              	
+              		<xsl:choose>
+              			<!-- case 0 -->
+   						<xsl:when test="not($theFragment)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href"/>
+							</xsl:message>
+						</xsl:when>
+              		
+              		
+              			<!-- case 1 REMOVED -->	 
+						              		
+              			<!-- case 2 -->
+              			<xsl:when test=" $theFragment/self::iso:schema ">
+                			 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              			</xsl:when>
+              			
+              			<!-- case 3 -->
+              			<xsl:otherwise> 
+		              		<xsl:apply-templates select=" $theFragment[1]" mode="dsdl:go"/>
+		              	</xsl:otherwise>
+		              </xsl:choose>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		  <!-- Case where there is no ID so we include the whole document -->
+		  <!-- Experimental addition: include fragments of children -->
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:* | $theDocument/*/xsl:* | $theDocument/*/xhtml:*" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+   			  </xsl:if>
+
+			  <!-- There are three cases for includes:
+					0) No text specified- error!
+					
+			    	1) REMOVED
+			    	      
+			        2) The linked-to element is sch:schema however the parent of the include
+			            is not a schema. In this case, it is an error. (Actually, it should
+			            be an error for other kinds of containment problems, but we won't
+			            check for them in this version.)
+			            
+			        3) Otherwise, include the pointed-to element
+			 -->  
+			<xsl:choose>
+				<!-- case 0 -->
+   				<xsl:when test="not($theFragment) and not ($theContainedFragments)">
+					<xsl:message terminate="no">
+						<xsl:text>Unable to locate id attribute: </xsl:text>
+						<xsl:value-of select="@href"/>
+					</xsl:message>
+				</xsl:when>
+			
+				<!-- case 1 removed --> 
+				
+				<!-- case 2 -->
+              	<xsl:when test=" $theFragment/self::iso:schema or $theContainedFragments/self::iso:schema">
+                	 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              	</xsl:when>
+
+				<!-- If this were XLST 2, we could use  
+					if ($theFragment) then $theFragment else $theContainedFragments
+					here (thanks to KN)
+				-->
+				<!-- case 3 -->
+       			<xsl:otherwise>	
+       				<xsl:apply-templates select="$theFragment "  mode="dsdl:go"/>
+ 		      	</xsl:otherwise>
+   			</xsl:choose>
+       	   </xsl:otherwise>
+       	</xsl:choose>
+       </xsl:otherwise>
+      </xsl:choose>
+      
+     	<xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xsl:template>
+	
+	
+	<!-- WARNING   sch:extends[@href] is experimental and non standard  --> 
+   <!-- Basically, it adds the children of the selected element, not the element itself.  -->
+   <xsl:template match="iso:extends[@href]" mode="dsdl:go">
+       
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+     
+     
+     	<xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+      
+    <xsl:choose>
+       	<xsl:when test="not( $include-schematron = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>    
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//iso:*[@xml:id= $fragment-id ]/* 
+              	 |id( $fragment-id)/*
+              	 | //iso:*[@id= $fragment-id ]/*"/>
+          </xslt:when>
+          
+          <!-- case where there is a fragment in another document (should be an iso: element) -->
+			<!-- There are three cases for includes with fragment:
+					0) No href file or no matching id - error!
+			    	1) REMOVED
+			    	      
+			        2) REMOVED
+			            
+			        3) Otherwise, include the pointed-to element
+			 -->  
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="originalParent" select=".." />
+              
+              <!-- case 0 -->
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			   </xsl:if>
+			   <!-- use for-each to rebase id() to external document -->
+			   <xsl:for-each select="$theDocument" >
+              		<xsl:variable name="theFragment" 
+              			select=" $theDocument//iso:*[@xml:id= $fragment-id ] |
+              	 		id($fragment-id) |
+              			$theDocument//iso:*[@id= $fragment-id ]" />
+              		 	
+              	
+              		<xsl:choose>
+              			<!-- case 0 -->
+   						<xsl:when test="not($theFragment)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href"/>
+							</xsl:message>
+						</xsl:when>
+              		
+              		
+              			<!-- case 1 REMOVED -->	 
+						              		
+              			<!-- case 2 REMOVED -->
+              			 
+              			
+              			<!-- case 3 -->
+              			<xsl:otherwise>
+              			
+		              		<xsl:apply-templates select=" $theFragment[1]/*" mode="dsdl:go"/>
+		              	</xsl:otherwise>
+		              </xsl:choose>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		  <!-- Case where there is no ID so we include the whole document -->
+		  <!-- Experimental addition: include fragments of children -->
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:* | $theDocument/*/xsl:* | $theDocument/*/xhtml:*" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+   			  </xsl:if>
+
+			  <!-- There are three cases for includes:
+					0) No text specified- error!
+					
+			    	1) REMOVED
+			    	      
+			        2) REMOVED
+			            
+			        3) Otherwise, include the pointed-to element
+			 -->  
+			<xsl:choose>
+				<!-- case 0 -->
+   				<xsl:when test="not($theFragment) and not ($theContainedFragments)">
+					<xsl:message terminate="no">
+						<xsl:text>Unable to locate id attribute: </xsl:text>
+						<xsl:value-of select="@href"/>
+					</xsl:message>
+				</xsl:when>
+			
+				<!-- case 1 removed --> 
+				
+				<!-- case 2 removed --> 
+
+				<!-- If this were XLST 2, we could use  
+					if ($theFragment) then $theFragment else $theContainedFragments
+					here (thanks to KN)
+				-->
+				<!-- case 3 -->
+       			<xsl:otherwise>	
+       				<xsl:apply-templates select="$theFragment/* "  mode="dsdl:go"/>
+ 		      	</xsl:otherwise>
+   			</xsl:choose>
+       	   </xsl:otherwise>
+       	</xsl:choose>
+       </xsl:otherwise>
+      </xsl:choose>
+      
+     	<xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xsl:template>
+	
+	
+	
+	<!-- =========================================================== -->
+	<!-- Handle Schematron 1.6 inclusions: clone of ISO code above   -->
+	<!-- =========================================================== -->
+	
+	
+   <!-- Extend the URI syntax to allow # references -->
+   <!-- Add experimental support for simple containers like  /xxx:xxx/schold:pattern to allow better includes -->
+   <xsl:template match="schold:include" mode="dsdl:go">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+     
+     	<xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+ 
+  <xsl:choose>
+       	<xsl:when test="not( $include-schematron = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>  
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//schold:*[@xml:id= $fragment-id ] 
+              	 |id( $fragment-id)
+              	 | //schold:*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <!-- case where there is a fragment in another document (should be an iso: element) -->
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+			<!-- use for-each to rebase id() to $theDocument -->
+			<xsl:for-each select="$theDocument">
+              <xsl:variable name="theFragment" 
+              	select=" $theDocument//schold:*[@xml:id= $fragment-id ] |
+              	id($fragment-id) |
+              	$theDocument//schold:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment/self::schold:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:apply-templates select=" $theFragment[1]" mode="dsdl:go"/>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		  <!-- Case where there is no ID so we include the whole document -->
+		  <!-- Experimental addition: include fragments of children -->
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/schold:* | $theDocument/*/xsl:* | $theDocument/*/xhtml:*" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+
+              <xsl:if test=" $theFragment/self::schold:schema or $theContainedFragments/self::schold:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+   			<xsl:if test="not($theFragment) and not ($theContainedFragments)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+			<!-- If this were XLST 2, we could use  
+					if ($theFragment) then $theFragment else $theContainedFragments
+				here (thanks to KN)
+			-->
+			<xsl:choose>
+				<xsl:when test=" $theFragment ">			
+       				<xsl:apply-templates select="$theFragment "  mode="dsdl:go"/>
+       			</xsl:when>
+       			<xsl:otherwise>	
+       				<!-- WARNING!  EXPERIMENTAL! Use at your own risk. This may be discontinued! -->
+ 		      		<xsl:apply-templates select="  $theContainedFragments " mode="dsdl:go"/>
+ 		      	</xsl:otherwise>
+ 		     </xsl:choose>
+       	   </xsl:otherwise>
+       </xsl:choose>
+       
+       </xsl:otherwise>
+      </xsl:choose>
+      
+      <xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xsl:template>
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 5 - DataType Library Language - DTLL                   -->
+	<!-- Committee Draft  Experimental support only                  -->
+	<!-- The <include> element may well be replaced by XInclude in   -->
+	<!-- any final version.                                          -->
+	<!-- =========================================================== -->
+	<xslt:template   match="dtll:include" mode="dsdl:go">
+	    <!-- Insert subschema -->
+       
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+  <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+  <xsl:choose>
+       	<xsl:when test="not( $include-dtll = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>     
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in DTLL include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//*[@xml:id= $fragment-id ] | id( $fragment-id) 
+              	| //*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if> 
+			<!-- use for-each to rebase id() to $theDocument -->
+			<xsl:for-each select="$theDocument" >
+              <xsl:variable name="theFragment" select="$theDocument//*[@xml:id= $fragment-id ]
+               | id( $fragment-id ) 
+               | $theDocument//*[@id= $fragment-id ]" />
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:apply-templates select=" $theFragment[1]" mode="dsdl:go"/>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/*" />
+              
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+ 
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+       		<xsl:apply-templates select="$theFragment " mode="dsdl:go"/>
+       	   </xsl:otherwise>
+       </xsl:choose> 
+       	
+       </xsl:otherwise>
+      </xsl:choose>
+      <xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xslt:template>
+	    
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 7 - Character Repertoire Description Language - CRDL   -->
+	<!-- Final Committee Draft 2008-01-11 Experimental support only  -->
+	<!-- =========================================================== -->
+	<xslt:template   match="crdl:ref" mode="dsdl:go">
+	    <!-- Insert subschema -->
+       
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+ <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+ <xsl:choose>
+       	<xsl:when test="not( $include-crdl = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in CRDL include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+          
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//*[@xml:id= $fragment-id ] | id( $fragment-id)
+              	| //*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+               <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>  
+			<!-- use for-each to rebase id() to $theDocument -->
+			<xsl:for-each select="$theDocument" >
+              <xsl:variable name="theFragment" select="$theDocument//*[@xml:id= $fragment-id ]
+               | id( $fragment-id )
+               | $theDocument//*[@id= $fragment-id ]" />
+			
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:apply-templates select=" $theFragment " mode="dsdl:go"/>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/*" />
+       
+               <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+  
+       		<xsl:apply-templates select="$theFragment " mode="dsdl:go"/>
+       	   </xsl:otherwise>
+       </xsl:choose> 
+       	
+       </xsl:otherwise>
+      </xsl:choose>
+      <xsl:processing-instruction name="DSDL_INCLUDE_END">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xslt:template>
+	    
+	    
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 4 - Namespace-based Validation Dispatching Language - NVDL -->
+	<!-- Note: This does not include schemas referenced for          -->
+	<!-- validation, it merely handles any simple XIncludes          -->
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 8 - Document Schema Renaming Language - DSRL           -->
+	<!-- Note: Final? Committee Draft   Experimental support only    -->
+	<!-- =========================================================== -->
+	<!-- XInclude support for id based references only, with 1 level -->
+	<!-- of fallback.                                                -->
+	<!-- =========================================================== -->
+	    
+	   <xslt:template mode="dsdl:go"
+	   	match="xi:include[@href][not(@parseType) or @parseType ='xml']">
+	    <!-- Simple inclusions only here --> 
+	    <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+  <xsl:choose>
+       	<xsl:when test="not( $include-xinclude = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>  
+       <xsl:choose> 
+          
+       		<xsl:when  test="contains( @href, '#')">
+       			<xsl:message terminate="yes">Fatal error: Xinclude href contains
+       				fragment identifier #</xsl:message>
+	       </xsl:when>
+	       
+	       
+       		<xsl:when  test="contains( @xpointer, '(')">
+       			<xsl:message terminate="yes">Fatal error: Sorry, this software
+       			only supports simple ids in XInclude xpointers</xsl:message>
+	       </xsl:when>
+    
+         <xsl:when test="string-length( @href ) = 0 and string-length( @xpointer ) = 0" >
+          	
+          	<xsl:message terminate="yes">Fatal Error: Impossible URL in XInclude include</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( @href ) = 0">
+           
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//*[@xml:id= current()/@xpointer  ] | id( @xpointer)
+              	| //*[@id= current()/@xpointer  ]"/>
+          </xslt:when>
+          
+          <xsl:when test="string-length( @xpointer ) > 0">
+              <xsl:variable name="theDocument" select="document( @href,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument//*[@xml:id= current()/@xpointer  ]
+             
+              | $theDocument//*[@id= current()/@xpointer  ]" /> 
+              <!-- removed
+               | $theDocument/id( @xpointer)
+               because it requires rebasing in XSLT1 and that would mess up the use of current()
+               -->
+              
+           
+              <!-- Allow one level of fallback, to another XInclude -->
+              <xsl:if test="not($theDocument)">
+                <xsl:choose>
+                    <xsl:when test="xi:fallback">
+                    	 <xsl:variable name="theDocument" select="document( xi:fallback[1]/xi:include[not(@parseType)
+                    	 or @parseType='xml']/@href,/ )" />
+              			<xsl:variable name="theFragment" select="$theDocument//*[@xml:id= current()/xi:fallback[1]/xi:include/@xpointer  ]
+              				| $theDocument//*[@id= current()/xi:fallback[1]/xi:include/@xpointer  ]" />
+              				<!-- removed 
+              						| $theDocument/id( xi:fallback[1]/xi:include/@xpointer)
+              				because it id() would need rebasing in XSLT1 and that would mess up use of current()
+              				-->
+             
+              				  				<xsl:if test="not($theDocument)">
+              				
+								<xsl:message terminate="no">
+									<xsl:text>Unable to open referenced included file and fallback
+									file: </xsl:text>
+										<xsl:value-of select="@href"/>
+								</xsl:message>
+							</xsl:if>
+                    </xsl:when>
+					<xsl:otherwise>
+						<xsl:message terminate="no">
+							<xsl:text>Unable to open referenced included file: </xsl:text>
+							<xsl:value-of select="@href"/>
+						</xsl:message>
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:if> 
+              <xsl:apply-templates select=" $theFragment" mode="dsdl:go"/> 
+		   </xsl:when>
+		  
+		  <!-- Document but no fragment specified -->
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( @href,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/*" />
+               
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+ 
+       		<xsl:apply-templates select="$theFragment " mode="dsdl:go"/>
+       	   </xsl:otherwise>
+       </xsl:choose> 
+       	
+       </xsl:otherwise>
+      </xsl:choose>
+      <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@href"/></xsl:processing-instruction>
+   </xslt:template>
+   
+   	<!-- =========================================================== -->
+	<!-- W3C XLink 1.1 embedded simple links                        -->
+	<!-- =========================================================== -->
+	<xslt:template   match="*[@xlink:href][not(parent::*[@xlink:type='complex'])]
+	           [not(@xlink:type) or (@xlink:type='simple')]
+	           [@xlink:show='embed']
+	           [not(@xlink:actuate) or (@xlink:actuate='onLoad')]" 
+	           mode="dsdl:go" priority="1"> 
+       
+       <xsl:variable name="document-uri" select="substring-before(concat(@xlink:href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@xlink:href, '#')"/>
+  <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@xlink:href"/></xsl:processing-instruction>
+	            <xsl:choose>
+       	<xsl:when test="not( $include-xlink = 'true' )">
+       	  <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go"   /> 
+		  </xslt:copy>
+		</xsl:when>
+		<xsl:otherwise>     
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in XLink embedding link</xsl:message>
+          </xsl:when> 
+          
+          <!-- this case is when there is in embedded schema in the same document elsewhere -->
+          <xslt:when test="string-length( $document-uri ) = 0">
+              <xslt:apply-templates mode="dsdl:go"
+              	select="//*[@xml:id= $fragment-id ] | id( $fragment-id) 
+              	| //*[@id= $fragment-id ]"/>
+          </xslt:when>
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@xlink:href"/>
+				</xsl:message>
+			</xsl:if> 
+			<!-- use for-each to rebase id() to $theDocument -->
+			<xsl:for-each select="$theDocument" >
+              <xsl:variable name="theFragment" select="$theDocument//*[@xml:id= $fragment-id ]
+               | id( $fragment-id ) 
+               | $theDocument//*[@id= $fragment-id ]" />
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@xlink:href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:apply-templates select=" $theFragment[1]" mode="dsdl:go"/>
+              </xsl:for-each>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/*" />
+              
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@xlink:href"/>
+				</xsl:message>
+			</xsl:if>
+ 
+   			<xsl:if test="not($theFragment)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to locate id attribute: </xsl:text>
+					<xsl:value-of select="@xlink:href"/>
+				</xsl:message>
+			</xsl:if>
+       		<xsl:apply-templates select="$theFragment " mode="dsdl:go"/>
+       	   </xsl:otherwise>
+       </xsl:choose> 
+       
+       </xsl:otherwise>
+      </xsl:choose>
+       	
+  <xsl:processing-instruction name="DSDL_INCLUDE_START">  <xsl:value-of select="@xlink:href"/></xsl:processing-instruction>
+   </xslt:template>
+	    
+	    
+</xslt:stylesheet>
\ No newline at end of file
diff --git a/Tools/validation/iso_schematron_skeleton_for_saxon.xsl b/Tools/validation/iso_schematron_skeleton_for_saxon.xsl
new file mode 100644
index 0000000..7ea7f36
--- /dev/null
+++ b/Tools/validation/iso_schematron_skeleton_for_saxon.xsl
@@ -0,0 +1,1884 @@
+<?xml version="1.0"?><?xar XSLT?>
+
+<!-- 
+   OVERVIEW
+   
+   ASCC/Schematron.com Skeleton Module for ISO Schematron (for XSLT2 systems)
+   
+   ISO Schematron is a language for making assertion about the presence or absense
+   of patterns in XML documents. It is typically used for as a schema language, or
+   to augment existing schema languages, and to check business rules. It is very
+   powerful, yet quite simple: a developer only need know XPath and about five other
+   elements.
+   
+   This is an open source implementation of ISO Schematron in XSLT. Although ISO does
+   not allow reference implementations which might compete with the text of the
+   standard, this code has been compiled by Rick Jelliffe, inventor of Schematron
+   and editor of the ISO standard; so developers can certainly use it as an 
+   unofficial reference implementation for clarification. 
+   
+   This implementation is based on one by Oliver Becker. API documentation is 
+   available separately; try www.schematron.com for this. Funding for this
+   stylesheet over the years has come from Topologi Pty. Ltd., Geotempo Ltd.,
+   and ASCC, Tapei.
+   
+   There are two versions of this skeleton: one is tailored for XSLT1 processors
+   and the other is tailored for XSLT2 processors. Future versions of the
+   XSLT2 skeleton may support more features than that the XSLT 1 skeleton.
+-->
+<!--
+   TIPS
+      
+   A tip for new users of Schematron: make your assertions contain positive messages
+   about what is expected, rather than error messages. For example, use the form
+   "An X should have a Y, because Z". 
+   
+   Another tip is that Schematron provides an
+   element <iso:ns> for declaring the namespaces and prefixes used in Xpaths in 
+   attribute values; it does not extend the XML Namespaces mechanism: if a name
+   in an XPath has a prefix, there must be an <iso:ns> element for that prefix; if
+   a name in an XPath does not have a prefix, it is always in no namespace.
+   
+   A tip for implementers of Schematron, either using this API or re-implementing it:
+   make the value of the diagnostics, flags and richer features available if possible;
+   Schematron has many of the optional richer features which, if implemented, provide
+   a compelling alternative approach to validation and business-rules checking compared
+   to other schema languages and programs. 
+   
+   If you create your own meta-stylesheet to override this one, it is a
+   good idea to have both in the same directory and to run the stylesheet
+   from that directory, as many XSLT implementations have ideosyncratic
+   handling of URLs: keep it simple.
+-->
+ 
+
+<!--
+  INVOCATION INFORMATION
+  
+  The following parameters are available
+  
+    phase           NMTOKEN | "#ALL" (default) Select the phase for validation
+    allow-foreign   "true" | "false" (default)   Pass non-Schematron elements to the generated stylesheet
+    sch.exslt.imports semi-colon delimited string of filenames for some EXSLT implementations  
+    message-newline "true" (default) | "false"   Generate an extra newline at the end of messages 
+    debug	    "true" | "false" (default)  Debug mode lets compilation continue despite problems
+    attributes "true" | "false"  (Autodetecting) Use only when the schema has no attributes as the context nodes
+    only-child-elements "true" | "false" (Autodetecting) Use only when the schema has no comments
+    or PI  as the context nodes
+  
+  The following parameters can be specified as Schematron variables in diagnostics, assertions and so on.
+    fileNameParameter string	  
+    fileDirParameter string				
+    archiveNameParameter string	  In case of ZIP files
+    archiveDirParameter string	  In case of ZIP files	 
+    
+ Experimental: USE AT YOUR OWN RISK   
+    visit-text "true" "false"   Also visist text nodes for context. WARNING: NON_STARDARD.
+    select-contents '' | 'key' | '//'   Select different implementation strategies
+ 
+ Conventions: Meta-stylesheets that override this may use the following parameters
+    generate-paths=true|false   generate the @location attribute with XPaths
+    diagnose= yes | no    Add the diagnostics to the assertion test in reports
+    terminate= yes | no   Terminate on the first failed assertion or successful report
+ 
+-->
+
+<!-- 
+  XSLT VERSION SUPPORT
+
+  XSLT 1:
+     A schema using the standard XSLT 1 query binding will have a /schema/@queryBinding='xslt' or 
+     nothing.
+
+       * Note: XT does not implement key() and will die if given it. 
+       * Add all formal parameters to default templates
+       * Fix missing apply-templates from process-ns and add params back
+
+  EXSLT:  Experimental support
+     A schema using the EXSLT query binding will have a /schema/@queryBinding='exslt'.
+     It is built on XSLT 1. After experience is gained, this binding is expected to be 
+     formalized as part of ISO Schematron, which currently reserves the "exslt" name for this purpose.
+
+     Some EXSLT engines have the extra functions built-in. For these, there is no need to
+     provide library locations. For engines that require the functions, either hard code
+     them in this script or provide them on the command-line argument.
+      
+  XSLT 2:   Experimental support
+     A schema using the XSLT 2 query binding will have a /schema/@queryBinding='xslt2'.
+     This binding is expected to be formalized as part of ISO
+     Schematron, which currently reserves the "xslt2" name for this purpose.
+     The xsl:import-schema, xsl:key and xsl:function elements are allowed as top elements. 
+     
+  XPATH:    Experimental support
+     A schema using the XPATH query binding will have a /schema/@queryBinding='xpath'.
+     It can run with XSLT 1 and is a strict superset of default ISO Schematron. After
+     experience is gained, this binding is expected to be formalized as part of ISO
+     Schematron, which currently reserves the "xpath" name for this purpose.
+
+     The intent of this query binding is to support minimal non-XSLT implementations of 
+     Schematron that use simple XPath APIs. These not only have fewer functions available
+     than the XSLT version of XPath, but some of them do not support variables. 
+     Consequently, in this binding, the <let> element and command-line variables passed
+     to the schema should not be used? 
+     The xsl:import-schema element is not allowed.
+     
+-->
+<!--
+   PROCESS INFORMATION
+   
+   This stylesheet compiles a Schematron schema (*.sch) into XSLT code (*.xsl). 
+   The generated XSLT code can then be run against an XML file (*.xml, etc) and
+   will produce validation results.
+   
+   The output of validation results is performed using named templates (process-*). 
+   These can be overridden easily by making a new XSLT stylesheet that imports this 
+   stylesheet but has its own version of the relevant process-* templates. Several
+   of these invoking stylesheets are available: "iso_svrl.xsl", for example generates
+   ISO Schematron Validation Report Language format results.
+   
+   In this version of the stylesheet, the ISO feature called "abstract patterns" is
+   implemented using macro processing: a prior XSLT stage to which converts uses
+   of abstract patterns into normal patterns. If you do not use abstract patterns,
+   it is not necessary to preprocess the schema.
+   
+   To summarize, a basic process flow for some commandline processor is like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_schematron_skeleton.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+   
+   iso_svrl.xslt is an implementation of Schematron that can use this skeleton and
+   generate ISO SVRL reports. A process flow for some commandline processor would
+   be like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_svrl.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+     
+   It is not impossible that ultimately a third stage, to handle macro-preprocessing
+   and inclusion, might be necessary. (The trade-off is in making this XSLT more
+   complex compared to making the outer process more complex.)
+             
+  This version has been developed to work with 
+     Saxon 8 
+  For versions for XSLT 1 processors, see www.xml.com
+
+ Please note that if you are using SAXON and JAXP, then you should use 
+  System.setProperty("javax.xml.transform.TransformerFactory",
+                          "net.sf.saxon.TransformerFactoryImpl");
+ rather than 
+  System.setProperty("javax.xml.xpath.TransformerFactory",
+                           "net.sf.saxon.TransformerFactoryImpl");
+ which is does not work, at least for the versions of SAXON we tried.
+-->
+<!--
+ LEGAL INFORMATION
+ 
+ Copyright (c) 2000-2008 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+<!--
+  VERSION INFORMATION
+   2008-08-11
+   		* TT report/@flag was missing
+   2008-08-06
+   		* TT Top-level lets need to be implemented using xsl:param not xsl:variable
+   		* TT xsl:param/@select must have XPath or not be specified
+   		
+   2008-08-04 
+   		* RJ add saxon namespace to output to allow extension functions
+   Version: 2008-07-28
+   		* KH schematron-get-full-path-3 has [index] even on top step
+   Version: 2008-07-24
+   		* RJ clean out commented out namespace handling code
+   		* RJ add support for experimental non-standard attribute report/@action
+   		and assert/@action, and add parameter not in the published API
+   		(should not break anything: marked up as a tunneling parameter)
+   		* RJ allow schema/@queryBinding='xpath2' and warn if variables are
+   		used
+   		
+   Version: 2008-07-14 update for XSLT2 and inclusion experiments
+   		* RJ Clean up zero-length fragment test on include
+   		* RJ Add experimental support for include containers
+   		* RJ Add support for xsl:import-schema (request Paul Hermans)
+   		* RJ Add support for xsl:function
+   		* RJ For path generation, test for //iso:schema not just /iso:schema, for potential embedded Schematron support
+   		* RJ Don't generate double error messages for old namespace elements
+   		* RJ Experimental iso:rule/iso:title just kept as comment (bigger request Uche Ogbuji)
+   		* RJ Fix bug that prevented including patterns in this (report Roger
+   	Costello)
+   Version: 2007-10-17
+     Forked out version just to support SAXON 8 and potentially other XSLT2 processors.
+       * RJ use xsl:namespace element
+       * RJ use schold as namespace for old schematron, to prevent SAXON complaining
+         when validating the Schematron schema for Schematron
+       * RJ fix FULL-PATH for attributes
+
+   Version: 2007-07-19
+     Accept most changes in David Carlisle's fork, but continue as XSLT1 script: 
+    	http://dpcarlisle.blogspot.com/search/label/schematron
+    	* DPC Remove "optimize" parameter
+    	* DPC Add autodetecting optimize parameter attribute to skip checking attribute
+    	context
+    	* DPC Add autodetecting optimize parameter only-child-elements turn off checking for 
+    	comments and PIs
+    	* DPC (Experimental: NON_STANDARD DANGER!) Add param visit-text to viist text
+    	nodes too for context 
+    	* DPC Fix inclusion syntax to allow #
+    	* DPC Priorities count up from 1000 not down from 4000 to allow more rules
+        * RJ Add new template for titles of schemas, with existing behaviour.  
+        Override process-schema-title for custom processing of title
+    		
+    
+   Version: 2007-04-04
+   	* RJ debug mode param
+	* RJ alter mixed test to only test mixed branches, so the same document
+	could have old and new namespaces schemas in it, but each schema must
+	be distinct, just so as not to overconstrain things.
+   	* KH zero-length include/@href is fatal error, but allow debug mode
+	* SB add hint on SAXON and JAXP
+	* DC generate-full-path-1 generates XLST1 code by default
+   Version: 2007-03-05
+      	* AS Typo for EXSLT randome, improve comment
+      	* KH get-schematron-full-path-2 needs to apply to attributes too
+      	* DP document policy on extensions better
+      	* DC use copy-of not copy for foreign elements
+      	* DC add generate-path-2
+      	* DC don't try to apply templates to attribute axis on attribute nodes, to
+      	stop SAXON warning.
+      	* RJ improve reporting of typos 
+   
+   Version: 2007-02-08
+   		* KH Schematron fullpath implementation: @* handled twice and / missing
+   		* KH Change stylesheetbody from named template to mode to allow implementers more flexibility.
+   		  Move process-ns to outside the stylesheet body.
+   		* DP, FG, fix handling of xslt:key
+   		* FG no iso:title/@class
+   		* Experimental optimization 'visit-no-attributes'
+   		* KH Experimental added schematron-get-full-path-2 which gives prefixed version for humans
+ 		* DC Move stylesheet/@version generation to after namespace handling
+ 		* DC, FG EXSLT namespace handling code
+ 		* FG add ref and commented code from FG's page on namespaces
+ 		* Start adding normalize-space() to parameter code
+ 		* Add a space between diagnostics
+   		   		 
+   Version: 2007-01-22
+   	* DP change = ($start) to = $start and =($phase) to =$phase 
+   	to run under Saxon 8.8j
+	* FG better title section using ( @id | iso:title)[last()]
+	* Default query language binding is "xslt" not "xslt1"
+  
+   Version: 2007-01-19
+   		* Simplify message newline code
+   		* Remove termination and xpath appending to message options: 
+   		   factor out as  iso_schematron_terminator.xsl
+   		* Comment out XSLT2 namespace fix temporarily
+  
+   Version: 2007-01-18 (First beta candidate for comment)
+          * DC remove xml:space="preserve"
+          * FG improve comment on import statement
+          * DC improve comments on invocation section
+          * Add exploratory support for iso:schema[@queryBinding='xpath']
+             by allowing it and warning as lets are found
+          * Be strict about queryBinding spelling errors
+          * Extra comments on the different queryBindings
+          * KH Add option "message-paths" to generate XPath from output 
+          * KH Add option "terminate" to halt with an error after the first assertion
+          * KH refactor paths in schematron-full-path
+          * Improve (?) namespace handling: no dummy attributes for prefix "xsl" generated
+   
+   Version: 2007-01-15
+          * FG fix for calling templates
+          * Add formal parameters to default templates: may help XSLT 2
+          * Fix get-schematron-full-path
+          * Include skeleton1-6 is commented out by default
+
+   Version:2007-01-12 (Pre-beta release to Schematron-love-in maillist)
+           * Add many extra parameters to the process-* calls, so that almost
+           all the information in the schema can be provided to client programs.
+           Also, rearrange the parameters to fit in with the ISO schema, which
+           has "rich" and "linkable" attribute groups.
+           * Warn on diagnostics with no ID once only
+           * Improved path reporting, to handle for namespaces
+           * Add process-title dummy template for API
+           * Add command-line parameter allow-foreign (true|false) to suppress
+            warnings one foreign elements and pass them through to the generated
+            stylesheet
+           * remove legacy templates for the old ASCC namespace and no namespace, 
+              and use an import statement instead. Much cleaner now!
+           * patterns use @id not @name
+           * titles can contain sub-elements
+           * start change iso:rule to allow attributes, PIs and comments 
+           * the default process-* for inline elements add a leading and trailing 
+             space, to reduce the chance of concatenation.
+           * add comments to make the generated code clearer
+           
+   Version:2006-11-07 (ISO: first release private to schematron-love-in maillist for review)
+           * Duplicate pattern templates, for handling ISO namespace
+           * Add priority onto default and paragraph templates
+           * Add namespace checks
+           * Handle key in xsl namespace not iso
+           * Add include
+           * Improve namespace handling
+           * Preliminary XSLT2 and EXSLT support
+	       * Refactor iso:schema for clarity
+
+    Version: 2003-05-26 
+    	    * Fix bug with key 
+    Version: 2003-04-16
+    	   * handle 1.6 let expressions
+    	   * make key use XSLT names, and allow anywhere
+    Version: 2001-06-13
+           * same skeleton now supports namespace or no namespace
+           * parameters to handlers updated for all 1.5 attributes 
+           * diagnostic hints supported: command-line option diagnose=yes|no
+           * phases supported: command-line option phase=#ALL|...
+           * abstract rules
+           * compile-time error messages  
+	   * add utility routine generate-id-from-path
+          
+    Contributors: Rick Jelliffe (original), Oliver Becker (architecture, XSLT2), 
+             Miloslav Nic (diagnostic, phase, options), Ludwig Svenonius (abstract)
+             Uche Ogbuji (misc. bug fixes), Jim Ancona (SAXON workaround),
+	 	     Francis Norton (generate-id-from-path), Robert Leftwich, Bryan Rasmussen,
+             Dave Pawson (include, fallback), Florent Georges (namespaces, exslt, attribute
+             context), Benoit Maisonny (attribute context), John Dumps (process-message newline),
+             Cliff Stanford (diagnostics and other newlines)
+
+    
+      
+    
+    KNOWN TYPICAL LIMITATIONS:
+      * Don't use <iso:ns prefix="xsl" .../> with a namespace other than the standard
+      XSLT one. This would be a bizarre thing to do anyway. 
+      * Don't use other prefixes for the XSLT namespace either; some implementations will
+      not handle it correctly.
+     
+     EXTENSIONS:
+      ISO Schematron is designed as a framework with some standard query language
+      bindings. If you need to support other features, please do so safely by making
+      up your own @queryLanguage name: this makes it clear that your schema requires
+      special features. For example, default ISO Schematron does not support user
+      defined functions; so if you want to use the user defined function feature
+      in XSLT, you need to have a schema with some queryBinding attribute name like
+      "XSLT-with-my-functions" or whatever.
+-->
+
+
+
+
+<xsl:stylesheet  
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 
+    xmlns:schold="http://www.ascc.net/xml/schematron"
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
+    xmlns:exsl="http://exslt.org/common" 
+    extension-element-prefixes="exsl"
+    version="2.0"
+	 >
+<!-- This program implements ISO Schematron, except for abstract patterns 
+which require a preprocess.
+-->
+  
+
+<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
+
+
+<!-- Category: top-level-element -->
+<xsl:output method="xml" omit-xml-declaration="no" standalone="yes"  indent="yes"/>
+
+
+
+<xsl:param name="phase">
+   <xsl:choose>   
+    <xsl:when test="//iso:schema/@defaultPhase">
+      <xsl:value-of select="//iso:schema/@defaultPhase"/>
+    </xsl:when>
+    <xsl:otherwise>#ALL</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="allow-foreign">false</xsl:param>
+
+<xsl:param name="message-newline">true</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on attribute nodes
+         defaults to true if there is any possibility that a context could match an attribute,
+         err on the side if caution, a context of *[.='@'] would cause this param to defualt to true
+         even though @ is in a string
+-->
+<xsl:param name="attributes">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'@') or contains(@context,'attribute')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on just elements in the child axis
+         defaults to true if there is any possibility that a context could match an comment or PI
+         err on the side if caution, a context of *[.='('] would cause this param to defualt to true
+         even though ( is in a string, but node() comment() and processing-instruction()  all have a (
+-->
+<xsl:param name="only-child-elements">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'(')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on text nodes nodes (if only-child-elements is false)
+         THIS IS NON CONFORMANT BEHAVIOUR JUST FOR DISCUSSION OF A POSSIBLE CHANGE TO THE
+         SPECIFICATION. THIS PARAM SHOULD GO IF THE FINAL DECISION IS THAT THE SPEC DOES NOT CHANGE.
+	 Always defaults to false
+-->
+<xsl:param name="visit-text" select="'false'"/>
+
+<!-- DPC
+  When selecting contexts the specified behaviour is
+    @*|node()[not(self::text())]
+    The automatic settings may use
+      node()[not(self::text())]
+      @*|*
+      *
+  instead for schema for which they are equivalent.
+  If the params are set explictly the above may be used, and also either if
+      @*
+      @*|node()
+   in all cases the result may not be equivalent, for example if you specify no attributes and the schema 
+   does have attribute contexts they will be silently ignored.
+
+  after testing it turns out that
+  node()[not(self::text())] is slower in saxon than *|comment()|processing-instruction() 
+  which I find a bit surprising but anyway I'll use the longr faster version.
+-->
+<xsl:variable name="context-xpath">
+  <xsl:if test="$attributes='true'">@*|</xsl:if>
+  <xsl:choose>
+    <xsl:when test="$only-child-elements='true'">*</xsl:when>
+    <xsl:when test="$visit-text='true'">node()</xsl:when>
+    <xsl:otherwise>*|comment()|processing-instruction()</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- DPC if this is set to 
+    '' use recursive templates to iterate over document tree,
+    'key' select  all contexts with a key rather than walking the tree explictly in each mode
+    '//' select all contexts with // a key rather than walking the tree explictly in each mode (XSLT2 only)
+-->
+<xsl:param name="select-contexts" select="''"/>
+
+
+<!-- e.g. saxon file.xml file.xsl "sch.exslt.imports=.../string.xsl;.../math.xsl" -->
+<xsl:param name="sch.exslt.imports"/>
+
+<xsl:param name="debug">false</xsl:param>
+
+<!-- Simple namespace check -->
+<xsl:template match="/">
+    <xsl:if  test="//schold:*[ancestor::iso:* or descendant::iso:*]">
+	<xsl:message>Schema error: Schematron elements in old and new namespaces found</xsl:message>
+ 
+    </xsl:if>
+
+    <xsl:apply-templates />
+</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON SCHEMA ELEMENT  -->
+<!-- Not handled: Abstract patterns. A pre-processor is assumed. -->
+<!-- ============================================================== -->
+
+<!-- SCHEMA -->
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema[not(@queryBinding) or @queryBinding='xslt' 
+     or @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'
+     or @queryBinding='xpath']">
+     <xsl:if test="
+	     @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'">
+	     <xsl:message>Schema error: in the queryBinding attribute, use 'xslt'</xsl:message>
+	</xsl:if>
+	<axsl:stylesheet>
+	    <xsl:apply-templates 
+		select="iso:ns" />
+
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+		<xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+<!-- Using EXSLT with all modeles (except function module: not applicable) -->
+<xsl:template match="iso:schema[@queryBinding='exslt']" priority="10">
+    <xsl:comment>This XSLT was automatically generated from a Schematron schema.</xsl:comment>
+	<axsl:stylesheet
+ 	  	xmlns:date="http://exslt.org/dates-and-times"
+ 	  	xmlns:dyn="http://exslt.org/dynamic"
+		xmlns:exsl="http://exslt.org/common"
+		xmlns:math="http://exslt.org/math"
+   		xmlns:random="http://exslt.org/random"
+  		xmlns:regexp="http://exslt.org/regular-expressions"
+   		xmlns:set="http://exslt.org/sets"
+   		xmlns:str="http://exslt.org/strings"
+   		extension-element-prefixes="date dyn exsl math random regexp set str" >
+	
+        <xsl:apply-templates
+		select="iso:ns" />
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+	    <xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+<!-- Using XSLT 2 -->
+<xsl:template 
+	match="iso:schema[@queryBinding='xslt2' or @queryBinding ='xpath2']" 
+	priority="10">
+	<axsl:stylesheet
+	   xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+	   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
+	   xmlns:saxon="http://saxon.sf.net/" 
+	   >
+        <xsl:apply-templates 
+		select="iso:ns" />
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">2.0</xsl:attribute>
+	    
+		<xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema" priority="-1">
+	<xsl:message terminate="yes" >Fail: This implementation of ISO Schematron does not work with 
+	schemas using the "<xsl:value-of select="@queryBinding"/>" query language.</xsl:message>        
+</xsl:template>
+
+<xsl:template match="*" mode="stylesheetbody">
+	<!--xsl:template name="stylesheetbody"-->
+    <xsl:comment>Implementers: please note that overriding process-prolog or process-root is 
+    the preferred method for meta-stylesheets to use where possible. </xsl:comment><xsl:text>
</xsl:text>
+ 
+    <!-- These parameters may contain strings with the name and directory of the file being
+   validated. For convenience, if the caller only has the information in a single string,
+   that string could be put in fileDirParameter. The archives parameters are available
+   for ZIP archives.
+	-->
+    
+	<axsl:param name="archiveDirParameter"  tunnel="yes"/>
+	<axsl:param name="archiveNameParameter"  tunnel="yes"/>
+	<axsl:param name="fileNameParameter"  tunnel="yes"/>
+	<axsl:param name="fileDirParameter" tunnel="yes"/> 
+    <xsl:call-template name="iso:exslt.add.imports" />
+    <xsl:text>

</xsl:text><xsl:comment>PHASES</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:call-template name="handle-phase"/>
+    <xsl:text>

</xsl:text><xsl:comment>PROLOG</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:call-template name="process-prolog"/> 
+    <xsl:text>

</xsl:text><xsl:comment>XSD TYPES</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:apply-templates mode="do-types"   select="xsl:import-schema"/>
+    <xsl:text>

</xsl:text><xsl:comment>KEYS AND FUCNTIONS</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:apply-templates mode="do-keys"   select="xsl:key | xsl:function "/>
+    <xsl:text>

</xsl:text><xsl:comment>DEFAULT RULES</xsl:comment><xsl:text>
</xsl:text>
+    <xsl:call-template name="generate-default-rules" />
+    <xsl:text>

</xsl:text><xsl:comment>SCHEMA METADATA</xsl:comment><xsl:text>
</xsl:text>
+    <xsl:call-template name="handle-root"/>
+    <xsl:text>

</xsl:text><xsl:comment>SCHEMATRON PATTERNS</xsl:comment><xsl:text>
</xsl:text>
+ 
+	<xsl:apply-templates select="*[not(self::iso:ns)] " />
+</xsl:template>
+ 
+    <xsl:template name="iso:exslt.add.imports">
+      <xsl:param name="imports" select="$sch.exslt.imports"/>
+      <xsl:choose>
+        <xsl:when test="contains($imports, ';')">
+          <axsl:import href="{ substring-before($imports, ';') }"/>
+          <xsl:call-template name="iso:exslt.add.imports">
+            <xsl:with-param name="imports"  select="substring-after($imports, ';')"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:when test="$imports">
+          <axsl:import href="{ $imports }"/>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:template>
+
+<xsl:template name="handle-phase" >
+	<xsl:if test="not(normalize-space( $phase ) = '#ALL')">
+	  <xsl:if test="not(iso:phase[@id = normalize-space( $phase )])">
+		  <xsl:message>Phase Error: no phase with name <xsl:value-of select="normalize-space( $phase )"
+		  /> has been defined.</xsl:message>
+	  </xsl:if>
+     </xsl:if>
+</xsl:template>
+
+<xsl:template name="generate-default-rules">
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: SCHEMATRON-FULL-PATH</xsl:comment><xsl:text>
</xsl:text>
+		<xsl:comment>This mode can be used to generate an ugly though full XPath for locators</xsl:comment><xsl:text>
</xsl:text>
+   		<axsl:template match="*" mode="schematron-get-full-path">
+			<axsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
+			<xsl:choose>
+				<xsl:when test="//iso:schema[@queryBinding='xslt2']">
+					<!-- XSLT2 syntax -->
+			<axsl:text>/</axsl:text>		
+			<axsl:choose>
+      			<axsl:when test="namespace-uri()=''"><axsl:value-of select="name()"/></axsl:when>
+      			<axsl:otherwise>
+      				<axsl:text>*:</axsl:text>
+      				<axsl:value-of select="local-name()"/>
+      				<axsl:text>[namespace-uri()='</axsl:text>
+      				<axsl:value-of select="namespace-uri()"/>
+      				<axsl:text>']</axsl:text>
+      			</axsl:otherwise>
+    		</axsl:choose>
+    		<axsl:variable name="preceding" select=
+    		"count(preceding-sibling::*[local-name()=local-name(current())
+	  		                             and namespace-uri() = namespace-uri(current())])" />
+			<axsl:text>[</axsl:text>
+	  		<axsl:value-of select="1+ $preceding"/>
+	  		<axsl:text>]</axsl:text>
+		</xsl:when>
+
+		<xsl:otherwise>
+			<!-- XSLT1 syntax -->
+
+			<axsl:text>/</axsl:text>
+			<axsl:choose>
+			<axsl:when test="namespace-uri()=''">
+			<axsl:value-of select="name()"/>
+			<axsl:variable name="p" select="1+
+			count(preceding-sibling::*[name()=name(current())])" />
+		<axsl:if test="$p>1 or following-sibling::*[name()=name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:when>
+		<axsl:otherwise>
+		<axsl:text>*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/>
+		<axsl:text>']</axsl:text>
+		<axsl:variable name="p" select="1+
+		count(preceding-sibling::*[local-name()=local-name(current())])" />
+		<axsl:if test="$p>1 or following-sibling::*[local-name()=local-name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:otherwise>
+		</axsl:choose> 
+		</xsl:otherwise>
+
+	</xsl:choose>
+       	 	</axsl:template>
+       	 	
+       	 	
+		<axsl:template match="@*" mode="schematron-get-full-path">
+			<xsl:choose>
+				<xsl:when test="//iso:schema[@queryBinding='xslt2']">
+					<!-- XSLT2 syntax -->
+			<axsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
+      		<axsl:text>/</axsl:text>
+			<axsl:choose>
+      			<axsl:when test="namespace-uri()=''">@<axsl:value-of select="name()"/></axsl:when>
+      			<axsl:otherwise>
+      				<axsl:text>@*[local-name()='</axsl:text>
+      				<axsl:value-of select="local-name()"/>
+      				<axsl:text>' and namespace-uri()='</axsl:text>
+      				<axsl:value-of select="namespace-uri()"/>
+      				<axsl:text>']</axsl:text>
+      			</axsl:otherwise>
+    		</axsl:choose>
+	</xsl:when>
+
+		<xsl:otherwise>
+			<!-- XSLT1 syntax -->
+		<axsl:text>/</axsl:text>
+		<axsl:choose>
+		<axsl:when test="namespace-uri()=''">@<axsl:value-of
+		select="name()"/></axsl:when>
+		<axsl:otherwise>
+		<axsl:text>@*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/>
+		<axsl:text>' and namespace-uri()='</axsl:text>
+		<axsl:value-of select="namespace-uri()"/>
+		<axsl:text>']</axsl:text>
+		</axsl:otherwise>
+		</axsl:choose> 
+
+			</xsl:otherwise>
+			</xsl:choose>
+		</axsl:template>
+	
+	<xsl:text>

</xsl:text>
+	
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-2</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-2">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="preceding-sibling::*[name(.)=name(current())]">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-3</xsl:comment>
+	
+	<xsl:text>
</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans 
+	(Top-level element has index)</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-3">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="parent::*">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-FROM-PATH </xsl:comment><xsl:text>
</xsl:text>
+		<!-- repeatable-id maker derived from Francis Norton's. -->
+		<!-- use this if you need generate ids in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These ids are not XML names but closer to paths. -->
+		<axsl:template match="/" mode="generate-id-from-path"/>
+		<axsl:template match="text()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')"/>
+		</axsl:template>
+		<axsl:template match="comment()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')"/>
+		</axsl:template>
+		<axsl:template match="processing-instruction()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of 
+			select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')"/>
+		</axsl:template>
+		<axsl:template match="@*" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.@', name())"/>
+		</axsl:template>
+		<axsl:template match="*" mode="generate-id-from-path" priority="-0.5">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:text>.</axsl:text>
+<!--
+			<axsl:choose>
+				<axsl:when test="count(. | ../namespace::*) = count(../namespace::*)">
+					<axsl:value-of select="concat('.namespace::-',1+count(namespace::*),'-')"/>
+				</axsl:when>
+				<axsl:otherwise>
+-->
+				<axsl:value-of 
+				select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')"/>
+<!--
+				</axsl:otherwise>
+			</axsl:choose>
+-->
+		</axsl:template>
+		
+		
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-2 </xsl:comment><xsl:text>
</xsl:text>
+		<!-- repeatable-id maker from David Carlisle. -->
+		<!-- use this if you need generate IDs in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These IDs are well-formed XML NMTOKENS -->
+	<axsl:template match="/" mode="generate-id-2">U</axsl:template>
+
+	<axsl:template match="*" mode="generate-id-2" priority="2">
+		<axsl:text>U</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+	</axsl:template>
+
+	<axsl:template match="node()" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>n</axsl:text>
+		<axsl:number count="node()"/>
+	</axsl:template>
+
+	<axsl:template match="@*" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="string-length(local-name(.))"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="translate(name(),':','.')"/>
+	</axsl:template> 
+		
+
+		<xsl:comment>Strip characters</xsl:comment>
+		<axsl:template match="text()" priority="-1" />
+			
+  </xsl:template>
+
+ <xsl:template name="handle-root">
+		<!-- Process the top-level element -->
+		<axsl:template match="/">
+			<xsl:call-template name="process-root">
+				<xsl:with-param 	
+				name="title" select="(@id | iso:title)[last()]"/>
+				<xsl:with-param name="version" select="'iso'" />
+				<xsl:with-param name="schemaVersion" select="@schemaVersion" />
+				<xsl:with-param name="queryBinding" select="@queryBinding" />
+				<xsl:with-param name="contents">
+					<xsl:apply-templates mode="do-all-patterns"/>
+				</xsl:with-param>
+				
+				<!-- "Rich" properties -->
+				<xsl:with-param name="fpi" select="@fpi"/>
+				<xsl:with-param name="icon" select="@icon"/>
+				<xsl:with-param name="id" select="@id"/>
+				<xsl:with-param name="lang" select="@xml:lang"/>
+				<xsl:with-param name="see" select="@see" />
+				<xsl:with-param name="space" select="@xml:space" />
+			</xsl:call-template>
+		</axsl:template>
+ 
+      
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON ELEMENTS -->
+<!-- ============================================================== -->
+
+	<!-- ISO ACTIVE -->
+	<xsl:template match="iso:active">
+                <xsl:if test="not(@pattern)">
+                    <xsl:message>Markup Error: no pattern attribute in <active></xsl:message>
+                </xsl:if>
+
+                <xsl:if test="not(../../iso:pattern[@id = current()/@pattern])
+                and not(../../iso:include)">
+                           <xsl:message>Reference Error: the pattern  "<xsl:value-of select="@pattern"
+						   />" has been activated but is not declared</xsl:message>
+                </xsl:if>
+        </xsl:template>
+
+	<!-- ISO ASSERT and REPORT -->
+	<xsl:template match="iso:assert">
+  
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in <assert</xsl:message>
+                </xsl:if>
+        <xsl:text>

		</xsl:text>
+		<xsl:comment>ASSERT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>
</xsl:text>      
+	
+		<axsl:choose>
+			<axsl:when test="{@test}"/>
+			<axsl:otherwise>
+				<xsl:call-template name="process-assert">
+					<xsl:with-param name="test" select="normalize-space(@test)" />
+					<xsl:with-param name="diagnostics" select="@diagnostics"/>
+					<xsl:with-param name="flag" select="@flag"/>
+					
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+					
+					<!-- Non-standard extensions not part of the API yet -->
+					<xsl:with-param name="action" select="@action" tunnel="yes" />
+				</xsl:call-template>
+ 			
+			</axsl:otherwise>
+		</axsl:choose>
+	</xsl:template>
+	<xsl:template match="iso:report">
+		 
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in <report></xsl:message>
+                </xsl:if>
+                
+        <xsl:text>

		</xsl:text>
+		<xsl:comment>REPORT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>
</xsl:text>      
+	
+		<axsl:if test="{@test}">
+		
+			<xsl:call-template name="process-report">
+				<xsl:with-param name="test" select="normalize-space(@test)" />
+				<xsl:with-param name="diagnostics" select="@diagnostics"/>
+					<xsl:with-param name="flag" select="@flag"/>
+				
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+				
+		</axsl:if>
+	</xsl:template>
+
+
+	<!-- ISO DIAGNOSTIC -->
+	<!-- We use a mode here to maintain backwards compatability, instead of adding it
+	     to the other mode.
+	-->
+	<xsl:template match="iso:diagnostic" mode="check-diagnostics">
+              <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in <diagnostic></xsl:message>
+               </xsl:if>
+    </xsl:template>
+    
+    <xsl:template match="iso:diagnostic"  >
+                <xsl:call-template name="process-diagnostic">
+                
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+               </xsl:call-template>
+        </xsl:template>
+
+	<!-- ISO DIAGNOSTICS -->
+	<xsl:template match="iso:diagnostics" >
+		<xsl:apply-templates mode="check-diagnostics" select="*" />
+	</xsl:template>
+
+	<!-- ISO DIR -->
+	<xsl:template match="iso:dir"  mode="text" >
+		<xsl:call-template name="process-dir">
+			<xsl:with-param name="value" select="@value"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO EMPH -->
+	<xsl:template match="iso:emph"  mode="text">
+	 
+		<xsl:call-template name="process-emph"/> 
+
+	</xsl:template>
+
+	<!-- ISO EXTENDS -->
+	<xsl:template match="iso:extends">
+		<xsl:if test="not(@rule)">
+                   <xsl:message>Markup Error: no rule attribute in <extends></xsl:message>
+                </xsl:if>
+     		<xsl:if test="not(//iso:rule[@abstract='true'][@id= current()/@rule] )">
+                    <xsl:message>Reference Error: the abstract rule  "<xsl:value-of select="@rule"
+					/>" has been referenced but is not declared</xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+
+  		<xsl:if test="//iso:rule[@id=current()/@rule]">
+    			<xsl:apply-templates select="//iso:rule[@id=current()/@rule]"
+				mode="extends"/>
+  		</xsl:if>
+
+	</xsl:template>
+
+	<!-- KEY: ISO has no KEY -->
+	<!-- NOTE: 
+	     Key has had a checkered history. Schematron 1.0 allowed it in certain places, but
+	     users came up with a different location, which has now been adopted. 
+	     
+	     XT, the early XSLT processor, did not implement key and died when it was present. 
+	     So there are some versions of the Schematron skeleton for XT that strip out all
+	     key elements.
+	     
+	     Xalan (e.g. Xalan4C 1.0 and a Xalan4J) also had a funny. A fix involved making 
+	     a top-level parameter called $hiddenKey and then using that instead of matching
+	     "key". This has been removed.
+	     
+	     Keys and functions are the same mode, to allow their declaration to be mixed up.
+	-->
+	<xsl:template  match="xsl:key" mode="do-keys" >
+	     <xsl:if test="not(@name)">
+              <xsl:message>Markup Error: no name attribute in <key></xsl:message>
+         </xsl:if>
+                <xsl:if test="not(@path) and not(@use)">
+                    <xsl:message>Markup Error: no path or use attribute in <key></xsl:message>
+                </xsl:if>         
+	     <xsl:choose>
+	     	<xsl:when test="parent::iso:rule ">
+	        <xsl:call-template name="IamEmpty" />
+	       <xsl:choose>
+	       	<xsl:when test="@path">
+				<axsl:key match="{../@context}" name="{@name}" use="{@path}"/>
+			</xsl:when>
+			<xsl:otherwise>
+							<axsl:key match="{../@context}" name="{@name}" use="{@use}"/>
+			</xsl:otherwise>
+			</xsl:choose>	
+		</xsl:when>
+		<xsl:otherwise>
+                <xsl:if test="not(@match) ">
+                    <xsl:message>Markup Error: no path or use attribute in <key></xsl:message>
+                </xsl:if>   		
+			<axsl:key>
+      			<xsl:copy-of select="@*"/>
+    		</axsl:key>	
+		</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template match="xsl:key "  /><!-- swallow -->
+
+	<xsl:template match="iso:key "  >
+		<xsl:message>Schema error: The key element is not in the ISO Schematron namespace. Use the XSLT namespace.</xsl:message>
+    </xsl:template>
+
+  <!-- XSL FUNCTION -->
+  <xsl:template  match="xsl:function" mode="do-keys" >
+	     <xsl:if test="not(@name)">
+              <xsl:message>Markup Error: no name attribute in <function></xsl:message>
+         </xsl:if>      
+	     <xsl:copy-of select="."/>
+  </xsl:template>
+
+	<xsl:template match="xsl:function "  /><!-- swallow -->
+
+	<xsl:template match="iso:function "  >
+		<xsl:message>Schema error: The function element is not in the ISO Schematron namespace. Use the XSLT namespace.</xsl:message>
+    </xsl:template>
+
+
+   <!-- ISO INCLUDE -->
+   <!-- This is only a fallback. Include really needs to have been done before this as a separate pass.-->
+
+   <xsl:template match="iso:include[not(normalize-space(@href))]"
+	   priority="1">
+	<xsl:if test=" $debug = 'false' ">
+		<xsl:message terminate="yes">Schema error: Empty href= attribute for include directive.</xsl:message>
+	</xsl:if>
+
+   </xsl:template>
+
+   <!-- Extend the URI syntax to allow # refererences -->
+   <!-- Note that XSLT2 actually already allows # references, but we override them because it
+   looks unreliable -->
+   <xsl:template match="iso:include">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument//iso:*[@id= $fragment-id]" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:if test=" $theFragment/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+		   	  <!-- Import the top-level element if it is in schematron namespace,
+		   	  or its children otherwise, to allow a simple containment mechanism. -->
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:*" />
+              <xsl:if test="not($theDocument)">
+				<xsl:message terminate="no">
+					<xsl:text>Unable to open referenced included file: </xsl:text>
+					<xsl:value-of select="@href"/>
+				</xsl:message>
+			</xsl:if>
+              <xsl:if test=" $theFragment/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment | $theContainedFragments "/>
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+   
+   <!-- This is to handle the particular case of including patterns -->  
+   <xsl:template match="iso:include" mode="do-all-patterns">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+ 
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument//iso:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment" mode="do-all-patterns"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+		   	  <!-- Import the top-level element if it is in schematron namespace,
+		   	  or its children otherwise, to allow a simple containment mechanism. -->
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:*" />
+              <xsl:if test=" $theFragment/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment | $theContainedFragments "
+       		mode="do-all-patterns" />
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+   
+	
+	<!-- XSL IMPORT-SCHEMA -->
+	<!-- Importing an XSD schema allows the variour type operations to be available. -->
+	<xsl:template  match="xsl:import-schema" mode="do-types" >	 
+		<xsl:choose>
+		  <xsl:when test="ancestor::iso:schema[@queryBinding='xslt2']">
+		  	<xsl:copy-of select="." />
+		  </xsl:when>
+		<xsl:otherwise>
+			<xsl:message>Schema error: XSD schemas may only be imported if you are using the 'xslt2' query language binding</xsl:message>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>  
+		   
+	<!-- swallow -->	   
+    <xsl:template match="xsl:import-schema" />
+ 
+	<xsl:template match="iso:import-schema "  >
+		<xsl:message>Schema error: The import-schema element is not available
+		in the ISO Schematron namespace. Use the XSLT namespace.</xsl:message>
+    </xsl:template>
+ 
+	<!-- ISO LET -->
+	<xsl:template match="iso:let" >
+	  <xsl:if test="ancestor::iso:schema[@queryBinding='xpath']">
+                    <xsl:message>Warning: Variables should not be used with the "xpath" query language binding.</xsl:message>
+       </xsl:if>
+	  <xsl:if test="ancestor::iso:schema[@queryBinding='xpath2']">
+                    <xsl:message>Warning: Variables should not be used with the "xpath2" query language binding.</xsl:message>
+       </xsl:if>
+       
+       <!-- lets at the top-level are implemented as parameters -->
+       	<xsl:choose>
+       		<xsl:when test="parent::iso:schema">
+       			<!-- it is an error to have an empty param/@select because an XPath is expected -->
+	      		 <axsl:param name="{@name}" select="{@value}">
+	      		 		<xsl:if test="string-length(@value) > 0">
+	      		 			<xsl:attribute name="select"><xsl:value-of select="@value"/></xsl:attribute>
+	      		 		</xsl:if>
+	      		 </axsl:param> 
+       		</xsl:when>
+       		<xsl:otherwise>
+				<axsl:variable name="{@name}" select="{@value}"/>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>	
+
+	<!-- ISO NAME -->
+	<xsl:template match="iso:name" mode="text">
+	
+		<xsl:if test="@path">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="concat('name(', at path,')')"/>
+			</xsl:call-template>
+		</xsl:if>
+		<xsl:if test="not(@path)">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="'name(.)'"/>
+			</xsl:call-template>
+		</xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	</xsl:template>
+
+	<!-- ISO NS -->
+	<!-- Namespace handling is XSLT is quite tricky and implementation dependent -->
+	<xsl:template match="iso:ns">
+ 		<xsl:call-template name="handle-namespace" />
+	</xsl:template>
+
+    <!-- This template is just to provide the API hook -->
+	<xsl:template match="iso:ns"  mode="do-all-patterns" >
+               <xsl:if test="not(@uri)">
+                    <xsl:message>Markup Error: no uri attribute in <ns></xsl:message>
+                </xsl:if>
+               <xsl:if test="not(@prefix)">
+                    <xsl:message>Markup Error: no prefix attribute in <ns></xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+		<xsl:call-template name="process-ns" >
+			<xsl:with-param name="prefix" select="@prefix"/>
+			<xsl:with-param name="uri" select="@uri"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO P -->
+	<xsl:template match="iso:schema/iso:p " mode="do-schema-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	<xsl:template match="iso:pattern/iso:p " mode="do-pattern-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	
+    <!-- Currently, iso:p in other position are not passed through to the API -->
+	<xsl:template match="iso:phase/iso:p" />
+	<xsl:template match="iso:p " priority="-1" />
+ 
+	<!-- ISO PATTERN -->
+	<xsl:template match="iso:pattern" mode="do-all-patterns">
+	<xsl:if test="($phase = '#ALL') 
+	or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+	 
+		<xsl:call-template name="process-pattern">
+			<!-- the following select statement assumes that
+			@id | iso:title returns node-set in document order:
+			we want the title if it is there, otherwise the @id attribute -->
+			<xsl:with-param name="name" select="(@id | iso:title )[last()]"/>
+			<xsl:with-param name="is-a" select="''"/>
+			
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+		</xsl:call-template>
+		<xsl:choose>
+		  <xsl:when test="$select-contexts='key'">
+		    <axsl:apply-templates select="key('M','M{count(preceding-sibling::*)}')" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:when>
+		  <xsl:when test="$select-contexts='//'">
+		    <axsl:apply-templates mode="M{count(preceding-sibling::*)}">
+		      <xsl:attribute name="select">
+			<xsl:text>//(</xsl:text>
+			<xsl:for-each select="iso:rule/@context">
+			  <xsl:text>(</xsl:text>
+			  <xsl:value-of select="."/>
+			  <xsl:text>)</xsl:text>
+			  <xsl:if test="position()!=last()">|</xsl:if>
+			</xsl:for-each>
+			<xsl:text>)</xsl:text>
+			<xsl:if test="$visit-text='false'">[not(self::text())]</xsl:if>
+		      </xsl:attribute>
+		    </axsl:apply-templates>
+		  </xsl:when>
+		  <xsl:otherwise>
+		    <axsl:apply-templates select="/" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:otherwise>
+		</xsl:choose>
+        </xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="iso:pattern[@abstract='true']">
+    
+             <xsl:message>Schema implementation error: This schema has abstract patterns, yet they are supposed to be preprocessed out already
+             </xsl:message>
+    </xsl:template>
+
+    <!-- Here is the template for the normal case of patterns -->
+	<xsl:template match="iso:pattern[not(@abstract='true')]">
+    
+      <xsl:if test="($phase = '#ALL') 
+	          or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+		<xsl:text>

</xsl:text>
+		<xsl:comment>PATTERN <xsl:value-of select="@id" /> <xsl:value-of select="iso:title" /> </xsl:comment><xsl:text>
</xsl:text>      
+		<xsl:apply-templates />
+		
+		<!-- DPC select-contexts test -->
+		<xsl:if test="not($select-contexts)">
+		  <axsl:template match="text()" priority="-1" mode="M{count(preceding-sibling::*)}">
+		    <!-- strip characters -->
+		  </axsl:template>
+		  
+		  <!-- DPC introduce context-xpath variable -->
+		  <axsl:template match="@*|node()"
+				 priority="-2"
+				 mode="M{ count(preceding-sibling::*) }">
+		    <axsl:apply-templates select="{$context-xpath}" mode="M{count(preceding-sibling::*)}"/>
+		  </axsl:template>
+		</xsl:if>
+      </xsl:if>
+	</xsl:template>
+
+	<!-- ISO PHASE -->
+	<xsl:template match="iso:phase" >
+                <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in <phase></xsl:message>
+                </xsl:if>
+		  <xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO RULE -->
+	<xsl:template match="iso:rule[not(@abstract='true')] ">
+                <xsl:if test="not(@context)">
+                    <xsl:message>Markup Error: no context attribute in <rule></xsl:message>
+                </xsl:if>
+        <xsl:text>

	</xsl:text>
+		<xsl:comment>RULE <xsl:value-of select="@id" /> </xsl:comment><xsl:text>
</xsl:text>   
+        <xsl:if test="iso:title">
+		    <xsl:comment><xsl:value-of select="iso:title" /></xsl:comment>
+		  </xsl:if>
+		<!-- DPC select-contexts -->
+		<xsl:if test="$select-contexts='key'">
+		    <axsl:key name="M"
+			      match="{@context}" 
+			      use="'M{count(../preceding-sibling::*)}'"/>
+		</xsl:if>
+   
+	
+<!-- DPC priorities count up from 1000 not down from 4000 (templates in same priority order as before) -->
+		<axsl:template match="{@context}"
+		priority="{1000 + count(following-sibling::*)}" mode="M{count(../preceding-sibling::*)}">
+		 
+			<xsl:call-template name="process-rule">
+				<xsl:with-param name="context" select="@context"/>
+				
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+			<xsl:apply-templates/>
+			<!-- DPC introduce context-xpath and select-contexts variables -->
+			<xsl:if test="not($select-contexts)">
+			  <axsl:apply-templates select="{$context-xpath}" mode="M{count(../preceding-sibling::*)}"/>
+			</xsl:if>
+		</axsl:template>
+	</xsl:template>
+
+
+	<!-- ISO ABSTRACT RULE -->
+	<xsl:template match="iso:rule[@abstract='true'] " >
+		<xsl:if test=" not(@id)">
+                    <xsl:message>Markup Error: no id attribute on abstract <rule></xsl:message>
+                </xsl:if>
+ 		<xsl:if test="@context">
+                    <xsl:message>Markup Error: (2) context attribute on abstract <rule></xsl:message>
+                </xsl:if>
+	</xsl:template>
+
+	<xsl:template match="iso:rule[@abstract='true']"
+		mode="extends" >
+                <xsl:if test="@context">
+                    <xsl:message>Markup Error: context attribute on abstract <rule></xsl:message>
+                </xsl:if>
+			<xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO SPAN -->
+	<xsl:template match="iso:span" mode="text">
+		<xsl:call-template name="process-span">
+			<xsl:with-param name="class" select="@class"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO TITLE -->
+	
+	<xsl:template match="iso:schema/iso:title"  priority="1">
+	     <xsl:call-template name="process-schema-title" />
+	</xsl:template>
+ 
+	
+	<xsl:template match="iso:title" >
+	     <xsl:call-template name="process-title" />
+	</xsl:template>
+ 
+
+	<!-- ISO VALUE-OF -->
+	<xsl:template match="iso:value-of" mode="text" >
+        <xsl:if test="not(@select)">
+            <xsl:message>Markup Error: no select attribute in <value-of></xsl:message>
+        </xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	         
+		<xsl:choose>
+			<xsl:when test="@select">
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="@select"/>  
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise >
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="'.'"/>
+				</xsl:call-template>
+			</xsl:otherwise>
+        </xsl:choose> 
+        
+	</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- DEFAULT TEXT HANDLING  -->
+<!-- ============================================================== -->
+	<xsl:template match="text()" priority="-1" mode="do-keys">
+		<!-- strip characters -->
+	</xsl:template>
+	<xsl:template match="text()" priority="-1" mode="do-all-patterns">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-schema-p">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-pattern-p">
+		<!-- strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" priority="-1">
+		<!-- Strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" mode="text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+	<xsl:template match="text()" mode="inline-text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UTILITY TEMPLATES -->
+<!-- ============================================================== -->
+<xsl:template name="IamEmpty">
+	<xsl:if test="count( * )">
+		<xsl:message>
+			<xsl:text>Warning: </xsl:text>
+			<xsl:value-of select="name(.)"/>
+			<xsl:text> must not contain any child elements</xsl:text>
+		</xsl:message>
+	</xsl:if>
+</xsl:template>
+
+<xsl:template name="diagnosticsSplit">
+  <!-- Process at the current point the first of the <diagnostic> elements
+       referred to parameter str, and then recurse -->
+  <xsl:param name="str"/>
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="contains($str,' ')">
+	<xsl:value-of  select="substring-before($str,' ')"/>
+      </xsl:when>
+      <xsl:otherwise><xsl:value-of select="$str"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="end">
+    <xsl:if test="contains($str,' ')">
+      <xsl:value-of select="substring-after($str,' ')"/>
+    </xsl:if>
+  </xsl:variable>
+
+  <!-- This works with all namespaces -->
+  <xsl:if test="not(string-length(normalize-space($start)) = 0)
+  		and not(//iso:diagnostic[@id = $start])
+		and not(//schold:diagnostic[@id = $start]) 
+		and not(//diagnostic[@id = $start])">
+	<xsl:message>Reference error: A diagnostic "<xsl:value-of select="string($start)"
+	/>" has been referenced but is not declared</xsl:message>
+  </xsl:if>
+
+  <xsl:if test="string-length(normalize-space($start)) > 0">
+     <xsl:text> </xsl:text>
+     <xsl:apply-templates 
+        select="//iso:diagnostic[@id = $start ]
+        	| //schold:diagnostic[@id = $start ] 
+            | //diagnostic[@id= $start ]"/>
+  </xsl:if>
+
+  <xsl:if test="not($end='')">
+    <xsl:call-template name="diagnosticsSplit">
+      <xsl:with-param name="str" select="$end"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<!-- It would be nice to use this but xsl:namespace does not
+  allow a fallback -->
+<!--xsl:template name="handle-namespace" version="2.0">
+   <xsl:namespace name="{@prefix}" select="@uri">
+</xsl:template-->
+
+<xsl:template name="handle-namespace">
+       <!-- experimental code from http://eccnet.eccnet.com/pipermail/schematron-love-in/2006-June/000104.html -->
+       <!-- Handle namespaces differently for exslt systems,   and default, only using XSLT1 syntax -->
+       <!-- For more info see  http://fgeorges.blogspot.com/2007/01/creating-namespace-nodes-in-xslt-10.html -->
+       <xsl:choose>
+   	<!-- The following code workds for XSLT2 -->
+         <xsl:when test="element-available('xsl:namespace')">
+             <xsl:namespace name="{@prefix}" select="@uri" />
+	 </xsl:when>
+ 
+	 <xsl:when use-when="not(element-available('xsl:namespace'))" 
+		   test="function-available('exsl:node-set')">
+           <xsl:variable name="ns-dummy-elements">
+             <xsl:element name="{@prefix}:dummy" namespace="{@uri}"/>
+           </xsl:variable>
+       	   <xsl:variable name="p" select="@prefix"/>
+           <xsl:copy-of select="exsl:node-set($ns-dummy-elements)
+                                  /*/namespace::*[local-name()=$p]"/>
+         </xsl:when>
+
+	<!-- end XSLT2 code -->
+
+        
+        <xsl:when test="@prefix = 'xsl' ">
+           <!-- Do not generate dummy attributes with the xsl: prefix, as these
+                are errors against XSLT, because we presume that the output
+                stylesheet uses the xsl prefix. In any case, there would already
+                be a namespace declaration for the XSLT namespace generated
+                automatically, presumably using "xsl:".
+           -->
+        </xsl:when>
+        
+        <xsl:when test="@uri = 'http://www.w3.org/1999/XSL/Transform'">
+          <xsl:message terminate="yes">
+            <xsl:text>Using the XSLT namespace with a prefix other than "xsl" in </xsl:text>
+            <xsl:text>Schematron rules is not supported </xsl:text>
+            <xsl:text>in this processor: </xsl:text>
+            <xsl:value-of select="system-property('xsl:vendor')"/>
+          </xsl:message>
+        </xsl:when>
+
+        <xsl:otherwise>
+          <xsl:attribute name="{concat(@prefix,':dummy-for-xmlns')}" namespace="{@uri}" />
+           
+        </xsl:otherwise>
+      </xsl:choose>
+
+
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UNEXPECTED ELEMENTS -->
+<!-- ============================================================== -->
+
+	<xsl:template match="iso:*"  priority="-2">
+	   <xsl:message>
+			<xsl:text>Error: unrecognized element in ISO Schematron namespace: check spelling
+			and capitalization</xsl:text>
+			<xsl:value-of select="name(.)"/>
+		</xsl:message>
+	</xsl:template>
+	
+	
+	<!-- Swallow old namespace elements: there is an upfront test for them elsewhere -->
+	<xsl:template match="schold:*"  priority="-2" />
+	 
+	<xsl:template match="*"  priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:template match="iso:*" mode="text" priority="-2" />
+	<xsl:template match="*" mode="text" priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- DEFAULT NAMED TEMPLATES -->
+<!-- These are the actions that are performed unless overridden -->
+<!-- ============================================================== -->
+ 
+	<xsl:template name="process-prolog"/>
+	<!-- no params -->
+
+	<xsl:template name="process-root">
+		<xsl:param name="contents"/>
+		<xsl:param name="id" />
+		<xsl:param name="version" />
+		<xsl:param name="schemaVersion" />
+		<xsl:param name="queryBinding" />
+		<xsl:param name="title" />
+
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:copy-of select="$contents"/>
+	</xsl:template>
+
+	<xsl:template name="process-assert">
+
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+		
+		
+	</xsl:template>
+
+	<xsl:template name="process-report">
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" /> 
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-diagnostic">
+		<xsl:param name="id" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-dir">
+      	<xsl:param name="value" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-emph"> 
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+	
+	<xsl:template name="process-name">
+		<xsl:param name="name"/>
+		
+		<!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$name}"/>
+		<axsl:text> </axsl:text>
+		
+    </xsl:template>
+
+	<xsl:template name="process-ns" >
+	<!-- Note that process-ns is for reporting. The iso:ns elements are 
+	     independently used in the iso:schema template to provide namespace bindings -->
+		<xsl:param name="prefix"/>
+		<xsl:param name="uri" />
+      </xsl:template>
+
+	<xsl:template name="process-p">
+		<xsl:param name="id" />
+		<xsl:param name="class" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+      </xsl:template>
+
+	<xsl:template name="process-pattern">
+		<xsl:param name="id" />
+		<xsl:param name="name" />
+		<xsl:param name="is-a" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+      
+
+	<xsl:template name="process-rule">
+		<xsl:param name="context" />
+
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+  
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+
+	<xsl:template name="process-span" >
+		<xsl:param name="class" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>		
+	</xsl:template>
+
+	<xsl:template name="process-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-p">
+	      <xsl:with-param  name="class">title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+		
+	<xsl:template name="process-schema-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-title">
+	      <xsl:with-param  name="class">schema-title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-value-of">
+		<xsl:param name="select"/>
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$select}"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<!-- default output action: the simplest customization is to just override this -->
+	<xsl:template name="process-message">
+		<xsl:param name="pattern" />
+            <xsl:param name="role" />
+
+		<xsl:apply-templates mode="text"/>	
+		 <xsl:if test=" $message-newline = 'true'" >
+			<axsl:value-of  select="string('
')"/>
+		</xsl:if>
+		
+	</xsl:template>
+</xsl:stylesheet>
+
+
+
diff --git a/Tools/validation/iso_schematron_skeleton_for_xslt1.xsl b/Tools/validation/iso_schematron_skeleton_for_xslt1.xsl
new file mode 100644
index 0000000..9b9e68d
--- /dev/null
+++ b/Tools/validation/iso_schematron_skeleton_for_xslt1.xsl
@@ -0,0 +1,1749 @@
+<?xml version="1.0"?><?xar XSLT?>
+
+<!-- 
+   OVERVIEW
+   
+   ASCC/Schematron.com Skeleton Module for ISO Schematron (for XSLT1 systems)
+   
+   ISO Schematron is a language for making assertion about the presence or absence
+   of patterns in XML documents. It is typically used for as a schema language, or
+   to augment existing schema languages, and to check business rules. It is very
+   powerful, yet quite simple: a developer only need know XPath and about five other
+   elements.
+   
+   This is an open source implementation of ISO Schematron in XSLT. Although ISO does
+   not allow reference implementations which might compete with the text of the
+   standard, this code has been compiled by Rick Jelliffe, inventor of Schematron
+   and editor of the ISO standard; so developers can certainly use it as an 
+   unofficial reference implementation for clarification. 
+   
+   This implementation is based on one by Oliver Becker. API documentation is 
+   available separately; try www.schematron.com for this. Funding for this
+   stylesheet over the years has come from Topologi Pty. Ltd., Geotempo Ltd.,
+   and ASCC, Tapei.
+   
+   There are two versions of this skeleton: one is tailored for XSLT1 processors
+   and the other is tailored for XSLT2 processors. Future versions of the
+   XSLT2 skeleton may support more features than that the XSLT 1 skeleton.
+-->
+<!--
+   TIPS
+      
+   A tip for new users of Schematron: make your assertions contain positive messages
+   about what is expected, rather than error messages. For example, use the form
+   "An X should have a Y, because Z". 
+   
+   Another tip is that Schematron provides an
+   element <sch:ns> for declaring the namespaces and prefixes used in Xpaths in 
+   attribute values; it does not extend the XML Namespaces mechanism: if a name
+   in an XPath has a prefix, there must be an <sch:ns> element for that prefix; if
+   a name in an XPath does not have a prefix, it is always in no namespace.
+   
+   A tip for implementers of Schematron, either using this API or re-implementing it:
+   make the value of the diagnostics, flags and richer features available if possible;
+   Schematron has many of the optional richer features which, if implemented, provide
+   a compelling alternative approach to validation and business-rules checking compared
+   to other schema languages and programs. 
+   
+   If you create your own meta-stylesheet to override this one, it is a
+   good idea to have both in the same directory and to run the stylesheet
+   from that directory, as many XSLT implementations have ideosyncratic
+   handling of URLs: keep it simple.
+-->
+ 
+
+<!--
+  INVOCATION INFORMATION
+  
+  The following parameters are available
+  
+    phase           NMTOKEN | "#ALL" (default) Select the phase for validation
+    allow-foreign   "true" | "false" (default)   Pass non-Schematron elements to the generated stylesheet
+    sch.exslt.imports semi-colon delimited string of filenames for some EXSLT implementations  
+    message-newline "true" (default) | "false"   Generate an extra newline at the end of messages
+    optimize        "visit-no-attributes"     
+    debug	    "true" | "false" (default)  Debug mode lets compilation continue despite problems
+    attributes "true" | "false"  (Autodetecting) Use only when the schema has no attributes as the context nodes
+    only-child-elements "true" | "false" (Autodetecting) Use only when the schema has no comments
+    or PI  as the context nodes
+    
+  The following parameters can be specified as Schematron variables in diagnostics, assertions and so on.
+    fileNameParameter string	  
+    fileDirParameter string				
+    archiveNameParameter string	  In case of ZIP files
+    archiveDirParameter string	  In case of ZIP files	
+    output-encoding				  Use when outputting to XML
+ 
+ Experimental: USE AT YOUR OWN RISK   
+    visit-text "true" "false"   Also visist text nodes for context. WARNING: NON_STARDARD.
+    select-contents '' | 'key' | '//'   Select different implementation strategies
+ 
+ Conventions: Meta-stylesheets that override this may use the following parameters
+    generate-paths=true|false   generate the @location attribute with XPaths
+    diagnose= yes | no    Add the diagnostics to the assertion test in reports
+    terminate= yes | no   Terminate on the first failed assertion or successful report
+-->
+
+<!-- 
+  XSLT VERSION SUPPORT
+
+  XSLT 1:
+     A schema using the standard XSLT 1 query binding will have a /schema/@queryBinding='xslt' or 
+     nothing.
+
+       * Note: XT does not implement key() and will die if given it. 
+       * Add all formal parameters to default templates
+       * Fix missing apply-templates from process-ns and add params back
+
+  EXSLT:  Experimental support
+     A schema using the EXSLT query binding will have a /schema/@queryBinding='exslt'.
+     It is built on XSLT 1. After experience is gained, this binding is expected to be 
+     formalized as part of ISO Schematron, which currently reserves the "exslt" name for this purpose.
+
+     Some EXSLT engines have the extra functions built-in. For these, there is no need to
+     provide library locations. For engines that require the functions, either hard code
+     them in this script or provide them on the command-line argument.
+ 
+-->
+<!--
+   PROCESS INFORMATION
+   
+   This stylesheet compiles a Schematron schema (*.sch) into XSLT code (*.xsl). 
+   The generated XSLT code can then be run against an XML file (*.xml, etc) and
+   will produce validation results.
+   
+   The output of validation results is performed using named templates (process-*). 
+   These can be overridden easily by making a new XSLT stylesheet that imports this 
+   stylesheet but has its own version of the relevant process-* templates. Several
+   of these invoking stylesheets are available: "iso_svrl.xsl", for example generates
+   ISO Schematron Validation Report Language format results.
+   
+   In this version of the stylesheet, the ISO feature called "abstract patterns" is
+   implemented using macro processing: a prior XSLT stage to which converts uses
+   of abstract patterns into normal patterns. If you do not use abstract patterns,
+   it is not necessary to preprocess the schema.
+   
+   To summarize, a basic process flow for some commandline processor is like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_schematron_skeleton.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+   
+   iso_svrl.xslt is an implementation of Schematron that can use this skeleton and
+   generate ISO SVRL reports. A process flow for some commandline processor would
+   be like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_svrl.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+     
+   It is not impossible that ultimately a third stage, to handle macro-preprocessing
+   and inclusion, might be necessary. (The trade-off is in making this XSLT more
+   complex compared to making the outer process more complex.)
+             
+  This version has so far been tested with
+     Saxon 8
+     MSXML 4 (or 6?)   
+
+ Please note that if you are using SAXON and JAXP, then you should use 
+  System.setProperty("javax.xml.transform.TransformerFactory",
+                          "net.sf.saxon.TransformerFactoryImpl");
+ rather than 
+  System.setProperty("javax.xml.xpath.TransformerFactory",
+                           "net.sf.saxon.TransformerFactoryImpl");
+ which is does not work, at least for the versions of SAXON we tried.
+-->
+<!--
+ LEGAL INFORMATION
+ 
+ Copyright (c) 2000-2008 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+<!--
+  VERSION INFORMATION
+   2008-08-06
+   		* TT Top-level lets need to be implemented using xsl:param not xsl:variable
+   		* TT xsl:param/@select must have XPath or not be specified
+   		
+    Version: 2008-07-28
+   		* KH schematron-get-full-path-3 has [index] even on top step
+   		* RJ fix schematron-get-full-path to have namespace predicate, I don't know why this was removed
+   		
+   Version: 2008-07-24
+   		* RJ clean out commented out namespace handling code
+   		* RJ add support for experimental non-standard attribute report/@action
+   		and assert/@action, and add parameter not in the published API (should
+   		not break anything, it is XSLT1)
+   		* RJ Remove remaining XSLT2 code for ease of reading
+   		
+   Version: 2008-07-14 minor update for inclusion experiments
+   	* RJ Clean up zero-length fragment test on include
+   	* RJ Add experimental support for include containers 
+   	* RJ For path generation, test for //iso:schema not just /iso:schema, for potential embedded Schematron support   
+   	* RJ Don't generate double error messages for old namespace elements
+   	* RJ Experimental iso:rule/iso:title just kept as comment (bigger request Uche Ogbuji)
+   	* RJ Remove spurious debug messages
+   	* RJ Fix bug that prevented including patterns in this (report Roger
+   	Costello)
+  
+   Version: 2007-10-17
+     From this version on I am forking XSLT2 support to a different version of the script.
+     This is due to the increasingly horrible state of the namespace handling code as well
+     as other inconsistencies between the major implementations of different versions.
+     The intent is that future versions of this will have XSLT2 isms removed and be simplified
+     to cope with only XSLT1 and EXLST. Note that though this version is called
+     iso_schematron_skeleton_for_xslt1, the various meta-stylesheets will continue to just call
+     iso_schematron_skeleton: it is up to you to rename the stylesheet to the one you want to
+     use.
+
+       * RJ fix FULL-PATH problem with attribute names
+
+
+   Version: 2007-07-19
+     Accept most changes in David Carlisle's fork, but continue as XSLT1 script: 
+    	http://dpcarlisle.blogspot.com/search/label/schematron
+    	* DPC Remove "optimize" parameter
+    	* DPC Add autodetecting optimize parameter attribute to skip checking attribute
+    	context
+    	* DPC Add autodetecting optimize parameter only-child-elements turn off checking for 
+    	comments and PIs
+    	* DPC (Experimental: NON_STANDARD DANGER!) Add param visit-text to viist text
+    	nodes too for context 
+    	* DPC Fix inclusion syntax to allow #
+    	* DPC Priorities count up from 1000 not down from 4000 to allow more rules
+        * RJ Add new template for titles of schemas, with existing behaviour.  
+        Override process-schema-title for custom processing of title
+    		
+    
+   Version: 2007-04-04
+   	* RJ debug mode param
+	* RJ alter mixed test to only test mixed branches, so the same document
+	could have old and new namespaces schemas in it, but each schema must
+	be distinct, just so as not to overconstrain things.
+   	* KH zero-length include/@href is fatal error, but allow debug mode
+	* SB add hint on SAXON and JAXP
+	* DC generate-full-path-1 generates XLST1 code by default
+   Version: 2007-03-05
+      	* AS Typo for EXSLT randome, improve comment
+      	* KH get-schematron-full-path-2 needs to apply to attributes too
+      	* DP document policy on extensions better
+      	* DC use copy-of not copy for foreign elements
+      	* DC add generate-path-2
+      	* DC don't try to apply templates to attribute axis on attribute nodes, to
+      	stop SAXON warning.
+      	* RJ improve reporting of typos 
+   
+   Version: 2007-02-08
+   		* KH Schematron fullpath implementation: @* handled twice and / missing
+   		* KH Change stylesheetbody from named template to mode to allow implementers more flexibility.
+   		  Move process-ns to outside the stylesheet body.
+   		* DP, FG, fix handling of xslt:key
+   		* FG no iso:title/@class
+   		* Experimental optimization 'visit-no-attributes'
+   		* KH Experimental added schematron-get-full-path-2 which gives prefixed version for humans
+ 		* DC Move stylesheet/@version generation to after namespace handling
+ 		* DC, FG EXSLT namespace handling code
+ 		* FG add ref and commented code from FG's page on namespaces
+ 		* Start adding normalize-space() to parameter code
+ 		* Add a space between diagnostics
+   		   		 
+   Version: 2007-01-22
+   	* DP change = ($start) to = $start and =($phase) to =$phase 
+   	to run under Saxon 8.8j
+	* FG better title section using ( @id | sch:title)[last()]
+	* Default query language binding is "xslt" not "xslt1"
+  
+   Version: 2007-01-19
+   		* Simplify message newline code
+   		* Remove termination and xpath appending to message options: 
+   		   factor out as  iso_schematron_terminator.xsl
+   		* Comment out XSLT2 namespace fix temporarily
+  
+   Version: 2007-01-18 (First beta candidate for comment)
+          * DC remove xml:space="preserve"
+          * FG improve comment on import statement
+          * DC improve comments on invocation section
+          * Add exploratory support for sch:schema[@queryBinding='xpath']
+             by allowing it and warning as lets are found
+          * Be strict about queryBinding spelling errors
+          * Extra comments on the different queryBindings
+          * KH Add option "message-paths" to generate XPath from output 
+          * KH Add option "terminate" to halt with an error after the first assertion
+          * KH refactor paths in schematron-full-path
+          * Improve (?) namespace handling: no dummy attributes for prefix "xsl" generated
+   
+   Version: 2007-01-15
+          * FG fix for calling templates
+          * Add formal parameters to default templates: may help XSLT 2
+          * Fix get-schematron-full-path
+          * Include skeleton1-6 is commented out by default
+
+   Version:2007-01-12 (Pre-beta release to Schematron-love-in maillist)
+           * Add many extra parameters to the process-* calls, so that almost
+           all the information in the schema can be provided to client programs.
+           Also, rearrange the parameters to fit in with the ISO schema, which
+           has "rich" and "linkable" attribute groups.
+           * Warn on diagnostics with no ID once only
+           * Improved path reporting, to handle for namespaces
+           * Add process-title dummy template for API
+           * Add command-line parameter allow-foreign (true|false) to suppress
+            warnings one foreign elements and pass them through to the generated
+            stylesheet
+           * remove legacy templates for the old ASCC namespace and no namespace, 
+              and use an import statement instead. Much cleaner now!
+           * patterns use @id not @name
+           * titles can contain sub-elements
+           * start change sch:rule to allow attributes, PIs and comments 
+           * the default process-* for inline elements add a leading and trailing 
+             space, to reduce the chance of concatenation.
+           * add comments to make the generated code clearer
+           
+   Version:2006-11-07 (ISO: first release private to schematron-love-in maillist for review)
+           * Duplicate pattern templates, for handling ISO namespace
+           * Add priority onto default and paragraph templates
+           * Add namespace checks
+           * Handle key in xsl namespace not iso
+           * Add include
+           * Improve namespace handling
+           * Preliminary XSLT2 and EXSLT support
+	       * Refactor iso:schema for clarity
+
+    Version: 2003-05-26 
+    	    * Fix bug with key 
+    Version: 2003-04-16
+    	   * handle 1.6 let expressions
+    	   * make key use XSLT names, and allow anywhere
+    Version: 2001-06-13
+           * same skeleton now supports namespace or no namespace
+           * parameters to handlers updated for all 1.5 attributes 
+           * diagnostic hints supported: command-line option diagnose=yes|no
+           * phases supported: command-line option phase=#ALL|...
+           * abstract rules
+           * compile-time error messages  
+	   * add utility routine generate-id-from-path
+          
+    Contributors: Rick Jelliffe (original), Oliver Becker (architecture, XSLT2), 
+             Miloslav Nic (diagnostic, phase, options), Ludwig Svenonius (abstract)
+             Uche Ogbuji (misc. bug fixes), Jim Ancona (SAXON workaround),
+	 	     Francis Norton (generate-id-from-path), Robert Leftwich, Bryan Rasmussen,
+             Dave Pawson (include, fallback), Florent Georges (namespaces, exslt, attribute
+             context), Benoit Maisonny (attribute context), John Dumps (process-message newline),
+             Cliff Stanford (diagnostics and other newlines)
+
+    
+    KNOWN TYPICAL LIMITATIONS:
+      * Don't use <sch:ns prefix="xsl" .../> with a namespace other than the standard
+      XSLT one. This would be a bizarre thing to do anyway. 
+      * Don't use other prefixes for the XSLT namespace either; some implementations will
+      not handle it correctly.
+     
+     EXTENSIONS:
+      ISO Schematron is designed as a framework with some standard query language
+      bindings. If you need to support other features, please do so safely by making
+      up your own @queryLanguage name: this makes it clear that your schema requires
+      special features. For example, default ISO Schematron does not support user
+      defined functions; so if you want to use the user defined function feature
+      in XSLT, you need to have a schema with some queryBinding attribute name like
+      "XSLT-with-my-functions" or whatever.
+-->
+
+
+
+
+<xsl:stylesheet version="1.0" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 
+	xmlns:sch="http://www.ascc.net/xml/schematron"
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
+    xmlns:exsl="http://exslt.org/common"
+    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
+    extension-element-prefixes="exsl  msxsl"
+	 >
+<!-- This program implements ISO Schematron, except for abstract patterns which require a preprocess. -->
+  
+
+<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
+
+
+<!-- Category: top-level-element -->
+<xsl:output method="xml" omit-xml-declaration="no" standalone="yes"  indent="yes"/>
+
+
+<xsl:param name="phase">
+  <xsl:choose>
+    <xsl:when test="//sch:schema/@defaultPhase">
+      <xsl:value-of select="//sch:schema/@defaultPhase"/>
+    </xsl:when>   
+    <xsl:when test="//iso:schema/@defaultPhase">
+      <xsl:value-of select="//iso:schema/@defaultPhase"/>
+    </xsl:when>
+    <xsl:otherwise>#ALL</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="allow-foreign">false</xsl:param>
+
+<xsl:param name="message-newline">true</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on attribute nodes
+         defaults to true if there is any possibility that a context could match an attribute,
+         err on the side if caution, a context of *[.='@'] would cause this param to defualt to true
+         even though @ is in a string
+-->
+<xsl:param name="attributes">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'@') or contains(@context,'attribute')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on just elements in the child axis
+         defaults to true if there is any possibility that a context could match an comment or PI
+         err on the side if caution, a context of *[.='('] would cause this param to defualt to true
+         even though ( is in a string, but node() comment() and processing-instruction()  all have a (
+-->
+<xsl:param name="only-child-elements">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'(')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on text nodes nodes (if only-child-elements is false)
+         THIS IS NON CONFORMANT BEHAVIOUR JUST FOR DISCUSSION OF A POSSIBLE CHANGE TO THE
+         SPECIFICATION. THIS PARAM SHOULD GO IF THE FINAL DECISION IS THAT THE SPEC DOES NOT CHANGE.
+	 Always defaults to false
+-->
+<xsl:param name="visit-text" select="'false'"/>
+
+<!-- DPC
+  When selecting contexts the specified behaviour is
+    @*|node()[not(self::text())]
+    The automatic settings may use
+      node()[not(self::text())]
+      @*|*
+      *
+  instead for schema for which they are equivalent.
+  If the params are set explictly the above may be used, and also either if
+      @*
+      @*|node()
+   in all cases the result may not be equivalent, for example if you specify no attributes and the schema 
+   does have attribute contexts they will be silently ignored.
+
+  after testing it turns out that
+  node()[not(self::text())] is slower in saxon than *|comment()|processing-instruction() 
+  which I find a bit surprising but anyway I'll use the longr faster version.
+-->
+<xsl:variable name="context-xpath">
+  <xsl:if test="$attributes='true'">@*|</xsl:if>
+  <xsl:choose>
+    <xsl:when test="$only-child-elements='true'">*</xsl:when>
+    <xsl:when test="$visit-text='true'">node()</xsl:when>
+    <xsl:otherwise>*|comment()|processing-instruction()</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- DPC if this is set to 
+    '' use recursive templates to iterate over document tree,
+    'key' select  all contexts with a key rather than walking the tree explictly in each mode
+    '//' select all contexts with // a key rather than walking the tree explictly in each mode (XSLT2 only)
+-->
+<xsl:param name="select-contexts" select="''"/>
+
+
+<xsl:param name="output-encoding"/>
+<!-- e.g. saxon file.xml file.xsl "sch.exslt.imports=.../string.xsl;.../math.xsl" -->
+<xsl:param name="sch.exslt.imports"/>
+
+<xsl:param name="debug">false</xsl:param>
+
+<!-- Simple namespace check -->
+<xsl:template match="/">
+    <xsl:if  test="//sch:*[ancestor::iso:* or descendant::iso:*]">
+	<xsl:message>Schema error: Schematron elements in old and new namespaces found</xsl:message>
+	<xsl:if test=" $debug = 'false' " />
+    </xsl:if>
+
+    <xsl:apply-templates />
+</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON SCHEMA ELEMENT  -->
+<!-- Not handled: Abstract patterns. A pre-processor is assumed. -->
+<!-- ============================================================== -->
+
+<!-- SCHEMA -->
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema[not(@queryBinding) or @queryBinding='xslt' 
+     or @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'
+     or @queryBinding='xpath']">
+     <xsl:if test="
+	     @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'">
+	     <xsl:message>Schema error: in the queryBinding attribute, use 'xslt'</xsl:message>
+	</xsl:if>
+	<axsl:stylesheet>
+	    <xsl:apply-templates select="iso:ns"/>
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+		<xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+<!-- Using EXSLT with all modeles (except function module: not applicable) -->
+<xsl:template match="iso:schema[@queryBinding='exslt']" priority="10">
+    <xsl:comment>This XSLT was automatically generated from a Schematron schema.</xsl:comment>
+	<axsl:stylesheet
+ 	  	xmlns:date="http://exslt.org/dates-and-times"
+ 	  	xmlns:dyn="http://exslt.org/dynamic"
+		xmlns:exsl="http://exslt.org/common"
+		xmlns:math="http://exslt.org/math"
+   		xmlns:random="http://exslt.org/random"
+  		xmlns:regexp="http://exslt.org/regular-expressions"
+   		xmlns:set="http://exslt.org/sets"
+   		xmlns:str="http://exslt.org/strings"
+   		extension-element-prefixes="date dyn exsl math random regexp set str" >
+	
+        <xsl:apply-templates select="iso:ns"/>
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+	    <xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema" priority="-1">
+	<xsl:message terminate="yes" >Fail: This implementation of ISO Schematron does not work with 
+	schemas using the "<xsl:value-of select="@queryBinding"/>" query language.</xsl:message>        
+</xsl:template>
+
+<xsl:template match="*" mode="stylesheetbody">
+	<!--xsl:template name="stylesheetbody"-->
+    <xsl:comment>Implementers: please note that overriding process-prolog or process-root is 
+    the preferred method for meta-stylesheets to use where possible. </xsl:comment><xsl:text>
</xsl:text>
+
+   <!-- These parameters may contain strings with the name and directory of the file being
+   validated. For convenience, if the caller only has the information in a single string,
+   that string could be put in fileDirParameter. The archives parameters are available
+   for ZIP archives.
+	-->
+
+	<axsl:param name="archiveDirParameter" />
+	<axsl:param name="archiveNameParameter" />
+	<axsl:param name="fileNameParameter" />
+	<axsl:param name="fileDirParameter" />
+
+    <xsl:call-template name="iso:exslt.add.imports" />
+    <xsl:text>

</xsl:text><xsl:comment>PHASES</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:call-template name="handle-phase"/>
+    <xsl:text>

</xsl:text><xsl:comment>PROLOG</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:call-template name="process-prolog"/>
+    <xsl:text>

</xsl:text><xsl:comment>KEYS</xsl:comment><xsl:text>
</xsl:text>
+	<xsl:apply-templates mode="do-keys"   select="xsl:key  "/>
+    <xsl:text>

</xsl:text><xsl:comment>DEFAULT RULES</xsl:comment><xsl:text>
</xsl:text>
+    <xsl:call-template name="generate-default-rules" />
+    <xsl:text>

</xsl:text><xsl:comment>SCHEMA METADATA</xsl:comment><xsl:text>
</xsl:text>
+    <xsl:call-template name="handle-root"/>
+    <xsl:text>

</xsl:text><xsl:comment>SCHEMATRON PATTERNS</xsl:comment><xsl:text>
</xsl:text>
+ 
+	<xsl:apply-templates select="*[not(self::iso:ns)] " />
+</xsl:template>
+ 
+    <xsl:template name="iso:exslt.add.imports">
+      <xsl:param name="imports" select="$sch.exslt.imports"/>
+      <xsl:choose>
+        <xsl:when test="contains($imports, ';')">
+          <axsl:import href="{ substring-before($imports, ';') }"/>
+          <xsl:call-template name="iso:exslt.add.imports">
+            <xsl:with-param name="imports"  select="substring-after($imports, ';')"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:when test="$imports">
+          <axsl:import href="{ $imports }"/>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:template>
+
+<xsl:template name="handle-phase" >
+	<xsl:if test="not(normalize-space( $phase ) = '#ALL')">
+	  <xsl:if test="not(iso:phase[@id = normalize-space( $phase )])">
+		  <xsl:message>Phase Error: no phase with name <xsl:value-of select="normalize-space( $phase )"
+		  /> has been defined.</xsl:message>
+	  </xsl:if>
+     </xsl:if>
+</xsl:template>
+
+<xsl:template name="generate-default-rules">
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: SCHEMATRON-FULL-PATH</xsl:comment><xsl:text>
</xsl:text>
+		<xsl:comment>This mode can be used to generate an ugly though full XPath for locators</xsl:comment><xsl:text>
</xsl:text>
+   		<axsl:template match="*" mode="schematron-get-full-path">
+			<axsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
+			
+			<!-- XSLT1 syntax -->
+
+			<axsl:text>/</axsl:text>
+			<axsl:choose>
+			<axsl:when test="namespace-uri()=''">
+			<axsl:value-of select="name()"/>
+			<axsl:variable name="p" select="1+
+			count(preceding-sibling::*[name()=name(current())])" />
+		<axsl:if test="$p>1 or following-sibling::*[name()=name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:when>
+		<axsl:otherwise>
+		<axsl:text>*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/><axsl:text>' and namespace-uri()='</axsl:text>
+		<axsl:value-of select="namespace-uri()"/>
+		<axsl:text>']</axsl:text>
+		<axsl:variable name="p" select="1+
+		count(preceding-sibling::*[local-name()=local-name(current())])" />
+		<axsl:if test="$p>1 or following-sibling::*[local-name()=local-name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:otherwise>
+		</axsl:choose> 
+       	 	</axsl:template>
+       	 	
+       	 	
+		<axsl:template match="@*" mode="schematron-get-full-path">
+		
+			<!-- XSLT1 syntax -->
+		<axsl:text>/</axsl:text>
+		<axsl:choose>
+		<axsl:when test="namespace-uri()=''">@<axsl:value-of
+		select="name()"/></axsl:when>
+		<axsl:otherwise>
+		<axsl:text>@*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/>
+		<axsl:text>' and namespace-uri()='</axsl:text>
+		<axsl:value-of select="namespace-uri()"/>
+		<axsl:text>']</axsl:text>
+		</axsl:otherwise>
+		</axsl:choose>   
+
+		</axsl:template>
+	
+	
+	<xsl:text>

</xsl:text>
+	
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-2</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-2">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="preceding-sibling::*[name(.)=name(current())]">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-FROM-PATH </xsl:comment><xsl:text>
</xsl:text>
+		<!-- repeatable-id maker derived from Francis Norton's. -->
+		<!-- use this if you need generate ids in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These ids are not XML names but closer to paths. -->
+		<axsl:template match="/" mode="generate-id-from-path"/>
+		<axsl:template match="text()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')"/>
+		</axsl:template>
+		<axsl:template match="comment()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')"/>
+		</axsl:template>
+		<axsl:template match="processing-instruction()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of 
+			select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')"/>
+		</axsl:template>
+		<axsl:template match="@*" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.@', name())"/>
+		</axsl:template>
+		<axsl:template match="*" mode="generate-id-from-path" priority="-0.5">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:text>.</axsl:text>
+<!--
+			<axsl:choose>
+				<axsl:when test="count(. | ../namespace::*) = count(../namespace::*)">
+					<axsl:value-of select="concat('.namespace::-',1+count(namespace::*),'-')"/>
+				</axsl:when>
+				<axsl:otherwise>
+-->
+				<axsl:value-of 
+				select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')"/>
+<!--
+				</axsl:otherwise>
+			</axsl:choose>
+-->
+		</axsl:template>
+		
+		
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-3</xsl:comment>
+	
+	<xsl:text>
</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans 
+	(Top-level element has index)</xsl:comment>
+	<xsl:text>
</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-3">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="parent::*">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+		<xsl:text>

</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-2 </xsl:comment><xsl:text>
</xsl:text>
+		<!-- repeatable-id maker from David Carlisle. -->
+		<!-- use this if you need generate IDs in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These IDs are well-formed XML NMTOKENS -->
+	<axsl:template match="/" mode="generate-id-2">U</axsl:template>
+
+	<axsl:template match="*" mode="generate-id-2" priority="2">
+		<axsl:text>U</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+	</axsl:template>
+
+	<axsl:template match="node()" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>n</axsl:text>
+		<axsl:number count="node()"/>
+	</axsl:template>
+
+	<axsl:template match="@*" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="string-length(local-name(.))"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="translate(name(),':','.')"/>
+	</axsl:template> 
+
+
+		<xsl:comment>Strip characters</xsl:comment>
+		<axsl:template match="text()" priority="-1" />
+			
+  </xsl:template>
+
+ <xsl:template name="handle-root">
+		<!-- Process the top-level element -->
+		<axsl:template match="/">
+			<xsl:call-template name="process-root">
+				<xsl:with-param 	
+				name="title" select="(@id | iso:title)[last()]"/>
+				<xsl:with-param name="version" select="'iso'" />
+				<xsl:with-param name="schemaVersion" select="@schemaVersion" />
+				<xsl:with-param name="queryBinding" select="@queryBinding" />
+				<xsl:with-param name="contents">
+					<xsl:apply-templates mode="do-all-patterns"/>
+				</xsl:with-param>
+				
+				<!-- "Rich" properties -->
+				<xsl:with-param name="fpi" select="@fpi"/>
+				<xsl:with-param name="icon" select="@icon"/>
+				<xsl:with-param name="id" select="@id"/>
+				<xsl:with-param name="lang" select="@xml:lang"/>
+				<xsl:with-param name="see" select="@see" />
+				<xsl:with-param name="space" select="@xml:space" />
+				
+				
+				<!-- Non-standard extensions not part of the API yet -->
+				<xsl:with-param name="action" select="@action" />
+			</xsl:call-template>
+		</axsl:template>
+ 
+      
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON ELEMENTS -->
+<!-- ============================================================== -->
+
+	<!-- ISO ACTIVE -->
+	<xsl:template match="iso:active">
+                <xsl:if test="not(@pattern)">
+                    <xsl:message>Markup Error: no pattern attribute in <active></xsl:message>
+                </xsl:if>
+
+                <xsl:if test="not(../../iso:pattern[@id = current()/@pattern])
+                and not(../../iso:include)">
+                           <xsl:message>Reference Error: the pattern  "<xsl:value-of select="@pattern"
+						   />" has been activated but is not declared</xsl:message>
+                </xsl:if>
+        </xsl:template>
+
+	<!-- ISO ASSERT and REPORT -->
+	<xsl:template match="iso:assert">
+  
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in <assert</xsl:message>
+                </xsl:if>
+        <xsl:text>

		</xsl:text>
+		<xsl:comment>ASSERT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>
</xsl:text>      
+	
+		<axsl:choose>
+			<axsl:when test="{@test}"/>
+			<axsl:otherwise>
+				<xsl:call-template name="process-assert">
+					<xsl:with-param name="test" select="normalize-space(@test)" />
+					<xsl:with-param name="diagnostics" select="@diagnostics"/>
+					<xsl:with-param name="flag" select="@flag"/>
+					
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+				</xsl:call-template>
+ 			
+			</axsl:otherwise>
+		</axsl:choose>
+	</xsl:template>
+	<xsl:template match="iso:report">
+		 
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in <report></xsl:message>
+                </xsl:if>
+                
+        <xsl:text>

		</xsl:text>
+		<xsl:comment>REPORT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>
</xsl:text>      
+	
+		<axsl:if test="{@test}">
+		
+			<xsl:call-template name="process-report">
+				<xsl:with-param name="test" select="normalize-space(@test)" />
+				<xsl:with-param name="diagnostics" select="@diagnostics"/>
+				
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+				
+		</axsl:if>
+	</xsl:template>
+
+
+	<!-- ISO DIAGNOSTIC -->
+	<!-- We use a mode here to maintain backwards compatability, instead of adding it
+	     to the other mode.
+	-->
+	<xsl:template match="iso:diagnostic" mode="check-diagnostics">
+              <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in <diagnostic></xsl:message>
+               </xsl:if>
+    </xsl:template>
+    
+    <xsl:template match="iso:diagnostic"  >
+                <xsl:call-template name="process-diagnostic">
+                
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+               </xsl:call-template>
+        </xsl:template>
+
+	<!-- ISO DIAGNOSTICS -->
+	<xsl:template match="iso:diagnostics" >
+		<xsl:apply-templates mode="check-diagnostics" select="*" />
+	</xsl:template>
+
+	<!-- ISO DIR -->
+	<xsl:template match="iso:dir"  mode="text" >
+		<xsl:call-template name="process-dir">
+			<xsl:with-param name="value" select="@value"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO EMPH -->
+	<xsl:template match="iso:emph"  mode="text">
+	 
+		<xsl:call-template name="process-emph"/> 
+
+	</xsl:template>
+
+	<!-- ISO EXTENDS -->
+	<xsl:template match="iso:extends">
+		<xsl:if test="not(@rule)">
+                   <xsl:message>Markup Error: no rule attribute in <extends></xsl:message>
+                </xsl:if>
+     		<xsl:if test="not(//iso:rule[@abstract='true'][@id= current()/@rule] )">
+                    <xsl:message>Reference Error: the abstract rule  "<xsl:value-of select="@rule"
+					/>" has been referenced but is not declared</xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+
+  		<xsl:if test="//iso:rule[@id=current()/@rule]">
+    			<xsl:apply-templates select="//iso:rule[@id=current()/@rule]"
+				mode="extends"/>
+  		</xsl:if>
+
+	</xsl:template>
+
+	<!-- KEY: ISO has no KEY -->
+	<!-- NOTE: 
+	     Key has had a checkered history. Schematron 1.0 allowed it in certain places, but
+	     users came up with a different location, which has now been adopted. 
+	     
+	     XT, the early XSLT processor, did not implement key and died when it was present. 
+	     So there are some versions of the Schematron skeleton for XT that strip out all
+	     key elements.
+	     
+	     Xalan (e.g. Xalan4C 1.0 and a Xalan4J) also had a funny. A fix involved making 
+	     a top-level parameter called $hiddenKey and then using that instead of matching
+	     "key". This has been removed.
+	-->
+	<xsl:template  match="xsl:key" mode="do-keys" >
+	     <xsl:if test="not(@name)">
+              <xsl:message>Markup Error: no name attribute in <key></xsl:message>
+         </xsl:if>
+                <xsl:if test="not(@path) and not(@use)">
+                    <xsl:message>Markup Error: no path or use attribute in <key></xsl:message>
+                </xsl:if>         
+	     <xsl:choose>
+	     	<xsl:when test="parent::iso:rule ">
+	        <xsl:call-template name="IamEmpty" />
+	       <xsl:choose>
+	       	<xsl:when test="@path">
+				<axsl:key match="{../@context}" name="{@name}" use="{@path}"/>
+			</xsl:when>
+			<xsl:otherwise>
+							<axsl:key match="{../@context}" name="{@name}" use="{@use}"/>
+			</xsl:otherwise>
+			</xsl:choose>	
+		</xsl:when>
+		<xsl:otherwise>
+                <xsl:if test="not(@match) ">
+                    <xsl:message>Markup Error: no path or use attribute in <key></xsl:message>
+                </xsl:if>   		
+			<axsl:key>
+      			<xsl:copy-of select="@*"/>
+    		</axsl:key>	
+		</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template match="xsl:key "  /><!-- swallow -->
+
+	<xsl:template match="iso:key "  >
+		<xsl:message>Schema error: The key element is not in the ISO Schematron namespace. Use the XSLT namespace.</xsl:message>
+    </xsl:template>
+
+   <!-- ISO INCLUDE -->
+   <!-- This is only a fallback. Include really needs to have been done before this as a separate pass.-->
+
+   <xsl:template match="iso:include[not(normalize-space(@href))]"
+	   priority="1">
+	<xsl:if test=" $debug = 'false' ">
+		<xsl:message terminate="yes">Schema error: Empty href= attribute for include directive.</xsl:message>
+	</xsl:if>
+
+   </xsl:template>
+
+   <!-- Extend the URI syntax to allow # refererences -->
+   <!-- Add experimental support for simple containers like  /xxx:xxx/iso:pattern to allow better includes -->
+   <xsl:template match="iso:include">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument//iso:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:*" />
+              <xsl:if test=" $theFragment/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment | $theContainedFragments "/>
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+
+   <!-- This is to handle the particular case of including patterns -->  
+   <xsl:template match="iso:include" mode="do-all-patterns">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+ 
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) > 0">
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument//iso:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment" mode="do-all-patterns"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+		   	  <!-- Import the top-level element if it is in schematron namespace,
+		   	  or its children otherwise, to allow a simple containment mechanism. -->
+              <xsl:variable name="theDocument" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment" select="$theDocument/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument/*/iso:*" />
+              <xsl:if test=" $theFragment/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment | $theContainedFragments "
+       		mode="do-all-patterns" />
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+   
+	<!-- ISO LET -->
+	<xsl:template match="iso:let" >
+	  <xsl:if test="ancestor::iso:schema[@queryBinding='xpath']">
+                    <xsl:message>Warning: Variables should not be used with the "xpath" query language binding.</xsl:message>
+       </xsl:if>
+		
+       <!-- lets at the top-level are implemented as parameters -->
+ 
+       	<xsl:choose>
+       		<xsl:when test="parent::iso:schema">
+       			<!-- it is an error to have an empty param/@select because an XPath is expected -->
+	      		 <axsl:param name="{@name}" select="{@value}">
+	      		 		<xsl:if test="string-length(@value) > 0">
+	      		 			<xsl:attribute name="select"><xsl:value-of select="@value"/></xsl:attribute>
+	      		 		</xsl:if>
+	      		 </axsl:param> 
+       		</xsl:when>
+       		<xsl:otherwise>
+				<axsl:variable name="{@name}" select="{@value}"/>
+			</xsl:otherwise>
+		</xsl:choose>
+		  
+	</xsl:template>	
+
+	<!-- ISO NAME -->
+	<xsl:template match="iso:name" mode="text">
+	
+		<xsl:if test="@path">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="concat('name(', at path,')')"/>
+			</xsl:call-template>
+		</xsl:if>
+		<xsl:if test="not(@path)">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="'name(.)'"/>
+			</xsl:call-template>
+		</xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	</xsl:template>
+
+	<!-- ISO NS -->
+	<!-- Namespace handling is XSLT is quite tricky and implementation dependent -->
+	<xsl:template match="iso:ns">
+ 		<xsl:call-template name="handle-namespace" />
+	</xsl:template>
+
+    <!-- This template is just to provide the API hook -->
+	<xsl:template match="iso:ns"  mode="do-all-patterns" >
+               <xsl:if test="not(@uri)">
+                    <xsl:message>Markup Error: no uri attribute in <ns></xsl:message>
+                </xsl:if>
+               <xsl:if test="not(@prefix)">
+                    <xsl:message>Markup Error: no prefix attribute in <ns></xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+		<xsl:call-template name="process-ns" >
+			<xsl:with-param name="prefix" select="@prefix"/>
+			<xsl:with-param name="uri" select="@uri"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO P -->
+	<xsl:template match="iso:schema/iso:p " mode="do-schema-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	<xsl:template match="iso:pattern/iso:p " mode="do-pattern-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	
+    <!-- Currently, iso:p in other position are not passed through to the API -->
+	<xsl:template match="iso:phase/iso:p" />
+	<xsl:template match="iso:p " priority="-1" />
+
+	<!-- ISO PATTERN -->
+	<xsl:template match="iso:pattern" mode="do-all-patterns">
+	<xsl:if test="($phase = '#ALL') 
+	or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+		<xsl:call-template name="process-pattern">
+			<!-- the following select statement assumes that
+			@id | sch:title returns node-set in document order:
+			we want the title if it is there, otherwise the @id attribute -->
+			<xsl:with-param name="name" select="(@id | iso:title )[last()]"/>
+			<xsl:with-param name="is-a" select="''"/>
+			
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+		</xsl:call-template>
+		<xsl:choose>
+		  <xsl:when test="$select-contexts='key'">
+		    <axsl:apply-templates select="key('M','M{count(preceding-sibling::*)}')" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:when>
+		  <xsl:when test="$select-contexts='//'">
+		    <axsl:apply-templates mode="M{count(preceding-sibling::*)}">
+		      <xsl:attribute name="select">
+			<xsl:text>//(</xsl:text>
+			<xsl:for-each select="iso:rule/@context">
+			  <xsl:text>(</xsl:text>
+			  <xsl:value-of select="."/>
+			  <xsl:text>)</xsl:text>
+			  <xsl:if test="position()!=last()">|</xsl:if>
+			</xsl:for-each>
+			<xsl:text>)</xsl:text>
+			<xsl:if test="$visit-text='false'">[not(self::text())]</xsl:if>
+		      </xsl:attribute>
+		    </axsl:apply-templates>
+		  </xsl:when>
+		  <xsl:otherwise>
+		    <axsl:apply-templates select="/" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:otherwise>
+		</xsl:choose>
+        </xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="iso:pattern[@abstract='true']">
+    
+             <xsl:message>Schema implementation error: This schema has abstract patterns, yet they are supposed to be preprocessed out already
+             </xsl:message>
+    </xsl:template>
+
+    <!-- Here is the template for the normal case of patterns -->
+	<xsl:template match="iso:pattern[not(@abstract='true')]">
+     
+      <xsl:if test="($phase = '#ALL') 
+	          or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+ 
+		<xsl:text>

</xsl:text>
+		<xsl:comment>PATTERN <xsl:value-of select="@id" /> <xsl:value-of select="iso:title" /> </xsl:comment><xsl:text>
</xsl:text>      
+		<xsl:apply-templates />
+		
+		<!-- DPC select-contexts test -->
+		<xsl:if test="not($select-contexts)">
+		  <axsl:template match="text()" priority="-1" mode="M{count(preceding-sibling::*)}">
+		    <!-- strip characters -->
+		  </axsl:template>
+		  
+		  <!-- DPC introduce context-xpath variable -->
+		  <axsl:template match="@*|node()"
+				 priority="-2"
+				 mode="M{ count(preceding-sibling::*) }">
+		    <axsl:apply-templates select="{$context-xpath}" mode="M{count(preceding-sibling::*)}"/>
+		  </axsl:template>
+		</xsl:if>
+      </xsl:if>
+	</xsl:template>
+
+	<!-- ISO PHASE -->
+	<xsl:template match="iso:phase" >
+                <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in <phase></xsl:message>
+                </xsl:if>
+		  <xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO RULE -->
+	<xsl:template match="iso:rule[not(@abstract='true')] ">
+                <xsl:if test="not(@context)">
+                    <xsl:message>Markup Error: no context attribute in <rule></xsl:message>
+                </xsl:if>
+        <xsl:text>

	</xsl:text>
+		<xsl:comment>RULE <xsl:value-of select="@id" /> </xsl:comment><xsl:text>
</xsl:text>   
+        <xsl:if test="iso:title">
+		    <xsl:comment><xsl:value-of select="iso:title" /></xsl:comment>
+		  </xsl:if>
+		<!-- DPC select-contexts -->
+		<xsl:if test="$select-contexts='key'">
+		    <axsl:key name="M"
+			      match="{@context}" 
+			      use="'M{count(../preceding-sibling::*)}'"/>
+		</xsl:if>
+   
+	
+<!-- DPC priorities count up from 1000 not down from 4000 (templates in same priority order as before) -->
+		<axsl:template match="{@context}"
+		priority="{1000 + count(following-sibling::*)}" mode="M{count(../preceding-sibling::*)}">
+			<xsl:call-template name="process-rule">
+				<xsl:with-param name="context" select="@context"/>
+				
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+			<xsl:apply-templates/>
+			<!-- DPC introduce context-xpath and select-contexts variables -->
+			<xsl:if test="not($select-contexts)">
+			  <axsl:apply-templates select="{$context-xpath}" mode="M{count(../preceding-sibling::*)}"/>
+			</xsl:if>
+		</axsl:template>
+	</xsl:template>
+
+
+	<!-- ISO ABSTRACT RULE -->
+	<xsl:template match="iso:rule[@abstract='true'] " >
+		<xsl:if test=" not(@id)">
+                    <xsl:message>Markup Error: no id attribute on abstract <rule></xsl:message>
+                </xsl:if>
+ 		<xsl:if test="@context">
+                    <xsl:message>Markup Error: (2) context attribute on abstract <rule></xsl:message>
+                </xsl:if>
+	</xsl:template>
+
+	<xsl:template match="iso:rule[@abstract='true']"
+		mode="extends" >
+                <xsl:if test="@context">
+                    <xsl:message>Markup Error: context attribute on abstract <rule></xsl:message>
+                </xsl:if>
+			<xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO SPAN -->
+	<xsl:template match="iso:span" mode="text">
+		<xsl:call-template name="process-span">
+			<xsl:with-param name="class" select="@class"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO TITLE -->
+	
+	<xsl:template match="iso:schema/iso:title"  priority="1">
+	     <xsl:call-template name="process-schema-title" />
+	</xsl:template>
+ 
+	
+	<xsl:template match="iso:title" >
+	     <xsl:call-template name="process-title" />
+	</xsl:template>
+ 
+
+	<!-- ISO VALUE-OF -->
+	<xsl:template match="iso:value-of" mode="text" >
+        <xsl:if test="not(@select)">
+            <xsl:message>Markup Error: no select attribute in <value-of></xsl:message>
+        </xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	         
+		<xsl:choose>
+			<xsl:when test="@select">
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="@select"/>  
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise >
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="'.'"/>
+				</xsl:call-template>
+			</xsl:otherwise>
+        </xsl:choose> 
+        
+	</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- DEFAULT TEXT HANDLING  -->
+<!-- ============================================================== -->
+	<xsl:template match="text()" priority="-1" mode="do-keys">
+		<!-- strip characters -->
+	</xsl:template>
+	<xsl:template match="text()" priority="-1" mode="do-all-patterns">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-schema-p">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-pattern-p">
+		<!-- strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" priority="-1">
+		<!-- Strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" mode="text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+	<xsl:template match="text()" mode="inline-text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UTILITY TEMPLATES -->
+<!-- ============================================================== -->
+<xsl:template name="IamEmpty">
+	<xsl:if test="count( * )">
+		<xsl:message>
+			<xsl:text>Warning: </xsl:text>
+			<xsl:value-of select="name(.)"/>
+			<xsl:text> must not contain any child elements</xsl:text>
+		</xsl:message>
+	</xsl:if>
+</xsl:template>
+
+<xsl:template name="diagnosticsSplit">
+  <!-- Process at the current point the first of the <diagnostic> elements
+       referred to parameter str, and then recurse -->
+  <xsl:param name="str"/>
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="contains($str,' ')">
+	<xsl:value-of  select="substring-before($str,' ')"/>
+      </xsl:when>
+      <xsl:otherwise><xsl:value-of select="$str"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="end">
+    <xsl:if test="contains($str,' ')">
+      <xsl:value-of select="substring-after($str,' ')"/>
+    </xsl:if>
+  </xsl:variable>
+
+  <!-- This works with all namespaces -->
+  <xsl:if test="not(string-length(normalize-space($start)) = 0)
+  		and not(//iso:diagnostic[@id = $start])
+		and not(//sch:diagnostic[@id = $start]) 
+		and not(//diagnostic[@id = $start])">
+	<xsl:message>Reference error: A diagnostic "<xsl:value-of select="string($start)"
+	/>" has been referenced but is not declared</xsl:message>
+  </xsl:if>
+
+  <xsl:if test="string-length(normalize-space($start)) > 0">
+     <xsl:text> </xsl:text>
+     <xsl:apply-templates 
+        select="//iso:diagnostic[@id = $start ]
+        	| //sch:diagnostic[@id = $start ] 
+            | //diagnostic[@id= $start ]"/>
+  </xsl:if>
+
+  <xsl:if test="not($end='')">
+    <xsl:call-template name="diagnosticsSplit">
+      <xsl:with-param name="str" select="$end"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<!-- It would be nice to use this but xsl:namespace does not
+  allow a fallback -->
+<!--xsl:template name="handle-namespace" version="2.0">
+   <xsl:namespace name="{@prefix}" select="@uri">
+</xsl:template-->
+
+<xsl:template name="handle-namespace">
+       <!-- experimental code from http://eccnet.eccnet.com/pipermail/schematron-love-in/2006-June/000104.html -->
+       <!-- Handle namespaces differently for exslt systems, msxml, and default, only using XSLT1 syntax -->
+       <!-- For more info see  http://fgeorges.blogspot.com/2007/01/creating-namespace-nodes-in-xslt-10.html -->
+       <xsl:choose>
+          <!-- The following code works for XSLT1 -->
+        <xsl:when test="function-available('exsl:node-set')">
+           <xsl:variable name="ns-dummy-elements">
+             <xsl:element name="{@prefix}:dummy" namespace="{@uri}"/>
+           </xsl:variable>
+       	   <xsl:variable name="p" select="@prefix"/>
+           <xsl:copy-of select="exsl:node-set($ns-dummy-elements)
+                                  /*/namespace::*[local-name()=$p]"/>
+         </xsl:when>        
+
+   			<!-- End XSLT1  code -->
+  
+        <!-- Not tested yet       
+    	<xsl:when test="function-available('msxsl:node-set')">
+      		<xsl:variable name="ns-dummy-elements">
+        		<xsl:element name="{ $prefix }:e" namespace="{ $uri }"/>
+      		</xsl:variable>
+      		<xsl:copy-of select="msxsl:node-set($ns-dummy-elements)/*/namespace::*"/>
+    	</xsl:when>
+        -->
+        
+        <xsl:when test="@prefix = 'xsl' ">
+           <!-- Do not generate dummy attributes with the xsl: prefix, as these
+                are errors against XSLT, because we presume that the output
+                stylesheet uses the xsl prefix. In any case, there would already
+                be a namespace declaration for the XSLT namespace generated
+                automatically, presumably using "xsl:".
+           -->
+        </xsl:when>
+        
+        <xsl:when test="@uri = 'http://www.w3.org/1999/XSL/Transform'">
+          <xsl:message terminate="yes">
+            <xsl:text>Using the XSLT namespace with a prefix other than "xsl" in </xsl:text>
+            <xsl:text>Schematron rules is not supported </xsl:text>
+            <xsl:text>in this processor: </xsl:text>
+            <xsl:value-of select="system-property('xsl:vendor')"/>
+          </xsl:message>
+        </xsl:when>
+
+        <xsl:otherwise>
+          <xsl:attribute name="{concat(@prefix,':dummy-for-xmlns')}" namespace="{@uri}" />
+           
+        </xsl:otherwise>
+      </xsl:choose>
+
+
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UNEXPECTED ELEMENTS -->
+<!-- ============================================================== -->
+
+	<xsl:template match="iso:*"  priority="-2">
+	   <xsl:message>
+			<xsl:text>Error: unrecognized element in ISO Schematron namespace: check spelling
+			and capitalization</xsl:text>
+			<xsl:value-of select="name(.)"/>
+		</xsl:message>
+	</xsl:template>
+	
+	
+	<!-- Swallow old namespace elements: there is an upfront test for them elsewhere -->
+	<xsl:template match="sch:*"  priority="-2" />
+	
+	<xsl:template match="*"  priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:template match="iso:*" mode="text" priority="-2" />
+	<xsl:template match="*" mode="text" priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- DEFAULT NAMED TEMPLATES -->
+<!-- These are the actions that are performed unless overridden -->
+<!-- ============================================================== -->
+ 
+	<xsl:template name="process-prolog"/>
+	<!-- no params -->
+
+	<xsl:template name="process-root">
+		<xsl:param name="contents"/>
+		<xsl:param name="id" />
+		<xsl:param name="version" />
+		<xsl:param name="schemaVersion" />
+		<xsl:param name="queryBinding" />
+		<xsl:param name="title" />
+
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:copy-of select="$contents"/>
+	</xsl:template>
+
+	<xsl:template name="process-assert">
+
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+		
+		
+	</xsl:template>
+
+	<xsl:template name="process-report">
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" /> 
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-diagnostic">
+		<xsl:param name="id" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-dir">
+      	<xsl:param name="value" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-emph"> 
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+	
+	<xsl:template name="process-name">
+		<xsl:param name="name"/>
+		
+		<!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$name}"/>
+		<axsl:text> </axsl:text>
+		
+    </xsl:template>
+
+	<xsl:template name="process-ns" >
+	<!-- Note that process-ns is for reporting. The sch:ns elements are 
+	     independently used in the sch:schema template to provide namespace bindings -->
+		<xsl:param name="prefix"/>
+		<xsl:param name="uri" />
+      </xsl:template>
+
+	<xsl:template name="process-p">
+		<xsl:param name="id" />
+		<xsl:param name="class" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+      </xsl:template>
+
+	<xsl:template name="process-pattern">
+		<xsl:param name="id" />
+		<xsl:param name="name" />
+		<xsl:param name="is-a" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+      
+
+	<xsl:template name="process-rule">
+		<xsl:param name="context" />
+
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+  
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+
+	<xsl:template name="process-span" >
+		<xsl:param name="class" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>		
+	</xsl:template>
+
+	<xsl:template name="process-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-p">
+	      <xsl:with-param  name="class">title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+		
+	<xsl:template name="process-schema-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-title">
+	      <xsl:with-param  name="class">schema-title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-value-of">
+		<xsl:param name="select"/>
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$select}"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<!-- default output action: the simplest customization is to just override this -->
+	<xsl:template name="process-message">
+		<xsl:param name="pattern" />
+            <xsl:param name="role" />
+
+		<xsl:apply-templates mode="text"/>	
+		 <xsl:if test=" $message-newline = 'true'" >
+			<axsl:value-of  select="string('
')"/>
+		</xsl:if>
+		
+	</xsl:template>
+</xsl:stylesheet>
+
+
+
diff --git a/Tools/validation/iso_svrl.xsl b/Tools/validation/iso_svrl.xsl
new file mode 100644
index 0000000..cdc5c51
--- /dev/null
+++ b/Tools/validation/iso_svrl.xsl
@@ -0,0 +1,583 @@
+<?xml version="1.0" ?>
+<!-- 
+   ISO_SVRL.xsl   
+
+   Implementation of Schematron Validation Report Language from ISO Schematron
+   ISO/IEC 19757 Document Schema Definition Languages (DSDL) 
+     Part 3: Rule-based validation  Schematron 
+     Annex D: Schematron Validation Report Language 
+
+  This ISO Standard is available free as a Publicly Available Specification in PDF from ISO.
+  Also see www.schematron.com for drafts and other information.
+
+  This implementation of SVRL is designed to run with the "Skeleton" implementation 
+  of Schematron which Oliver Becker devised. The skeleton code provides a 
+  Schematron implementation but with named templates for handling all output; 
+  the skeleton provides basic templates for output using this API, but client
+  validators can be written to import the skeleton and override the default output
+  templates as required. (In order to understand this, you must understand that
+  a named template such as "process-assert" in this XSLT stylesheet overrides and
+  replaces any template with the same name in the imported skeleton XSLT file.)
+
+  The other important thing to understand in this code is that there are different
+  versions of the Schematron skeleton. These track the development of Schematron through
+  Schematron 1.5, Schematron 1.6 and now ISO Schematron. One only skeleton must be
+  imported. The code has templates for the different skeletons commented out for 
+  convenience. ISO Schematron has a different namespace than Schematron 1.5 and 1.6;
+  so the ISO Schematron skeleton has been written itself with an optional import
+  statement to in turn import the Schematron 1.6 skeleton. This will allow you to 
+  validate with schemas from either namespace.
+  
+
+  History: 
+    2008-08-11
+   		* RJ Fix attribute/@select which saxon allows  in XSLT 1
+   2008-08-07
+    	* RJ Add output-encoding attribute to specify final encoding to use
+    	* Alter allow-foreign functionality so that Schematron span, emph and dir elements make 
+    	  it to the output, for better formatting and because span can be used to mark up
+    	  semantically interesting information embedded in diagnostics, which reduces the
+    	  need to extend SVRL itself
+    	* Diagnostic-reference had an invalid attribute @id that duplicated @diagnostic: removed
+  	2008-08-06
+    	* RJ Fix invalid output:  svrl:diagnostic-reference is not contained in an svrl:text
+    	* Output comment to SVRL file giving filename if available (from command-line parameter)
+  	2008-08-04
+  		* RJ move sch: prefix to schold: prefix to prevent confusion (we want people to
+  		be able to switch from old namespace to new namespace without changing the
+  		sch: prefix, so it is better to keep that prefix completely out of the XSLT)
+  		* Extra signature fixes (PH)
+    2008-08-03
+    	* Repair missing class parameter on process-p
+    2008-07-31
+    	* Update skeleton names
+    2007-04-03 
+    	* Add option generate-fired-rule (RG)
+    2007-02-07
+    	* Prefer true|false for parameters. But allow yes|no on some old for compatability
+    	* DP Diagnostics output to svrl:text. Diagnosis put out after assertion text.
+      	* Removed non-SVRL elements and attributes: better handled as an extra layer that invokes this one
+      	* Add more formal parameters
+      	* Correct confusion between $schemaVersion and $queryBinding
+     	* Indent
+     	* Validate against RNC schemas for XSLT 1 and 2 (with regex tests removed)
+     	* Validate output with UniversalTest.sch against RNC schema for ISO SVRL
+    	
+    2007-02-01
+       	* DP. Update formal parameters of overriding named templates to handle more attributes.
+       	* DP. Refactor handling of rich and linkable parameters to a named template.
+
+    2007-01-22
+    	* DP change svrl:ns to svrl:ns-in-attribute-value
+		* Change default when no queryBinding from "unknown" to "xslt"
+	
+    2007-01-18:
+     	* Improve documentation
+     	* KH Add command-line options to generate paths or not 
+       	* Use axsl:attribute rather than xsl:attribute to shut XSLT2 up
+       	* Add extra command-line options to pass to the iso_schematron_skeleton
+  
+    2006-12-01: iso_svrl.xsl Rick Jelliffe, 
+          * update namespace, 
+          * update phase handling,
+          * add flag param to process-assert and process-report & @ flag on output
+  
+    2001: Conformance1-5.xsl Rick Jelliffe, 
+          * Created, using the skeleton code contributed by Oliver Becker
+-->
+<!--
+ Derived from Conformance1-5.xsl.
+
+ Copyright (c) 2001, 2006 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+
+<!-- Ideas nabbed from schematrons by Francis N., Miloslav N. and David C. -->
+
+<!-- The command-line parameters are:
+  			phase           NMTOKEN | "#ALL" (default) Select the phase for validation
+    		allow-foreign   "true" | "false" (default)   Pass non-Schematron elements to the generated stylesheet
+            diagnose= true | false|yes|no    Add the diagnostics to the assertion test in reports (yes|no are obsolete)
+            generate-paths=true|false|yes|no   generate the @location attribute with XPaths (yes|no are obsolete)
+            sch.exslt.imports semi-colon delimited string of filenames for some EXSLT implementations          
+   		 optimize        "visit-no-attributes"     Use only when the schema has no attributes as the context nodes
+		 generate-fired-rule "true"(default) | "false"  Generate fired-rule elements
+            
+-->
+
+<xsl:stylesheet
+   version="1.0"
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+	xmlns:xs="http://www.w3.org/2001/XMLSchema"
+   xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
+   xmlns:schold="http://www.ascc.net/xml/schematron" 
+   xmlns:iso="http://purl.oclc.org/dsdl/schematron"
+   xmlns:svrl="http://purl.oclc.org/dsdl/svrl" 
+>
+
+<!-- Select the import statement and adjust the path as 
+   necessary for your system.
+-->
+<xsl:import href="iso_schematron_skeleton_for_xslt1.xsl"/>
+<!--
+<xsl:import href="iso_schematron_skeleton_for_saxon.xsl"/>
+ 
+<xsl:import href="iso_schematron_skeleton.xsl"/>
+<xsl:import href="skeleton1-5.xsl"/>
+<xsl:import href="skeleton1-6.xsl"/>
+-->
+
+<xsl:param name="diagnose" >true</xsl:param>
+<xsl:param name="phase" >
+	<xsl:choose>
+		<!-- Handle Schematron 1.5 and 1.6 phases -->
+		<xsl:when test="//schold:schema/@defaultPhase">
+			<xsl:value-of select="//schold:schema/@defaultPhase"/>
+		</xsl:when>
+		<!-- Handle ISO Schematron phases -->
+		<xsl:when test="//iso:schema/@defaultPhase">
+			<xsl:value-of select="//iso:schema/@defaultPhase"/>
+		</xsl:when>
+		<xsl:otherwise>#ALL</xsl:otherwise>
+	</xsl:choose>
+</xsl:param>
+<xsl:param name="allow-foreign" >false</xsl:param>
+<xsl:param name="generate-paths" >true</xsl:param>
+<xsl:param name="generate-fired-rule" >true</xsl:param>
+<xsl:param name="optimize"/>
+
+<xsl:param name="output-encoding" ></xsl:param>
+
+<!-- e.g. saxon file.xml file.xsl "sch.exslt.imports=.../string.xsl;.../math.xsl" -->
+<xsl:param name="sch.exslt.imports" />
+
+
+
+<!-- Experimental: If this file called, then must be generating svrl -->
+<xsl:variable name="svrlTest" select="true()" />
+
+  
+ 
+<!-- ================================================================ -->
+
+<xsl:template name="process-prolog">
+	<axsl:output method="xml" omit-xml-declaration="no" standalone="yes"
+		indent="yes">
+		<xsl:if test=" string-length($output-encoding) > 0">
+			<xsl:attribute name="encoding"><xsl:value-of select=" $output-encoding" /></xsl:attribute>
+		</xsl:if>
+    </axsl:output>
+     
+</xsl:template>
+
+<!-- Overrides skeleton.xsl -->
+<xsl:template name="process-root">
+	<xsl:param name="title"/>
+	<xsl:param name="contents" />
+	<xsl:param name="queryBinding" >xslt1</xsl:param>
+	<xsl:param name="schemaVersion" />
+	<xsl:param name="id" />
+	<xsl:param name="version"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	
+	<svrl:schematron-output title="{$title}" schemaVersion="{$schemaVersion}" >
+		<xsl:if test=" string-length( normalize-space( $phase )) > 0 and 
+		not( normalize-space( $phase ) = '#ALL') ">
+			<axsl:attribute name="phase">
+				<xsl:value-of select=" $phase " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" $allow-foreign = 'true'">
+		</xsl:if>
+		  <xsl:if  test=" $allow-foreign = 'true'">
+	
+		<xsl:call-template name='richParms'>
+			<xsl:with-param name="fpi" select="$fpi" />
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see"  select="$see" />
+			<xsl:with-param name="space"  select="$space" />
+		</xsl:call-template>
+	</xsl:if>
+		 
+		 <axsl:comment><axsl:value-of select="$archiveDirParameter"/>  &#xA0;
+		 <axsl:value-of select="$archiveNameParameter"/> &#xA0;
+		 <axsl:value-of select="$fileNameParameter"/> &#xA0;
+		 <axsl:value-of select="$fileDirParameter"/></axsl:comment> 
+		 
+		
+		<xsl:apply-templates mode="do-schema-p" />
+		<xsl:copy-of select="$contents" />
+	</svrl:schematron-output>
+</xsl:template>
+
+
+<xsl:template name="process-assert">
+	<xsl:param name="test"/>
+	<xsl:param name="diagnostics" />
+	<xsl:param name="id" />
+	<xsl:param name="flag" />
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:failed-assert test="{$test}" >
+		<xsl:if test="string-length( $id ) > 0">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $flag ) > 0">
+			<axsl:attribute name="flag">
+				<xsl:value-of select=" $flag " />
+			</axsl:attribute>
+		</xsl:if>
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:call-template name='linkableParms'>
+			<xsl:with-param name="role" select="$role" />
+			<xsl:with-param name="subject" select="$subject"/>
+		</xsl:call-template>
+		<xsl:if test=" $generate-paths = 'true' or $generate-paths= 'yes' ">
+			<!-- true/false is the new way -->
+			<axsl:attribute name="location">
+				<axsl:apply-templates select="." mode="schematron-get-full-path"/>
+			</axsl:attribute>
+		</xsl:if>
+		  
+		<svrl:text>
+			<xsl:apply-templates mode="text" />
+	
+		</svrl:text>
+		    <xsl:if test="$diagnose = 'yes' or $diagnose= 'true' ">
+			<!-- true/false is the new way -->
+				<xsl:call-template name="diagnosticsSplit">
+					<xsl:with-param name="str" select="$diagnostics"/>
+				</xsl:call-template>
+			</xsl:if>
+	</svrl:failed-assert>
+</xsl:template>
+
+<xsl:template name="process-report">
+	<xsl:param name="id"/>
+	<xsl:param name="test"/>
+	<xsl:param name="diagnostics"/>
+	<xsl:param name="flag" />
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:successful-report test="{$test}" >
+		<xsl:if test=" string-length( $id ) > 0">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $flag ) > 0">
+			<axsl:attribute name="flag">
+				<xsl:value-of select=" $flag " />
+			</axsl:attribute>
+		</xsl:if>
+		
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:call-template name='linkableParms'>
+			<xsl:with-param name="role" select="$role" />
+			<xsl:with-param name="subject" select="$subject"/>
+		</xsl:call-template>
+		<xsl:if test=" $generate-paths = 'yes' or $generate-paths = 'true' ">
+			<!-- true/false is the new way -->
+			<axsl:attribute name="location">
+				<axsl:apply-templates select="." mode="schematron-get-full-path"/>
+			</axsl:attribute>
+		</xsl:if>
+	 
+		<svrl:text>
+			<xsl:apply-templates mode="text" />
+
+		</svrl:text>
+			<xsl:if test="$diagnose = 'yes' or $diagnose='true' ">
+			<!-- true/false is the new way -->
+				<xsl:call-template name="diagnosticsSplit">
+					<xsl:with-param name="str" select="$diagnostics"/>
+				</xsl:call-template>
+			</xsl:if>
+	</svrl:successful-report>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-dir" >
+		<xsl:param name="value" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when>
+       
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+		 </xsl:choose>		
+	</xsl:template>
+
+<xsl:template name="process-diagnostic">
+	<xsl:param name="id"/>
+	<!-- Rich parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:diagnostic-reference diagnostic="{$id}" >
+	  
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template> 
+<xsl:text>
+</xsl:text>
+ 
+		<xsl:apply-templates mode="text"/>
+		 
+	</svrl:diagnostic-reference>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-emph" >
+		<xsl:param name="class" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when> 
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+	 	</xsl:choose>	
+	</xsl:template>
+
+<xsl:template name="process-rule">
+	<xsl:param name="id"/>
+	<xsl:param name="context"/>
+	<xsl:param name="flag"/>
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<xsl:if test=" $generate-fired-rule = 'true'">
+	<svrl:fired-rule context="{$context}" >
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:if test=" string( $id )">
+			<xsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</xsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $role ) > 0">
+			<xsl:attribute name="role">
+				<xsl:value-of select=" $role " />
+			</xsl:attribute>
+		</xsl:if> 
+	</svrl:fired-rule>
+</xsl:if>
+</xsl:template>
+
+<xsl:template name="process-ns">
+	<xsl:param name="prefix"/>
+	<xsl:param name="uri"/>
+	<svrl:ns-prefix-in-attribute-values uri="{$uri}" prefix="{$prefix}" />
+</xsl:template>
+
+<xsl:template name="process-p"> 
+	<xsl:param name="icon"/>
+	<xsl:param name="class"/>
+	<xsl:param name="id"/>
+	<xsl:param name="lang"/>
+	 
+	<svrl:text> 
+		<xsl:apply-templates mode="text"/>
+	</svrl:text>
+</xsl:template>
+
+<xsl:template name="process-pattern">
+	<xsl:param name="name"/>
+	<xsl:param name="id"/>
+	<xsl:param name="is-a"/>
+	
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:active-pattern >
+		<xsl:if test=" string( $id )">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string( $name )">
+			<axsl:attribute name="name">
+				<xsl:value-of select=" $name " />
+			</axsl:attribute>
+		</xsl:if> 
+		 
+		<xsl:call-template name='richParms'>
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		
+		<!-- ?? report that this screws up iso:title processing  -->
+		<xsl:apply-templates mode="do-pattern-p"/>
+		<!-- ?? Seems that this apply-templates is never triggered DP -->
+		<axsl:apply-templates />
+	</svrl:active-pattern>
+</xsl:template>
+
+<!-- Overrides skeleton -->
+<xsl:template name="process-message" > 
+	<xsl:param name="pattern"/>
+	<xsl:param name="role"/>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-span" >
+		<xsl:param name="class" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when> 
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+	 	</xsl:choose>	
+	</xsl:template>
+
+<!-- =========================================================================== -->
+<!-- processing rich parameters. -->
+<xsl:template name='richParms'>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<!-- Process rich attributes.  -->
+	<xsl:if  test=" $allow-foreign = 'true'">
+	<xsl:if test="string($fpi)"> 
+		<axsl:attribute name="fpi">
+			<xsl:value-of select="$fpi "/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($icon)"> 
+		<axsl:attribute name="icon">
+			<xsl:value-of select="$icon "/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($see)"> 
+		<axsl:attribute name="see ">
+			<xsl:value-of select="$see "/>
+		</axsl:attribute>
+	</xsl:if>
+	</xsl:if>
+	<xsl:if test="string($space)">
+		<axsl:attribute name="xml:space">
+			<xsl:value-of select="$space"/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($lang)">
+		<axsl:attribute name="xml:lang">
+			<xsl:value-of select="$lang"/>
+		</axsl:attribute>
+	</xsl:if>
+</xsl:template>
+
+<!-- processing linkable parameters. -->
+<xsl:template name='linkableParms'>
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	
+	<!-- ISO SVRL has a role attribute to match the Schematron role attribute -->
+	<xsl:if test=" string($role )">
+		<axsl:attribute name="role">
+			<xsl:value-of select=" $role " />
+		</axsl:attribute>
+	</xsl:if>
+	<!-- ISO SVRL does not have a subject attribute to match the Schematron subject attribute.
+       Instead, the Schematron subject attribute is folded into the location attribute -->
+</xsl:template>
+   
+
+</xsl:stylesheet>
+
diff --git a/Tools/validation/remove_base.xsl b/Tools/validation/remove_base.xsl
new file mode 100644
index 0000000..acb6baa
--- /dev/null
+++ b/Tools/validation/remove_base.xsl
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:template match="/">
+		<xsl:apply-templates />
+	</xsl:template>
+
+	<!--
+		general templates copy everything you find in the input, unless
+		otherwise specified
+	-->
+	<xsl:template match="*">
+		<!-- copy element -->
+		<xsl:copy>
+			<!-- copy its attributes -->
+			<xsl:apply-templates select="@*" />
+			<!-- process its sub-elements -->
+			<xsl:apply-templates />
+		</xsl:copy>
+	</xsl:template>
+
+	<xsl:template match="@*">
+    <xsl:copy/>
+  </xsl:template>	
+
+	<!-- Remove xml:base attributes introduced by XInclude -->
+	<xsl:template match="@xml:*" />
+
+	
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/UPDATE b/UPDATE
new file mode 100644
index 0000000..90a6dca
--- /dev/null
+++ b/UPDATE
@@ -0,0 +1,71 @@
+                        
+                     !!!!!!!!!    UPDATE WARNING     !!!!!!!!!
+The service descriptions validation rules have changed in version 1.0.4: to 
+correct some bugs in the portal interface, parameter names are now restricted,
+and some values for parameter names are now forbidden.
+
+1- What does this mean for me?
+==============================
+As a Mobyle server owner, you will have to update the service descriptions to
+correct parameter names that are invalid, either by updating "external" definitions
+(e.g., update pasteur-programs to version 3.0 if you use them), or by correcting
+yourself the definitions you authored. Otherwise, *invalid service description won't
+deploy successfully anymore*.
+
+2- What are the forbidden values?
+=================================
+There are several, but most of them are mostly improbable in your descriptions. Some
+will be likely, like name, length but we doubt you named one of your parameters
+"insertAdjacentHTML". We will however tell you if you did. Here is a complete list
+of the forbidden values:
+
+acceptCharset,action,autocomplete,elements,encoding,enctype,length,method,name,
+noValidate,checkValidity,dispatchFormChange,dispatchFormInput,item,namedItem,
+submit,reset,attributes,baseURI,baseURIObject,childElementCount,childNodes,children,
+classList,className,clientHeight,clientLeft,clientTop,clientWidth,contentEditable,
+dataset,dir,firstChild,firstElementChild,id,innerHTML,isContentEditable,lang,
+lastChild,lastElementChild,localName,name,namespaceURI,nextSibling,nextElementSibling,
+nodeName,nodePrincipal,nodeType,nodeValue,offsetHeight,offsetLeft,offsetParent,
+offsetTop,offsetWidth,ownerDocument,parentNode,prefix,previousSibling,
+previousElementSibling,schemaTypeInfo,scrollHeight,scrollLeft,scrollTop,scrollWidth,
+spellcheck,style,tabIndex,tagName,textContent,title,addEventListener,appendChild,blur,
+click,cloneNode,compareDocumentPosition,dispatchEvent,focus,getAttribute,
+getAttributeNS,getAttributeNode,getAttributeNodeNS,getBoundingClientRect,
+getClientRects,getElementsByTagName,getElementsByTagNameNS,getFeature,getUserData,
+hasAttribute,hasAttributeNS,hasAttributes,hasChildNodes,insertBefore,
+isDefaultNamespace,isEqualNode,isSameNode,isSupported,lookupNamespaceURI,
+lookupPrefix,mozMatchesSelector,normalize,querySelector,querySelectorAll,
+removeAttribute,removeAttributeNode,removeChild,removeEventListener,replaceChild,
+scrollIntoView,setAttribute,setAttributeNS,setAttributeNode,setAttributeNodeNS,
+setCapture,setIdAttribute,setIdAttributeNS,setIdAttributeNode,setUserData,
+insertAdjacentHTML,onbeforescriptexecute,onafterscriptexecute,oncopy,oncut,
+onpaste,onbeforeunload,onblur,onchange,onclick,oncontextmenu,ondblclick,onfocus,
+onkeydown,onkeypress,onkeyup,onmousedown,onmousemove,onmouseout,onmouseover,
+onmouseup,onresize,onscroll
+
+3- What do I have to correct in case I used one of these forbidden values?
+==========================================================================
+* the parameter name,
+* any reference to it you might have added in the python code which is
+embedded in the XML (precond or ctrl tags),
+* any reference in the datatype references,
+* and any reference you might have done to it in a workflow description that
+uses your service as a task (in the link tags).
+
+4- What can I do to check if I have any existing incompatible service description?
+==================================================================================
+grep and mobvalid are your friends ;)
+
+5- I want to learn more about the fascinating cause of this technical incompatibility!
+======================================================================================
+Please refer to:
+https://projets.pasteur.fr/issues/show/708
+https://projets.pasteur.fr/issues/show/683
+
+5- Questions
+============
+
+If you have some questions about this problem, feel free to post on 
+the mobyle-users at pasteur.fr list, or to email us at mobyle-support at pasteur.fr
+
+The Mobyle Team
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..5963b99
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,619 @@
+
+import glob
+import os
+import re
+import sys
+import time
+import types
+
+from stat import ST_MODE
+
+try:
+    from distutils import log
+    from distutils.core import setup
+    from distutils.ccompiler import *
+    from distutils.command.build import build
+    from distutils.command.clean import clean
+    from distutils.command.install import install
+    from distutils.command.install_egg_info import install_egg_info
+    from distutils.command.sdist import sdist
+    from distutils.dep_util import newer
+    from distutils.dir_util import copy_tree, remove_tree
+    from distutils.errors import DistutilsFileError
+    from distutils.versionpredicate import VersionPredicate
+    import distutils.filelist
+except:
+  sys.exit("Your python distutils version is too old, please try a newer one")
+
+## Basics ...
+package_nam = 'Mobyle'
+package_ver = '1.5.3'
+package_url = 'https://projets.pasteur.fr/wiki/mobyle/'
+package_aut = 'The Mobyle team'
+package_mel = 'mobyle at pasteur.fr'
+package_platforms = ['Unix']
+package_license = 'GNU General Public License, version 2 (GPLv2)'
+
+## Prerequisites ...
+require_pyt = [ 'python (>=2.5, <3.0)' ]
+require_mod = [ ('Image (>=1.1.5)', 'PIL (>=2.0.0)'), #Imaging or pillow
+                'lxml (>=2.2.4)',
+                'simpletal (>=4.1, <5.0)',
+                'simplejson (>=1.7.1)' ]
+
+## Installation ...
+core_dat = [ 'Src/Mobyle/*.py', 'Src/Mobyle/Classes/*.py',
+             'Src/Mobyle/Execution/*.py', 'Src/Mobyle/Converter/*.py', 'Src/Mobyle/Captcha/*',
+             'Src/Mobyle/Captcha/data/fonts/vera/*', 'Src/Mobyle/Captcha/data/pictures/abstract/*',
+             'Src/Mobyle/Captcha/data/pictures/nature/*', 'Src/Mobyle/Captcha/data/words/*',
+             'Schema/*', 'Local/*.py', 'Local/Config/Execution/*.py',
+             'Local/*/*.py', 'Tools/validation/*.xsl',
+             'Tools/README', 'Example/Local/*.py', 'Example/Local/*/*.py',
+             'Doc/Admin/*.pdf', 'Doc/*.pdf', 'Doc/*.html',
+             'Doc/Admin/*.tex', 'Doc/Admin/*.png', 'Doc/*.tex', 'Doc/*.jpeg']
+core_cfg = [ 'Local/Policy.py', 'Local/black_list.py', 'Local/mailTemplate.py', 'Local/CustomClasses/__init__.py' ]
+core_scr = [ 'Src/Mobyle/RunnerChild.py',
+             'Tools/mob*',
+             'Tools/job_updater.py',
+             'Tools/session_updater.py',
+             'Local/Config/Execution/__init__.py' ]
+
+core_tst = [ 'Src/Mobyle/Test/*.py', 'Src/Mobyle/Test/Converter/*.py',
+             'Src/Mobyle/Test/Converter/DataSequences/*',
+             'Src/Mobyle/Test/Converter/DataAlignments/*' ]
+core_exe = [ 'Tools/setsid.c' ]
+core_fix = core_scr + [ 'Src/Mobyle/ConfigManager.py',
+                        'Src/Mobyle/Converter/__init__.py',
+                        'Src/Mobyle/Execution/__init__.py' ]
+core_tre = [ 'Services', 
+             'Services/Programs', 
+             'Services/Viewers', 
+             'Services/Workflows', 
+             'Services/Tutorials',
+             'Local/Services', 
+             'Local/Services/Programs', 
+             'Local/Services/Viewers',
+             'Local/Services/Workflows', 
+             'Local/Services/Tutorials' ]
+
+cgis_scr = [ 'Src/Portal/cgi-bin/MobylePortal/*' ]
+cgis_fix = [ 'Src/Portal/cgi-bin/MobylePortal/mb_cgi.py' ]
+
+html_dat = [ 'Src/Portal/htdocs/MobylePortal/*/*',
+             'Src/Portal/htdocs/MobylePortal/*/openid/*' ]
+html_cfg = [ 'Src/Portal/htdocs/MobylePortal/css/local.css',
+             'Src/Portal/htdocs/MobylePortal/html/announcement.txt' ]
+html_tre = [ 'sessions/anonymous', 'sessions/authentified',
+             'jobs/ADMINDIR' ]
+
+misc_dat = [ 'AUTHORS', 'COPYING', 'INSTALL', 'NEWS', 'UPDATE' ]
+
+## Distribution ...
+dist_lst  = core_dat + core_cfg + core_scr + core_exe + core_tst
+dist_lst += cgis_scr + html_dat + html_cfg + misc_dat
+
+excl_lst  = [ 'setup.cfg', 
+              'Local/Policy.pasteur.py', 'Local/Config/Config.py' ]
+
+## Let the hard code begins ...
+
+core_dir = 'core'
+cgis_dir = 'cgis'
+html_dir = 'html'
+niaid_dir = 'niaid'
+
+#############
+#           #
+# Utilities #
+#           #
+#############
+
+def copy_script(src, dst, exe):
+    ## Mostly stolen from distutils.build_scripts.run() ...
+    if not newer(src, dst): return
+    dir = os.path.dirname(dst)
+    log.info("copying and adjusting %s -> %s", src, dir)
+    try:
+        f = open(src, "r")
+    except os.error, (errstr):
+        raise DistutilsFileError, "could not open '%s': %s" % (src, errstr)
+    try:
+        o = open(dst, "w")
+    except:
+        raise DistutilsFileError, "could not create '%s': %s" % (dst, errstr)
+    l = f.readline()
+    l = re.sub("^#!.*python", "#!" + exe, l)
+    o.write(l)
+    o.writelines(f.readlines())
+    o.close()
+    f.close()
+    omod = os.stat(dst)[ST_MODE] & 07777
+    nmod = (omod | 0555) & 07777
+    if nmod != omod:
+        log.info("changing mode of %s from %o to %o", dst, omod, nmod)
+        os.chmod(dst, nmod)
+
+def fix_script(src, dst, mob, htm):
+    log.info("adjusting %s -> %s", src, dst)
+    try:
+        f = open(src, "r")
+    except os.error, (errstr):
+        raise DistutilsFileError, "could not open '%s': %s" % (src, errstr)
+    try:
+        g = open(dst, "w")
+    except os.error, (errstr):
+        raise DistutilsFileError, "could not create '%s': %s" % (dst, errstr)
+    while True:
+        l = f.readline()
+        if not l: 
+            break
+        if mob and l == 'MOBYLEHOME = None\n':
+            l = l.replace('None', "'"+mob+"'", 1)
+        if htm and l == 'MOBYLEHTDOCS = None\n':
+            l = l.replace('None', "'"+htm+"'", 1)
+        g.write(l)
+    g.close()
+    f.close()
+    mod = os.stat(src)[ST_MODE] & 07777
+    os.chmod(dst, mod)
+
+def find_file(src, msk):
+    lst = distutils.filelist.FileList()
+    lst.findall(src)
+    lst.include_pattern(msk, anchor=False)
+    return lst.files
+
+###################
+#                 #
+# building Mobyle # 
+#                 #
+###################
+
+class build_mobyle(build):
+    
+    def run(self):
+        chk = True
+        for req in require_pyt:
+            chk &= self.chkpython(req)
+        for req in require_mod:
+            if isinstance(req, types.StringTypes):
+                req = [req]
+            chk = False
+            for alt in req:
+                predicate = VersionPredicate(alt)
+                if self.chkmodule(predicate):
+                     print >> sys.stderr, "%s python module found" % predicate.name
+                     chk = True
+                     break 
+            if not chk:   
+                print >> sys.stderr, "Missing mandatory %s python module" % predicate.name
+                sys.exit(1)
+        build.run(self)
+        
+    def chkpython(self, req):
+        chk = VersionPredicate(req)
+        ver = '.'.join([str(v) for v in sys.version_info[:2]])
+        if not chk.satisfied_by(ver):
+            print >> sys.stderr, "Invalid python version, expected %s" % req
+            return False
+        return True
+
+    def chkmodule(self, predicate):
+        try:
+            mod = __import__(predicate.name)
+        except:
+            return False
+        for v in [ '__version__', 'version' ]:
+            ver = getattr(mod, v, None)
+            break
+        try:
+            if ver and not predicate.satisfied_by(ver):
+                print >> sys.stderr, "Invalid module version, expected %s" % req
+                return False
+        except:
+            pass
+        return True
+
+
+class build_core(build):
+    
+    build.sub_commands += [('build_core', lambda self:True)]
+  
+    def run(self):
+        base = os.path.join(self.build_base, core_dir)
+        self.mkpath(base)
+        (dat, cfg, scr, exe) = ([], [], [], [])
+        for m in core_dat: 
+            dat += glob.glob(m)
+        for m in core_cfg: 
+            cfg += glob.glob(m)
+        for m in core_scr: 
+            scr += glob.glob(m)
+        for m in core_exe: 
+            exe += glob.glob(m)
+        for f in dat:
+          if f in scr + cfg + exe: 
+              continue
+          if not os.path.isfile(f): 
+              continue
+          dst = os.path.join(base, f)
+          self.mkpath(os.path.dirname(dst))
+          self.copy_file(f, dst, preserve_mode=0)
+        for f in cfg:
+          if not os.path.isfile(f): 
+              continue
+          dst = os.path.join(base, f + '.new')
+          self.mkpath(os.path.dirname(dst))
+          self.copy_file(f, dst, preserve_mode=0)
+        for f in scr:
+            if f in exe: 
+                continue
+            if not os.path.isfile(f): 
+                continue
+            dst = os.path.join(base, f)
+            self.mkpath(os.path.dirname(dst))
+            copy_script(f, dst, self.executable)
+        cc = new_compiler()
+        for f in core_exe:
+            objs = cc.compile([f], self.build_temp)
+            cc.link_executable(objs, f[0:-2], base)
+        for f in core_tre:
+            dir = os.path.join(base, f)
+            self.mkpath(dir)
+
+
+class build_html(build):
+    
+    build.sub_commands += [('build_html', lambda self:True)]
+      
+    def run(self):
+        base = os.path.join(self.build_base, html_dir)
+        self.mkpath(base)
+        (dat, cfg) = ([], [])
+        for m in html_dat: 
+            dat += glob.glob(m)
+        for m in html_cfg: 
+            cfg += glob.glob(m)
+        com = os.path.commonprefix(dat + cfg)
+        for f in dat:
+            if f in cfg: 
+                continue
+            if not os.path.isfile(f): 
+                continue
+            dst = os.path.join(base, 'portal', f)
+            dst = dst.replace(com, '', 1)
+            self.mkpath(os.path.dirname(dst))
+            self.copy_file(f, dst, preserve_mode=0)
+        for f in cfg:
+            if not os.path.isfile(f): 
+                continue
+            dst = os.path.join(base, 'portal', f + '.new')
+            dst = dst.replace(com, '', 1)
+            self.mkpath(os.path.dirname(dst))
+            self.copy_file(f, dst, preserve_mode=0)
+        for f in html_tre:
+            dir = os.path.join(base, 'data', f)
+            self.mkpath(dir)
+
+
+class build_cgis(build):
+    
+    build.sub_commands += [('build_cgis', lambda self:True)]
+  
+    def run(self):
+        base = os.path.join(self.build_base, cgis_dir)
+        self.mkpath(base)
+        scr = []
+        for m in cgis_scr: 
+            scr += glob.glob(m)
+        for s in scr:
+            if not os.path.isfile(s): 
+                continue
+            dst = os.path.join(base, os.path.basename(s))
+            copy_script(s, dst, self.executable)
+
+
+class build_bmps(build):
+        
+    build.sub_commands.append( ('build_bmps', lambda self:True) )
+    
+    def run(self):
+        if not os.path.exists( os.path.join( niaid_dir, 'Workflow' ) ):
+            return
+        workflow_src_dir = os.path.join( niaid_dir, 'Workflow', 'target', 'mobyleWorkflow' )
+        workflow_dest_dir = os.path.join(self.build_base, html_dir , "workflow")
+        #by default copy_tree preserve permissions
+        copy_tree( workflow_src_dir, workflow_dest_dir )
+        
+    
+class build_bmid(build):
+    
+    build.sub_commands.append( ('build_bmid', lambda self:True) )
+    
+    def run(self):
+        if not os.path.exists( os.path.join( niaid_dir, 'InterfaceDesigner' ) ):
+            return
+        bmid_src_dir = os.path.join( niaid_dir, 'InterfaceDesigner', 'target', 'BMID' )
+        bmid_dest_dir = os.path.join(self.build_base, html_dir , "BMID")
+        #by default copy_tree preserve permissions
+        copy_tree( bmid_src_dir, bmid_dest_dir )
+    
+    
+#######################
+#                     #
+# Mobyle Installation #  
+#                     # 
+#######################
+    
+class install_core(install):
+    
+    install.sub_commands += [('install_core', lambda self:True)]
+    setattr(install, 'install_core', None)
+    install.user_options.append(
+                                 ('install-core=', None, 'installation directory for Core files'))
+  
+    def run(self):
+        base = os.path.join(self.build_base, core_dir)
+        inst = self.distribution.command_options.get('install')
+        if not inst.has_key('install_core'):
+            sys.exit('Missing mandatory Core installation directory')
+        if not inst.has_key('install_htdocs'):
+            sys.exit('Missing mandatory htdocs installation directory')
+        self.install_dir = inst['install_core'][1]
+        copy_tree(base, self.install_dir)
+        for f in find_file(base, '*.new'):
+            dst = f.replace(base, self.install_dir, 1)[:-4]
+            if os.path.lexists(dst): 
+                continue
+            move_file(dst + '.new', dst)
+        fix = []
+        for m in core_fix: 
+            fix += glob.glob(m)
+        for f in fix:
+          f = os.path.join(self.install_dir, f)
+          n = f + '.tmp'
+          fix_script(f, n, self.install_dir, inst['install_htdocs'][1])
+          os.unlink(f)
+          move_file(n, f)
+
+
+class install_html(install):
+    
+    install.sub_commands += [('install_html', lambda self:True)]
+    setattr(install, 'install_htdocs', None)
+    install.user_options.append(
+    ('install-htdocs=', None, 'installation directory for HTML files'))
+  
+    def run(self):
+        inst = self.distribution.command_options.get('install')
+        if not inst.has_key('install_htdocs'):
+            sys.exit('Missing mandatory htdocs installation directory')
+        for target_dir in ('portal' , 'data'):
+            build_dir = os.path.join(self.build_base, html_dir, target_dir )
+            inst_dir = os.path.join(inst['install_htdocs'][1], target_dir)
+            copy_tree(build_dir, inst_dir)
+            for f in find_file(build_dir, '*.new'):
+                dst = f.replace(build_dir, inst_dir, 1)[:-4]
+                if os.path.lexists(dst): 
+                    continue
+                move_file(dst + '.new', dst)
+
+
+class install_cgis(install):
+    
+    install.sub_commands += [('install_cgis', lambda self:True)]
+    setattr(install, 'install_cgis', None)
+    install.user_options.append(('install-cgis=', None, 'installation directory for CGIs files')) 
+          
+    def run(self):
+        cgi_build_dir = os.path.join(self.build_base, cgis_dir)
+        inst = self.distribution.command_options.get('install')
+        if not inst.has_key('install_cgis'):
+            sys.exit('Missing mandatory CGIs installation directory')
+        if not inst.has_key('install_core'):
+            sys.exit('Missing mandatory Core installation directory')
+        cgi_inst_dir = inst['install_cgis'][1]
+        copy_tree(cgi_build_dir, cgi_inst_dir)
+        fix = []
+        for m in cgis_fix: 
+            fix += glob.glob(m)
+        for f in fix:
+          f = os.path.join(cgi_inst_dir, os.path.basename(f))
+          n = f + '.tmp'
+          fix_script(f, n, inst['install_core'][1], None)
+          os.unlink(f)
+          move_file(n, f)
+
+
+class install_bmps(install):
+        
+    install.sub_commands.append( ('install_bmps', lambda self: self.install_bmps ) )
+    setattr(install, 'install_bmps', False)
+    install.user_options.append(('install-bmps', None, 'installation of BCBB Mobyle Pipeline System'))
+    
+    def initialize_options(self):
+        install.initialize_options(self)
+        self.install_bmps = False
+        
+    def run(self):
+        inst = self.distribution.command_options.get('install')
+        if not inst.has_key('install_htdocs'):
+            sys.exit('Missing mandatory htdocs installation directory')
+        print "[DEBUG] ",os.path.join( self.build_base, html_dir, 'workflow' )
+        if not os.path.exists( os.path.join( self.build_base, html_dir, 'workflow' ) ):
+            sys.exit('BMPS is not part of this tarball. Get a tarball with BMPS from Mobyle website (https://projets.pasteur.fr/projects/mobyle/wiki/download)')
+        bmps_build_dir = os.path.join(self.build_base, html_dir , 'workflow')
+        bmps_inst_dir = os.path.join( inst['install_htdocs'][1] , 'workflow')
+        copy_tree(bmps_build_dir, bmps_inst_dir)
+            
+    
+class install_bmid(install):
+    
+    install.sub_commands.append( ('install_bmid', lambda self: self.install_bmid ) )
+    setattr(install, 'install_bmid', False)
+    install.user_options.append(('install-bmid', None, 'installation of BCBB Mobyle Interface Designer'))
+    
+    def initialize_options(self):
+        install.initialize_options(self)
+        self.install_bmid = False
+        
+    def run(self):
+        inst = self.distribution.command_options.get('install')
+        if not inst.has_key('install_htdocs'):
+            sys.exit('Missing mandatory htdocs installation directory')
+        if not os.path.exists( os.path.join( self.build_base, html_dir, 'BMID' ) ):
+            sys.exit('BMID is not part of this tarball. Get a tarball with BMID from Mobyle website (https://projets.pasteur.fr/projects/mobyle/wiki/download)')
+        bmid_build_dir = os.path.join(self.build_base, html_dir , 'BMID')
+        bmid_inst_dir = os.path.join( inst['install_htdocs'][1] , 'BMID')
+        copy_tree(bmid_build_dir, bmid_inst_dir)
+
+###################################
+#                                 #
+# creation of source distribution #
+#                                 #
+###################################
+
+class sdist_mobyle(sdist):
+    
+    user_options = [('with-bmps', None, 'build the gwt libraries for BMPS, as part of mobyle distribution [DEFAULT=False]'),
+                    ('with-bmid', None, 'build the gwt libraries for BMID, as part of mobyle distribution [DEFAULT=False]'),
+                    ('niaid-home-dir=', None, 'the home directory of niaid BMID and BMPS src project [DEFAULT ./niaid]' ),
+                    ('maven-bin-name=', None, 'the maven binary name to use [DEFAULT mvn]')] + sdist.user_options
+   
+    def initialize_options(self):
+        sdist.initialize_options(self)
+        self.with_bmps = False
+        self.with_bmid = False
+        self.niaid_home_dir = os.path.realpath('niaid')
+        self.maven_bin_name = 'mvn'
+        
+           
+    def run(self):
+        self.niaid_link_name = 'niaid'
+        self.must_clean = False
+        if self.with_bmid or self.with_bmps:
+            self.set_up()
+        if self.with_bmid:
+            self.build_bmid()   
+            self.distribution.metadata.name = self.distribution.metadata.name + "+BMID" 
+        if self.with_bmps:
+            self.build_bmps()
+            self.distribution.metadata.name = self.distribution.metadata.name + "+BMPS"
+        self.mktemplate(dist_lst, excl_lst)
+        sdist.run(self)
+        if self.with_bmid or self.with_bmps:
+            self.clean_up()
+        
+    def mktemplate(self, incl, excl):
+        fd = open('MANIFEST.in', 'w')
+        print >> fd, '# WARNING: This is a generated file, *DO NOT* edit.'
+        for nam in incl:
+            print >> fd, 'include %s' % nam
+        for nam in excl:
+            print >> fd, 'exclude %s' % nam
+        if self.with_bmid:
+            print >> fd, 'recursive-include %s *' % os.path.join( self.niaid_link_name, 'InterfaceDesigner', 'target', 'BMID')
+        if self.with_bmps:
+            print >> fd, 'recursive-include %s *' % os.path.join( self.niaid_link_name, 'Workflow', 'target', 'mobyleWorkflow')
+        fd.close()
+
+    def set_up(self):
+        if os.path.exists( self.niaid_link_name ):
+            if os.path.realpath(self.niaid_link_name) == os.path.realpath(self.niaid_home_dir):
+                self.must_clean = False
+                return
+            else:
+                raise RuntimeError( "there already exist a 'niaid' path in %s and is different than %s"%( os.getcwd(), self.niaid_home_dir) )
+        os.symlink( self.niaid_home_dir , self.niaid_link_name )
+        self.must_clean = True
+        
+    def clean_up(self):
+        if self.must_clean:  
+            os.unlink( self.niaid_link_name )
+    
+    def build_niaid(self, command):
+        from subprocess import Popen, PIPE
+        import select 
+        process_ = Popen( 
+                      command,
+                      shell = True ,
+                      stdout= sys.stdout,
+                      stdin = None ,
+                      stderr = sys.stderr,
+                      cwd = self.niaid_home_dir
+                      )
+        process_.wait()
+        if process_.returncode != 0:
+            raise RuntimeError
+    
+    def build_bmps(self):
+        print "\n[INFO] Building BMPS project"
+        command = '%s -pl CommonLib,Workflow package' %self.maven_bin_name 
+        try:
+            self.build_niaid( command )
+        except RuntimeError,err:
+            print >> sys.stderr , "[ERROR] BMPS project BUILD FAILURE" 
+            sys.exit(0)
+            
+    def build_bmid(self):
+        print "\n[INFO] Building BMID project"
+        command = '%s -pl CommonLib,InterfaceDesigner package' %self.maven_bin_name 
+        try:
+            self.build_niaid( command )
+        except Exception, err:
+            print >> sys.stderr , "[ERROR] BMID project BUILD FAILURE"
+        
+
+
+    
+###################
+#                 #
+# clean all stuff #
+#                 #
+###################
+
+class clean_mobyle(clean):
+    
+    def run(self):
+        for d in [ core_dir, cgis_dir, html_dir ]:
+            base = os.path.join(self.build_base, d)
+            if not os.path.exists(base): 
+                continue
+            remove_tree(base)
+        clean.run(self)
+
+
+class install_mobyle_egg_info(install_egg_info):
+    
+    def run(self):
+        pass
+
+#############################
+# Overwrite default methods #
+#############################
+
+cmdclass = {'build': build_mobyle, 
+            'build_core': build_core, 
+            'build_html': build_html,
+            'build_cgis': build_cgis,
+            'build_bmid': build_bmid,
+            'build_bmps': build_bmps,
+            'install_egg_info': install_mobyle_egg_info,
+            'install_core': install_core,
+            'install_cgis': install_cgis,
+            'install_html': install_html, 
+            'install_bmid': install_bmid,
+            'install_bmps': install_bmps,
+            'sdist': sdist_mobyle,
+            'clean': clean_mobyle, 
+            }
+
+setup( name = package_nam, 
+       version = package_ver, 
+       url = package_url,
+       author = package_aut, 
+       author_email = package_mel,
+       platforms = package_platforms ,
+       license = package_license,
+       cmdclass = cmdclass 
+       )

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/mobyle.git



More information about the debian-med-commit mailing list