diff --git a/composer.json b/composer.json
index e9c6c263276afa188f65dd80e1b34c2d0e81897d..4d3227c08d34f1bff09e2d8f89054347ebe831c2 100755
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,8 @@
         "unicaen/unicaen-app":                  "dev-trunk",
         "unicaen/unicaen-auth":                 "dev-trunk",
         "unicaen/unicaen-ldap":                 "dev-trunk",
-        "zendframework/zend-code":              ">=2.3"
+        "zendframework/zend-code":              ">=2.3",
+        "bjyoungblood/bjy-authorize":           "dev-master"
     },
     "require-dev": {
         "zendframework/zend-test":              ">=2.3",
diff --git a/composer.lock b/composer.lock
index f5a53c8f4ad892451de9d690f0c3a885fe96f259..f6c0f671723cc046e7bd0e16a3d6e1b7f85e6a59 100644
--- a/composer.lock
+++ b/composer.lock
@@ -3,31 +3,31 @@
         "This file locks the dependencies of your project to a known state",
         "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
     ],
-    "hash": "da3eba423b417df609189e5af2dfb2ba",
+    "hash": "f2d0e4ac21618082b1fd832714225418",
     "packages": [
         {
             "name": "bjyoungblood/bjy-authorize",
-            "version": "1.4.0",
+            "version": "dev-master",
             "source": {
                 "type": "git",
                 "url": "https://github.com/bjyoungblood/BjyAuthorize.git",
-                "reference": "02723bd19f0bba33649d708022f8d0e0b795f57a"
+                "reference": "bf31161bf598532cf98dbb0a3a7f7b8efb29bb21"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/bjyoungblood/BjyAuthorize/zipball/02723bd19f0bba33649d708022f8d0e0b795f57a",
-                "reference": "02723bd19f0bba33649d708022f8d0e0b795f57a",
+                "url": "https://api.github.com/repos/bjyoungblood/BjyAuthorize/zipball/bf31161bf598532cf98dbb0a3a7f7b8efb29bb21",
+                "reference": "bf31161bf598532cf98dbb0a3a7f7b8efb29bb21",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3",
-                "zendframework/zend-cache": "~2.1",
-                "zendframework/zend-eventmanager": "~2.1",
-                "zendframework/zend-http": "~2.1",
-                "zendframework/zend-mvc": "~2.1",
-                "zendframework/zend-permissions-acl": "~2.1",
-                "zendframework/zend-servicemanager": "~2.1",
-                "zendframework/zend-view": "~2.1"
+                "zendframework/zend-cache": "~2.2",
+                "zendframework/zend-eventmanager": "~2.2",
+                "zendframework/zend-http": "~2.2",
+                "zendframework/zend-mvc": "~2.2",
+                "zendframework/zend-permissions-acl": "~2.2",
+                "zendframework/zend-servicemanager": "~2.2",
+                "zendframework/zend-view": "~2.2"
             },
             "require-dev": {
                 "doctrine/common": ">=2.3,<2.5-dev",
@@ -40,7 +40,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.4-dev"
+                    "dev-master": "1.5-dev"
                 }
             },
             "autoload": {
@@ -73,7 +73,7 @@
                 "zf2",
                 "zfc-user"
             ],
-            "time": "2013-07-05 11:59:46"
+            "time": "2014-04-03 17:12:07"
         },
         {
             "name": "doctrine/annotations",
@@ -964,17 +964,17 @@
         },
         {
             "name": "symfony/console",
-            "version": "v2.4.3",
+            "version": "v2.4.4",
             "target-dir": "Symfony/Component/Console",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Console.git",
-                "reference": "ef20f1f58d7f693ee888353962bd2db336e3bbcb"
+                "reference": "2e452005b1e1d003d23702d227e23614679eb5ca"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Console/zipball/ef20f1f58d7f693ee888353962bd2db336e3bbcb",
-                "reference": "ef20f1f58d7f693ee888353962bd2db336e3bbcb",
+                "url": "https://api.github.com/repos/symfony/Console/zipball/2e452005b1e1d003d23702d227e23614679eb5ca",
+                "reference": "2e452005b1e1d003d23702d227e23614679eb5ca",
                 "shasum": ""
             },
             "require": {
@@ -1015,21 +1015,21 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "http://symfony.com",
-            "time": "2014-03-01 17:35:04"
+            "time": "2014-04-27 13:34:57"
         },
         {
             "name": "symfony/process",
-            "version": "v2.4.3",
+            "version": "v2.4.4",
             "target-dir": "Symfony/Component/Process",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Process.git",
-                "reference": "c09c3b08455c35688eee3e481fdfc85518ef01d7"
+                "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Process/zipball/c09c3b08455c35688eee3e481fdfc85518ef01d7",
-                "reference": "c09c3b08455c35688eee3e481fdfc85518ef01d7",
+                "url": "https://api.github.com/repos/symfony/Process/zipball/8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7",
+                "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7",
                 "shasum": ""
             },
             "require": {
@@ -1064,7 +1064,7 @@
             ],
             "description": "Symfony Process Component",
             "homepage": "http://symfony.com",
-            "time": "2014-03-26 11:35:33"
+            "time": "2014-04-27 13:34:57"
         },
         {
             "name": "unicaen/unicaen-app",
@@ -1072,7 +1072,7 @@
             "source": {
                 "type": "svn",
                 "url": "https://svn.unicaen.fr/svn/UnicaenApp",
-                "reference": "/trunk/@474"
+                "reference": "/trunk/@481"
             },
             "require": {
                 "doctrine/doctrine-orm-module": ">=0.7",
@@ -1121,7 +1121,7 @@
             "source": {
                 "type": "svn",
                 "url": "https://svn.unicaen.fr/svn/UnicaenAuth",
-                "reference": "/trunk/@188"
+                "reference": "/trunk/@201"
             },
             "require": {
                 "bjyoungblood/bjy-authorize": ">=1.4",
@@ -3371,20 +3371,22 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "3.7.35",
+            "version": "3.7.37",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "83f9537e46346f75ed6925441ac259453213c7cc"
+                "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83f9537e46346f75ed6925441ac259453213c7cc",
-                "reference": "83f9537e46346f75ed6925441ac259453213c7cc",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ae6cefd7cc84586a5ef27e04bae11ee940ec63dc",
+                "reference": "ae6cefd7cc84586a5ef27e04bae11ee940ec63dc",
                 "shasum": ""
             },
             "require": {
+                "ext-ctype": "*",
                 "ext-dom": "*",
+                "ext-json": "*",
                 "ext-pcre": "*",
                 "ext-reflection": "*",
                 "ext-spl": "*",
@@ -3400,9 +3402,6 @@
                 "pear-pear.php.net/pear": "1.9.4"
             },
             "suggest": {
-                "ext-json": "*",
-                "ext-simplexml": "*",
-                "ext-tokenizer": "*",
                 "phpunit/php-invoker": "~1.1"
             },
             "bin": [
@@ -3441,7 +3440,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2014-04-21 06:25:54"
+            "time": "2014-04-30 12:24:19"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
@@ -3494,17 +3493,17 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v2.4.3",
+            "version": "v2.4.4",
             "target-dir": "Symfony/Component/Yaml",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Yaml.git",
-                "reference": "77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd"
+                "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Yaml/zipball/77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd",
-                "reference": "77a41c2835ab7cfe8bf6d15e25d3af8f3eb3bacd",
+                "url": "https://api.github.com/repos/symfony/Yaml/zipball/65539ecde838f9c0d18b006b2101e3deb4b5c9ff",
+                "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff",
                 "shasum": ""
             },
             "require": {
@@ -3539,7 +3538,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "http://symfony.com",
-            "time": "2014-03-12 18:29:58"
+            "time": "2014-04-18 20:37:09"
         },
         {
             "name": "zendframework/zend-dom",
@@ -3646,7 +3645,8 @@
     "stability-flags": {
         "unicaen/unicaen-app": 20,
         "unicaen/unicaen-auth": 20,
-        "unicaen/unicaen-ldap": 20
+        "unicaen/unicaen-ldap": 20,
+        "bjyoungblood/bjy-authorize": 20
     },
     "platform": [
 
diff --git a/module/Application/Module.php b/module/Application/Module.php
index 1a23103b5e9d89bca62af39eb9fa130a64b31d5f..23abbbb13997186fee93c1acb150c962faab4711 100755
--- a/module/Application/Module.php
+++ b/module/Application/Module.php
@@ -99,3 +99,9 @@ class Module implements ControllerPluginProviderInterface, ViewHelperProviderInt
         );
     }
 }
+
+
+class Listener extends \BjyAuthorize\Guard\Controller
+{
+    
+}
\ No newline at end of file
diff --git a/module/Application/config/demo.config.php b/module/Application/config/demo.config.php
index 75fb1cd428696743304a1bab0004ae87a26d42d7..492bc74ed78efe988c0279e54caea569d7d287a8 100644
--- a/module/Application/config/demo.config.php
+++ b/module/Application/config/demo.config.php
@@ -24,42 +24,42 @@ return array(
         'default' => array(
             'home' => array(
                 'pages' => array(
-                    'demo' => array(
-                        'label'    => 'Démo',
-                        'route'    => 'demo',
-                        'params' => array(
-                            'action' => 'index',
-                        ),
-                        'pages' => array(
-//                            'of' => array(
-//                                'label'  => "Offre de formation",
+//                    'demo' => array(
+//                        'label'    => 'Démo',
+//                        'route'    => 'demo',
+//                        'params' => array(
+//                            'action' => 'index',
+//                        ),
+//                        'pages' => array(
+////                            'of' => array(
+////                                'label'  => "Offre de formation",
+////                                'route'  => 'demo',
+////                                'params' => array(
+////                                    'action' => 'of',
+////                                ),
+////                                'visible' => true,
+////                                'pages' => array(),
+////                            ),
+//                            'intervenant' => array(
+//                                'label'  => "Intervenants",
 //                                'route'  => 'demo',
 //                                'params' => array(
-//                                    'action' => 'of',
+//                                    'action' => 'intervenant',
 //                                ),
 //                                'visible' => true,
 //                                'pages' => array(),
 //                            ),
-                            'intervenant' => array(
-                                'label'  => "Intervenants",
-                                'route'  => 'demo',
-                                'params' => array(
-                                    'action' => 'intervenant',
-                                ),
-                                'visible' => true,
-                                'pages' => array(),
-                            ),
-                            'service-ref' => array(
-                                'label'  => "Service référentiel",
-                                'route'  => 'demo',
-                                'params' => array(
-                                    'action' => 'saisir-service-referentiel-intervenant',
-                                ),
-                                'visible' => true,
-                                'pages' => array(),
-                            ),
-                        ),
-                    ),
+//                            'service-ref' => array(
+//                                'label'  => "Service référentiel",
+//                                'route'  => 'demo',
+//                                'params' => array(
+//                                    'action' => 'saisir-service-referentiel-intervenant',
+//                                ),
+//                                'visible' => true,
+//                                'pages' => array(),
+//                            ),
+//                        ),
+//                    ),
                 ),
             ),
         ),
diff --git a/module/Application/config/intervenant.config.php b/module/Application/config/intervenant.config.php
index c684a189a0529787a1f5e476fd3409fad003e2bd..b762c8c3eab495043b264fa98bc41b1bba0b6a65 100644
--- a/module/Application/config/intervenant.config.php
+++ b/module/Application/config/intervenant.config.php
@@ -50,40 +50,40 @@ return array(
         'default' => array(
             'home' => array(
                 'pages' => array(
-                    'intervenant' => array(
-                        'label'    => 'Intervenant',
-                        'title'    => "Gestion des intervenants",
-                        'route'    => 'intervenant',
-                        'resource' => 'controller/Application\Controller\Intervenant:index',
-                        'pages' => array(
-//                            'rechercher' => array(
-//                                'label'  => "Rechercher",
-//                                'title'  => "Rechercher un intervenant",
+//                    'intervenant' => array(
+//                        'label'    => 'Intervenant',
+//                        'title'    => "Gestion des intervenants",
+//                        'route'    => 'intervenant',
+//                        'resource' => 'controller/Application\Controller\Intervenant:index',
+//                        'pages' => array(
+////                            'rechercher' => array(
+////                                'label'  => "Rechercher",
+////                                'title'  => "Rechercher un intervenant",
+////                                'route'  => 'intervenant/default',
+////                                'params' => array(
+////                                    'action' => 'rechercher',
+////                                ),
+////                                'visible' => true,
+////                                'pages' => array(),
+////                            ),
+//                            'voir' => array(
+//                                'label'  => "Voir",
+//                                'title'  => "Voir l'intervenant {id}",
 //                                'route'  => 'intervenant/default',
-//                                'params' => array(
-//                                    'action' => 'rechercher',
-//                                ),
-//                                'visible' => true,
-//                                'pages' => array(),
-//                            ),
-                            'voir' => array(
-                                'label'  => "Voir",
-                                'title'  => "Voir l'intervenant {id}",
-                                'route'  => 'intervenant/default',
-                                'visible' => false,
-                                'withtarget' => true,
-                                'pages' => array(),
-                            ),
-//                            'modifier' => array(
-//                                'label'  => "Modifier",
-//                                'title'  => "Modifier l'intervenant {id}",
-//                                'route'  => 'intervenant/modifier',
 //                                'visible' => false,
 //                                'withtarget' => true,
 //                                'pages' => array(),
 //                            ),
-                        ),
-                    ),
+////                            'modifier' => array(
+////                                'label'  => "Modifier",
+////                                'title'  => "Modifier l'intervenant {id}",
+////                                'route'  => 'intervenant/modifier',
+////                                'visible' => false,
+////                                'withtarget' => true,
+////                                'pages' => array(),
+////                            ),
+//                        ),
+//                    ),
                 ),
             ),
         ),
@@ -106,9 +106,9 @@ return array(
     'service_manager' => array(
         'invokables' => array(
             'ApplicationOffreFormation' => 'Application\\Service\\OffreFormation',
+            'ApplicationIntervenant'    => 'Application\\Service\\Intervenant',
         ),
         'factories' => array(
-            'ApplicationIntervenant' => 'Application\\Service\\IntervenantFactory',
         ),
     ),
 );
\ No newline at end of file
diff --git a/module/Application/config/module.config.php b/module/Application/config/module.config.php
index 02255fa3afccb4f14df285cb0f447d56f91ce94a..48dd9b4e54c82a0685e21eadd836c3c067e0d39a 100755
--- a/module/Application/config/module.config.php
+++ b/module/Application/config/module.config.php
@@ -149,6 +149,9 @@ $main =  array(
             'ApplicationRoleProvider'     => 'Application\Provider\Role\RoleProviderFactory',
             'ApplicationIdentityProvider' => 'Application\Provider\Identity\IdentityProviderFactory',
         ),
+        'initializers' => array(
+            'Application\Service\ContextProviderAwareInitializer',
+        ),
     ),
     'view_helpers' => array(
         'invokables' => array(
diff --git a/module/Application/config/offre-formation.config.php b/module/Application/config/offre-formation.config.php
index 80dd6844d7c849fb0c995e45413e69cc1f73a0fd..b21f24712861196ea5fbf0212740866fe0c0548a 100644
--- a/module/Application/config/offre-formation.config.php
+++ b/module/Application/config/offre-formation.config.php
@@ -30,35 +30,131 @@ return array(
                             ),
                         ),
                     ),
+                    'element' => array(
+                        'type'    => 'Literal',
+                        'options' => array(
+                            'route'    => '/element',
+                            'defaults' => array(
+                                '__NAMESPACE__' => 'Application\Controller\OffreFormation',
+                                'controller'    => 'ElementPedagogique',
+                            ),
+                        ),
+                        'may_terminate' => false,
+                        'child_routes' => array(
+                            'default' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/:action[/:id]',
+                                    'constraints' => array(
+                                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
+                                        'id'     => '[0-9]*',
+                                    ),
+                                ),
+                            ),
+                            'voir' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/voir/:id',
+                                    'constraints' => array( 'id' => '[0-9]*' ),
+                                    'defaults' => array( 'action' => 'voir' ),
+                                ),
+                            ),
+                            'apercevoir' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/apercevoir/:id',
+                                    'constraints' => array( 'id' => '[0-9]*' ),
+                                    'defaults' => array( 'action' => 'apercevoir' ),
+                                ),
+                            ),
+                            'ajouter' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/ajouter/:etape',
+                                    'constraints' => array( 'etape' => '[0-9]*' ),
+                                    'defaults' => array( 'action' => 'ajouter' ),
+                                ),
+                            ),
+                            'modifier' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/modifier/:etape/:id',
+                                    'constraints' => array(
+                                        'etape' => '[0-9]*',
+                                        'id'    => '[0-9]*',
+                                    ),
+                                    'defaults' => array( 'action' => 'modifier' ),
+                                ),
+                            ),
+                            'supprimer' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/supprimer/:id',
+                                    'constraints' => array( 'id' => '[0-9]*' ),
+                                    'defaults' => array( 'action' => 'supprimer' ),
+                                ),
+                            ),
+                        ),
+                    ),
                     'etape' => array(
                         'type'    => 'Literal',
                         'options' => array(
                             'route'    => '/etape',
+                            'defaults' => array(
+                                '__NAMESPACE__' => 'Application\Controller\OffreFormation',
+                                'controller'    => 'Etape',
+                            ),
                         ),
                         'may_terminate' => false,
                         'child_routes' => array(
+                            'default' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/:action[/:id]',
+                                    'constraints' => array(
+                                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
+                                        'id'     => '[0-9]*',
+                                    ),
+                                ),
+                            ),
+                            'voir' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/voir/:id',
+                                    'constraints' => array( 'id' => '[0-9]*' ),
+                                    'defaults' => array( 'action' => 'voir' ),
+                                ),
+                            ),
                             'apercevoir' => array(
                                 'type'    => 'Segment',
                                 'options' => array(
-                                    'route'    => '/apercevoir[/:id]',
+                                    'route'    => '/apercevoir/:id',
                                     'constraints' => array( 'id' => '[0-9]*' ),
-                                    'defaults' => array( 'action' => 'etapeApercevoir' ),
+                                    'defaults' => array( 'action' => 'apercevoir' ),
                                 ),
                             ),
-                            'saisie' => array(
+                            'ajouter' => array(
                                 'type'    => 'Segment',
                                 'options' => array(
-                                    'route'    => '/saisie[/:id]',
+                                    'route'    => '/ajouter/:structure',
+                                    'constraints' => array( 'id' => '[0-9]*', ),
+                                    'defaults' => array( 'action' => 'ajouter' ),
+                                ),
+                            ),
+                            'modifier' => array(
+                                'type'    => 'Segment',
+                                'options' => array(
+                                    'route'    => '/modifier/:structure/:id',
                                     'constraints' => array( 'id' => '[0-9]*' ),
-                                    'defaults' => array( 'action' => 'etapeSaisie' ),
+                                    'defaults' => array( 'action' => 'modifier' ),
                                 ),
                             ),
-                            'suppression' => array(
+                            'supprimer' => array(
                                 'type'    => 'Segment',
                                 'options' => array(
-                                    'route'    => '/suppression[/:id]',
+                                    'route'    => '/supprimer/:id',
                                     'constraints' => array( 'id' => '[0-9]*' ),
-                                    'defaults' => array( 'action' => 'etapeSuppression' ),
+                                    'defaults' => array( 'action' => 'supprimer' ),
                                 ),
                             ),
                         ),
@@ -75,9 +171,66 @@ return array(
                         'label'    => 'Offre de formation',
                         'title'    => "Gestion de l'offre de formation",
                         'route'    => 'of',
-//                        'resource' => 'controller/Application\Controller\OffreFormation:index',
+                        'resource' => 'controller/Application\Controller\OffreFormation:index',
                         'pages' => array(
-
+                            'element-ajouter' => array(
+                                'label'    => "Créer un nouvel élément pédagogique",
+                                'title'    => "Créer un nouvel élément pédagogique pour l'étape sélectionnée",
+                                'route'    => 'of/element/ajouter',
+                                'resource' => 'controller/Application\Controller\OffreFormation\ElementPedagogique:ajouter',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-plus',
+                                'category' => 'element',
+                            ),
+                            'element-modifier' => array(
+                                'label'    => "Modifier cet élément",
+                                'title'    => "Modifier cet élément pédagogique",
+                                'route'    => 'of/element/modifier',
+                                'resource' => 'controller/Application\Controller\OffreFormation\ElementPedagogique:modifier',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-edit',
+                                'withtarget' => true,
+                                'category' => 'element',
+                            ),
+                            'element-supprimer' => array(
+                                'label'    => "Supprimer cette étape",
+                                'title'    => "Supprimer cette étape",
+                                'route'    => 'of/element/supprimer',
+                                'resource' => 'controller/Application\Controller\OffreFormation\ElementPedagogique:supprimer',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-remove',
+                                'withtarget' => true,
+                                'category' => 'element',
+                            ),
+                            'etape-ajouter' => array(
+                                'label'    => "Créer une nouvelle étape",
+                                'title'    => "Créer une nouvelle étape",
+                                'route'    => 'of/etape/ajouter',
+                                'resource' => 'controller/Application\Controller\OffreFormation\Etape:ajouter',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-plus',
+                                'category' => 'etape',
+                            ),
+                            'etape-modifier' => array(
+                                'label'    => "Modifier cette étape",
+                                'title'    => "Modifier cette étape",
+                                'route'    => 'of/etape/modifier',
+                                'resource' => 'controller/Application\Controller\OffreFormation\Etape:modifier',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-edit',
+                                'withtarget' => true,
+                                'category' => 'etape',
+                            ),
+                            'etape-supprimer' => array(
+                                'label'    => "Supprimer cette étape",
+                                'title'    => "Supprimer cette étape",
+                                'route'    => 'of/etape/supprimer',
+                                'resource' => 'controller/Application\Controller\OffreFormation\Etape:supprimer',
+                                'visible'  => false,
+                                'icon'     => 'glyphicon glyphicon-remove',
+                                'withtarget' => true,
+                                'category' => 'etape',
+                            ),
                         ),
                     ),
                 ),
@@ -89,22 +242,40 @@ return array(
             'BjyAuthorize\Guard\Controller' => array(
                 array(
                     'controller' => 'Application\Controller\OffreFormation',
-//                    'action' => array(),
-                    'roles' => array('user')),
+                    'action' => array('index', 'search-structures', 'search-niveaux', 'search-element'),
+                    'roles' => array('user')
+                ),
+                array(
+                    'controller' => 'Application\Controller\OffreFormation\Etape',
+                    'action' => array('voir',   'apercevoir',   'ajouter',   'modifier',   'supprimer', 'search'),
+                    'roles' => array('user')
+                ),
+                array(
+                    'controller' => 'Application\Controller\OffreFormation\ElementPedagogique',
+                    'action' => array('voir',   'apercevoir',   'ajouter',   'modifier',   'supprimer', 'search'),
+                    'roles' => array('user')
+                ),
             ),
         ),
     ),
     'controllers' => array(
         'invokables' => array(
-            'Application\Controller\OffreFormation' => 'Application\Controller\OffreFormationController',
+            'Application\Controller\OffreFormation'                    => 'Application\Controller\OffreFormationController',
+            'Application\Controller\OffreFormation\Etape'              => 'Application\Controller\OffreFormation\EtapeController',
+            'Application\Controller\OffreFormation\ElementPedagogique' => 'Application\Controller\OffreFormation\ElementPedagogiqueController',
+        ),
+        'initializers' => array(
+            'Application\Service\ContextProviderAwareInitializer',
         ),
     ),
     'service_manager' => array(
         'invokables' => array(
             'ApplicationElementPedagogique'           => 'Application\\Service\\ElementPedagogique',
+            'ApplicationCheminPedagogique'            => 'Application\\Service\\CheminPedagogique',
             'ApplicationEtape'                        => 'Application\\Service\\Etape',
             'ApplicationTypeFormation'                => 'Application\\Service\\TypeFormation',
-            'FormElementPedagogiqueRechercheHydrator' => 'Application\Form\OffreFormation\ElementPedagogiqueRechercheHydrator'
+            'FormElementPedagogiqueRechercheHydrator' => 'Application\Form\OffreFormation\ElementPedagogiqueRechercheHydrator',
+            'OffreFormationAssertion'                 => 'Application\\Service\\OffreFormationAssertion',
         ),
     ),
     'form_elements' => array(
@@ -112,7 +283,11 @@ return array(
             'FormElementPedagogiqueRechercheFieldset' => 'Application\Form\OffreFormation\ElementPedagogiqueRechercheFieldsetFactory',
         ),
         'invokables' => array(
-            'EtapeSaisie' => 'Application\Form\OffreFormation\EtapeSaisie',
+            'EtapeSaisie'              => 'Application\Form\OffreFormation\EtapeSaisie',
+            'ElementPedagogiqueSaisie' => 'Application\Form\OffreFormation\ElementPedagogiqueSaisie',
+        ),
+        'initializers' => array(
+            'Application\Service\ContextProviderAwareInitializer',
         ),
     ),
 
diff --git a/module/Application/config/service.config.php b/module/Application/config/service.config.php
index 89c8b52152cbc9056f83a6a78220ba199de6c24e..5229232b0235b7c0dfda655dbf0fbece2616194c 100644
--- a/module/Application/config/service.config.php
+++ b/module/Application/config/service.config.php
@@ -129,14 +129,14 @@ return array(
                             'action' => 'index',
                         ),
                         'pages' => array(
-                            'consultation' => array(
-                                'label'  => "Consultation",
-                                'title'  => "Consultation des services",
-                                'route'  => 'service',
-                                'visible' => true,
-                                'withtarget' => true,
-                                'pages' => array(),
-                            ),
+//                            'consultation' => array(
+//                                'label'  => "Consultation",
+//                                'title'  => "Consultation des services",
+//                                'route'  => 'service',
+//                                'visible' => true,
+//                                'withtarget' => true,
+//                                'pages' => array(),
+//                            ),
                         ),
                     ),
                 ),
@@ -164,13 +164,13 @@ return array(
     'service_manager' => array(
         'invokables' => array(
             'ApplicationService'            => 'Application\\Service\\Service',
+            'ApplicationServiceReferentiel' => 'Application\\Service\\ServiceReferentiel',
             'ApplicationServiceValidation'  => 'Application\\Service\\ServiceValidation',
             'ApplicationPeriode'            => 'Application\\Service\\Periode',
             'ApplicationMotifNonPaiement'   => 'Application\\Service\\MotifNonPaiement',
             'FormServiceRechercheHydrator'  => 'Application\Form\Service\RechercheHydrator',
         ),
         'factories' => array(
-            'ApplicationServiceReferentiel' => 'Application\\Service\\ServiceReferentielFactory',
         ),
     ),
     'form_elements' => array(
diff --git a/module/Application/config/volume-horaire.config.php b/module/Application/config/volume-horaire.config.php
index 5e508ed2e29f221dcac123e94ec027141b5162e4..48a3e340387033757186ef8c4bdae781cc5eb6f7 100644
--- a/module/Application/config/volume-horaire.config.php
+++ b/module/Application/config/volume-horaire.config.php
@@ -98,4 +98,9 @@ return array(
             'volumeHoraireListe'   => 'Application\View\Helper\VolumeHoraire\Liste'            
         ),
     ),
+    'form_elements' => array(
+        'invokables' => array(
+            'VolumeHoraireSaisie' => 'Application\Form\VolumeHoraire\Saisie',
+        ),
+    ),
 );
diff --git a/module/Application/src/Application/Acl/ComposanteRole.php b/module/Application/src/Application/Acl/ComposanteRole.php
new file mode 100644
index 0000000000000000000000000000000000000000..7066715ac83d1abdbcd4a3eb830ab074fe88d0a7
--- /dev/null
+++ b/module/Application/src/Application/Acl/ComposanteRole.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Application\Acl;
+
+/**
+ * Description of IntervenantRole
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class ComposanteRole extends DbRole
+{
+    /**
+     * Retourne la représentation littérale de cet objet.
+     * 
+     * @return string
+     */
+    public function __toString()
+    {
+        return sprintf("%s (%s)", "Composante", $this->getStructure());
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Acl/IntervenantExterieurRole.php b/module/Application/src/Application/Acl/IntervenantExterieurRole.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb3f31c262a207906d5865a32f97c3fe964d5388
--- /dev/null
+++ b/module/Application/src/Application/Acl/IntervenantExterieurRole.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Application\Acl;
+
+use UnicaenAuth\Acl\NamedRole;
+
+/**
+ * Description of IntervenantExterieurRole
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class IntervenantExterieurRole extends IntervenantRole
+{
+    const ROLE_ID = "intervenant-exterieur";
+    
+    /**
+     * Constructeur.
+     * 
+     * @param string|null               $id
+     * @param RoleInterface|string|null $parent
+     * @param string                    $name
+     * @param string                    $description
+     * @param bool                      $selectable
+     */
+    public function __construct($id = null, $parent = null, $name = null, $description = null, $selectable = true)
+    {
+        NamedRole::__construct(
+                $id = static::ROLE_ID, 
+                $parent = IntervenantRole::ROLE_ID, 
+                $name = "Intervenant (vacataire)", 
+                $description, 
+                $selectable);
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Acl/IntervenantPermanentRole.php b/module/Application/src/Application/Acl/IntervenantPermanentRole.php
new file mode 100644
index 0000000000000000000000000000000000000000..0eaf92e21fe31e1945e14cc6aebc83b717ddb148
--- /dev/null
+++ b/module/Application/src/Application/Acl/IntervenantPermanentRole.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Application\Acl;
+
+use UnicaenAuth\Acl\NamedRole;
+
+/**
+ * Description of IntervenantPermanentRole
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class IntervenantPermanentRole extends IntervenantRole
+{
+    const ROLE_ID = "intervenant-permanent";
+    
+    /**
+     * Constructeur.
+     * 
+     * @param string|null               $id
+     * @param RoleInterface|string|null $parent
+     * @param string                    $name
+     * @param string                    $description
+     * @param bool                      $selectable
+     */
+    public function __construct($id = null, $parent = null, $name = null, $description = null, $selectable = true)
+    {
+        NamedRole::__construct(
+                $id = static::ROLE_ID, 
+                $parent = IntervenantRole::ROLE_ID, 
+                $name = "Intervenant (permanent)", 
+                $description, 
+                $selectable);
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Acl/IntervenantRole.php b/module/Application/src/Application/Acl/IntervenantRole.php
index f172978655387f2194e8a36e270bdd20bf391cd0..138935207d46503ab249f1495c89fd3fe579d712 100644
--- a/module/Application/src/Application/Acl/IntervenantRole.php
+++ b/module/Application/src/Application/Acl/IntervenantRole.php
@@ -24,6 +24,11 @@ class IntervenantRole extends NamedRole
      */
     public function __construct($id = null, $parent = null, $name = null, $description = null, $selectable = true)
     {
-        parent::__construct($id = self::ROLE_ID, $parent = 'user', $name = "Intervenant", $description, $selectable);
+        NamedRole::__construct(
+                $id = static::ROLE_ID, 
+                $parent = 'user', 
+                $name = "Intervenant", 
+                $description, 
+                $selectable);
     }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Controller/IntervenantController.php b/module/Application/src/Application/Controller/IntervenantController.php
index 7f72e90d933473b8978a4df4bb6669b51938a68f..997fd5050f1423dd93e83dc1437df42891e86922 100644
--- a/module/Application/src/Application/Controller/IntervenantController.php
+++ b/module/Application/src/Application/Controller/IntervenantController.php
@@ -113,10 +113,11 @@ class IntervenantController extends AbstractActionController
 
         $import = $this->getServiceLocator()->get('ImportProcessusImport');
         $changements = $import->intervenantGetDifferentiel($intervenant);
+        $title = "Détails d'un intervenant";
         $short = $this->params()->fromQuery('short', false);
 
         $view = new \Zend\View\Model\ViewModel();
-        $view->setVariables(compact('intervenant', 'changements', 'short'));
+        $view->setVariables(compact('intervenant', 'changements', 'title', 'short'));
         $view->setTerminal($this->getRequest()->isXmlHttpRequest());
         
         return $view;
@@ -130,10 +131,11 @@ class IntervenantController extends AbstractActionController
 
         $import = $this->getServiceLocator()->get('ImportProcessusImport');
         $changements = $import->intervenantGetDifferentiel($intervenant);
+        $title = "Aperçu d'un intervenant";
         $short = $this->params()->fromQuery('short', false);
 
         $view = new \Zend\View\Model\ViewModel();
-        $view->setVariables(compact('intervenant', 'changements', 'short'));
+        $view->setVariables(compact('intervenant', 'changements', 'title', 'short'));
         $view->setTerminal($this->getRequest()->isXmlHttpRequest());
         
         return $view;
diff --git a/module/Application/src/Application/Controller/OffreFormation/ElementPedagogiqueController.php b/module/Application/src/Application/Controller/OffreFormation/ElementPedagogiqueController.php
new file mode 100644
index 0000000000000000000000000000000000000000..92a4ed8abee03a310aa22192eeba97069333a79e
--- /dev/null
+++ b/module/Application/src/Application/Controller/OffreFormation/ElementPedagogiqueController.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace Application\Controller\OffreFormation;
+
+use Zend\Mvc\Controller\AbstractActionController;
+use Application\Service\ElementPedagogique as ElementPedagogiqueService;
+use Application\Exception\DbException;
+use Application\Service\ContextProviderAwareInterface;
+use Application\Service\ContextProviderAwareTrait;
+
+/**
+ * Description of ElementPedagogiqueController
+ *
+ * @method \Doctrine\ORM\EntityManager            em()
+ * @method \Application\Controller\Plugin\Context context()
+ * 
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class ElementPedagogiqueController extends AbstractActionController implements ContextProviderAwareInterface
+{
+    use ContextProviderAwareTrait;
+
+    /**
+     * @return \Application\Service\ServiceReferentiel
+     */
+    public function getContextProvider()
+    {
+        return $this->getServiceLocator()->get('ApplicationContextProvider');
+    }
+
+    public function voirAction()
+    {
+        $element = $this->context()->mandatory()->elementPedagogiqueFromRoute('id');
+        $title   = "Détails d'un élément pédagogique";
+        $short   = $this->params()->fromQuery('short', false);
+        
+        $viewModel = new \Zend\View\Model\ViewModel();
+        $viewModel->setVariables(compact('element', 'title', 'short'));
+        
+        return $viewModel;
+    }
+
+    public function apercevoirAction()
+    {
+        $element = $this->context()->mandatory()->elementPedagogiqueFromRoute('id');
+        $title   = "Aperçu d'un élément pédagogique";
+        $short   = $this->params()->fromQuery('short', false);
+        
+        $viewModel = new \Zend\View\Model\ViewModel();
+        $viewModel->setVariables(compact('element', 'title', 'short'));
+
+        return $viewModel;
+    }
+
+    public function ajouterAction()
+    {
+        return $this->saisirAction();
+    }
+
+    public function modifierAction()
+    {
+        return $this->saisirAction();
+    }
+    
+    protected function saisirAction()
+    {
+        $etape   = $this->context()->mandatory()->etapeFromRoute(); /* @var $etape \Application\Entity\Db\Etape */
+        $id      = $this->params()->fromRoute('id');
+        $service = $this->getServiceElementPedagogique();
+        $title   = $id ? "Modification d'un élément pédagogique" : "Création d'un élément pédagogique";
+        $form    = $this->getFormAjouterModifier();
+        $errors  = array();
+
+        $service->canAdd(true);
+        
+        if ($id) {
+            $entity = $service->getRepo()->find($id);
+            $form->bind($entity);
+        }
+        else {
+            $entity = $service->newEntity(); /* @var $entity \Application\Entity\Db\ElementPedagogique */
+            $entity->setEtape($etape)
+                   ->setStructure($etape->getStructure());
+            $form->setObject($entity);
+        }
+        
+        $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
+        
+        $request = $this->getRequest();
+        if ($request->isPost()) {
+            $form->setData($request->getPost());
+            if ($form->isValid()) {
+                try {
+                    $service->save($entity);
+                    $form->get('id')->setValue($entity->getId()); // transmet le nouvel ID
+                }
+                catch (\Exception $e) {
+                    $e        = DbException::translate($e);
+                    $errors[] = $e->getMessage();
+                }
+            }
+        }
+
+        $viewModel = new \Zend\View\Model\ViewModel();
+        $viewModel->setTemplate('application/offre-formation/element-pedagogique/saisir')
+                ->setVariables(compact('form', 'title', 'errors'));
+        
+        return $viewModel;
+    }
+
+    public function supprimerAction()
+    {
+        $id = $this->params()->fromRoute('id', 0);
+        if (!($id = $this->params()->fromRoute('id'))){
+            throw new \Common\Exception\RuntimeException('L\'identifiant n\'est pas bon ou n\'a pas été fourni');
+        }
+        
+        $service   = $this->getServiceElementPedagogique();
+        $entity    = $service->getRepo()->find($id);
+        $title     = "Suppression d'élément pédagogique";
+        $form      = new \Application\Form\Supprimer('suppr');
+        $errors = array();
+        $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
+
+        $service->canAdd(true);
+        
+        if ($this->getRequest()->isPost()) {
+            try {
+                $service->delete($entity);
+            }catch(\Exception $e){
+                $e = DbException::translate($e);
+                $errors[] = $e->getMessage();
+            }
+        }
+        return compact('entity', 'title', 'form', 'errors');
+    }
+    
+    /**
+     * Action pour rechercher des éléments pédagogiques.
+     * 
+     * Les filtres pris en compte sont :
+     * - structure du contexte local,
+     * - niveau du contexte local,
+     * - étape du contexte local,
+     * Éventuellement écrasés par ceux-là :
+     * - paramètre GET 'structure' (id d'une structure),
+     * - paramètre GET 'niveau' (ex: 'L-2'),
+     * - paramètre GET 'etape' (id d'une étape),
+     * 
+     * NB: Les résultats sont renvoyés au format JSON.
+     * 
+     * @return \Zend\View\Model\JsonModel
+     */
+    public function searchAction()
+    {
+        if (!($term = $this->params()->fromQuery('term'))) {
+            exit;
+        }
+        
+        $localContext = $this->getContextProvider()->getLocalContext();
+        
+        $structure = $this->context()->structureFromQuery() ?: $localContext->getStructure();
+        $niveau    = $this->context()->niveauFromQuery()    ?: $localContext->getNiveau();
+        $etape     = $this->context()->etapeFromQuery()     ?: $localContext->getEtape();
+        
+        // respect des filtres éventuels spécifiés en GET ou sinon en session
+        $params = array();
+        $params['structure'] = $structure;
+        $params['niveau']    = $niveau;
+        $params['etape']     = $etape;
+        $params['term']      = $term;
+        $params['limit']     = $limit = 101;
+        
+        // fetch
+        $serviceEp = $this->getServiceLocator()->get('applicationElementPedagogique'); /* @var $serviceEp ElementPedagogiqueService */
+        $found     = $serviceEp->finderByTerm($params);
+
+        $result = array();
+        foreach ($found as $item) {
+            $extra = '';
+            $extra .= sprintf('<span class="element-rech niveau" title="%s">%s</span>', "Niveau", $item['LIBELLE_GTF'] . $item['NIVEAU']);
+            $extra .= sprintf('<span class="element-rech etape" title="%s">%s</span>', "Étape", $item['LIBELLE_ETAPE']);
+            $extra .= "Année" !== $item['LIBELLE_PE'] ? sprintf('<span class="element-rech periode" title="%s">%s</span>', "Période", $item['LIBELLE_PE']) : null;
+            $template = sprintf('<span class="element-rech extra">{extra}</span><span class="element-rech element" title="%s">{label}</span>', "Élément pédagogique");
+            $result[$item['ID']] = array(
+                'id'       => $item['ID'],
+                'label'    => $item['SOURCE_CODE'] . ' ' . $item['LIBELLE'],
+                'extra'    => $extra,
+                'template' => $template,
+            );
+        };
+
+        $result = \UnicaenApp\Form\Element\SearchAndSelect::truncatedResult($result, $limit - 1);
+        
+        return new \Zend\View\Model\JsonModel($result);
+    }
+
+    /**
+     * Retourne le formulaire d'ajout/modif d'ElementPedagogique.
+     * 
+     * @return \Application\Form\OffreFormation\ElementPedagogiqueSaisie
+     */
+    protected function getFormAjouterModifier()
+    {
+        return $this->getServiceLocator()->get('FormElementManager')->get('ElementPedagogiqueSaisie');
+    }
+
+    /**
+     * Retourne le service ElementPedagogique.
+     * 
+     * @return ElementPedagogiqueService
+     */
+    protected function getServiceElementPedagogique()
+    {
+        return $this->getServiceLocator()->get('applicationElementPedagogique');
+    }
+}
diff --git a/module/Application/src/Application/Controller/OffreFormation/EtapeController.php b/module/Application/src/Application/Controller/OffreFormation/EtapeController.php
new file mode 100644
index 0000000000000000000000000000000000000000..de03f54c97f3f6ec48663683acb32ba7bb87cbff
--- /dev/null
+++ b/module/Application/src/Application/Controller/OffreFormation/EtapeController.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace Application\Controller\OffreFormation;
+
+use Zend\Mvc\Controller\AbstractActionController;
+use Application\Service\ElementPedagogique as ElementPedagogiqueService;
+use Application\Service\Etape as EtapeService;
+use Application\Exception\DbException;
+use Application\Service\ContextProviderAwareInterface;
+use Application\Service\ContextProviderAwareTrait;
+
+/**
+ * Description of EtapeController
+ *
+ * @method \Doctrine\ORM\EntityManager            em()
+ * @method \Application\Controller\Plugin\Context context()
+ * 
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class EtapeController extends AbstractActionController implements ContextProviderAwareInterface
+{
+    use ContextProviderAwareTrait;
+
+    public function ajouterAction()
+    {
+        return $this->saisirAction();
+    }
+
+    public function modifierAction()
+    {
+        return $this->saisirAction();
+    }
+    
+    protected function saisirAction()
+    {
+        $structure = $this->context()->mandatory()->structureFromRoute();
+        $id        = $this->params()->fromRoute('id');
+        $service   = $this->getServiceEtape();
+        $title     = $id ? "Modification d'une étape" : "Création d'une nouvelle étape";
+        $form      = $this->getFormAjouterModifier();
+        $errors    = array();
+
+        $service->canAdd(true);
+        
+        if ($id) {
+            $entity = $service->getRepo()->find($id);
+            $form->bind($entity);
+        }
+        else {
+            $entity = $service->newEntity();
+            $entity->setStructure($structure);
+            $form->setObject($entity);
+        }
+        
+        $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
+        
+        $request = $this->getRequest();
+        if ($request->isPost()) {
+            $form->setData($request->getPost());
+            if ($form->isValid()) {
+                try {
+                    $service->save($entity);
+                    $form->get('id')->setValue($entity->getId()); // transmet le nouvel ID
+                }
+                catch (\Exception $e) {
+                    $e        = DbException::translate($e);
+                    $errors[] = $e->getMessage();
+                }
+            }
+        }
+
+        $viewModel = new \Zend\View\Model\ViewModel();
+        $viewModel->setTemplate('application/offre-formation/etape/saisie')
+                ->setVariables(compact('form', 'title', 'errors'));
+        
+        return $viewModel;
+    }
+
+    public function supprimerAction()
+    {
+        if (!($id = $this->params()->fromRoute('id'))) {
+            throw new \Common\Exception\RuntimeException('L\'identifiant n\'est pas bon ou n\'a pas été fourni');
+        }
+        $service   = $this->getServiceEtape();
+        $entity    = $service->getRepo()->find($id);
+        $title     = "Suppression d'étape";
+        $form      = new \Application\Form\Supprimer('suppr');
+        $errors = array();
+        $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
+
+        $service->canAdd(true);
+
+        if ($this->getRequest()->isPost()) {
+            try {
+                $service->delete($entity);
+            }catch(\Exception $e){
+                $e = DbException::translate($e);
+                $errors[] = $e->getMessage();
+            }
+        }
+        return compact('entity', 'title', 'form', 'errors');
+    }
+
+    public function apercevoirAction()
+    {
+        $etape       = $this->context()->mandatory()->etapeFromRoute('id');
+        $import      = $this->getServiceLocator()->get('ImportProcessusImport');
+        $changements = $import->etapeGetDifferentiel($etape);
+        $title       = "Aperçu d'une étape";
+        $short       = $this->params()->fromQuery('short', false);
+
+        return compact('etape','short','title','changements');
+    }
+
+    /**
+     * Retourne au format JSON les étapes distincts des éléments pédagogiques 
+     * pour la structure et le niveau éventuellement spécifiés en GET.
+     * 
+     * @return \Zend\View\Model\JsonModel
+     */
+    public function searchAction()
+    {
+        $structure = $this->context()->structureFromQuery();
+        $niveau    = $this->context()->niveauFromQuery();
+        
+        $params = array();
+        $params['structure'] = $structure instanceof \Application\Entity\Db\Structure ? $structure : null;
+        $params['niveau']    = $niveau;
+        
+        $result = $this->getServiceElementPedagogique()->finderDistinctEtapes($params)->getQuery()->getResult();
+        
+        return new \Zend\View\Model\JsonModel(\UnicaenApp\Util::collectionAsOptions($result));
+    }
+
+    /**
+     * Retourne le formulaire d'ajout/modif d'Etape.
+     * 
+     * @return \Application\Form\OffreFormation\EtapeSaisie
+     */
+    protected function getFormAjouterModifier()
+    {
+        return $this->getServiceLocator()->get('FormElementManager')->get('EtapeSaisie');
+    }
+
+    /**
+     * Retourne le service ElementPedagogique.
+     * 
+     * @return ElementPedagogiqueService
+     */
+    protected function getServiceElementPedagogique()
+    {
+        return $this->getServiceLocator()->get('applicationElementPedagogique');
+    }
+
+    /**
+     * Retourne le service Etape
+     *
+     * @return EtapeService
+     */
+    protected function getServiceEtape()
+    {
+        return $this->getServiceLocator()->get('applicationEtape');
+    }
+}
diff --git a/module/Application/src/Application/Controller/OffreFormationController.php b/module/Application/src/Application/Controller/OffreFormationController.php
index ac586e5c8d4a193f1c4a1dec45a0aeb9e74f7f38..76dffabd870067351f84b01493e8214446ffa61b 100644
--- a/module/Application/src/Application/Controller/OffreFormationController.php
+++ b/module/Application/src/Application/Controller/OffreFormationController.php
@@ -3,11 +3,9 @@
 namespace Application\Controller;
 
 use Zend\Mvc\Controller\AbstractActionController;
-use Common\Exception\LogicException;
-use Application\Entity\Db\Repository\ElementPedagogiqueRepository;
 use Application\Service\OffreFormation as OffreFormationService;
+use Application\Service\ElementPedagogique as ElementPedagogiqueService;
 use Application\Service\Etape as EtapeService;
-use Application\Exception\DbException;
 use Application\Service\ContextProviderAwareInterface;
 use Application\Service\ContextProviderAwareTrait;
 
@@ -28,60 +26,64 @@ class OffreFormationController extends AbstractActionController implements Conte
      */
     protected $sessionContainer;
 
-
-    /**
-     * @return \Application\Service\ServiceReferentiel
-     */
-    public function getContextProvider()
-    {
-        return $this->getServiceLocator()->get('ApplicationContextProvider');
-    }
-
     /**
      * 
      * @return \Zend\View\Model\ViewModel
      */
     public function indexAction()
     {
-        $em        = $this->em(); /* @var $em \Doctrine\ORM\EntityManager */
-        $serviceOf = $this->getServiceLocator()->get('applicationOffreFormation'); /* @var $serviceOf OffreFormationService */
-        $repoOf    = $serviceOf->getRepoElementPedagogique(); /* @var $serviceOf ElementPedagogiqueRepository */
-        $criteria  = array_filter($this->params()->fromQuery());
+        $em           = $this->em(); /* @var $em \Doctrine\ORM\EntityManager */
+        $serviceEp    = $this->getServiceLocator()->get('applicationElementPedagogique'); /* @var $serviceEp ElementPedagogiqueService */
+        $localContext = $this->getContextProvider()->getLocalContext();
+        $role         = $this->getContextProvider()->getSelectedIdentityRole();
+
         $em->getFilters()->enable('historique');
 //        $em->getFilters()->enable('validite');
         
-        // extraction des critères spécifiés dans la requête
+        // extraction des filtres spécifiés dans la requête
         $structure = $this->context()->structureFromQuery();
         $niveau    = $this->context()->niveauFromQuery();
         $etape     = $this->context()->etapeFromQuery();
         
-        // structures distinctes
-        $structuresDistinctes = $repoOf->finderDistinctStructures(array('niveau' => 2))->getQuery()->getResult();
+        // structure de responsabilité si aucun filtre spécifié
+        if (!$structure && $role instanceof \Application\Acl\DbRole) {
+            $structure = $role->getStructure();
+        }
         
-        // niveaux distincts pour la structure spécifiée
-        $niveauxDistincts = $repoOf->finderDistinctNiveaux(array('structure' => $structure))->getQuery()->getResult();
+        // persiste les filtres dans le contexte local
+        $localContext
+                ->setStructure($structure)
+                ->setNiveau($niveau)
+                ->setEtape($etape);
         
-        // etapes distinctes pour la structure spécifiée
-        $etapesDistinctes = $repoOf->finderDistinctEtapes(array('structure' => $structure, 'niveau' => $niveau))->getQuery()->getResult();
-        // filtre étape (facultatif)
-        $etape = isset($criteria['etape']) ? $criteria['etape'] : null;
-        if (null !== $etape && is_scalar($etape)) {
-            $etape = $this->em()->find('Application\Entity\Db\Etape', $etape);
-        }
+        // liste des structures distinctes
+        $structuresDistinctes = $serviceEp->finderDistinctStructures(array('niveau' => 2))->getQuery()->getResult();
+        // niveaux distincts pour la structure spécifiée
+        $niveauxDistincts = $structure ? 
+                $serviceEp->finderDistinctNiveaux(array('structure' => $structure))->getQuery()->getResult() :
+                array();
+        $niveauxDistincts = \Application\Entity\NiveauEtape::getInstancesFromEtapes($niveauxDistincts);
+        // liste des etapes distinctes pour la structure et le niveau spécifiés
+        $etapesDistinctes = $structure ? 
+                $serviceEp->finderDistinctEtapes(array('structure' => $structure, 'niveau' => $niveau))->getQuery()->getResult() :
+                array();
+        // liste des etapes orphelines (sans ÉP) pour la structure et le niveau spécifiés
+        $etapesOrphelines = $structure ? 
+                $serviceEp->finderDistinctEtapesOrphelines(array('structure' => $structure, 'niveau' => $niveau))->getQuery()->getResult() :
+                array();
         
         $ep = new \UnicaenApp\Form\Element\SearchAndSelect('element');
         $ep
-                ->setAutocompleteSource($this->url()->fromRoute('of/default', array('action' => 'search-element'), array('query' => $criteria)))
+                ->setAutocompleteSource($this->url()->fromRoute('of/element/default', array('action' => 'search')))
                 ->setLabel("Recherche :")
                 ->setAttributes(array('title' => "Saisissez 2 lettres au moins"));
         $form = new \Zend\Form\Form('search');
         $form->setAttributes(array('class' => 'element-rech'));
         $form->add($ep);
         
-        // élément
+        // élément pédagogique sélectionné dans le champ de recherche
         if (($element = $this->params()->fromPost('element')) && isset($element['id'])) {
             $form->get('element')->setValue($element);
-            $criteria['id'] = $element['id'];
         }
 
         // mise en session des filtres courants (utilisés dans la recherche d'élément pédagogique)
@@ -93,8 +95,8 @@ class OffreFormationController extends AbstractActionController implements Conte
         // fetch
         $entities = null;
         if ($structure) {
-            //$em->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
-            $qb = $serviceOf->getRepoElementPedagogique()->finder(array(
+//            $em->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
+            $qb = $serviceEp->finder(array(
                 'structure' => $structure, 
                 'niveau' => $niveau, 
                 'etape' => $etape));
@@ -103,100 +105,20 @@ class OffreFormationController extends AbstractActionController implements Conte
 
         $viewModel = new \Zend\View\Model\ViewModel();
         $viewModel->setVariables(array(
-            'entities'      => $entities,
-            'structures'    => $structuresDistinctes,
-            'niveaux'       => $niveauxDistincts,
-            'etapes'        => $etapesDistinctes,
-            'structure'     => $structure ? $structure->getId() : null,
-            'niveau'        => $niveau,
-            'etape'         => $etape ? $etape->getId() : null,
-            'form'          => $form,
-            'serviceEtape'  => $this->getServiceEtape() // pour déterminer les droits
+            'entities'         => $entities,
+            'structures'       => $structuresDistinctes,
+            'niveaux'          => $niveauxDistincts,
+            'etapes'           => $etapesDistinctes,
+            'etapesOrphelines' => $etapesOrphelines,
+            'structure'        => $structure,
+            'niveau'           => $niveau,
+            'etape'            => $etape,
+            'form'             => $form,
+            'serviceEtape'     => $this->getServiceEtape() // pour déterminer les droits
         ));
 
         return $viewModel;
     }
-
-    public function voirElementAction()
-    {
-        if (!($id = $this->params()->fromQuery('id'))) {
-            throw new LogicException("Aucun élément spécifié.");
-        }
-
-        $em      = $this->intervenant()->getEntityManager(); /* @var $em \Doctrine\ORM\EntityManager */
-        $repoEp  = $em->getRepository('Application\Entity\Db\ElementPedagogique'); /* @var $repoEp ElementPedagogiqueRepository */
-        $element = $repoEp->find($id);
-        $title   = "Détails de l'élément pédagogique";
-        $short   = $this->params()->fromQuery('short', false);
-        
-        $viewModel = new \Zend\View\Model\ViewModel();
-        $viewModel->setTemplate('application/offre-formation/voir-element')
-                ->setVariables(compact('element', 'title', 'short'));
-        
-        return $viewModel;
-    }
-
-    public function apercevoirElementAction()
-    {
-        if (!($id = $this->params()->fromQuery('id'))) {
-            throw new LogicException("Aucun élément spécifié.");
-        }
-
-        $em      = $this->intervenant()->getEntityManager(); /* @var $em \Doctrine\ORM\EntityManager */
-        $repoEp  = $em->getRepository('Application\Entity\Db\ElementPedagogique'); /* @var $repoEp ElementPedagogiqueRepository */
-        $element = $repoEp->find($id);
-        $short   = $this->params()->fromQuery('short', false);
-        
-        $viewModel = new \Zend\View\Model\ViewModel();
-        $viewModel->setTerminal($this->getRequest()->isXmlHttpRequest())
-                ->setVariables(compact('element', 'short'));
-
-        return $viewModel;
-    }
-
-    public function searchOfAction()
-    {
-        throw new \BadMethodCallException("Méthode obsolète, veuillez utiliser searchElementAction().");
-    }
-    
-    public function searchElementAction()
-    {
-        if (!($term = $this->params()->fromQuery('term'))) {
-            exit;
-        }
-        
-        // respect des filtres éventuels spécifiés en GET ou sinon en session
-        $params = array();
-        $params['structure'] = $this->context()->structureFromQuery();
-        $params['niveau']    = $this->context()->niveauFromQuery();
-        $params['etape']     = $this->context()->etapeFromQuery();
-        $params['term']      = $term;
-        $params['limit']     = $limit = 101;
-        
-        // fetch
-        $serviceOf = $this->getServiceLocator()->get('applicationOffreFormation'); /* @var $serviceOf OffreFormationService */
-        $repoOf = $serviceOf->getRepoElementPedagogique(); /* @var $serviceOf ElementPedagogiqueRepository */
-        $found = $repoOf->finderByTerm($params);
-
-        $result = array();
-        foreach ($found as $item) {
-            $extra = '';
-            $extra .= sprintf('<span class="element-rech niveau" title="%s">%s</span>', "Niveau", $item['LIBELLE_GTF'] . $item['NIVEAU']);
-            $extra .= sprintf('<span class="element-rech etape" title="%s">%s</span>', "Étape", $item['LIBELLE_ETAPE']);
-            $extra .= "Année" !== $item['LIBELLE_PE'] ? sprintf('<span class="element-rech periode" title="%s">%s</span>', "Période", $item['LIBELLE_PE']) : null;
-            $template = sprintf('<span class="element-rech extra">{extra}</span><span class="element-rech element" title="%s">{label}</span>', "Élément pédagogique");
-            $result[$item['ID']] = array(
-                'id'       => $item['ID'],
-                'label'    => $item['SOURCE_CODE'] . ' ' . $item['LIBELLE'],
-                'extra'    => $extra,
-                'template' => $template,
-            );
-        };
-
-        $result = \UnicaenApp\Form\Element\SearchAndSelect::truncatedResult($result, $limit - 1);
-        
-        return new \Zend\View\Model\JsonModel($result);
-    }
     
     /**
      * Retourne au format JSON les structures distinctes des éléments pédagogiques.
@@ -207,7 +129,7 @@ class OffreFormationController extends AbstractActionController implements Conte
     {
         $params = array('niveau' => 2);
         
-        $result = $this->getServiceOffreFormation()->getRepoElementPedagogique()->finderDistinctStructures($params)->getQuery()->getResult();
+        $result = $this->getServiceElementPedagogique()->finderDistinctStructures($params)->getQuery()->getResult();
         
         return new \Zend\View\Model\JsonModel(\UnicaenApp\Util::collectionAsOptions($result));
     }
@@ -225,113 +147,30 @@ class OffreFormationController extends AbstractActionController implements Conte
         $params = array();
         $params['structure'] = $structure instanceof \Application\Entity\Db\Structure ? $structure : null;
         
-        $result = $this->getServiceOffreFormation()->getRepoElementPedagogique()->finderDistinctNiveaux($params)->getQuery()->getResult();
-
-        $result = array_combine(
-                $tmp = array_map(function($v) { return $v['libelleCourt'] . $v['niveau']; }, $result), 
-                $tmp); 
+        $etapes  = $this->getServiceElementPedagogique()->finderDistinctNiveaux($params)->getQuery()->getResult();
+        $niveaux = \Application\Entity\NiveauEtape::getInstancesFromEtapes($etapes);
                 
-        return new \Zend\View\Model\JsonModel(\UnicaenApp\Util::collectionAsOptions($result));
+        return new \Zend\View\Model\JsonModel(\UnicaenApp\Util::collectionAsOptions($niveaux));
     }
-    
+
     /**
-     * Retourne au format JSON les étapes distincts des éléments pédagogiques 
-     * pour la structure et le niveau éventuellement spécifiés en GET.
+     * Retourne le service OffreFormation.
      * 
-     * @return \Zend\View\Model\JsonModel
+     * @return OffreFormationService
      */
-    public function searchEtapesAction()
-    {
-        $structure = $this->context()->structureFromQuery();
-        $niveau    = $this->context()->niveauFromQuery();
-        
-        $params = array();
-        $params['structure'] = $structure instanceof \Application\Entity\Db\Structure ? $structure : null;
-        $params['niveau']    = $niveau;
-        
-        $result = $this->getServiceOffreFormation()->getRepoElementPedagogique()->finderDistinctEtapes($params)->getQuery()->getResult();
-        
-        return new \Zend\View\Model\JsonModel(\UnicaenApp\Util::collectionAsOptions($result));
-    }
-
-    public function etapeSaisieAction()
-    {
-        $id = (int)$this->params()->fromRoute('id');
-        $errors = array();
-        if ($id){
-            $entity = $this->getServiceEtape()->getRepo()->find($id);
-        }else{
-            $entity = $this->getServiceEtape()->newEntity();
-        }
-        $form = $this->getServiceLocator()->get('FormElementManager')->get('EtapeSaisie');
-        /* @var $form \Application\Form\OffreFormation\EtapeSaisie */
-        $form->bind($entity);
-
-        $request = $this->getRequest();
-        if ($request->isPost()) {
-            $form->setData($request->getPost());
-            if ($form->isValid()) {
-                try{
-                    $this->getServiceEtape()->save($entity);
-                    $form->get('id')->setValue( $entity->getId() ); // transmet le nouvel ID
-                }catch(\Exception $e){
-                    $e = DbException::translate($e);
-                    $errors[] = $e->getMessage();
-                }
-            }
-        }
-
-        return compact('form','errors');
-    }
-
-    public function etapeSuppressionAction()
-    {
-        $id        = (int) $this->params()->fromRoute('id', 0);
-        if (! $id){
-            throw new \Common\Exception\RuntimeException('L\'identifiant n\'est pas bon ou n\'a pas été fourni');
-        }
-        $service   = $this->getServiceEtape();
-        $entity    = $service->getRepo()->find($id);
-        $title     = "Suppression d'étape";
-        $form      = new \Application\Form\Supprimer('suppr');
-        $errors = array();
-        $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
-
-        if ($this->getRequest()->isPost()) {
-            try {
-                $service->delete($entity);
-            }catch(\Exception $e){
-                $e = DbException::translate($e);
-                $errors[] = $e->getMessage();
-            }
-        }
-        return compact('entity', 'title', 'form', 'errors');
-    }
-
-    public function etapeApercevoirAction()
+    protected function getServiceOffreFormation()
     {
-        if (!($id = $this->params()->fromRoute('id'))) {
-            throw new LogicException("Aucune étape spécifiée.");
-        }
-
-        $em      = $this->em();
-        $repoEp  = $em->getRepository('Application\Entity\Db\Etape');
-        $etape = $repoEp->find($id);
-        $import = $this->getServiceLocator()->get('ImportProcessusImport');
-        $changements = $import->etapeGetDifferentiel($etape);
-        $short = $this->params()->fromQuery('short', false);
-
-        return compact('etape','short','changements');
+        return $this->getServiceLocator()->get('applicationOffreFormation');
     }
 
     /**
-     * Retourne le service OffreFormation.
+     * Retourne le service ElementPedagogique.
      * 
-     * @return OffreFormationService
+     * @return ElementPedagogiqueService
      */
-    protected function getServiceOffreFormation()
+    protected function getServiceElementPedagogique()
     {
-        return $this->getServiceLocator()->get('applicationOffreFormation');
+        return $this->getServiceLocator()->get('applicationElementPedagogique');
     }
 
     /**
diff --git a/module/Application/src/Application/Controller/Plugin/Context.php b/module/Application/src/Application/Controller/Plugin/Context.php
index 82481a1eae9f898428029974bb12292f4c34984d..46bff98257f250e7c64ce14f6760272f23d3dd39 100644
--- a/module/Application/src/Application/Controller/Plugin/Context.php
+++ b/module/Application/src/Application/Controller/Plugin/Context.php
@@ -2,8 +2,12 @@
 
 namespace Application\Controller\Plugin;
 
+use Zend\Mvc\Controller\Plugin\Params;
 use Zend\ServiceManager\ServiceLocatorAwareInterface;
 use Zend\ServiceManager\ServiceLocatorInterface;
+use Zend\Session\Container;
+use Application\Service\LocalContext;
+use Application\Service\GlobalContext;
 use Common\Exception\LogicException;
 use Common\Exception\RuntimeException;
 
@@ -19,9 +23,9 @@ use Common\Exception\RuntimeException;
  * @method mixed *FromQueryPost($name = null, $default = null) Description
  * 
  * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
- * @see \Zend\Mvc\Controller\Plugin\Params
+ * @see Params
  */
-class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocatorAwareInterface
+class Context extends Params implements ServiceLocatorAwareInterface
 {
     /**
      * @var ServiceLocatorInterface
@@ -34,12 +38,17 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
     protected $mandatory = false;
     
     /**
-     * @var \Application\Service\Context
+     * @var GlobalContext
      */
-    protected $context;
+    protected $globalContext;
     
     /**
-     * @var \Zend\Session\Container
+     * @var LocalContext
+     */
+    protected $localContext;
+    
+    /**
+     * @var Container
      */
     protected $sessionContainer;
     
@@ -61,8 +70,8 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
      * 1 : $argDefault : Valeur de retour par défaut
      * 2 : $argSources : Utile dans certains cas seulement : liste des sources où rechercher (post, query, context, etc.)
      *
-     * @param type $name
-     * @param type $arguments
+     * @param string $name
+     * @param array $arguments
      * @throws LogicException
      */
     public function __call($name, $arguments)
@@ -82,7 +91,9 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
                 break;
             case ($method = 'FromSession') === substr($name, $length = -11):
                 break;
-            case ($method = 'FromContext') === substr($name, $length = -11):
+            case ($method = 'FromGlobalContext') === substr($name, $length = -17):
+                break;
+            case ($method = 'FromLocalContext') === substr($name, $length = -16):
                 break;
             case ($method = 'FromSources') === substr($name, $length = -11):
                 break;
@@ -108,7 +119,7 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
         if (! $argName) $argName = $target;
 
         /* Récupération de la valeur */
-        if ('fromSources' === $method){
+        if ('FromSources' === $method){
             $value = $this->fromSources($argName, $argDefault, $argSources);
         }else{
             $value = call_user_func_array(array($this, lcfirst($method)), array($argName,$argDefault));
@@ -150,7 +161,8 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
             if (!is_object($value) && ! is_array($value)){
                 $id = (int)$value;
                 if ($id){
-                    if (!($value = $em->find($className, $id))) {
+                    $value = $em->find($className, $id);
+                    if (!$value && $this->mandatory) {
                         throw new RuntimeException($className." introuvable avec cet id : $id.");
                     }
                 }else{
@@ -202,6 +214,25 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
         return $this->getSessionContainer()->$name;
     }
     
+    /**
+     * Return a single local context parameter.
+     *
+     * @param  string $name Parameter name to retrieve.
+     * @param  mixed $default Default value to use when the requested parameter is not set.
+     * @return mixed
+     */
+    public function fromLocalContext($name, $default = null)
+    {
+        try {
+            $value = $this->getLocalContext()->get($name);
+        }
+        catch (LogicException $exc) {
+            $value = $default;
+        }
+
+        return $value;
+    }
+    
     /**
      * Return a single global context parameter.
      *
@@ -209,7 +240,7 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
      * @param  mixed $default Default value to use when the requested parameter is not set.
      * @return mixed
      */
-    public function fromContext($name, $default = null)
+    public function fromGlobalContext($name, $default = null)
     {
         try {
             $value = $this->getGlobalContext()->get($name);
@@ -222,23 +253,34 @@ class Context extends \Zend\Mvc\Controller\Plugin\Params implements ServiceLocat
     }
     
     /**
-     * @return \Application\Service\GlobalContext
+     * @return GlobalContext
      */
     public function getGlobalContext()
     {
-        if (null === $this->context) {
-            $this->context = $this->sl->get('ApplicationContextProvider')->getGlobalContext();
+        if (null === $this->globalContext) {
+            $this->globalContext = $this->sl->get('ApplicationContextProvider')->getGlobalContext();
+        }
+        return $this->globalContext;
+    }
+    
+    /**
+     * @return LocalContext
+     */
+    public function getLocalContext()
+    {
+        if (null === $this->localContext) {
+            $this->localContext = $this->sl->get('ApplicationContextProvider')->getLocalContext();
         }
-        return $this->context;
+        return $this->localContext;
     }
     
     /**
-     * @return \Zend\Session\Container
+     * @return Container
      */
     protected function getSessionContainer()
     {
         if (null === $this->sessionContainer) {
-            $this->sessionContainer = new \Zend\Session\Container(get_class($this->getController()));
+            $this->sessionContainer = new Container(get_class($this->getController()));
         }
         return $this->sessionContainer;
     }
diff --git a/module/Application/src/Application/Controller/RechercheController.php b/module/Application/src/Application/Controller/RechercheController.php
index b28b0a5ed01be1922e792acdcc42203ddd770f25..35c814e4d07095c759dade77f930caf15a0ccbf0 100644
--- a/module/Application/src/Application/Controller/RechercheController.php
+++ b/module/Application/src/Application/Controller/RechercheController.php
@@ -4,6 +4,7 @@ namespace Application\Controller;
 
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\View\Model\JsonModel;
+use Application\Service\Intervenant as IntervenantService;
 
 /**
  * Description of RechercheController
@@ -39,11 +40,11 @@ class RechercheController extends AbstractActionController
         if (!($term = $this->params()->fromQuery('term'))) {
             return new JsonModel(array());
         }
-           
-        $repo      = $this->intervenant()->getRepo();
-        $entities  = $repo->findByNomPrenomId($term);
+        
         $template  = "{label} <small>{extra}</small>";
         $resultOSE = array();
+        $qb        = $this->getServiceIntervenant()->finderByNomPrenomId($term);
+        $entities  = $qb->getQuery()->execute();
 
         $f = new \Common\Filter\IntervenantTrouveFormatter();
         foreach ($entities as $item) { /* @var $item \Application\Entity\Db\Intervenant */
@@ -84,4 +85,14 @@ class RechercheController extends AbstractActionController
         
         return new JsonModel($result);
     }
+
+    /**
+     * Retourne le service Intervenant.
+     * 
+     * @return IntervenantService
+     */
+    protected function getServiceIntervenant()
+    {
+        return $this->getServiceLocator()->get('applicationIntervenant');
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Controller/ServiceController.php b/module/Application/src/Application/Controller/ServiceController.php
index 139f91ad0bcd96ffcd43d4063e1e6e6d16e0fdd7..7dfaaad99c17ff3dee8b20c8d882d1a609a8eec8 100644
--- a/module/Application/src/Application/Controller/ServiceController.php
+++ b/module/Application/src/Application/Controller/ServiceController.php
@@ -41,7 +41,8 @@ class ServiceController extends AbstractActionController
         $annee   = $this->getContextProvider()->getGlobalContext()->getAnnee();
         $qb      = $service->finderByContext();
         $viewModel = new \Zend\View\Model\ViewModel();
-
+        $filter    = new \stdClass();
+        
         /* Initialisation, si ce n'est pas un intervenant, du formulaire de recherche */
         if (! $role instanceof \Application\Acl\IntervenantRole){
             $action = $this->getRequest()->getQuery('action', null); // ne pas afficher par défaut, sauf si demandé explicitement
@@ -54,10 +55,16 @@ class ServiceController extends AbstractActionController
             /* @var $rechercheForm \Application\Form\Service\Recherche */
             $filter = $rechercheForm->hydrateFromSession();
             $service->finderByFilterObject($filter, null, $qb);
-        }else{
+        }
+        else {
             $action = 'afficher'; // Affichage par défaut
         }
 
+        // sauvegarde des filtres dans le contexte local
+        $this->getContextProvider()->getLocalContext()->fromArray(
+                (new \Zend\Stdlib\Hydrator\ObjectProperty())->extract($filter)
+        );
+            
         /* Préparation et affichage */
         if ('afficher' === $action){
             $services = $service->getList($qb);
@@ -69,7 +76,8 @@ class ServiceController extends AbstractActionController
             $params['query']  = $this->params()->fromQuery();
             $listeViewModel   = $this->forward()->dispatch($controller, $params);
             $viewModel->addChild($listeViewModel, 'servicesRefListe');
-        }else{
+        }
+        else {
             $services = array();
         }
 
@@ -91,7 +99,8 @@ class ServiceController extends AbstractActionController
             if ($rechercheForm->isValid()){
                 $rechercheForm->sessionUpdate();
             }
-        }else{
+        }
+        else {
             $rechercheForm = null; // pas de filtrage
         }
         return compact('rechercheForm', 'role');
@@ -160,7 +169,7 @@ class ServiceController extends AbstractActionController
             $title   = "Modification de service";
         }else{
             $entity = $service->newEntity();
-            $entity->setAnnee( $this->context()->anneeFromContext() );
+            $entity->setAnnee( $this->context()->anneeFromGlobalContext() );
             $entity->setValiditeDebut(new \DateTime );
             if ($role instanceof \Application\Acl\IntervenantRole){
                 $entity->setIntervenant( $context->getIntervenant() );
@@ -176,7 +185,7 @@ class ServiceController extends AbstractActionController
             }
             $entity->setElementPedagogique( $this->context()->elementPedagogiqueFromPost("elementPedagogique[element][id]") );
             $entity->setEtablissement( $this->context()->etablissementFromPost("etablissement[id]") );
-            if (! $entity->getEtablissement()) $entity->setEtablissement( $this->context()->etablissementFromContext() );
+            if (! $entity->getEtablissement()) $entity->setEtablissement( $this->context()->etablissementFromGlobalContext() );
         }
         $errors  = array();
         $form->bind( $entity );
diff --git a/module/Application/src/Application/Controller/ServiceReferentielController.php b/module/Application/src/Application/Controller/ServiceReferentielController.php
index 3fb22a8f413be60920c5363f340803ba64b19c2d..3d42becc96b155125a36fdac61f6da23f3629a42 100644
--- a/module/Application/src/Application/Controller/ServiceReferentielController.php
+++ b/module/Application/src/Application/Controller/ServiceReferentielController.php
@@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 use Common\Exception\MessageException;
 use Common\Exception\RuntimeException;
 use Common\Exception\LogicException;
+use Application\Service\ElementPedagogique as ElementPedagogiqueService;
 use Application\Service\ContextProviderAwareInterface;
 use Application\Service\ContextProviderAwareTrait;
 use Application\Exception\DbException;
@@ -55,7 +56,6 @@ class ServiceReferentielController extends AbstractActionController implements C
         $cp       = $this->getContextProvider();
         $annee    = $cp->getGlobalContext()->getAnnee();
         $criteria = array();
-//        $criteria = array('structure' => $this->em()->find('Application\Entity\Db\Structure', 8494));
         $services = $service->getFinder($criteria)
                 ->orderBy("i.nomUsuel, s.libelleCourt")
                 ->getQuery()->execute();
@@ -89,25 +89,9 @@ class ServiceReferentielController extends AbstractActionController implements C
     public function voirListeAction()
     {
         $service  = $this->getServiceServiceReferentiel();
-        $criteria = array();
-        
-        // récupère l'éventuel intervenant sélectionné dans le formulaire de filtrage des services
-        $rechercheForm = $this->getServiceLocator()->get('FormElementManager')->get('ServiceRecherche');
-        $filters = $rechercheForm->hydrateFromSession();
-        if (isset($filters->intervenant) && $filters->intervenant) {
-            $criteria['intervenant'] = $filters->intervenant;
-        }
-        if (isset($filters->structureEns) && $filters->structureEns) {
-            $criteria['structure-ens'] = $filters->structureEns;
-        }
-        
-        $criteria = array_merge($criteria, $this->params()->fromQuery());
-        
-//        $criteria = array('structure' => $this->em()->find('Application\Entity\Db\Structure', 8474));
-        $services = $service->getFinder($criteria)
-                ->orderBy("i.nomUsuel, s.libelleCourt")
-                ->getQuery()->execute();
-        
+        $qb       = $service->getFinder()->orderBy("i.nomUsuel, s.libelleCourt");
+        $services = $qb->getQuery()->execute();
+
         return compact('services');
     }
 
@@ -265,13 +249,13 @@ class ServiceReferentielController extends AbstractActionController implements C
         }
         
         $repoFonctionReferentiel = $this->em()->getRepository('Application\Entity\Db\FonctionReferentiel'); /* @var $repoFonctionReferentiel \Doctrine\ORM\EntityRepository */
-        $repoElementPedagogique  = $this->em()->getRepository('Application\Entity\Db\ElementPedagogique');  /* @var $repoElementPedagogique \Application\Entity\Db\Repository\ElementPedagogiqueRepository */
-
+        $serviceEp = $this->getServiceLocator()->get('applicationElementPedagogique'); /* @var $serviceEp ElementPedagogiqueService */
+        
         $annee = $context->getAnnee();
         
 //        var_dump(get_class($intervenant), "".$annee, count($intervenant->getServiceReferentiel($annee)));
         
-        $structures = $repoElementPedagogique->finderDistinctStructures(array('niveau' => 2))->getQuery()->getResult();
+        $structures = $serviceEp->finderDistinctStructures(array('niveau' => 2))->getQuery()->getResult();
         $fonctions  = $repoFonctionReferentiel->findBy(array('validiteFin' => null), array('libelleCourt' => 'asc'));
         FonctionServiceReferentielFieldset::setStructuresPossibles(new ArrayCollection($structures));
         FonctionServiceReferentielFieldset::setFonctionsPossibles(new ArrayCollection($fonctions));
diff --git a/module/Application/src/Application/Controller/VolumeHoraireController.php b/module/Application/src/Application/Controller/VolumeHoraireController.php
index c168254974e75f653251e48fa4129a5dcace4fe1..332cb5b2e16e4314ca9817c6f01b5d67013b1440 100644
--- a/module/Application/src/Application/Controller/VolumeHoraireController.php
+++ b/module/Application/src/Application/Controller/VolumeHoraireController.php
@@ -66,7 +66,7 @@ class VolumeHoraireController extends AbstractActionController
             $entity->setTypeIntervention( $this->context()->typeInterventionFromQueryPost() );
         }
 
-        $form = new Saisie( $this->getServiceLocator() );
+        $form = $this->getForm();
         $form->setAttribute('action', $this->url()->fromRoute(null, array(), array(), true));
 
         $request = $this->getRequest();
@@ -108,4 +108,14 @@ class VolumeHoraireController extends AbstractActionController
         }
         return $viewModel;
     }
+
+    /**
+     * Retourne le formulaire de modif de Volume Horaire.
+     * 
+     * @return \Application\Form\VolumeHoraire\Saisie
+     */
+    protected function getForm()
+    {
+        return $this->getServiceLocator()->get('FormElementManager')->get('VolumeHoraireSaisie');
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Entity/Db/CheminPedagogique.php b/module/Application/src/Application/Entity/Db/CheminPedagogique.php
index b5145191d8d4fb74c5620480f27cda24958b7eec..2232f3886ef255a51ecb8610e1ea474231d15046 100644
--- a/module/Application/src/Application/Entity/Db/CheminPedagogique.php
+++ b/module/Application/src/Application/Entity/Db/CheminPedagogique.php
@@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
  * CheminPedagogique
  */
-class CheminPedagogique
+class CheminPedagogique implements HistoriqueAwareInterface, ValiditeAwareInterface
 {
     /**
      * @var \DateTime
diff --git a/module/Application/src/Application/Entity/Db/ElementPedagogique.php b/module/Application/src/Application/Entity/Db/ElementPedagogique.php
index e94d9d6715d325bb2d5bc5ea56b2277bebaee62f..fa592b5e33d7c86269a5c9c47a8a2abdeb6541de 100644
--- a/module/Application/src/Application/Entity/Db/ElementPedagogique.php
+++ b/module/Application/src/Application/Entity/Db/ElementPedagogique.php
@@ -2,12 +2,10 @@
 
 namespace Application\Entity\Db;
 
-use Doctrine\ORM\Mapping as ORM;
-
 /**
  * ElementPedagogique
  */
-class ElementPedagogique
+class ElementPedagogique implements HistoriqueAwareInterface, ValiditeAwareInterface
 {
     public function __toString()
     {
diff --git a/module/Application/src/Application/Entity/Db/Etape.php b/module/Application/src/Application/Entity/Db/Etape.php
index d7e3383ecc68677553709523427bba1db78ff098..12734861c9ed83dce0ed59801edafc4eb06bef11 100644
--- a/module/Application/src/Application/Entity/Db/Etape.php
+++ b/module/Application/src/Application/Entity/Db/Etape.php
@@ -20,7 +20,7 @@ class Etape implements HistoriqueAwareInterface, ValiditeAwareInterface
     }
     
     /**
-     * Retourne la représentation littérale de cet objet.
+     * Retourne la représentation littérale du niveau corresponadnt à cette étape.
      *
      * @return string
      */
diff --git a/module/Application/src/Application/Entity/Db/Finder/AbstractFinder.php b/module/Application/src/Application/Entity/Db/Finder/AbstractFinder.php
index 72646049d43c696a9a3672c569485acb00dd0090..468dada057b6333bb1dbb916033f28e78843f435 100644
--- a/module/Application/src/Application/Entity/Db/Finder/AbstractFinder.php
+++ b/module/Application/src/Application/Entity/Db/Finder/AbstractFinder.php
@@ -17,18 +17,29 @@ abstract class AbstractFinder extends QueryBuilder
 {
     use ContextProviderAwareTrait;
     
+    /**
+     * @var array
+     */
+    protected $filter = array();
+
+    /**
+     * @var bool
+     */
     protected $queryCreated = false;
     
     /**
      * 
      * @param EntityManager $em
      * @param ContextProvider $contextProvider
+     * @param array $filter
      */
-    public function __construct(EntityManager $em, ContextProvider $contextProvider = null)
+    public function __construct(EntityManager $em, ContextProvider $contextProvider = null, array $filter = array())
     {
         parent::__construct($em);
         
-        $this->setContextProvider($contextProvider);
+        $this
+                ->setContextProvider($contextProvider)
+                ->setFilter($filter);
     }
     
     /**
@@ -50,4 +61,24 @@ abstract class AbstractFinder extends QueryBuilder
         
         return parent::getQuery();
     }
+    
+    /**
+     * 
+     * @return array
+     */
+    public function getFilter()
+    {
+        return $this->filter;
+    }
+
+    /**
+     * 
+     * @param array $filter
+     * @return self
+     */
+    public function setFilter(array $filter = array())
+    {
+        $this->filter = $filter;
+        return $this;
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Entity/Db/Finder/FinderServiceReferentiel.php b/module/Application/src/Application/Entity/Db/Finder/FinderServiceReferentiel.php
index af983a7c4cca1cff88c36e3f0eaa920fcaf31309..c2a08fe655d8746b895db76ed288687bfabd0d6c 100644
--- a/module/Application/src/Application/Entity/Db/Finder/FinderServiceReferentiel.php
+++ b/module/Application/src/Application/Entity/Db/Finder/FinderServiceReferentiel.php
@@ -52,6 +52,35 @@ class FinderServiceReferentiel extends AbstractFinder
                     ->setParameter('annee', $annee);
         }
         
+        $this->applyLocalContext();
+        
+        return $this;
+    }
+    
+    /**
+     * Applique le contexte local (filtres).
+     * 
+     * @return self
+     */
+    public function applyLocalContext()
+    {
+        $filter = $this->getContextProvider()->getLocalContext();
+//        $filter->debug();
+        
+        if (($intervenant = $filter->getIntervenant())) {
+            $this
+                    ->andWhere("sr.intervenant = :intervenant")
+                    ->setParameter('intervenant', $intervenant);
+        }
+        if (($structureEns = $filter->getStructure())) {
+            $this
+                    ->andWhere("sr.structure = :structure")
+                    ->setParameter('structure', $structureEns);
+        }
+        if (($statutInterv = $filter->getStatutInterv()) && $statutInterv !== "Application\Entity\Db\IntervenantPermanent") {
+            $this->andWhere("0 = 1");
+        }
+        
         return $this;
     }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Entity/Db/Structure.php b/module/Application/src/Application/Entity/Db/Structure.php
index c6826cb77c62caf749477561c18cfc38e1365fd0..50268061dfcc86d5d503c8f0c74100a6722379c9 100644
--- a/module/Application/src/Application/Entity/Db/Structure.php
+++ b/module/Application/src/Application/Entity/Db/Structure.php
@@ -563,4 +563,15 @@ class Structure implements HistoriqueAwareInterface
     {
         return $this->getSource()->getLibelle();
     }
+
+    /**
+     * Teste si cette structure est une structure fille de la structure de niveau 2 spécifiée.
+     *
+     * @param \Application\Entity\Db\Structure $structureDeNiv2 
+     * @return bool 
+     */
+    public function estFilleDeLaStructureDeNiv2(\Application\Entity\Db\Structure $structureDeNiv2)
+    {
+        return $this->getParenteNiv2()->getId() === $structureDeNiv2->getId();
+    }
 }
diff --git a/module/Application/src/Application/Entity/NiveauEtape.php b/module/Application/src/Application/Entity/NiveauEtape.php
new file mode 100644
index 0000000000000000000000000000000000000000..14b3ee1b906254c1c04a5a60848c072ebb329029
--- /dev/null
+++ b/module/Application/src/Application/Entity/NiveauEtape.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Application\Entity;
+
+use Application\Entity\Db\Etape;
+
+/**
+ * Description of NiveauEtape
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class NiveauEtape
+{
+    /**
+     * @var Etape 
+     */
+    protected $etape;
+    
+    /**
+     * @var string 
+     */
+    protected $niv;
+    
+    /**
+     * @var string 
+     */
+    protected $lib;
+    
+    /**
+     * 
+     * @param \Application\Entity\Db\Etape $etape
+     */
+    static public function getInstanceFromEtape(Etape $etape)
+    {
+        $i = new self();
+        $i->setEtape($etape);
+        
+        return $i;
+    }
+    
+    /**
+     * 
+     * @param string $lib
+     * @param string $niv
+     * @return NiveauEtape
+     */
+    static public function getInstance($lib, $niv = null)
+    {
+        $i = new self();
+        $i->setLib($lib)->setNiv($niv);
+        
+        return $i;
+    }
+    
+    /**
+     * 
+     * @param \Traversable $etapes
+     * @return NiveauEtape[]
+     */
+    static public function getInstancesFromEtapes($etapes)
+    {
+        $instances = array();
+        
+        foreach ($etapes as $e) {
+            $n = static::getInstanceFromEtape($e);
+            $instances[$n->__toString()] = $n;
+        }
+        
+        return $instances;
+    }
+    
+    public function __toString()
+    {
+        return $this->getLib() . $this->getNiv();
+    }
+    
+    public function getId()
+    {
+        return sprintf("%s-%s", $this->getLib(), $this->getNiv());
+    }
+    
+    public function getEtape()
+    {
+        return $this->etape;
+    }
+
+    public function getNiv()
+    {
+        return $this->niv;
+    }
+
+    public function getLib()
+    {
+        return $this->lib;
+    }
+
+    public function setNiv($niv)
+    {
+        $this->niv = $niv;
+        return $this;
+    }
+
+    public function setLib($lib)
+    {
+        $this->lib = $lib;
+        return $this;
+    }
+
+    public function setEtape(Etape $etape)
+    {
+        $this->etape = $etape;
+        $this->niv   = $this->etape->getNiveau();
+        $this->lib   = $this->etape->getTypeFormation()->getGroupe()->getLibelleCourt();
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisie.php b/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisie.php
new file mode 100644
index 0000000000000000000000000000000000000000..02720c0b6f96df84abc4c8e67b44bd8cb1548dc2
--- /dev/null
+++ b/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisie.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace Application\Form\OffreFormation;
+
+use Zend\Form\Form;
+use Zend\InputFilter\InputFilterProviderInterface;
+use Zend\ServiceManager\ServiceLocatorAwareInterface;
+use Zend\ServiceManager\ServiceLocatorAwareTrait;
+use Application\Service\ContextProviderAwareInterface;
+use Application\Service\ContextProviderAwareTrait;
+
+/**
+ * Description of ElementPedagogiqueSaisie
+ *
+ * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
+ */
+class ElementPedagogiqueSaisie extends Form implements InputFilterProviderInterface, ServiceLocatorAwareInterface, ContextProviderAwareInterface
+{
+    use ServiceLocatorAwareTrait;
+    use ContextProviderAwareTrait;
+    
+    /**
+     * This function is automatically called when creating element with factory. It
+     * allows to perform various operations (add elements...)
+     */
+    public function init()
+    {
+        /* Définition de l'hydrateur */
+        $hydrator = new ElementPedagogiqueSaisieHydrator();
+        $hydrator->setServiceLocator($this->getServiceLocator()->getServiceLocator());
+        $this->setHydrator($hydrator);
+
+        $this->add(array(
+            'name'    => 'etape',
+            'options' => array(
+                'label' => 'Étape',
+            ),
+            'attributes' => array(
+                'disabled' => true,
+            ),
+            'type'    => 'Select',
+        ));
+        
+        $this->add(array(
+            'name'    => 'source-code',
+            'options' => array(
+                'label' => 'Code',
+            ),
+            'type'    => 'Text'
+        ));
+
+        $this->add(array(
+            'name'    => 'libelle',
+            'options' => array(
+                'label' => 'Libellé',
+            ),
+            'type'    => 'Text'
+        ));
+
+        $this->add(array(
+            'name'    => 'periode',
+            'options' => array(
+                'label'         => 'Période',
+            ),
+            'type'    => 'Select',
+        ));
+
+        $this->add(array(
+            'name'       => 'taux-foad',
+            'options'    => array(
+                'label' => 'FOAD',
+            ),
+            'attributes' => array(
+                'title' => "Formation ouverte à distance",
+            ),
+            'type'       => 'Checkbox',
+        ));
+
+        $this->add(array(
+            'name'    => 'structure',
+            'options' => array(
+                'label' => 'Structure',
+            ),
+            'attributes' => array(
+                'disabled' => true,
+            ),
+            'type'    => 'Select',
+        ));
+
+        $this->add(array(
+            'name' => 'id',
+            'type' => 'Hidden'
+        ));
+
+        $this->add(array(
+            'name'       => 'submit',
+            'type'       => 'Submit',
+            'attributes' => array(
+                'value' => 'Enregistrer',
+                'class' => 'btn btn-primary',
+            ),
+        ));
+        
+        $localContext = $this->getContextProvider()->getLocalContext();
+        
+        // init étape
+        if (($etape = $localContext->getEtape())) {
+            // si un filtre étape est positionné dans le contexte local, on l'utilise
+            $this->get('etape')
+                    ->setValueOptions(array($id = $etape->getId() => (string) $etape))
+                    ->setValue($id);
+        }
+        else {
+            $serviceEtape = $this->getServiceLocator()->getServiceLocator()->get('ApplicationEtape');
+            $this->get('etape')
+                    ->setValueOptions( \UnicaenApp\Util::collectionAsOptions( $serviceEtape->getList() ) )
+                    ->setAttribute('disabled', false);
+        }
+        
+        // peuplement liste des périodes
+        $servicePeriode = $this->getServiceLocator()->getServiceLocator()->get('ApplicationPeriode');
+        $this->get('periode')->setValueOptions(\UnicaenApp\Util::collectionAsOptions($servicePeriode->getList()));
+        
+        // peuplement liste des structures
+        if ($localContext->getStructure()) {
+            // si un filtre structure est positionné dans le contexte local, on l'utilise
+            $this->get('structure')
+                    ->setValueOptions(array($id = $localContext->getStructure()->getId() => (string) $localContext->getStructure()))
+                    ->setValue($id);
+        }
+        else {
+            $serviceStructure = $this->getServiceLocator()->getServiceLocator()->get('ApplicationStructure');
+            $qb = $serviceStructure->finderByEnseignement( $serviceStructure->finderByNiveau(2) );
+            $this->get('structure')
+                    ->setValueOptions( \UnicaenApp\Util::collectionAsOptions( $serviceStructure->getList($qb) ) )
+                    ->setAttribute('disabled', false);
+        }
+    }
+    
+    /**
+     * Should return an array specification compatible with
+     * {@link Zend\InputFilter\Factory::createInputFilter()}.
+     *
+     * @return array
+     */
+    public function getInputFilterSpecification()
+    {
+        return array(
+            'taux-foad' => array(
+                'required' => true,
+//                'validators' => array(
+//                    array('name' => 'Float'),
+//                ),
+            ),
+            'source-code' => array(
+                'required' => true,
+            ),
+            'libelle' => array(
+                'required' => true,
+            ),
+            'periode' => array(
+                'required' => true,
+            ),
+            'etape' => array(
+                'required' => false,
+            ),
+            'structure' => array(
+                'required' => false,
+            ),
+        );
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisieHydrator.php b/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisieHydrator.php
new file mode 100644
index 0000000000000000000000000000000000000000..49cce05a02a9ad18c00fb984a6fb35dfaf3b58cb
--- /dev/null
+++ b/module/Application/src/Application/Form/OffreFormation/ElementPedagogiqueSaisieHydrator.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Application\Form\OffreFormation;
+
+use Zend\Stdlib\Hydrator\HydratorInterface;
+use Zend\ServiceManager\ServiceLocatorAwareInterface;
+use Zend\ServiceManager\ServiceLocatorAwareTrait;
+
+/**
+ *
+ *
+ * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
+ */
+class ElementPedagogiqueSaisieHydrator implements HydratorInterface, ServiceLocatorAwareInterface
+{
+
+    use ServiceLocatorAwareTrait;
+
+    /**
+     * Hydrate $object with the provided $data.
+     *
+     * @param  array $data
+     * @param  \Application\Entity\Db\ElementPedagogique $object
+     * @return object
+     */
+    public function hydrate(array $data, $object)
+    {
+        $object->setSourceCode($data['source-code']);
+        $object->setLibelle($data['libelle']);
+//        $object->setEtape($this->getServiceLocator()->get('ApplicationEtape')->get($data['etape']));
+        $object->setPeriode($this->getServiceLocator()->get('ApplicationPeriode')->get($data['periode']));
+        $object->setTauxFoad($data['taux-foad']);
+//        $object->setStructure($this->getServiceLocator()->get('ApplicationStructure')->get($data['structure']));
+
+        return $object;
+    }
+
+    /**
+     * Extract values from an object
+     *
+     * @param  \Application\Entity\Db\ElementPedagogique $object
+     * @return array
+     */
+    public function extract($object)
+    {
+        $data = array(
+            'etape'          => ($e = $object->getEtape()) ? $e->getId() : null,
+            'source-code'    => $object->getSourceCode(),
+            'libelle'        => $object->getLibelle(),
+            'id'             => $object->getId(),
+            'periode'        => ($tf              = $object->getPeriode()) ? $tf->getId() : null,
+            'taux-foad'      => $object->getTauxFoad(),
+            'structure'      => ($s               = $object->getStructure()) ? $s->getId() : null,
+        );
+        return $data;
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Form/OffreFormation/EtapeSaisie.php b/module/Application/src/Application/Form/OffreFormation/EtapeSaisie.php
index 29c6ca7e7c12513c3d6b2b373757144e5f672124..aa61d3e338fa6f56e8ad92032a1eebee3abded4a 100644
--- a/module/Application/src/Application/Form/OffreFormation/EtapeSaisie.php
+++ b/module/Application/src/Application/Form/OffreFormation/EtapeSaisie.php
@@ -6,17 +6,25 @@ use Zend\Form\Form;
 use Zend\InputFilter\InputFilterProviderInterface;
 use Zend\ServiceManager\ServiceLocatorAwareInterface;
 use Zend\ServiceManager\ServiceLocatorAwareTrait;
-use UnicaenApp\Form\Element\SearchAndSelect;
+use Application\Service\ContextProviderAwareInterface;
+use Application\Service\ContextProviderAwareTrait;
 
 /**
  * Description of EtapeSaisie
  *
  * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
  */
-class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceLocatorAwareInterface
+class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceLocatorAwareInterface, ContextProviderAwareInterface
 {
     use ServiceLocatorAwareTrait;
+    use ContextProviderAwareTrait;
 
+    private $typesFormation;
+    
+    /**
+     * This function is automatically called when creating element with factory. It
+     * allows to perform various operations (add elements...)
+     */
     public function init()
     {
         /* Définition de l'hydrateur */
@@ -48,17 +56,12 @@ class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceL
             ),
             'type' => 'Select',
         ) );
-        $serviceTypeFormation = $this->getServiceLocator()->getServiceLocator()->get('ApplicationTypeFormation');
-        $this->get('type-formation')->setValueOptions( \UnicaenApp\Util::collectionAsOptions( $serviceTypeFormation->getList() ) );
 
         $this->add( array(
             'name' => 'niveau',
             'options' => array(
                 'label' => 'Niveau',
             ),
-            'validators' => array(
-                'integer',
-            ),
             'type' => 'Text',
         ) );
 
@@ -77,9 +80,6 @@ class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceL
             ),
             'type' => 'Select',
         ) );
-        $serviceStructure = $this->getServiceLocator()->getServiceLocator()->get('ApplicationStructure');
-        $qb = $serviceStructure->finderByEnseignement( $serviceStructure->finderByNiveau(2) );
-        $this->get('structure')->setValueOptions( \UnicaenApp\Util::collectionAsOptions( $serviceStructure->getList($qb) ) );
 
         $this->add( array(
             'name' => 'id',
@@ -94,8 +94,86 @@ class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceL
                 'class' => 'btn btn-primary',
             ),
         ));
+        
+        $localContext = $this->getContextProvider()->getLocalContext();
+        
+        // peuplement liste des structures
+        if ($localContext->getStructure()) {
+            // si un filtre structure est positionné dans le contexte local, on l'utilise
+            $this->get('structure')
+                    ->setValueOptions(array($id = $localContext->getStructure()->getId() => (string) $localContext->getStructure()))
+                    ->setValue($id)
+                    ->setAttribute('disabled', true);
+        }
+        else {
+            $serviceStructure = $this->getServiceLocator()->getServiceLocator()->get('ApplicationStructure');
+            $qb = $serviceStructure->finderByEnseignement( $serviceStructure->finderByNiveau(2) );
+            $this->get('structure')
+                    ->setValueOptions(\UnicaenApp\Util::collectionAsOptions($serviceStructure->getList($qb)));
+        }
+        
+        // peuplement liste des types de formation
+        $valueOptions = \UnicaenApp\Util::collectionAsOptions($this->getTypesFormation());
+        $this->get('type-formation')
+                ->setEmptyOption(count($valueOptions) > 1 ? "(Sélectionnez un type...)" : null)
+                ->setValueOptions($valueOptions);
+        
+        // init niveau
+        if ($localContext->getNiveau()) {
+            // si un filtre niveau est positionné dans le contexte local, on l'utilise
+            $this->get('niveau')
+                    ->setValue($localContext->getNiveau()->getNiv())
+                    ->setAttribute('readonly', true);
+        }
+    }
+    
+    /**
+     * @return \Application\Entity\Db\TypeFormation[]
+     */
+    private function getTypesFormation()
+    {
+        if (null === $this->typesFormation) {
+            $serviceTypeFormation = $this->getServiceLocator()->getServiceLocator()->get('ApplicationTypeFormation');
+            $localContext         = $this->getContextProvider()->getLocalContext();
+            $qb                   = null;
+            
+            if (($niveau = $localContext->getNiveau())) {
+                $qb = $serviceTypeFormation->finderByNiveau($niveau);
+            }
+            
+            $this->typesFormation = $serviceTypeFormation->getList($qb);
+        }
+        
+        return $this->typesFormation;
+    }
+    
+    /**
+     * Retourne pour chaque type de formation le flag indiquant si la saisie d'un niveau est pertienent ou non.
+     * 
+     * @return array id => bool
+     */
+    public function getPertinencesNiveau()
+    {
+        $pertinencesNiveau = array();
+        foreach ($this->getTypesFormation() as $tf) { /* @var $tf \Application\Entity\Db\TypeFormation */
+            $pertinencesNiveau[$tf->getId()] = (bool) $tf->getGroupe()->getPertinenceNiveau();
+        }
+        
+        return $pertinencesNiveau;
+    }
+    
+    /**
+     * 
+     * @return bool
+     */
+    private function getRequiredNiveau()
+    {
+        $typeFormation     = $this->get('type-formation')->getValue();
+        $pertinencesNiveau = $this->getPertinencesNiveau();
+        $pertinent         = isset($pertinencesNiveau[$typeFormation]) && (bool) $pertinencesNiveau[$typeFormation];
+        
+        return $pertinent;
     }
-
 
     /**
      * Should return an array specification compatible with
@@ -103,8 +181,27 @@ class EtapeSaisie extends Form implements InputFilterProviderInterface, ServiceL
      *
      * @return array
      */
-    public function getInputFilterSpecification(){
+    public function getInputFilterSpecification()
+    {
         return array(
+            'source-code' => array(
+                'required' => true,
+            ),
+            'libelle' => array(
+                'required' => true,
+            ),
+            'type-formation' => array(
+                'required' => true,
+            ),
+            'niveau' => array(
+                'required' => $this->getRequiredNiveau(),
+                'validators' => array(
+                    array('name' => 'Int'),
+                ),
+            ),
+            'structure' => array(
+                'required' => false,
+            ),
         );
     }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Form/OffreFormation/EtapeSaisieHydrator.php b/module/Application/src/Application/Form/OffreFormation/EtapeSaisieHydrator.php
index 4e56d584517f870e96e5fd669b51cae5b5754b13..e1000016796423ff9b2a410b232f4334bb2551c6 100644
--- a/module/Application/src/Application/Form/OffreFormation/EtapeSaisieHydrator.php
+++ b/module/Application/src/Application/Form/OffreFormation/EtapeSaisieHydrator.php
@@ -31,7 +31,9 @@ class EtapeSaisieHydrator implements HydratorInterface, ServiceLocatorAwareInter
             $object->setNiveau( $data['niveau'] );
         }
         $object->setSpecifiqueEchanges( $data['specifique-echanges'] );
-        $object->setStructure( $this->getServiceLocator()->get('ApplicationStructure')->get( $data['structure'] ) );
+        if (array_key_exists('structure', $data)) {
+            $object->setStructure( $this->getServiceLocator()->get('ApplicationStructure')->get( $data['structure'] ) );
+        }
         return $object;
     }
 
diff --git a/module/Application/src/Application/Form/Service/Recherche.php b/module/Application/src/Application/Form/Service/Recherche.php
index 1cbbf6e99806b3db808afc2e3037d59fb3993332..b2504443e886d9ef77b70d6192903ec0ac65d0e9 100644
--- a/module/Application/src/Application/Form/Service/Recherche.php
+++ b/module/Application/src/Application/Form/Service/Recherche.php
@@ -25,16 +25,28 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
      */
     protected $sessionContainer;
 
-    public function __construct($name = null, $options = array())
-    {
-        parent::__construct($name, $options);
+    /**
+     * @var \Zend\Mvc\Controller\Plugin\Url
+     */
+    protected $urlPlugin;
 
+    /**
+     * 
+     */
+    public function init()
+    {
         $this   ->setAttribute('method', 'get')
-                ->setAttribute('class', 'service-recherche')
-         ;
-
-        $intervenant = new Select('intervenant');
-        $intervenant->setLabel('Intervenant :');
+                ->setAttribute('class', 'service-recherche');
+
+        $intervenantUrl = $this->getUrlPlugin()->fromRoute(
+                'recherche', 
+                array('action' => 'intervenantFind'),
+                array('query' => array('having-services' => 1)));
+        
+        $intervenant = new \UnicaenApp\Form\Element\SearchAndSelect('intervenant');
+        $intervenant
+                ->setAutocompleteSource($intervenantUrl)
+                ->setLabel('Intervenant :');
         $this->add($intervenant);
 
         $element = new Select('element-pedagogique');
@@ -49,6 +61,16 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
         $structureEns->setLabel('Structure d\'enseignement :');
         $this->add($structureEns);
 
+        $statutInterv = new \Zend\Form\Element\Radio('statut-interv');
+        $statutInterv
+                ->setValueOptions(array(
+                    '' => "Peu importe",
+                    'Application\Entity\Db\IntervenantPermanent' => "Permanent",
+                    'Application\Entity\Db\IntervenantExterieur' => "Vacataire"))
+                ->setValue('')
+                ->setLabel("Statut de l'intervenant :");
+        $this->add($statutInterv);
+
         $action = new Hidden('action');
         $action->setValue('afficher');
         $this->add($action);
@@ -66,6 +88,18 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
         ));
     }
 
+    /**
+     * 
+     * @return \Zend\Mvc\Controller\Plugin\Url
+     */
+    protected function getUrlPlugin()
+    {
+        if (null === $this->urlPlugin) {
+            $this->urlPlugin = $this->getServiceLocator()->getServiceLocator()->get('ControllerPluginManager')->get('url');
+        }
+        return $this->urlPlugin;
+    }
+    
     /**
      *
      * @param Service[] $services
@@ -80,12 +114,12 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
         $structure          = $sl->get('ApplicationStructure');
         $service            = $sl->get('ApplicationService');
 
-        $qb = $intervenant->initQuery()[0];
-        $intervenant->join( $service, $qb, 'id', 'intervenant' );
-        $service->finderByContext( $qb );
-        $this->get('intervenant')->setValueOptions( \UnicaenApp\Util::collectionAsOptions(
-                                                            array( '' => '(Tous)') + $intervenant->getList($qb))
-                                                  );
+//        $qb = $intervenant->initQuery()[0];
+//        $intervenant->join( $service, $qb, 'id', 'intervenant' );
+//        $service->finderByContext( $qb );
+//        $this->get('intervenant')->setValueOptions( \UnicaenApp\Util::collectionAsOptions(
+//                                                            array( '' => '(Tous)') + $intervenant->getList($qb))
+//                                                  );
 
         $qb = $elementPedagogique->initQuery()[0];
         $elementPedagogique->join( $service, $qb, 'id', 'elementPedagogique' );
@@ -119,6 +153,9 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
     public function getInputFilterSpecification()
     {
         return array(
+            'statut-interv' => array(
+                'required' => false
+            ),
             'intervenant' => array(
                 'required' => false
             ),
@@ -162,7 +199,9 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
      */
     public function hydrateFromSession($object=null)
     {
-        if (! $object) $object = new \stdClass;
+        if (! $object) {
+            $object = new \stdClass;
+        }
         $session = $this->getSessionContainer();
         if ($session->offsetExists('data')){
             $data = $session->data;
diff --git a/module/Application/src/Application/Form/Service/RechercheHydrator.php b/module/Application/src/Application/Form/Service/RechercheHydrator.php
index 686a67b9084c3e9f343274d8d9bde340f2075b15..c6179b1d27491c8e17b67970ce915988facd3b1b 100644
--- a/module/Application/src/Application/Form/Service/RechercheHydrator.php
+++ b/module/Application/src/Application/Form/Service/RechercheHydrator.php
@@ -36,8 +36,16 @@ class RechercheHydrator implements HydratorInterface, ServiceLocatorAwareInterfa
         $id = isset($data['structure-ens']) ? (int)$data['structure-ens'] : null;
         $object->structureEns = $id ? $em->find('Application\Entity\Db\Structure', $id) : null;
 
+        $id = isset($data['statut-interv']) ? $data['statut-interv'] : null;
+        $object->statutInterv = $id ?: null;
+
+        // l'intervenant provient d'un SearchAndSelect
+        if (isset($data['intervenant']['id'])) {
+            $data['intervenant'] = $data['intervenant']['id'];
+        }
         $id = isset($data['intervenant']) ? (int)$data['intervenant'] : null;
-        $object->intervenant = $id ? $em->find('Application\Entity\Db\Intervenant', $id) : null;
+        $object->intervenant = $id ? $em->getRepository('Application\Entity\Db\Intervenant')->findOneBySourceCode($id) : null;
+        
         return $object;
     }
 
@@ -54,6 +62,7 @@ class RechercheHydrator implements HydratorInterface, ServiceLocatorAwareInterfa
             'element-pedagogique' => isset($object->elementPedagogique) && $object->elementPedagogique ? $object->elementPedagogique->getId() : null,
             'etape' => isset($object->etape) && $object->etape ? $object->etape->getId() : null,
             'structure-ens' => isset($object->structureEns) && $object->structureEns ? $object->structureEns->getId() : null,
+            'statut-interv' => isset($object->statutInterv) && $object->statutInterv ? $object->statutInterv : null,
         );
         return $data;
     }
diff --git a/module/Application/src/Application/Form/Service/Saisie.php b/module/Application/src/Application/Form/Service/Saisie.php
index 0ca619069ea304d3e301067cc469eea1421ae207..8de93cbd6c6e477babde9239b150c5120997555e 100644
--- a/module/Application/src/Application/Form/Service/Saisie.php
+++ b/module/Application/src/Application/Form/Service/Saisie.php
@@ -74,11 +74,14 @@ class Saisie extends Form implements \Zend\InputFilter\InputFilterProviderInterf
         ));
         $this->add($interneExterne);
 
+        /**
+         * @todo : fourrer l'init des URL dans la classe ElementPedagogiqueRechercheFieldset
+         */
         $queryTemplate = array('structure' => '__structure__', 'niveau' => '__niveau__', 'etape' => '__etape__');
-        $urlStructures = $url->fromRoute('of/default', array('action' => 'search-structures'), array('query' => $queryTemplate));
-        $urlNiveaux    = $url->fromRoute('of/default', array('action' => 'search-niveaux'), array('query' => $queryTemplate));
-        $urlEtapes     = $url->fromRoute('of/default', array('action' => 'search-etapes'), array('query' => $queryTemplate));
-        $urlElements   = $url->fromRoute('of/default', array('action' => 'search-element'), array('query' => $queryTemplate));
+        $urlStructures = $url->fromRoute('of/default',         array('action' => 'search-structures'), array('query' => $queryTemplate));
+        $urlNiveaux    = $url->fromRoute('of/default',         array('action' => 'search-niveaux'),    array('query' => $queryTemplate));
+        $urlEtapes     = $url->fromRoute('of/etape/default',   array('action' => 'search'),            array('query' => $queryTemplate));
+        $urlElements   = $url->fromRoute('of/element/default', array('action' => 'search'),            array('query' => $queryTemplate));
 
         $fs = new \Application\Form\OffreFormation\ElementPedagogiqueRechercheFieldset('elementPedagogique');
         $fs
diff --git a/module/Application/src/Application/Form/VolumeHoraire/Saisie.php b/module/Application/src/Application/Form/VolumeHoraire/Saisie.php
index 5b80f852a6fee2a9ae46d2378a6df9292da2f20d..5fe50729d22be23d57bc99d9590d41f5711e5528 100644
--- a/module/Application/src/Application/Form/VolumeHoraire/Saisie.php
+++ b/module/Application/src/Application/Form/VolumeHoraire/Saisie.php
@@ -3,24 +3,28 @@
 namespace Application\Form\VolumeHoraire;
 
 use Zend\Form\Form;
-use Zend\InputFilter\InputFilter;
-use Zend\Form\Element\Csrf;
-use Zend\Stdlib\Hydrator\ClassMethods;
+use Zend\InputFilter\InputFilterProviderInterface;
+use Zend\ServiceManager\ServiceLocatorAwareInterface;
+use Zend\ServiceManager\ServiceLocatorAwareTrait;
 use Zend\Form\Element\Hidden;
-use Application\Entity\Db\VolumeHoraire;
-use Zend\ServiceManager\ServiceLocatorInterface;
+use Application\Service\ContextProviderAwareInterface;
+use Application\Service\ContextProviderAwareTrait;
 
 /**
  * Description of Saisie
  *
  * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
  */
-class Saisie extends Form implements \Zend\InputFilter\InputFilterProviderInterface
+class Saisie extends Form implements InputFilterProviderInterface, ServiceLocatorAwareInterface, ContextProviderAwareInterface
 {
-    public function __construct( ServiceLocatorInterface $serviceLocator )
+    use ServiceLocatorAwareTrait;
+    use ContextProviderAwareTrait;
+    
+    /**
+     * 
+     */
+    public function init()
     {
-        parent::__construct('volume-horaire');
-        
         $this   ->setAttribute('method', 'post')
                 ->setAttribute('class', 'volume-horaire')
 //                ->setHydrator(new ClassMethods(false))
@@ -54,13 +58,21 @@ class Saisie extends Form implements \Zend\InputFilter\InputFilterProviderInterf
             'type' => 'Select'
         ));
 
-        $motifsNonPaiement = $serviceLocator->get('ApplicationMotifNonPaiement')->getMotifsNonPaiement();
-        foreach( $motifsNonPaiement as $id => $motifNonPaiement ){
-            $motifsNonPaiement[$id] = (string)$motifNonPaiement;
+        $role = $this->getContextProvider()->getSelectedIdentityRole();
+        
+        if ($role instanceof \Application\Acl\DbRole) {
+            $motifsNonPaiement = $this->getServiceLocator()->getServiceLocator()->get('ApplicationMotifNonPaiement')
+                    ->getMotifsNonPaiement();
+            foreach( $motifsNonPaiement as $id => $motifNonPaiement ){
+                $motifsNonPaiement[$id] = (string)$motifNonPaiement;
+            }
+            $motifsNonPaiement[0] = 'Aucun motif : paiement prévu';
+            $this->get('motifNonPaiement')->setValueOptions( $motifsNonPaiement );
         }
-        $motifsNonPaiement[0] = 'Aucun motif : paiement prévu';
-        $this->get('motifNonPaiement')->setValueOptions( $motifsNonPaiement );
-
+        else {
+            $this->remove('motifNonPaiement');
+        }
+        
         $this->add( new Hidden('id') );
         $this->add( new Hidden('service') );
         $this->add( new Hidden('periode') );
@@ -90,7 +102,8 @@ class Saisie extends Form implements \Zend\InputFilter\InputFilterProviderInterf
     }
 
     /* Associe une entity VolumeHoraire au formulaire */
-    public function bind( $object, $flags=17){
+    public function bind( $object, $flags=17)
+    {
         /* @var $object \Application\Entity\Db\VolumeHoraire */
         $data = array(
             'id'               => $object->getId(),
@@ -109,7 +122,8 @@ class Saisie extends Form implements \Zend\InputFilter\InputFilterProviderInterf
      *
      * @return array
      */
-    public function getInputFilterSpecification(){
+    public function getInputFilterSpecification()
+    {
         return array(
             'motifNonPaiement' => array(
                 'required' => false
diff --git a/module/Application/src/Application/Provider/Identity/IdentityProvider.php b/module/Application/src/Application/Provider/Identity/IdentityProvider.php
index 9266a8602ed589eac7e5349a7b40d56e3b7b8e3b..da1ce3be18ddfc398bfd859407abe334cb7c1fda 100644
--- a/module/Application/src/Application/Provider/Identity/IdentityProvider.php
+++ b/module/Application/src/Application/Provider/Identity/IdentityProvider.php
@@ -1,14 +1,23 @@
 <?php
 namespace Application\Provider\Identity;
 
-use Zend\ServiceManager\ServiceLocatorAwareInterface;
-use Zend\ServiceManager\ServiceLocatorAwareTrait;
+use Application\Acl\DbRole;
+use Application\Acl\IntervenantExterieurRole;
+use Application\Acl\IntervenantPermanentRole;
+use Application\Acl\IntervenantRole;
+use Application\Entity\Db\IntervenantExterieur;
+use Application\Entity\Db\IntervenantPermanent;
+use Application\Entity\Db\Role;
+use Application\Entity\Db\Utilisateur;
+use Common\Exception\RuntimeException;
 use Doctrine\ORM\EntityManager;
-use UnicaenAuth\Provider\Identity\ChainableProvider;
 use UnicaenApp\Service\EntityManagerAwareInterface;
-use Application\Acl\IntervenantRole;
-use Application\Acl\DbRole;
 use UnicaenApp\Service\EntityManagerAwareTrait;
+use UnicaenAuth\Provider\Identity\ChainableProvider;
+use UnicaenAuth\Provider\Identity\ChainEvent;
+use Zend\Permissions\Acl\Role\RoleInterface;
+use Zend\ServiceManager\ServiceLocatorAwareInterface;
+use Zend\ServiceManager\ServiceLocatorAwareTrait;
 
 /**
  * Classe chargée de fournir les rôles que possède l'identité authentifiée.
@@ -39,7 +48,7 @@ class IdentityProvider implements ServiceLocatorAwareInterface, ChainableProvide
     /**
      * {@inheritDoc}
      */
-    public function injectIdentityRoles(\UnicaenAuth\Provider\Identity\ChainEvent $event)
+    public function injectIdentityRoles(ChainEvent $event)
     {
         $event->addRoles($this->getIdentityRoles());
     }
@@ -62,9 +71,9 @@ class IdentityProvider implements ServiceLocatorAwareInterface, ChainableProvide
             $this->roles = array_merge($this->roles, $this->getDbRoles());
             
             /**
-             * Tout le monde possède le rôle "intervenant"
+             * Rôle correspondant au type d'intervenant auquel appartient l'utilisateur
              */
-            $this->roles[] = new IntervenantRole();
+            $this->roles[] = $this->getIntervenantRole();
         }
         
 //        var_dump($this->roles);
@@ -79,17 +88,49 @@ class IdentityProvider implements ServiceLocatorAwareInterface, ChainableProvide
      */
     protected function getDbRoles()
     {
-        $dbUser = $this->getServiceLocator()->get('AuthUserContext')->getDbUser(); /* @var $dbUser \Application\Entity\Db\Utilisateur */
+        $dbUser = $this->getServiceLocator()->get('AuthUserContext')->getDbUser(); /* @var $dbUser Utilisateur */
         
         if (!$dbUser) {
             return array();
         }
         
         $roles = array();
-        foreach ($dbUser->getPersonnel()->getRole() as $role) { /* @var $role \Application\Entity\Db\Role */
-            $roles[] = new DbRole($role->getType(), $role->getStructure());
+        foreach ($dbUser->getPersonnel()->getRole() as $role) { /* @var $role Role */
+            $roles[] = DbRole::createRoleId($role->getType(), $role->getStructure()); // le role id suffit, pas besoin d'instance
         }
         
         return $roles;
     }
+    
+    /**
+     * Retourne le rôle correspondant au type d'intervenant auquel appartient l'utilisateur.
+     * 
+     * @return RoleInterface
+     */
+    protected function getIntervenantRole()
+    {
+        $dbUser = $this->getServiceLocator()->get('AuthUserContext')->getDbUser(); /* @var $dbUser Utilisateur */
+        
+        if (!$dbUser) {
+            return array();
+        }
+        
+        $intervenant = $dbUser->getIntervenant();
+        
+        if (!$intervenant) {
+            return IntervenantRole::ROLE_ID;
+        }
+        
+        if ($intervenant instanceof IntervenantPermanent) {
+            $role = IntervenantPermanentRole::ROLE_ID;
+        }
+        elseif ($intervenant instanceof IntervenantExterieur) {
+            $role = IntervenantExterieurRole::ROLE_ID;
+        }
+        else {
+            throw new RuntimeException("Type d'intervenant inattendu : " . get_class($intervenant));
+        }
+        
+        return $role;
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Provider/Role/RoleProvider.php b/module/Application/src/Application/Provider/Role/RoleProvider.php
index 01968063e4db489a82eaa74692334bdf60de5d86..ee6247f25fa9a285258958f3a9c17b1fb461a470 100644
--- a/module/Application/src/Application/Provider/Role/RoleProvider.php
+++ b/module/Application/src/Application/Provider/Role/RoleProvider.php
@@ -7,7 +7,11 @@ use Doctrine\ORM\EntityManager;
 use UnicaenApp\Service\EntityManagerAwareInterface;
 use UnicaenApp\Service\EntityManagerAwareTrait;
 use Application\Acl\IntervenantRole;
+use Application\Acl\IntervenantPermanentRole;
+use Application\Acl\IntervenantExterieurRole;
 use Application\Acl\DbRole;
+use Application\Acl\ComposanteRole;
+use Application\Entity\Db\Role as RoleEntity;
 
 /**
  * Fournisseur des rôles utilisateurs de l'application :
@@ -52,12 +56,14 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface
     {
         if (null === $this->roles) {            
             /**
-             * Rôle de base "intervenant"
+             * Rôles "intervenant"
              */
-            $roleIntervenant = new IntervenantRole();
+            $roleIntervenant          = new IntervenantRole();
+            $roleIntervenantPermanent = new IntervenantPermanentRole();
+            $roleIntervenantExterieur = new IntervenantExterieurRole();
             
             /**
-             * Rôles exercés sur une structure de niveau 2 porteuse d'éléments pédagogiques
+             * Rôles "composante" : exercés sur une structure de niveau 2 PORTEUSE d'éléments pédagogiques
              */
             $qb = $this->getEntityManager()->getRepository('Application\Entity\Db\Role')->createQueryBuilder('r')
                     ->select('r, tr, s')
@@ -67,23 +73,64 @@ class RoleProvider implements ProviderInterface, EntityManagerAwareInterface
                     ->innerJoin('s.elementPedagogique', 'ep')
                     ->where('tr.code <> :code')->setParameter('code', 'IND')
                     ->andWhere('s.niveau = :niv')->setParameter('niv', 2);
-            $dbRoles = $qb->getQuery()->getResult();
-
+            $rolesComposante = $qb->getQuery()->getResult();
+            
+            /**
+             * Rôles "autres" : exercés sur une structure de niveau 2 NON PORTEUSE d'éléments pédagogiques
+             */
+            $qb = $this->getEntityManager()->getRepository('Application\Entity\Db\Role')->createQueryBuilder('r')
+                    ->select('r, tr, s')
+                    ->distinct()
+                    ->innerJoin('r.type', 'tr')
+                    ->innerJoin('r.structure', 's')
+                    ->where('tr.code <> :code')->setParameter('code', 'IND')
+                    ->andWhere('s.niveau = :niv')->setParameter('niv', 2)
+                    ->andWhere('SIZE(s.elementPedagogique) = 0');
+            $rolesAutres = $qb->getQuery()->getResult();
+            
+//            var_dump($qb->getQuery()->getSQL());
+//            foreach ($dbRoles as $r) { /* @var $r \Application\Entity\Db\Role */
+//                var_dump($r->getType() . "", "" . $r->getStructure() );
+//            }
+            
             /**
              * Collecte des rôles
              */
-            $this->roles = array();
-            $this->roles[$roleIntervenant->getRoleId()] = $roleIntervenant;
-            foreach ($dbRoles as $r) { /* @var $r \Application\Entity\Db\Role */
+            $roles = array();
+            $roles[$roleIntervenant->getRoleId()]          = $roleIntervenant;
+            $roles[$roleIntervenantPermanent->getRoleId()] = $roleIntervenantPermanent;
+            $roles[$roleIntervenantExterieur->getRoleId()] = $roleIntervenantExterieur;
+            foreach ($rolesComposante as $r) { /* @var $r \Application\Entity\Db\Role */
+                $role = new ComposanteRole($r->getType(), $r->getStructure(), null);
+                $roles[$role->getRoleId()] = $role;
+            }
+            foreach ($rolesAutres as $r) { /* @var $r \Application\Entity\Db\Role */
                 $role = new DbRole($r->getType(), $r->getStructure(), null);
-                $this->roles[$role->getRoleId()] = $role;
+                $roles[$role->getRoleId()] = $role;
             }
+            
+            $this->roles = $roles;
         }
         
-//        foreach ($this->roles as $r) { /* @var $r \Zend\Permissions\Acl\Role\RoleInterface */
-//            var_dump($r->getRoleId());
-//        }
+//        var_dump(array_keys($this->roles));
         
         return $this->roles;
     }
+    
+    /**
+     * 
+     * @param \Application\Entity\Db\Role $roleEntity
+     * @return \Zend\Permissions\Acl\Role\GenericRole
+     */
+    public function getRoleFromRoleEntity(RoleEntity $roleEntity)
+    {
+        $roles  = $this->getRoles();
+        $roleId = DbRole::createRoleId($roleEntity->getType(), $roleEntity->getStructure());
+        
+        if (isset($roles[$roleId])) {
+            return $roles[$roleId];
+        }
+        
+        return new \Zend\Permissions\Acl\Role\GenericRole($roleId);
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/AbstractContext.php b/module/Application/src/Application/Service/AbstractContext.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ea5887c880e567e09384895853791e4820b36d0
--- /dev/null
+++ b/module/Application/src/Application/Service/AbstractContext.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Application\Service;
+
+use Application\Entity\Db\Annee;
+use Application\Entity\Db\Intervenant as EntityIntervenant;
+use Application\Entity\Db\Utilisateur;
+
+/**
+ * Classe mère des classes de contexte.
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+abstract class AbstractContext
+{
+    /**
+     * @var Utilisateur
+     */
+    protected $utilisateur;
+    
+    /**
+     * @var EntityIntervenant
+     */
+    protected $intervenant;
+    
+    /**
+     * @var Annee
+     */
+    protected $annee;
+    
+    /**
+     * Retourne la valeur d'un attribut.
+     * 
+     * @param string $name
+     * @return null
+     */
+    public function get($name)
+    {
+        if (method_exists($this, $method = 'get' . ucfirst($name))) {
+            return $this->$method();
+        }
+        
+        return null;
+    }
+    
+    public function getUtilisateur()
+    {
+        return $this->utilisateur;
+    }
+
+    public function getIntervenant()
+    {
+        return $this->intervenant;
+    }
+
+    public function getAnnee()
+    {
+        return $this->annee;
+    }
+
+    public function setUtilisateur(Utilisateur $utilisateur = null)
+    {
+        $this->utilisateur = $utilisateur;
+        return $this;
+    }
+
+    public function setIntervenant(EntityIntervenant $intervenant = null)
+    {
+        $this->intervenant = $intervenant;
+        return $this;
+    }
+
+    public function setAnnee(Annee $annee = null)
+    {
+        $this->annee = $annee;
+        return $this;
+    }
+
+    public function fromArray(array $context = array())
+    {
+        $h = new \Zend\Stdlib\Hydrator\ObjectProperty();
+        $h->hydrate($context, $this);
+        return $this;
+    }
+
+    public function __set($name, $value)
+    {
+        if (method_exists($this, $method = 'set' . ucfirst($name))) {
+            return $this->$method($value);
+        }
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/AbstractService.php b/module/Application/src/Application/Service/AbstractService.php
index cf899998e3f68b62bb381a995febc6837fb40eb5..11841f288ca297ddba6e0602b817060ea2c793fc 100644
--- a/module/Application/src/Application/Service/AbstractService.php
+++ b/module/Application/src/Application/Service/AbstractService.php
@@ -16,10 +16,11 @@ use BjyAuthorize\Exception\UnAuthorizedException;
  *
  * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
  */
-class AbstractService implements ServiceLocatorAwareInterface, EntityManagerAwareInterface
+class AbstractService implements ServiceLocatorAwareInterface, EntityManagerAwareInterface, ContextProviderAwareInterface
 {
     use ServiceLocatorAwareTrait;
     use EntityManagerAwareTrait;
+    use ContextProviderAwareTrait;
 
     /**
      * Retourne le gestionnaire d'entités Doctrine
@@ -44,8 +45,13 @@ class AbstractService implements ServiceLocatorAwareInterface, EntityManagerAwar
      */
     public function cannotDoThat($why, $runEx=false)
     {
-        if ($runEx){
-            throw new UnAuthorizedException($why);
+        if ($runEx) {
+            /**
+             * @todo: pb avec UnAuthorizedException : le code 403 est interprêté en AJAX comme une fin de session 
+             * (cf. unicaen.js)
+             */
+//            throw new UnAuthorizedException($why);
+            throw new \Common\Exception\MessageException($why);
         }
         return false;
     }
diff --git a/module/Application/src/Application/Service/CheminPedagogique.php b/module/Application/src/Application/Service/CheminPedagogique.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8152f3b9447752acedc8c23a345a2b1783a1299
--- /dev/null
+++ b/module/Application/src/Application/Service/CheminPedagogique.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Application\Service;
+
+/**
+ * Description of CheminPedagogique
+ *
+ * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
+ */
+class CheminPedagogique extends AbstractEntityService
+{
+    /**
+     * retourne la classe des entités
+     *
+     * @return string
+     * @throws RuntimeException
+     */
+    public function getEntityClass()
+    {
+        return 'Application\Entity\Db\CheminPedagogique';
+    }
+
+    /**
+     * Retourne l'alias d'entité courante
+     *
+     * @return string
+     */
+    public function getAlias() 
+    {
+        return 'cp';
+    }
+
+    /**
+     * Retourne une nouvelle entité, initialisée avec les bons paramètres
+     * @return EtapeEntity
+     */
+    public function newEntity()
+    {
+        $entity = parent::newEntity();
+        // toutes les entités créées ont OSE pour source!!
+        $entity->setSource( $this->getServiceLocator()->get('ApplicationSource')->getOse() );
+        $entity->setOrdre(1);
+        return $entity;
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/ContextProvider.php b/module/Application/src/Application/Service/ContextProvider.php
index 6ef79f8f920c7dbf952a2dc1b3c5ac4c5915cffa..146806cc2e00fc226f74b44ec59a20d09194d59c 100644
--- a/module/Application/src/Application/Service/ContextProvider.php
+++ b/module/Application/src/Application/Service/ContextProvider.php
@@ -23,6 +23,11 @@ class ContextProvider extends AbstractService
      */
     protected $globalContext;
     
+    /**
+     * @var LocalContext
+     */
+    protected $localContext;
+    
     /**
      * Retourne le contexte global.
      * 
@@ -41,7 +46,7 @@ class ContextProvider extends AbstractService
 
             if (null === $intervenant) {
                 $ldapUser = $authUserContext->getLdapUser();
-                $intervenant = $this->getServiceLocator()->get('ApplicationIntervenant')->importer($ldapUser->getSupannEmpId());
+                $intervenant = $this->getServiceLocator()->get('ApplicationIntervenant')->importer((int) $ldapUser->getSupannEmpId());
             }
             
             $this->globalContext = new GlobalContext();
@@ -56,6 +61,20 @@ class ContextProvider extends AbstractService
         return $this->globalContext;
     }
     
+    /**
+     * Retourne le contexte local (filtres, etc.)
+     * 
+     * @return LocalContext
+     */
+    public function getLocalContext()
+    {
+        if (null === $this->localContext) {
+            $this->localContext = new LocalContext($this->getEntityManager());
+        }
+        
+        return $this->localContext;
+    }
+    
     /**
      * 
      * @return DbRole|IntervenantRole
diff --git a/module/Application/src/Application/Service/ElementPedagogique.php b/module/Application/src/Application/Service/ElementPedagogique.php
index 04126ac51a216da93b904d7454c87bf6f417eaec..2976847fdf4fcb828d27229f4e3ccddbcb970630 100644
--- a/module/Application/src/Application/Service/ElementPedagogique.php
+++ b/module/Application/src/Application/Service/ElementPedagogique.php
@@ -9,7 +9,6 @@ namespace Application\Service;
  */
 class ElementPedagogique extends AbstractEntityService
 {
-
     /**
      * retourne la classe des entités
      *
@@ -26,7 +25,376 @@ class ElementPedagogique extends AbstractEntityService
      *
      * @return string
      */
-    public function getAlias(){
+    public function getAlias() 
+    {
         return 'ep';
     }
+    
+    /**
+     * Retourne le chercheur des structures distinctes.
+     * 
+     * @param array $filters
+     * @param \Doctrine\ORM\QueryBuilder $qb
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function finder(array $filters = array(), \Doctrine\ORM\QueryBuilder $qb = null)
+    {
+        if (null === $qb) {
+            $qb = $this->getEntityManager()->createQueryBuilder();
+        }
+
+        $qb
+                ->select('ep, e, tf, gtf, p')
+                ->from('Application\Entity\Db\ElementPedagogique', 'ep')
+                ->leftJoin('ep.periode', 'p')
+                ->innerJoin('ep.etape', 'e')
+                ->innerJoin('e.typeFormation', 'tf')
+                ->innerJoin('tf.groupe', 'gtf')
+                ->innerJoin('ep.structure', 's')
+                ->orderBy('gtf.ordre, e.niveau, e.sourceCode, ep.libelle');
+        
+        if (isset($filters['structure'])) {
+            $qb->andWhere('s.structureNiv2 = :structure')->setParameter('structure', $filters['structure']);
+        }
+        
+        if (isset($filters['niveau']) && $filters['niveau']) {
+            if ($filters['niveau'] instanceof \Application\Entity\NiveauEtape) {
+                $filters['niveau'] = $filters['niveau']->getId();
+            }
+            $niveau = str_replace('-', '', $filters['niveau']);
+            $qb->andWhere('CONCAT(gtf.libelleCourt, e.niveau) = :niveau')->setParameter('niveau', $niveau);
+        }
+        
+        if (isset($filters['etape'])) {
+            if (!$filters['etape'] instanceof \Application\Entity\Db\Etape) {
+                throw new \Common\Exception\LogicException("L'étape spécifiée dans le contexte n'est pas du type attendu.");
+            }
+            $qb->andWhere('ep.etape = :etape')->setParameter('etape', $filters['etape']);
+        }
+        
+        return $qb;
+    }
+    
+    /**
+     * Retourne le chercheur des structures distinctes.
+     * 
+     * @param array $filters
+     * @param \Doctrine\ORM\QueryBuilder $qb
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function finderDistinctStructures(array $filters = array(), \Doctrine\ORM\QueryBuilder $qb = null)
+    {
+        if (null === $qb) {
+            $qb = new \Doctrine\ORM\QueryBuilder($this->getEntityManager());
+        }
+        
+        $qb
+                ->select('s')
+                ->distinct()
+                ->from('Application\Entity\Db\Structure', 's')
+                ->innerJoin('s.elementPedagogique', 'ep')
+                ->innerJoin('ep.etape', 'e')
+                ->innerJoin('e.typeFormation', 'tf')
+                ->innerJoin('tf.groupe', 'gtf')
+                ->orderBy('s.libelleCourt');
+        
+        if (isset($filters['niveau']) && is_numeric($filters['niveau'])) {
+            $qb->where('s.niveau = :niv')->setParameter('niv', $filters['niveau']);
+        }
+        
+        return $qb;
+    }
+    
+    /**
+     * Retourne le chercheur des niveaux distincts.
+     * 
+     * @param array $filters
+     * @param \Doctrine\ORM\QueryBuilder $qbWithEp
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function finderDistinctNiveaux(array $filters = array(), \Doctrine\ORM\QueryBuilder $qb = null)
+    {
+        $qb = new \Doctrine\ORM\QueryBuilder($this->getEntityManager());
+        $qb
+                ->select('e, tf, gtf')
+                ->distinct()
+                ->from('Application\Entity\Db\Etape', 'e')
+                ->innerJoin('e.typeFormation', 'tf')
+                ->innerJoin('tf.groupe', 'gtf')
+                ->innerJoin('e.elementPedagogique', 'ep')
+//                ->innerJoin('ep.structure', 's')
+                ->orderBy('gtf.ordre, e.niveau');
+        
+        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+            $qb
+                    ->andWhere('ep.structure = :struct')
+                    ->setParameter('struct', $filters['structure']);
+        }
+        
+//        var_dump($qb->getQuery()->getSQL());
+        return $qb;
+    }
+    
+    /**
+     * Retourne le chercheur d'étapes distinctes.
+     * 
+     * @param array $filters
+     * @param \Doctrine\ORM\QueryBuilder $qb
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function finderDistinctEtapes(array $filters = array(), \Doctrine\ORM\QueryBuilder $qb = null)
+    {
+        $qb = new \Doctrine\ORM\QueryBuilder($this->getEntityManager());
+        $qb
+                ->select('e, tf, gtf')
+                ->distinct()
+                ->from('Application\Entity\Db\Etape', 'e')
+                ->innerJoin('e.elementPedagogique', 'ep')
+                ->leftJoin('ep.periode', 'p')
+                ->innerJoin('e.typeFormation', 'tf')
+                ->innerJoin('tf.groupe', 'gtf')
+                ->innerJoin('ep.structure', 's')
+                ->orderBy('gtf.ordre, e.niveau, e.sourceCode');
+        
+//        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+//            $qb->andWhere('ep.structure = :struct')->setParameter('struct', $filters['structure']);
+//        }
+        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+            $qb->andWhere('s.structureNiv2 = :structure')->setParameter('structure', $filters['structure']);
+        }
+        if (isset($filters['niveau']) && $filters['niveau']) {
+            if ($filters['niveau'] instanceof \Application\Entity\NiveauEtape) {
+                $filters['niveau'] = $filters['niveau']->getId();
+            }
+            $niveau = str_replace('-', '', $filters['niveau']);
+            $qb->andWhere('CONCAT(gtf.libelleCourt, e.niveau) = :niveau')->setParameter('niveau', $niveau);
+        }
+//        print_r($qb->getQuery()->getSQL());die;
+        return $qb;
+    }
+    
+    /**
+     * Retourne le chercheur d'étapes orphelines (i.e. sans EP).
+     * 
+     * @param array $filters
+     * @param \Doctrine\ORM\QueryBuilder $qb
+     * @return \Doctrine\ORM\QueryBuilder
+     */
+    public function finderDistinctEtapesOrphelines(array $filters = array(), \Doctrine\ORM\QueryBuilder $qb = null)
+    {
+        $qb = new \Doctrine\ORM\QueryBuilder($this->getEntityManager());
+        $qb
+                ->select('e, tf, gtf')
+                ->distinct()
+                ->from('Application\Entity\Db\Etape', 'e')
+//                ->innerJoin('e.elementPedagogique', 'ep')
+//                ->leftJoin('ep.periode', 'p')
+                ->innerJoin('e.typeFormation', 'tf')
+                ->innerJoin('tf.groupe', 'gtf')
+                ->innerJoin('e.structure', 's')
+                ->andWhere('SIZE (e.elementPedagogique) < 1')
+                ->orderBy('gtf.ordre, e.niveau, e.sourceCode');
+        
+//        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+//            $qb->andWhere('ep.structure = :struct')->setParameter('struct', $filters['structure']);
+//        }
+        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+            $qb->andWhere('s.structureNiv2 = :structure')->setParameter('structure', $filters['structure']);
+        }
+        if (isset($filters['niveau']) && $filters['niveau']) {
+            if ($filters['niveau'] instanceof \Application\Entity\NiveauEtape) {
+                $filters['niveau'] = $filters['niveau']->getId();
+            }
+            $niveau = str_replace('-', '', $filters['niveau']);
+            $qb->andWhere('CONCAT(gtf.libelleCourt, e.niveau) = :niveau')->setParameter('niveau', $niveau);
+        }
+        
+        return $qb;
+    }
+    
+    /**
+     * Recherche textuelle d'element pédagogique.
+     * 
+     * @param array $filters
+     * <p>Paramètres possibles :</p>
+     * <i>term</i>         : Texte recherché <b>obligatoire</b><br />
+     * <i>limit</i>        : Nombre de résultats maxi<br />
+     * <i>structure</i>    : Structure concernée sous forme d'une entité<br />
+     * <i>niveau</i>       : Niveau, i.e. CONCAT(gtf.libelle_court, e.niveau), ex: L1, M2<br />
+     * <i>etape</i>        : Etape concernée sous forme d'une entité<br />
+     * @return array
+     */
+    public function finderByTerm(array $filters = array())
+    {
+        if (!isset($filters['term'])) {
+            return array();
+        }
+        if (!isset($filters["limit"])) {
+            $filters["limit"] = 100;
+        }
+        
+        $term      = preg_replace('#\s{2,}#', ' ', trim($filters['term']));
+        $criterion = explode(' ', $term);
+
+        $concat = "ep.source_code || ' ' || ep.libelle|| ' ' || e.source_code || ' ' || e.libelle || ' ' || gtf.LIBELLE_COURT || ' ' || e.NIVEAU || ' ' || tf.LIBELLE_COURT";
+        $parts  = $params = array();
+        for ($i = 0; $i < count($criterion); $i++) {
+            $parts[] = "(UPPER(CONVERT($concat, 'US7ASCII')) LIKE UPPER(CONVERT(:criterionStr$i, 'US7ASCII'))) ";
+            $params["criterionStr$i"] = '%' . $criterion[$i] . '%';
+        }
+        $whereTerm = implode(' AND ', $parts);
+        
+        $whereContext = array();
+        if (isset($filters['structure']) && $filters['structure'] instanceof \Application\Entity\Db\Structure) {
+            $whereContext[] = 's.structure_niv2_id = :structure';
+            $params['structure'] = $filters['structure']->getId();
+        }
+        if (isset($filters['niveau']) && $filters['niveau']) {
+            if ($filters['niveau'] instanceof \Application\Entity\NiveauEtape) {
+                $filters['niveau'] = $filters['niveau']->getId();
+            }
+            $niveau = str_replace('-', '', $filters['niveau']);
+            $whereContext[] = 'CONCAT(gtf.libelle_court, e.niveau) = :niveau';
+            $params['niveau'] = $niveau;
+        }
+        if (isset($filters['etape']) && $filters['etape'] instanceof \Application\Entity\Db\Etape) {
+            $whereContext[] = 'ep.etape_id = :etape';
+            $params['etape'] = $filters['etape']->getId();
+        }
+        $whereContext = implode(PHP_EOL . 'AND ', array_filter($whereContext));
+        $whereContext = $whereContext ? 'AND ' . $whereContext : null;
+        
+        $sql = <<<EOS
+select * from (
+  select ep.id,
+    rank() over (partition by ep.id order by cp.ordre) rang,
+    ep.source_code, ep.libelle, e.libelle libelle_etape, e.niveau, pe.libelle_long libelle_pe, gtf.libelle_court libelle_gtf, tf.libelle_long libelle_tf,
+    ep.source_code || ' ' || ep.libelle|| ' ' || e.source_code || ' ' || e.libelle || ' ' || gtf.LIBELLE_COURT || ' ' || e.NIVEAU || ' ' || tf.LIBELLE_COURT etape_info
+  from chemin_pedagogique cp
+  JOIN element_pedagogique ep ON cp.element_pedagogique_id = ep.id  and  ep.HISTO_DESTRUCTEUR_ID is null and sysdate between ep.VALIDITE_DEBUT and nvl(ep.VALIDITE_FIN, sysdate)
+  JOIN etape e ON cp.etape_id = e.id                                and   e.HISTO_DESTRUCTEUR_ID is null and sysdate between  e.VALIDITE_DEBUT and nvl( e.VALIDITE_FIN, sysdate)
+  JOIN TYPE_FORMATION tf on e.TYPE_FORMATION_ID = tf.ID             and  tf.HISTO_DESTRUCTEUR_ID is null and sysdate between tf.VALIDITE_DEBUT and nvl(tf.VALIDITE_FIN, sysdate)
+  JOIN GROUPE_TYPE_FORMATION gtf on tf.GROUPE_ID = gtf.ID           and gtf.HISTO_DESTRUCTEUR_ID is null
+  JOIN structure s ON ep.structure_id = s.id
+  LEFT JOIN periode pe ON ep.periode_id = pe.id
+  where cp.HISTO_DESTRUCTEUR_ID is null and sysdate between cp.VALIDITE_DEBUT and nvl(cp.VALIDITE_FIN, sysdate)
+  and $whereTerm
+  $whereContext
+  order by gtf.ordre, e.niveau, ep.libelle
+)
+where rang = 1
+EOS;
+        
+        $params["limit"] = $filters["limit"];
+        
+        $result = $this->getEntityManager()->getConnection()->executeQuery($sql, $params);
+//        var_dump($sql, $params);die;
+
+        return $result->fetchAll();
+    }
+
+    /**
+     * 
+     * @param array $result
+     * @param integer $length
+     * @return array
+     */
+    protected function truncateResult($result, $length = 15)
+    {
+        if ($length && ($remain = count($result) - $length) > 0) {
+            $result   = array_slice($result, 0, $length);
+            $result[] = array('id'    => null, 'label' => "<em><small>$remain résultats restant, affinez vos critères, svp.</small></em>");
+        }
+        return $result;
+    }
+    
+    /**
+     * Détermine si on peut ajouter une étape ou non
+     *
+     * @return boolean
+     */
+    public function canAdd($runEx = false)
+    {
+        $localContext = $this->getContextProvider()->getLocalContext();
+        $role         = $this->getServiceLocator()->get('ApplicationContextProvider')->getSelectedIdentityRole();
+        
+        if ($role instanceof \Application\Acl\DbRole) { 
+            if (!$localContext->getStructure()) {
+                throw new \Common\Exception\LogicException("Le filtre structure est requis dans la méthode " . __METHOD__);
+            }
+            if ($localContext->getStructure()->getId() === $role->getStructure()->getId()
+                    || $localContext->getStructure()->estFilleDeLaStructureDeNiv2($role->getStructure())) {
+                return true;
+            }
+            
+            $this->cannotDoThat(
+                    "Votre structure de responsabilité ('{$role->getStructure()}') ne vous permet pas d'ajouter/modifier d'élément pédagogique"
+                    . "pour la structure '{$localContext->getStructure()}'", $runEx);
+        }
+
+        return $this->cannotDoThat('Vous n\'avez pas les droits nécessaires pour ajouter/modifier un élément pédagogique', $runEx);
+    }
+
+    /**
+     * Retourne une nouvelle entité, initialisée avec les bons paramètres
+     * @return EtapeEntity
+     */
+    public function newEntity()
+    {
+        $this->canAdd(true);
+        $entity = parent::newEntity();
+        // toutes les entités créées ont OSE pour source!!
+        $entity->setSource( $this->getServiceLocator()->get('ApplicationSource')->getOse() );
+        return $entity;
+    }
+
+    /**
+     * Sauvegarde une entité
+     *
+     * @param \Application\Entity\Db\ElementPedagogique $entity
+     * @throws \Common\Exception\RuntimeException
+     */
+    public function save($entity)
+    {
+        // si absence de chemin pédagogique, création du chemin
+        if (!$entity->getCheminPedagogique()->count()) {
+            $cp = $this->getServiceCheminPedagogique()->newEntity(); /* @var $cp \Application\Entity\Db\CheminPedagogique */
+            $cp
+                    ->setEtape($entity->getEtape())
+                    ->setElementPedagogique($entity);
+            
+            $entity->addCheminPedagogique($cp);
+            
+            $this->getEntityManager()->persist($cp);
+        }
+        
+        parent::save($entity);
+    }
+
+    /**
+     * Supprime (historise par défaut) le service spécifié.
+     *
+     * @param \Application\Entity\Db\ElementPedagogique $entity Entité à détruire
+     * @param bool $softDelete
+     * @return self
+     */
+    public function delete($entity, $softDelete = true)
+    {
+        foreach ($entity->getCheminPedagogique() as $cp) { /* @var $cp \Application\Entity\Db\CheminPedagogique */
+            $cp->getEtape()->removeCheminPedagogique($cp);
+            $entity->removeCheminPedagogique($cp);
+            $this->getServiceCheminPedagogique()->delete($cp);
+        }
+        
+        return parent::delete($entity, $softDelete);
+    }
+    
+    /**
+     * 
+     * @return CheminPedagogique
+     */
+    protected function getServiceCheminPedagogique()
+    {
+        return $this->getServiceLocator()->get('ApplicationCheminPedagogique');
+    }
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/Etape.php b/module/Application/src/Application/Service/Etape.php
index 6f18f37b88020fe685133349c20f83e53a12c437..7c882dd5c4406960eef06b662f006e63ebc87753 100644
--- a/module/Application/src/Application/Service/Etape.php
+++ b/module/Application/src/Application/Service/Etape.php
@@ -33,34 +33,76 @@ class Etape extends AbstractEntityService
         return 'etp';
     }
 
+    /**
+     * 
+     * @param \Application\Entity\NiveauEtape $niveau
+     * @param \Doctrine\ORM\QueryBuilder $qb
+     * @param string $alias
+     * @return QueryBuilder
+     */
+    public function finderByNiveau(\Application\Entity\NiveauEtape $niveau, QueryBuilder $qb=null, $alias=null)
+    {
+        list($qb, $alias) = $this->initQuery($qb, $alias);
+        $qb
+                ->innerJoin("$alias.typeFormation", 'tf')
+                ->innerJoin("tf.groupe", 'gtf')
+                ->andWhere("$alias.niveau = :niv AND gtf.libelleCourt = :lib")
+                ->setParameter('niv', $niveau->getNiv())
+                ->setParameter('lib', $niveau->getLib());
+        
+        return parent::getList($qb, $alias);
+    }
+    
     /**
      * Détermine si on peut ajouter une étape ou non
      *
      * @return boolean
      */
-    public function canAdd($runEx=false)
+    public function canAdd($runEx = false)
     {
-        $role = $this->getServiceLocator()->get('ApplicationContextProvider')->getSelectedIdentityRole();
-        if ($role instanceof \Application\Acl\IntervenantRole){ /** @todo refactoriser car pas très sûr */
-            return $this->cannotDoThat('Vous n\'avez pas les droits nécessaires pour ajouter ou modifier une formation', $runEx);
+        $localContext = $this->getContextProvider()->getLocalContext();
+        $role         = $this->getServiceLocator()->get('ApplicationContextProvider')->getSelectedIdentityRole();
+        
+        if ($role instanceof \Application\Acl\DbRole) { 
+            if (!$localContext->getStructure()) {
+                throw new \Common\Exception\LogicException("Le filtre structure est requis dans la méthode " . __METHOD__);
+            }
+            if ($localContext->getStructure()->getId() === $role->getStructure()->getId()
+                    || $localContext->getStructure()->estFilleDeLaStructureDeNiv2($role->getStructure())) {
+                return true;
+            }
+            
+            $this->cannotDoThat(
+                    "Votre structure de responsabilité ('{$role->getStructure()}') ne vous permet pas d'ajouter/modifier d'étape"
+                    . "pour la structure '{$localContext->getStructure()}'", $runEx);
         }
-        return true;
+
+        return $this->cannotDoThat('Vous n\'avez pas les droits nécessaires pour ajouter ou modifier une étape', $runEx);
     }
 
     /**
      * Détermine si l'étape peut être éditée ou non
      * 
-     * @param \Application\Entity\Db\Etape $etape
+     * @param int|\Application\Entity\Db\Etape $etape
      * @return boolean
      */
-    public function canSave(EtapeEntity $etape, $runEx=false)
+    public function canSave($etape, $runEx = false)
     {
-        if (! $this->canAdd($runEx)) return false;
+        if (! $this->canAdd($runEx)) {
+            return false;
+        }
+        
+        if (!$etape instanceof EtapeEntity) {
+            $etape = $this->get($etape);
+        }
+        
         if ($etape->getSource()->getCode() !== \Application\Entity\Db\Source::CODE_SOURCE_OSE){
             $errStr = 'Cette formation n\'est pas modifiable dans OSE car elle provient du logiciel '.$etape->getSource();
             $errStr .= '. Si vous souhaitez mettre à jour ces informations, nous vous invitons donc à les modifier directement dans '.$etape->getSource().'.';
+            
             return $this->cannotDoThat($errStr, $runEx);
         }
+        
         return true;
     }
 
diff --git a/module/Application/src/Application/Service/Intervenant.php b/module/Application/src/Application/Service/Intervenant.php
index d72264a5ac8fabad07958f500b4ed9608693e4dd..4f18ae93fb0ff6ef1a397b84d2e14df1383b8278 100644
--- a/module/Application/src/Application/Service/Intervenant.php
+++ b/module/Application/src/Application/Service/Intervenant.php
@@ -19,7 +19,49 @@ use Import\Processus\Import;
  */
 class Intervenant extends AbstractEntityService
 {
-    use \Application\Service\ContextProviderAwareTrait;
+    /**
+     * Recherche par :
+     * - id source exact (numéro Harpege ou autre), 
+     * - ou nom usuel (et prénom), 
+     * - ou nom patronymique (et prénom).
+     * 
+     * @param string $term
+     * @return QueryBuilder
+     */
+    public function finderByNomPrenomId($term)
+    {
+        $term = str_replace(' ', '', $term);
+        
+        $qb = $this->getRepo()->createQueryBuilder('i');
+        
+        $concatNomUsuelPrenom = new \Doctrine\ORM\Query\Expr\Func('CONVERT', 
+                array($qb->expr()->concat('i.nomUsuel', 'i.prenom'), 
+                '?3'));
+        $concatNomPatroPrenom = new \Doctrine\ORM\Query\Expr\Func('CONVERT', 
+                array($qb->expr()->concat('i.nomPatronymique', 'i.prenom'), 
+                '?3'));
+        $concatPrenomNomUsuel = new \Doctrine\ORM\Query\Expr\Func('CONVERT', 
+                array($qb->expr()->concat('i.prenom', 'i.nomUsuel'), 
+                '?3'));
+        $concatPrenomNomPatro = new \Doctrine\ORM\Query\Expr\Func('CONVERT', 
+                array($qb->expr()->concat('i.prenom', 'i.nomPatronymique'), 
+                '?3'));
+        
+        $qb
+//                ->select('i.')
+                ->where('i.sourceCode = ?1')
+                ->orWhere($qb->expr()->like($qb->expr()->upper($concatNomUsuelPrenom), $qb->expr()->upper('CONVERT(?2, ?3)')))
+                ->orWhere($qb->expr()->like($qb->expr()->upper($concatNomPatroPrenom), $qb->expr()->upper('CONVERT(?2, ?3)')))
+                ->orWhere($qb->expr()->like($qb->expr()->upper($concatPrenomNomUsuel), $qb->expr()->upper('CONVERT(?2, ?3)')))
+                ->orWhere($qb->expr()->like($qb->expr()->upper($concatPrenomNomPatro), $qb->expr()->upper('CONVERT(?2, ?3)')))
+                ->orderBy('i.nomUsuel, i.prenom');
+        
+        $qb->setParameters(array(1 => $term, 2 => "%$term%", 3 => 'US7ASCII'));
+        
+//        print_r($qb->getQuery()->getSQL()); var_dump($qb->getQuery()->getParameters());die;
+        
+        return $qb;
+    }
     
     /**
      * 
diff --git a/module/Application/src/Application/Service/IntervenantFactory.php b/module/Application/src/Application/Service/IntervenantFactory.php
deleted file mode 100644
index 130cc86bbb45ca3e85028e407cb472596007fa37..0000000000000000000000000000000000000000
--- a/module/Application/src/Application/Service/IntervenantFactory.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace Application\Service;
-
-use Zend\ServiceManager\FactoryInterface;
-use Zend\ServiceManager\ServiceLocatorInterface;
-
-/**
- * Description of ServiceReferentielFactory
- *
- * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
- */
-class IntervenantFactory implements FactoryInterface
-{
-    /**
-     * Create service
-     *
-     * @param ServiceLocatorInterface $serviceLocator
-     * @return mixed
-     */
-    public function createService(ServiceLocatorInterface $serviceLocator)
-    {
-        $service = new Intervenant();
-        $service->setEntityManager($serviceLocator->get('doctrine.entitymanager.orm_default'));
-        $service->setContextProvider($serviceLocator->get('ApplicationContextProvider'));
-        
-        return $service;
-    }
-}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/LocalContext.php b/module/Application/src/Application/Service/LocalContext.php
new file mode 100644
index 0000000000000000000000000000000000000000..596734c25bfea327c6b20183beb36fcca7a5910c
--- /dev/null
+++ b/module/Application/src/Application/Service/LocalContext.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Application\Service;
+
+use UnicaenApp\Service\EntityManagerAwareInterface;
+use UnicaenApp\Service\EntityManagerAwareTrait;
+use Application\Entity\Db\Structure;
+use Application\Entity\Db\Etape;
+use Application\Entity\NiveauEtape;
+
+/**
+ * Classe regroupant des données locales (filtres, etc.)
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class LocalContext extends AbstractContext implements EntityManagerAwareInterface
+{
+    use EntityManagerAwareTrait;
+    
+    /**
+     * @var \Zend\Session\Container
+     */
+    protected $sessionContainer;
+    
+    /**
+     * @var string
+     */
+    protected $statutInterv;
+    
+    /**
+     * @var Structure
+     */
+    protected $structure;
+    
+    /**
+     * @var NiveauEtape
+     */
+    protected $niveau;
+    
+    /**
+     * @var Etape
+     */
+    protected $etape;
+
+    /**
+     * Constructeur!
+     */
+    public function __construct(\Doctrine\ORM\EntityManager $entityManager)
+    {
+        $this->setEntityManager($entityManager);
+    }
+    
+    /**
+     * @return string
+     */
+    public function getStatutInterv()
+    {
+        if (null === $this->statutInterv) {
+            $this->statutInterv = $this->getSessionContainer()->statutInterv;
+        }
+        return $this->statutInterv;
+    }
+    
+    /**
+     * @return Structure
+     */
+    public function getStructure()
+    {
+        if (null === $this->structure) {
+            $this->structure = $this->getSessionContainer()->structure;
+            if ($this->structure && !$this->structure instanceof Structure) {
+                $this->structure = $this->getEntityManager()->find("Application\Entity\Db\Structure", $this->structure);
+            }
+        }
+        return $this->structure;
+    }
+    
+    /**
+     * @return NiveauEtape
+     */
+    public function getNiveau()
+    {
+        if (null === $this->niveau) {
+            $this->niveau = $this->getSessionContainer()->niveau;
+            
+            if (is_string($this->niveau)) {
+                list($lib, $niv) = explode('-', $this->niveau);
+                $this->niveau = NiveauEtape::getInstance($lib, $niv);
+            }
+        }
+        return $this->niveau;
+    }
+
+    /**
+     * @return Etape
+     */
+    public function getEtape()
+    {
+        if (null === $this->etape) {
+            $this->etape = $this->getSessionContainer()->etape;
+            if ($this->etape && !$this->etape instanceof Etape) {
+                $this->etape = $this->getEntityManager()->find("Application\Entity\Db\Etape", $this->etape);
+            }
+        }
+        return $this->etape;
+    }
+
+    public function setStatutInterv($statutInterv = null)
+    {
+        $this->statutInterv = $statutInterv;
+        $this->getSessionContainer()->statutInterv = $statutInterv ?: null;
+        return $this;
+    }
+
+    public function setStructure(Structure $structure = null)
+    {
+        $this->structure = $structure;
+        $this->getSessionContainer()->structure = $structure ? $structure->getId() : null;
+        return $this;
+    }
+
+    public function setNiveau($niveau = null)
+    {             
+        $this->niveau = $niveau;
+        $this->getSessionContainer()->niveau = $niveau;
+        return $this;
+    }
+
+    public function setEtape(Etape $etape = null)
+    {
+        $this->etape = $etape;
+        $this->getSessionContainer()->etape = $etape ? $etape->getId() : null;
+        return $this;
+    }
+    
+    /**
+     * @return \Zend\Session\Container
+     */
+    protected function getSessionContainer()
+    {
+        if (null === $this->sessionContainer) {
+            $this->sessionContainer = new \Zend\Session\Container(get_class($this));
+        }
+        return $this->sessionContainer;
+    }
+    
+    public function fromArray(array $context = array())
+    {
+        $this->setStructure(isset($context['structureEns']) ? $context['structureEns'] : null);
+
+        return parent::fromArray($context);
+    }
+    
+    public function debug()
+    {
+        var_dump("statut = " . $this->getStatutInterv());
+        var_dump("intervenant = " . $this->getIntervenant());
+        var_dump("structure = " . $this->getStructure());
+        var_dump("niveau = " . $this->getNiveau());
+        var_dump("etape = " . $this->getEtape());
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/OffreFormation.php b/module/Application/src/Application/Service/OffreFormation.php
index 48fd209bfdf1dfc97994b5ff9524ac058bfcd765..a835447fd27405f59dea9714698667ed5ed3054a 100644
--- a/module/Application/src/Application/Service/OffreFormation.php
+++ b/module/Application/src/Application/Service/OffreFormation.php
@@ -2,8 +2,6 @@
 
 namespace Application\Service;
 
-use Application\Entity\Db\Repository\ElementPedagogiqueRepository;
-
 /**
  * Service métier dédié à l'offre de formation.
  *
@@ -11,25 +9,5 @@ use Application\Entity\Db\Repository\ElementPedagogiqueRepository;
  */
 class OffreFormation extends AbstractService
 {
-    /**
-     * @var ElementPedagogiqueRepository
-     */
-    protected $repoElementPedagogique;
-    
-    
     
-    
-    /**
-     * 
-     * @return ElementPedagogiqueRepository
-     */
-    public function getRepoElementPedagogique()
-    {
-        if (null === $this->repoElementPedagogique) {
-            $this->repoElementPedagogique = $this->getEntityManager()->getRepository('Application\Entity\Db\ElementPedagogique');
-        }
-        return $this->repoElementPedagogique;
-    }
-
-
 }
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/OffreFormationAssertion.php b/module/Application/src/Application/Service/OffreFormationAssertion.php
new file mode 100644
index 0000000000000000000000000000000000000000..7eb1c9ac0ac547fe02b83c47c3c593201aeed5d9
--- /dev/null
+++ b/module/Application/src/Application/Service/OffreFormationAssertion.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Application\Service;
+
+use Zend\Permissions\Acl\Acl;
+use Zend\Permissions\Acl\Assertion\AssertionInterface;
+use Zend\Permissions\Acl\Resource\ResourceInterface;
+use Zend\Permissions\Acl\Role\RoleInterface;
+
+/**
+ * Description of OffreFormationAssertion
+ *
+ * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
+ */
+class OffreFormationAssertion extends AbstractService implements AssertionInterface
+{
+    /**
+     * 
+     * 
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array();
+    }
+    
+    /**
+     * Returns true if and only if the assertion conditions are met
+     *
+     * This method is passed the ACL, Role, Resource, and privilege to which the authorization query applies. If the
+     * $role, $resource, or $privilege parameters are null, it means that the query applies to all Roles, Resources, or
+     * privileges, respectively.
+     *
+     * @param  Acl                        $acl
+     * @param  RoleInterface         $role
+     * @param  ResourceInterface $resource
+     * @param  string                         $privilege
+     * @return bool
+     */
+    public function assert(Acl $acl, RoleInterface $role = null, ResourceInterface $resource = null, $privilege = null)
+    {
+//        $event   = $this->getServiceLocator()->get('application')->getMvcEvent(); /* @var $event \Zend\Mvc\MvcEvent */
+//        $request = $event->getRequest(); /* @var $request \Zend\Http\Request */
+//        $role    = $this->getContextProvider()->getSelectedIdentityRole();
+//        
+//        $match      = $event->getRouteMatch();
+//        $controller = $match->getParam('controller');
+//        $action     = $match->getParam('action');
+//        $request    = $event->getRequest();
+        
+//        var_dump($event->getName(), $controller, $action);
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/Service.php b/module/Application/src/Application/Service/Service.php
index 607a100cf91ff0caed24ff8aa376123ab87ce1e7..4963dea36eace8ff5f9b7297c5e590d554c13ad4 100644
--- a/module/Application/src/Application/Service/Service.php
+++ b/module/Application/src/Application/Service/Service.php
@@ -14,8 +14,6 @@ use Application\Entity\Db\Structure as StructureEntity;
  */
 class Service extends AbstractEntityService
 {
-    use ContextProviderAwareTrait;
-
     /**
      * retourne la classe des entités
      *
@@ -97,8 +95,8 @@ class Service extends AbstractEntityService
      */
     public function finderByContext( QueryBuilder $qb=null, $alias=null )
     {
-        $context = $this->getServiceLocator()->get('ApplicationContextProvider')->getGlobalContext();
-        $role    = $this->getServiceLocator()->get('ApplicationContextProvider')->getSelectedIdentityRole();
+        $context = $this->getContextProvider()->getGlobalContext();
+        $role    = $this->getContextProvider()->getSelectedIdentityRole();
 
         list($qb,$alias) = $this->initQuery($qb, $alias);
         
@@ -115,6 +113,25 @@ class Service extends AbstractEntityService
         return $qb;
     }
 
+    /**
+     * Retourne la liste des services selon l'étape donnée
+     *
+     * @param string $statutInterv "Application\Entity\Db\IntervenantPermanent" ou "Application\Entity\Db\IntervenantExterieur"
+     * @param QueryBuilder|null $queryBuilder
+     * @return QueryBuilder
+     */
+    public function finderByStatutInterv($statutInterv, QueryBuilder $qb=null, $alias=null )
+    {
+        list($qb,$alias) = $this->initQuery($qb, $alias);
+        if (!in_array((string)$statutInterv, array("Application\Entity\Db\IntervenantPermanent", "Application\Entity\Db\IntervenantExterieur"))) {
+            return $qb;
+        }
+        $qb
+                ->join("$alias.intervenant", 'i2')
+                ->andWhere("i2 INSTANCE OF $statutInterv");
+        return $qb;
+    }
+
     /**
      * Retourne, par ID du type d'intervention, la liste des heures saisies pour le service donné
      *
diff --git a/module/Application/src/Application/Service/ServiceReferentiel.php b/module/Application/src/Application/Service/ServiceReferentiel.php
index 22a512de67fe38b44e205d8b8e5538c4323d4d80..cd287fefd095f5ac34d89c04b8d252d5225e08ea 100644
--- a/module/Application/src/Application/Service/ServiceReferentiel.php
+++ b/module/Application/src/Application/Service/ServiceReferentiel.php
@@ -11,10 +11,8 @@ use Application\Entity\Db\Finder\FinderServiceReferentiel;
  *
  * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
  */
-class ServiceReferentiel extends AbstractService implements ContextProviderAwareInterface
+class ServiceReferentiel extends AbstractService
 {
-    use ContextProviderAwareTrait;
-
     /**
      * Supprime (historise par défaut) le service spécifié.
      *
@@ -39,42 +37,19 @@ class ServiceReferentiel extends AbstractService implements ContextProviderAware
     /**
      * Retourne le requêteur des services référentiels contraint par les critères spécifiés.
      *
-     * @param array $criteria
+     * @param array $filter
      * @return FinderServiceReferentiel
      */
-    public function getFinder(array $criteria = array())
+    public function getFinder(array $filter = array())
     {
-        $qb = new FinderServiceReferentiel($this->getEntityManager(), $this->getContextProvider());
-        
-        // application des critères locaux (filtrage par ex)
-        $this->applyLocalContext($qb, $criteria);
+        $qb = new FinderServiceReferentiel(
+                $this->getEntityManager(), 
+                $this->getContextProvider(),
+                $filter);
 
         return $qb;
     } 
     
-    /**
-     * Applique le contexte local (filtres).
-     * 
-     * @param QueryBuilder $qb
-     * @param array $criteria
-     * @return self
-     */
-    public function applyLocalContext(QueryBuilder $qb, array $criteria = array())
-    {
-        if (!empty($criteria['intervenant'])) {
-            $qb->andWhere("sr.intervenant = :intervenant")->setParameter('intervenant', $criteria['intervenant']);
-        }
-        if (!empty($criteria['structure-ens'])) {
-            $qb->andWhere("sr.structure = :structure")->setParameter('structure', $criteria['structure-ens']);
-        }
-        
-        return $this;
-    }
-    
-    
-    
-    
-    
     
     
     
diff --git a/module/Application/src/Application/Service/ServiceReferentielFactory.php b/module/Application/src/Application/Service/ServiceReferentielFactory.php
deleted file mode 100644
index 523a3f8c906b029849da009121aa5719315ae0b6..0000000000000000000000000000000000000000
--- a/module/Application/src/Application/Service/ServiceReferentielFactory.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace Application\Service;
-
-use Zend\ServiceManager\FactoryInterface;
-use Zend\ServiceManager\ServiceLocatorInterface;
-
-/**
- * Description of ServiceReferentielFactory
- *
- * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
- */
-class ServiceReferentielFactory implements FactoryInterface
-{
-    /**
-     * Create service
-     *
-     * @param ServiceLocatorInterface $serviceLocator
-     * @return mixed
-     */
-    public function createService(ServiceLocatorInterface $serviceLocator)
-    {
-        $service = new ServiceReferentiel();
-        $service->setEntityManager($serviceLocator->get('doctrine.entitymanager.orm_default'));
-        $service->setContextProvider($serviceLocator->get('ApplicationContextProvider'));
-        
-        return $service;
-    }
-}
\ No newline at end of file
diff --git a/module/Application/src/Application/Service/Source.php b/module/Application/src/Application/Service/Source.php
index 7f7ae33ba3053684bfb8cc68ca4c33cbb6638daa..de291455422fd482ca22e12485954d0a07592df8 100644
--- a/module/Application/src/Application/Service/Source.php
+++ b/module/Application/src/Application/Service/Source.php
@@ -12,8 +12,6 @@ use Application\Entity\Db\Source as SourceEntity;
  */
 class Source extends AbstractEntityService
 {
-    use ContextProviderAwareTrait;
-
     /**
      * retourne la classe des entités
      *
diff --git a/module/Application/src/Application/Service/TypeFormation.php b/module/Application/src/Application/Service/TypeFormation.php
index cb8ddba4e6d1a2262d3ab8e23517144724e8821a..88e1ffc15e3f39057eefbf0a2c36e47867bab894 100644
--- a/module/Application/src/Application/Service/TypeFormation.php
+++ b/module/Application/src/Application/Service/TypeFormation.php
@@ -31,6 +31,18 @@ class TypeFormation extends AbstractEntityService
     public function getAlias(){
         return 'typefor';
     }
+    
+    public function finderByNiveau(\Application\Entity\NiveauEtape $niveau, QueryBuilder $qb = null, $alias = null)
+    {
+        list($qb, $alias) = $this->initQuery($qb, $alias);
+        
+        $qb
+                ->join("$alias.groupe", "gtf")
+                ->andWhere("gtf.libelleCourt = :lib")
+                ->setParameter('lib', $niveau->getLib());
+        
+        return $qb;
+    }
 
     /**
      * Retourne la liste des types de formation
diff --git a/module/Application/src/Application/View/Helper/Service/Ligne.php b/module/Application/src/Application/View/Helper/Service/Ligne.php
index 4ce0ebe854b554a2405dc2a132599e3ceeb7340b..5041ce81d32f1057c446b0f2f23abe3492a1a635 100644
--- a/module/Application/src/Application/View/Helper/Service/Ligne.php
+++ b/module/Application/src/Application/View/Helper/Service/Ligne.php
@@ -111,8 +111,8 @@ class Ligne extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
     protected function renderElementPedagogique($element)
     {
         if (! $element) return '';
-        $url = $this->getView()->url('of/default', array('action' => 'voir-element'), array('query' => array('id' => $element->getId())));
-        $pourl = $this->getView()->url('of/default', array('action' => 'apercevoir-element'), array('query' => array('id' => $element->getId())));
+        $url = $this->getView()->url('of/element/voir', array('id' => $element->getId()));
+        $pourl = $this->getView()->url('of/element/apercevoir', array('id' => $element->getId()));
         $out = '<a href="'.$url.'" data-po-href="'.$pourl.'" class="ajax-modal">'.$element.'</a>';
         return $out;
     }
diff --git a/module/Application/src/Application/View/Helper/Service/Liste.php b/module/Application/src/Application/View/Helper/Service/Liste.php
index 1eeffb3dfdec7cd3da82ea754f5b62f33d3b98a4..9419c37f6bc293a5f34777ce6305f14a79b6434d 100644
--- a/module/Application/src/Application/View/Helper/Service/Liste.php
+++ b/module/Application/src/Application/View/Helper/Service/Liste.php
@@ -54,7 +54,7 @@ class Liste extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
         $typesIntervention = $this->getServiceLocator()->getServiceLocator()->get('ApplicationTypeIntervention')->getTypesIntervention();
         $colspan = 4;
         $out = $this->renderShowHide();
-        $out .= '<table id="services" class="table service">';
+        $out .= '<table id="services" class="table table-bordered service">';
         $out .= '<tr>';
 
         if (!$role instanceof \Application\Acl\IntervenantRole) {
@@ -80,9 +80,9 @@ class Liste extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
         }
         $out .= '</table>'."\n";
         $out .= $this->renderShowHide();
-
-        $url = $this->getView()->url('service/default', array('action' => 'saisie'));
-        $out .= '<br /><a class="ajax-modal services btn btn-default" data-event="service-add-message" href="'.$url.'" title="Ajouter un service"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>';
+//
+//        $url = $this->getView()->url('service/default', array('action' => 'saisie'));
+//        $out .= '<br /><a class="ajax-modal services btn btn-default" data-event="service-add-message" href="'.$url.'" title="Ajouter un service"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>';
         $out .= '<script type="text/javascript">';
         $out .= '$(function() { Service.init("'.$this->getView()->url('service/default', array('action' => 'voirLigne') ).'"); });';
         $out .= '</script>';
diff --git a/module/Application/src/Application/View/Helper/ServiceReferentiel/Ligne.php b/module/Application/src/Application/View/Helper/ServiceReferentiel/Ligne.php
index 041193def3937257b3110f7bdc3e5b4efbc3334b..27292fcf803cee49c2a0a2f48da5ad67b2b9a91f 100644
--- a/module/Application/src/Application/View/Helper/ServiceReferentiel/Ligne.php
+++ b/module/Application/src/Application/View/Helper/ServiceReferentiel/Ligne.php
@@ -58,7 +58,7 @@ class Ligne extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
     {
         $parts = array();
         
-        $parts['intervenant'] = '<td>' . $this->service->getIntervenant() . "</td>\n";
+        $parts['intervenant'] = '<td>' . $this->renderIntervenant($this->service->getIntervenant()) . "</td>\n";
         $parts[]              = '<td>' . $this->renderStructure($this->service->getStructure()) . "</td>\n";
         $parts['annee']       = '<td>' . $this->renderAnnee($this->service->getAnnee()) . "</td>\n";
         $parts[]              = '<td>' . $this->renderFonction($this->service->getFonction()) . "</td>\n";
@@ -92,6 +92,14 @@ class Ligne extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
         return $this;
     }
 
+    protected function renderIntervenant($intervenant)
+    {
+        $pourl = $this->getView()->url('intervenant/default', array('action' => 'apercevoir', 'id' => $intervenant->getSourceCode()));
+        $out   = '<a href="'.$pourl.'" data-po-href="'.$pourl.'" class="ajax-modal services">'.$intervenant.'</a>';
+        
+        return $out;
+    }
+
     protected function renderStructure($structure)
     {
         if (!$structure) {
diff --git a/module/Application/src/Application/View/Helper/ServiceReferentiel/Liste.php b/module/Application/src/Application/View/Helper/ServiceReferentiel/Liste.php
index e38dbf8bb12b2a4cfb8c4f39c5a1757a81d0ac8a..6653958639f8c17a3242bf745d874356848ab8dd 100644
--- a/module/Application/src/Application/View/Helper/ServiceReferentiel/Liste.php
+++ b/module/Application/src/Application/View/Helper/ServiceReferentiel/Liste.php
@@ -53,7 +53,7 @@ class Liste extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
      */
     public function render($details = false)
     {
-        $urlSaisir    = $this->getView()->url('service-ref/default', array('action' => 'saisir'));
+//        $urlSaisir    = $this->getView()->url('service-ref/default', array('action' => 'saisir'));
         $urlVoirListe = $this->getView()->url('service-ref/default', array('action' => 'voirListe'));
         $parts        = array();
         
@@ -75,7 +75,7 @@ class Liste extends AbstractHelper implements ServiceLocatorAwareInterface, Cont
         
         $parts[] = '</table>';
 
-        $parts[] = '<a class="ajax-modal services-ref btn btn-default" data-event="service-ref-add-message" href="' . $urlSaisir . '" title="Ajouter un service référentiel"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>';
+//        $parts[] = '<a class="ajax-modal services-ref btn btn-default" data-event="service-ref-add-message" href="' . $urlSaisir . '" title="Ajouter un service référentiel"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>';
         
         $parts[] = '<script type="text/javascript">';
         $parts[] = '$(function() { ServiceReferentiel.init("' . $urlVoirListe . '"); });';
diff --git a/module/Application/src/Application/View/Helper/VolumeHoraire/Liste.php b/module/Application/src/Application/View/Helper/VolumeHoraire/Liste.php
index 771fcc0e1887f78062fb225fe271993762818c9a..6712821644a44e9539cb884d9463820e10145740 100644
--- a/module/Application/src/Application/View/Helper/VolumeHoraire/Liste.php
+++ b/module/Application/src/Application/View/Helper/VolumeHoraire/Liste.php
@@ -76,7 +76,7 @@ class Liste extends AbstractHelper implements ServiceLocatorAwareInterface
      * @return string
      */
     public function render(){
-        $out = '<table class="table volume-horaire">';
+        $out = '<table class="table table-condensed volume-horaire">';
         $out .= '<tr>';
         $out .= "<th style=\"width:10%\">Période</th>\n";
         foreach( $this->typesIntervention as $ti ){
diff --git a/module/Application/view/application/intervenant/apercevoir.phtml b/module/Application/view/application/intervenant/apercevoir.phtml
index d8932e9161b7698b48a6f95fb681afb537190c1c..dd8dcc3ffeeb8a8ef0c8bfc38425cdccc847482a 100644
--- a/module/Application/view/application/intervenant/apercevoir.phtml
+++ b/module/Application/view/application/intervenant/apercevoir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $intervenant ?> <small><?php echo $intervenant->getType() ?></small></h1>
+<h3><?php echo $intervenant ?> <small><?php echo $intervenant->getType() ?></small></h3>
 
 <?php echo $this->intervenantDl($intervenant, true, $short) ?>
 
diff --git a/module/Application/view/application/intervenant/choisir.phtml b/module/Application/view/application/intervenant/choisir.phtml
index a4ccdb51aa5df1217ceaf6f6ee41adcd90258c4c..c9ac759eda64911d741a53cb81ed63290ffb014e 100644
--- a/module/Application/view/application/intervenant/choisir.phtml
+++ b/module/Application/view/application/intervenant/choisir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $this->title ?></h1>
+<h3><?php echo $this->title ?></h3>
 
 <?php echo $this->messenger(true) ?>
 
diff --git a/module/Application/view/application/intervenant/voir.phtml b/module/Application/view/application/intervenant/voir.phtml
index 601dfe30396a1d7929e682f2e1a6ac9a7d0189ca..0b64a1fe31d5ff488432daf5bb94b840634e0363 100644
--- a/module/Application/view/application/intervenant/voir.phtml
+++ b/module/Application/view/application/intervenant/voir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $intervenant ?> <small><?php echo $intervenant->getType() ?></small></h1>
+<h3><?php echo $intervenant ?> <small><?php echo $intervenant->getType() ?></small></h3>
 
 <?php echo $this->intervenantDl($intervenant, true) ?>
 
diff --git a/module/Application/view/application/offre-formation/voir-element.phtml b/module/Application/view/application/offre-formation/element-pedagogique/apercevoir.phtml
similarity index 66%
rename from module/Application/view/application/offre-formation/voir-element.phtml
rename to module/Application/view/application/offre-formation/element-pedagogique/apercevoir.phtml
index d55bf5fc4e6dadba3774f80a02610c09648165ce..4908d2695670b011c60a302d8952e015f3bd1940 100644
--- a/module/Application/view/application/offre-formation/voir-element.phtml
+++ b/module/Application/view/application/offre-formation/element-pedagogique/apercevoir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $element ?> <small><?php echo $element->getSourceCode() ?></small></h1>
+<h3><?php echo $element ?> <small><?php echo $element->getSourceCode() ?></small></h3>
 
 <?php echo $this->elementPedagogiqueDl($element, true, $short) ?>
 
diff --git a/module/Application/view/application/offre-formation/element-pedagogique/saisir.phtml b/module/Application/view/application/offre-formation/element-pedagogique/saisir.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..0ba214433deb640b85360c302ae5b2840fb210fc
--- /dev/null
+++ b/module/Application/view/application/offre-formation/element-pedagogique/saisir.phtml
@@ -0,0 +1,56 @@
+<?php echo $this->form()->openTag($form->prepare()); ?>
+
+<?php
+if ($errors) {
+    echo $this->messenger()->setMessages(array(UnicaenApp\View\Helper\Messenger::ERROR => array($errors)));
+}
+?>
+
+<div class="row">
+    <div class="col-sm-12">
+        <?php
+        echo $this->formControlGroup($form->get('etape'));
+        ?>
+    </div>
+</div>
+<div class="row">
+    <div class="col-sm-4">
+        <?php
+        echo $this->formControlGroup($form->get('source-code'));
+        ?>
+    </div>
+    <div class="col-sm-8">
+        <?php
+        echo $this->formControlGroup($form->get('structure'));
+        ?>
+    </div>
+</div>
+<div class="row">
+    <div class="col-sm-12">
+        <?php
+        echo $this->formControlGroup($form->get('libelle'));
+        ?>
+    </div>
+</div>
+<div class="row">
+    <div class="col-sm-4">
+        <?php
+        echo $this->formControlGroup($form->get('periode'));
+        ?>
+    </div>
+    <div class="col-sm-2">
+        <?php
+        echo $this->formControlGroup($form->get('taux-foad'));
+        ?>
+    </div>
+    <div class="col-sm-4">
+        <?php
+//        echo $this->formControlGroup($form->get('specifique-echanges'));
+        ?>
+    </div>
+</div>
+
+<?php 
+echo $this->formRow($form->get('submit'));
+echo $this->formHidden($form->get('id'));
+echo $this->form()->closeTag();
\ No newline at end of file
diff --git a/module/Application/view/application/offre-formation/element-pedagogique/supprimer.phtml b/module/Application/view/application/offre-formation/element-pedagogique/supprimer.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..5b5b472ab189ef321c79008a695f60eea0e43307
--- /dev/null
+++ b/module/Application/view/application/offre-formation/element-pedagogique/supprimer.phtml
@@ -0,0 +1,14 @@
+<?php
+if (isset($errors) && $errors) {
+    echo $this->messenger()->setMessages(array(UnicaenApp\View\Helper\Messenger::ERROR => array($errors)));
+}
+else {
+    echo '<p class="lead text-danger"><strong>Attention!</strong> Confirmez-vous la suppression de cet enregistrement ?</p>';
+    echo $this->elementPedagogiqueDl($entity, true, true);
+
+    echo $this->form()->openTag($form);
+    echo $this->formHidden($form->get('id'));
+    echo $this->formHidden($form->get('security'));
+    echo $this->formSubmit($form->get('submit')->setAttribute('class', 'btn btn-primary'));
+    echo $this->form()->closeTag();
+}
\ No newline at end of file
diff --git a/module/Application/view/application/offre-formation/apercevoir-element.phtml b/module/Application/view/application/offre-formation/element-pedagogique/voir.phtml
similarity index 66%
rename from module/Application/view/application/offre-formation/apercevoir-element.phtml
rename to module/Application/view/application/offre-formation/element-pedagogique/voir.phtml
index d55bf5fc4e6dadba3774f80a02610c09648165ce..4908d2695670b011c60a302d8952e015f3bd1940 100644
--- a/module/Application/view/application/offre-formation/apercevoir-element.phtml
+++ b/module/Application/view/application/offre-formation/element-pedagogique/voir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $element ?> <small><?php echo $element->getSourceCode() ?></small></h1>
+<h3><?php echo $element ?> <small><?php echo $element->getSourceCode() ?></small></h3>
 
 <?php echo $this->elementPedagogiqueDl($element, true, $short) ?>
 
diff --git a/module/Application/view/application/offre-formation/etape-saisie.phtml b/module/Application/view/application/offre-formation/etape-saisie.phtml
deleted file mode 100644
index 19647c7928bfea8d0ae9e78b87e51609f7dc050d..0000000000000000000000000000000000000000
--- a/module/Application/view/application/offre-formation/etape-saisie.phtml
+++ /dev/null
@@ -1,23 +0,0 @@
-<h1>&Eacute;dition d'une formation</h1>
-
-<?php
-
-$form->prepare();
-echo $this->form()->openTag($form);
-
-echo $this->formControlGroup($form->get('source-code'));
-echo $this->formControlGroup($form->get('libelle'));
-echo $this->formControlGroup($form->get('type-formation'));
-echo $this->formControlGroup($form->get('niveau'));
-echo $this->formControlGroup($form->get('specifique-echanges'));
-echo $this->formControlGroup($form->get('structure'));
-
-echo $this->formRow($form->get('submit'));
-if ($id = (int)$form->get('id')->getValue()){
-    $suppressionUrl = $this->url('of/etape/suppression', array('id' => $form->get('id')->getValue()));
-    echo ' <a class="ajax-modal btn btn-default" data-event="etape-after-suppression" href="'.$suppressionUrl.'">Supprimer</a>';
-}
-echo $this->formHidden($form->get('id'));
-echo $this->form()->closeTag().'<br />';
-echo $this->formErrors( $form );
-if ($errors) echo $this->messenger()->setMessages(array(UnicaenApp\View\Helper\Messenger::ERROR => array($errors)));
\ No newline at end of file
diff --git a/module/Application/view/application/offre-formation/etape-apercevoir.phtml b/module/Application/view/application/offre-formation/etape/apercevoir.phtml
similarity index 65%
rename from module/Application/view/application/offre-formation/etape-apercevoir.phtml
rename to module/Application/view/application/offre-formation/etape/apercevoir.phtml
index 7ceef8abb6cca5d1be0438fb18a6b33ddc298f9e..09ed904d15c27f92988dd71f7b35d42f73d99e80 100644
--- a/module/Application/view/application/offre-formation/etape-apercevoir.phtml
+++ b/module/Application/view/application/offre-formation/etape/apercevoir.phtml
@@ -1,4 +1,4 @@
-<h1><?php echo $etape ?> <small><?php echo $etape->getSourceCode() ?></small></h1>
+<h3><?php echo $etape ?> <small><?php echo $etape->getSourceCode() ?></small></h3>
 
 <?php echo $this->etapeDl($etape, true, $short) ?>
 
diff --git a/module/Application/view/application/offre-formation/etape/saisie.phtml b/module/Application/view/application/offre-formation/etape/saisie.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..e2d2372321d887ea7a6324a392515220b1c16a42
--- /dev/null
+++ b/module/Application/view/application/offre-formation/etape/saisie.phtml
@@ -0,0 +1,62 @@
+<?php echo $this->form()->openTag($form->prepare()); ?>
+
+<?php
+if ($errors) {
+    echo $this->messenger()->setMessages(array(UnicaenApp\View\Helper\Messenger::ERROR => array($errors)));
+}
+?>
+
+<div class="row">
+    <div class="col-sm-4">
+        <?php
+        echo $this->formControlGroup($form->get('source-code'));
+        ?>
+    </div>
+    <div class="col-sm-8">
+        <?php
+        echo $this->formControlGroup($form->get('structure'));
+        ?>
+    </div>
+</div>
+<div class="row">
+    <div class="col-sm-12">
+        <?php
+        echo $this->formControlGroup($form->get('libelle'));
+        ?>
+    </div>
+</div>
+<div class="row">
+    <div class="col-sm-5">
+        <?php
+        echo $this->formControlGroup($form->get('type-formation'));
+        ?>
+    </div>
+    <div class="col-sm-3">
+        <?php
+        echo $this->formControlGroup($form->get('niveau'));
+        ?>
+    </div>
+    <div class="col-sm-4">
+        <?php
+        echo $this->formControlGroup($form->get('specifique-echanges'));
+        ?>
+    </div>
+</div>
+
+<?php 
+echo $this->formRow($form->get('submit'));
+echo $this->formHidden($form->get('id'));
+echo $this->form()->closeTag();
+?>
+
+<script>
+    var pertinencesNiveau = <?php echo \Zend\Json\Json::encode($form->getPertinencesNiveau()) ?>;
+    $(function() {
+        $("select[name=type-formation]").change(function() {
+            var niveau = $("input[name=niveau]");
+            var tfId = $(this).val();
+            var pertinent = tfId in pertinencesNiveau && pertinencesNiveau[tfId];
+            pertinent ? niveau.attr('disabled', false): niveau.attr('disabled', true).val("");
+        });
+    });
+</script>
\ No newline at end of file
diff --git a/module/Application/view/application/offre-formation/etape-suppression.phtml b/module/Application/view/application/offre-formation/etape/supprimer.phtml
similarity index 100%
rename from module/Application/view/application/offre-formation/etape-suppression.phtml
rename to module/Application/view/application/offre-formation/etape/supprimer.phtml
diff --git a/module/Application/view/application/offre-formation/index.phtml b/module/Application/view/application/offre-formation/index.phtml
index 5fa1ec069f2c5b4b96275aff314ace4092b137fa..5f868fb289f14c772a82a3bdc6377db29953f474 100755
--- a/module/Application/view/application/offre-formation/index.phtml
+++ b/module/Application/view/application/offre-formation/index.phtml
@@ -1,14 +1,30 @@
 <?php
-$structuresOptions = array('' => "(Sélectionnez une structure)") + \UnicaenApp\Util::collectionAsOptions($structures);
-$niveauxOptions    = $niveaux;
-$etapesOptions     = \UnicaenApp\Util::collectionAsOptions($etapes);
-
+$structuresOptions = array('' => "(Sélectionnez...)") + \UnicaenApp\Util::collectionAsOptions($structures);
+$niveauxOptions    = \UnicaenApp\Util::collectionAsOptions($niveaux);
+$etapesOptions     = \UnicaenApp\Util::collectionAsOptions(
+        $etapes, 
+        false, 
+        function($e) { return sprintf("%s (%s EP)", $e, count($e->getElementPedagogique())); /* @var $e \Application\Entity\Db\Etape */ }
+);
+$etapesOrphelinesOptions = \UnicaenApp\Util::collectionAsOptions(
+        $etapesOrphelines, 
+        false, 
+        function($e) { return sprintf("%s (%s EP)", $e, count($e->getElementPedagogique())); /* @var $e \Application\Entity\Db\Etape */ }
+);
+$mc = $this->navigation('navigation')->menuContextuel(); /* @var $mc \UnicaenApp\View\Helper\Navigation\MenuContextuel */
+$mc->setUlClass('navigation navigation-etape pull-right');
 ?>
 
-<div class="jumbotron">
-    <h1>Offre de formation <?php echo $structure ? '<small>' . $structuresOptions[$structure] . '</small>' : null ?></h1>
-    <!--<p>L'offre de formation est constituée de ses éléments constitutifs les plus précis, correspondant aux cours TD et TP.</p>-->
-</div>
+<style>
+    ul.navigation-etape { padding: 0 0 3px 5px; }
+    ul.navigation-etape li { list-style: none; display: inline; }
+    /*th.action, td.action { min-width: 70px; }*/
+    div.element-rech div.panel-body label,
+    div.element-rech div.panel-body input { display: inline; }
+    div.element-rech div.panel-body input.form-control { width: 75% }
+</style>
+
+<h1 class="page-header">Offre de formation <?php echo $structure ? '<small>' . $structure . '</small>' : null ?></h1>
 
 <div class="row">
     
@@ -22,7 +38,7 @@ $etapesOptions     = \UnicaenApp\Util::collectionAsOptions($etapes);
                 $selectStructure = new \Zend\Form\Element\Select('structure');
                 $selectStructure
                         ->setValueOptions($structuresOptions)
-                        ->setValue($structure)
+                        ->setValue($structure ? $structure->getId() : null)
                         ->setAttributes(array('class' => 'struct'));
                 echo $this->formSelect($selectStructure);
                 ?>
@@ -31,100 +47,157 @@ $etapesOptions     = \UnicaenApp\Util::collectionAsOptions($etapes);
                         $("*").css('cursor', 'wait');
                         var id = $(this).val();
                         var url = '<?php echo $this->url('of', 
-                                 array('action' => 'of'), 
+                                 array(), 
                                  array('query' => array(
-                                     'structure' => '__structure__', 
-                                     /*'niveau' => $niveau, 
-                                     'etape' => $etape*/))) ?>';
+                                     'structure' => '__structure__'))) ?>';
                         $(location).attr('href', url.replace('__structure__', id));
                     });
                 </script>
             </div>
         </div>
 
-        <!---------------- Champ de sélection d'un niveau --------------->
+        <!---------------- Filtre niveau --------------->
         <div class="panel panel-default">
             <div class="panel-heading">Niveau</div>
             <div class="panel-body">
-                <?php
-                $niveauxOptions = array_combine(
-                        $tmp = array_map(function($v) { return $v['libelleCourt'] . $v['niveau']; }, $niveaux), 
-                        $tmp); 
-                ?>
-                <ul>
-                    <li>
-                    <?php if (null !== $niveau): ?> 
-                         <a class="niveau" href="<?php echo $this->url('of', 
-                                 array('action' => 'of'), 
-                                 array('query' => array(
-                                     'structure' => $structure, 
-                                     'niveau' => null,
-                                     /*'etape' => $etape*/))) ?>">Tous</a>
-                    <?php else: ?>
-                        <strong>Tous</strong>
-                    <?php endif; ?>
-                    </li>
-                    <?php foreach ($niveauxOptions as $code => $lib): ?>
-                    <li>
-                    <?php if ($code != $niveau): ?> 
-                         <a class="niveau" href="<?php echo $this->url('of', 
-                                 array('action' => 'of'), 
-                                 array('query' => array(
-                                     'structure' => $structure, 
-                                     'niveau' => $code, 
-                                     /*'etape' => $etape*/))) ?>"><?php echo $lib ?></a>
-                    <?php else: ?>
-                        <strong><?php echo $lib ?></strong>
-                    <?php endif; ?>
-                    </li>
-                    <?php endforeach; ?>
-                </ul>
-                <script>
-                    $("body").on("click", "a.niveau", function(event) {
-                        $("*").css('cursor', 'wait');
-                    });
-                </script>
+                Cliquez sur le niveau dont vous voulez voir les étapes et éléments pédagogiques...
             </div>
+            <?php if (count($niveauxOptions)): ?>
+            <ul class="list-group">
+                <?php if (null !== $niveau): ?> 
+                     <li class="list-group-item">
+                         <a class="niveau" href="<?php echo $this->url('of', 
+                                array(), 
+                                array('query' => array(
+                                    'structure' => $structure ? $structure->getId() : null, 
+                                    'niveau' => null))) ?>">Tous niveaux confondus</a>
+                     </li>
+                <?php else: ?>
+                     <li class="list-group-item active"><strong>Tous niveaux confondus</strong></li>
+                <?php endif; ?>
+                
+                <?php foreach ($niveauxOptions as $code => $lib): ?>
+                <?php if ($code != $niveau): ?> 
+                     <li class="list-group-item">
+                         <a class="niveau" title="Cliquez pour afficher les étapes et les éléments pédagogiques de ce niveau" href="<?php echo $this->url('of', 
+                                array(), 
+                                array('query' => array(
+                                    'structure' => $structure ? $structure->getId() : null, 
+                                    'niveau' => $code))) ?>"><?php echo $lib ?></a>
+                     </li>
+                <?php else: ?>
+                     <li class="list-group-item active"><strong><?php echo $lib ?></strong></li>
+                <?php endif; ?>
+                <?php endforeach; ?>
+            </ul>
+            <?php endif; ?>
+            <script>
+                $("body").on("click", "a.niveau", function(event) {
+                    $("*").css('cursor', 'wait');
+                });
+            </script>
         </div>
 
-        <!---------------- Champ de sélection d'une étape --------------->
+        <!---------------- Filtre étape --------------->
         <div class="panel panel-default">
-            <div class="panel-heading">Étape</div>
+            <div class="panel-heading">
+                Étape
+                <!--------------- Bouton d'ajout d'étape --------------->
+                <?php if (null !== $entities): ?>
+                <?php if ($serviceEtape->canAdd()): /* @var $serviceEtape \Application\Service\Etape */ ?>
+                <?php echo $mc
+                        ->withoutTarget()
+                        ->addParam('structure', $structure ? $structure->getId() : null)
+                        ->withProp('category', 'etape')
+                        ->addProp('class', 'iconify ajax-modal btn btn-default btn-xs'); ?>
+                <?php endif; ?>
+                <?php endif; ?>
+            </div>
             <div class="panel-body">
-                <ul>
-                    <li>
-                    <?php if (null !== $etape): ?> 
-                         <a class="etape" href="<?php echo $this->url('of', 
-                                 array('action' => 'of'), 
-                                 array('query' => array(
-                                     'structure' => $structure, 
-                                     'niveau' => $niveau, 
-                                     'etape' => null))) ?>">Toutes</a>
-                    <?php else: ?>
-                        <strong>Toutes</strong>
-                    <?php endif; ?>
+                Cliquez sur l'étape dont vous voulez voir les éléments pédagogiques...
+            </div>
+            
+            <?php if (count($etapesOptions)): ?>
+            <ul class="list-group">
+                <?php if (null !== $etape): ?> 
+                    <li class="list-group-item">
+                        <a class="etape" href="<?php echo $this->url('of', 
+                                array(), 
+                                array('query' => array(
+                                    'structure' => $structure ? $structure->getId() : null, 
+                                    'niveau' => $niveau, 
+                                    'etape' => null))) ?>">Toutes étapes confondues</a>
                     </li>
-                    <?php foreach ($etapesOptions as $code => $lib): ?>
-                    <li>
-                    <?php if ($code != $etape): ?> 
-                         <a class="etape" href="<?php echo $this->url('of', 
-                                 array('action' => 'of'), 
-                                 array('query' => array(
-                                     'structure' => $structure,
-                                     'niveau' => $niveau, 
-                                     'etape' => $code))) ?>"><?php echo $lib ?></a>
-                    <?php else: ?>
+                <?php else: ?>
+                    <li class="list-group-item active"><strong>Toutes étapes confondues</strong></li>
+                <?php endif; ?>
+                </li>
+                
+                <!-- Etapes sans EP -->
+                <?php foreach ($etapesOrphelinesOptions as $code => $lib): ?>
+                <!--------------- Boutons de modif d'étape --------------->
+                <?php $actions = null; ?>
+                <?php if ($serviceEtape->canSave($code)): /* @var $serviceEtape \Application\Service\Etape */ ?>
+                <?php $actions = (string) $mc
+                        ->withTarget($code)
+                        ->withProp('category', 'etape')
+                        ->addProp('class', 'iconify ajax-modal btn btn-default btn-xs'); ?>
+                <?php endif; ?>
+                <?php if (!$etape || $code != $etape->getId()): ?> 
+                     <li class="list-group-item">
+                         <?php echo $actions ?>
+                         <a class="etape text-danger" title="Cliquez pour afficher les éléments pédagogiques de cette étape" href="<?php echo $this->url('of', 
+                                array(), 
+                                array('query' => array(
+                                    'structure' => $structure ? $structure->getId() : null,
+                                    'niveau' => $niveau, 
+                                    'etape' => $code))) ?>"><?php echo $lib ?></a>
+                     </li>
+                <?php else: ?>
+                    <li class="list-group-item active">
+                        <?php echo $actions ?>
                         <strong><?php echo $lib ?></strong>
-                    <?php endif; ?>
                     </li>
-                    <?php endforeach; ?>
-                </ul>
-                <script>
-                    $("body").on("click", "a.etape", function(event) {
-                        $("*").css('cursor', 'wait');
-                    });
-                </script>
-            </div>
+                <?php endif; ?>
+                <?php endforeach; ?>
+                
+                <!-- Etapes avec EP -->
+                <?php foreach ($etapesOptions as $code => $lib): ?>
+                <!--------------- Boutons de modif d'étape --------------->
+                <?php $actions = null; ?>
+                <?php if ($serviceEtape->canSave($code)): /* @var $serviceEtape \Application\Service\Etape */ ?>
+                <?php $actions = (string) $mc
+                        ->withTarget($code)
+                        ->addParam('structure', $structure ? $structure->getId() : null)
+                        ->withProp('category', 'etape')
+                        ->addProp('class', 'iconify ajax-modal btn btn-default btn-xs'); ?>
+                <?php endif; ?>
+                <?php if (!$etape || $code != $etape->getId()): ?> 
+                     <li class="list-group-item">
+                         <?php echo $actions ?>
+                         <a class="etape" title="Cliquez pour afficher les éléments pédagogiques de cette étape" href="<?php echo $this->url('of', 
+                                array(), 
+                                array('query' => array(
+                                    'structure' => $structure ? $structure->getId() : null,
+                                    'niveau' => $niveau, 
+                                    'etape' => $code))) ?>"><?php echo $lib ?></a>
+                     </li>
+                <?php else: ?>
+                    <li class="list-group-item active">
+                         <?php echo $actions ?>
+                        <strong><?php echo $lib ?></strong>
+                    </li>
+                <?php endif; ?>
+                <?php endforeach; ?>
+            </ul>
+            <?php endif; ?>
+
+            <script>
+                $("body").on("click", "a.etape", function(event) {
+                    $("*").css('cursor', 'wait');
+                });
+            </script>
+            
         </div>
         
     </div>
@@ -138,77 +211,108 @@ $etapesOptions     = \UnicaenApp\Util::collectionAsOptions($etapes);
         <?php else: ?>
         
         <!---------------- Champ de recherche d'un élément pédagogique --------------->
-        <div class="panel panel-default">
-            <div class="panel-heading">Élément</div>
+        <div class="element-rech panel panel-default">
+            <div class="panel-heading">
+                <?php $compl = $etape ? "de l'étape <strong>$etape</strong>" : "toutes étapes confondues"; ?>
+                Éléments pédagogiques <?php echo $compl ?> 
+                <!--------------- Bouton d'ajout d'élément pédagogique --------------->
+                <?php if ($etape): ?>
+                    <?php echo $mc
+                            ->withoutTarget()
+                            ->withProp('category', 'element')
+                            ->addParam('etape', $etape->getId())
+                            ->addParam('etape', $etape->getId())
+                            ->addProp('class', 'iconify ajax-modal btn btn-default btn-xs'); ?>
+                <?php endif; ?>
+            </div>
             <div class="panel-body">
-                <?php echo $this->form()->openTag($form) ?> 
-                <?php echo $this->formControlGroup($form->get('element')); ?>
-                <input id="filter" class="btn btn-primary" type="submit" value="Y aller..." name="submit" />
-                <?php echo $this->form()->closeTag() ?> 
+                <p>
+                    <?php echo $this->form()->openTag($form) ?> 
+                    <label class="control-label"><?php echo $form->get('element')->getLabel() ?></label>
+                    <?php echo $this->formSearchAndSelect($form->get('element')); ?>
+                    <input id="filter" class="btn btn-primary element-rech-submit" type="submit" value="Y aller..." name="submit" />
+                    <?php echo $this->form()->closeTag() ?> 
+                </p>
+                <script>
+                    $("body").on("submit", "form.element-rech", function(event) {
+                        var form = $(event.target);
+                        var id = $("input.sas", form).val();
+                        if (id && $("#" + id).offset()) {
+                            $('html, body').animate({
+                                scrollTop: $("#" + id).offset().top - $(window).height() / 2
+                            }, 500, 'swing', function() { $("#" + id).effect("highlight"); });
+                        }
+                        event.preventDefault();
+                    });
+                </script>
+                
+                <p><?php echo count($entities) ?> élément(s) pédagogique(s) trouvé(s).</p>
+                
             </div>
-            <script>
-                $("body").on("submit", "form.element-rech", function(event) {
-                    var form = $(event.target);
-                    var id = $("input.sas", form).val();
-                    if (id && $("#" + id).offset()) {
-                        $('html, body').animate({
-                            scrollTop: $("#" + id).offset().top - $(window).height() / 2
-                        }, 500, 'swing', function() { $("#" + id).effect("highlight"); });
-                    }
-                    event.preventDefault();
-                });
-            </script>
+                
+            <!---------------- Tableau des éléments pédagogiques --------------->
+            <table class="table table-bordered table-condensed table-hover">
+                <thead>
+                    <tr>
+                        <th colspan="3">Étape</th>
+                        <th colspan="2">Élément pédagogique</th>
+                        <th class="action" rowspan="2"></th>
+                    </tr>
+                    <tr>
+                        <th>Niveau</th>
+                        <th>Libellé</th>
+                        <th>Période</th>
+                        <th>Code</th>
+                        <th>Libellé</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <?php foreach ($entities as $ep): /* @var $ep \Application\Entity\Db\ElementPedagogique */ ?>
+                    <tr id="<?php echo $ep->getId() ?>">
+                        <td><?php echo $ep->getEtape()->getNiveauToString() ?></td>
+                        <?php
+                        $etapeEp  = $ep->getEtape();
+                        $urlEtape = $this->url('of/etape/apercevoir', array('id' => $etapeEp->getId()));
+                        $urlElem  = $this->url('of/element/apercevoir', array('id' => $ep->getId()));
+                        ?>
+                        <td class="etape">
+                            <a class="ajax-modal" title="Cliquez pour voir le détail de cette étape" 
+                               href="<?php echo $urlEtape ?>"><?php echo $etapeEp ?></a>
+                        </td>
+                        <td class="periode"><?php echo $ep->getPeriode() ?: '-' ?></td>
+                        <td class="element"><?php echo $ep->getSourceCode() ?></td>
+                        <td class="element">
+                            <a class="ajax-modal" title="Cliquez pour voir le détail de cet élément" 
+                               href="<?php echo $urlElem ?>"><?php echo $ep->getLibelle() ?></a>
+                        </td>
+                        <td class="action">
+                            <!--------------- Boutons de modif d'élément --------------->
+                            <?php if ($etape): ?>
+                            <?php if ($serviceEtape->canSave($etape)): /* @var $serviceEtape \Application\Service\Etape */ ?>
+                            <?php echo $mc
+                                    ->withTarget($ep)
+                                    ->withProp('category', 'element')
+                                    ->addParam('etape', $etapeEp->getId())
+                                    ->addProp('class', 'btn btn-default btn-xs iconify ajax-modal'); ?>
+                            <?php endif; ?>
+                            <?php endif; ?>
+                        </td>
+                    </tr>
+                    <?php endforeach; ?>
+                </tbody>
+            </table>
+            
         </div>
         
-        <p><?php echo count($entities) ?> éléments trouvés.</p>
-        
-        <!---------------- Tableau des éléments pédagogiques --------------->
-        <table class="table table-bordered table-condensed table-hover">
-            <thead>
-                <tr>
-                    <th>Niveau</th>
-                    <th>Étape</th>
-                    <th>Période</th>
-                    <th>Code élément</th>
-                    <th>Libellé élément</th>
-                    <th class="action"></th>
-                </tr>
-            </thead>
-            <tbody>
-                <?php foreach ($entities as $entity): /* @var $entity \Application\Entity\Db\ElementPedagogique */ ?>
-                <tr id="<?php echo $entity->getId() ?>">
-                    <td class=""   ><?php echo $entity->getEtape()->getNiveauToString() ?></td>
-                    <?php
-                    $etape = $entity->getEtape();
-                    if ($serviceEtape->canSave($etape)){
-                        $link = '<a href="'.$this->url('of/etape/saisie', array('id' => $etape->getId())).'">';
-                    }else{
-                        $link = '<a class="ajax-modal" href="'.$this->url('of/etape/apercevoir', array('id' => $etape->getId())).'">';
-                    }
-
-                    ?>
-                    <td class="etape"><?php echo $link ?><?php echo $etape ?></a></td>
-                    <td class="periode"><?php echo $entity->getPeriode() ?></td>
-                    <td class="element"><?php echo $entity->getSourceCode() ?></td>
-                    <td class="element"><?php echo $entity->getLibelle() ?></td>
-                    <td class="action">
-                        <?php $url = $this->url('of/default', 
-                                array('action' => 'voir-element'), 
-                                array('query' => array('id' => $entity->getId()))) ?>
-                        <a class="ajax-modal" title="Voir le détails de cet élément" 
-                           href="<?php echo $url ?>"><span class="glyphicon glyphicon-eye-open"></span></a>
-                    </td>
-                </tr>
-                <?php endforeach; ?>
-            </tbody>
-        </table>
-
-        <!--------------- Boutons d'ajout d'ODF --------------->
-        <?php if ($serviceEtape->canAdd()): ?>
-        <a class="btn btn-default" href="<?php echo $this->url('of/etape/saisie') ?>">Ajout d'une nouvelle étape</a>
-        <?php endif; ?>
         <?php endif; ?>
 
     </div>
 
-</div>
\ No newline at end of file
+</div>
+
+<script type="text/javascript">
+    $(function() { 
+        Etape.init("<?php echo $this->url('of/default', array('action' => 'voirLigne')) ?>"); 
+        ElementPedagogique.init("<?php echo $this->url('of/default', array('action' => 'voirLigne')) ?>"); 
+    });
+</script>
\ No newline at end of file
diff --git a/module/Application/view/application/service/filtres.phtml b/module/Application/view/application/service/filtres.phtml
index 16440573b46e6c11069eef2d9f8dcf59d8563daa..cc18d1227e43df956cfbc152644b8758a1ad2b17 100644
--- a/module/Application/view/application/service/filtres.phtml
+++ b/module/Application/view/application/service/filtres.phtml
@@ -1,35 +1,44 @@
 <?php if ($rechercheForm): ?>
-<h1>Filtres</h1>
-<div class="well well-sm filter">
-<?php $rechercheForm->prepare(); ?>
-<?php echo $this->form()->openTag($rechercheForm); ?>
 
-<div class="row">
-  <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('intervenant')->getLabel() ?></label></div>
-  <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('intervenant')->setAttribute('class','form-control')); ?></div>
+<div class="panel panel-default filter">
+    
+    <div class="panel-heading">Filtres</div>
+    <div class="panel-body">
+        <?php echo $this->form()->openTag($rechercheForm->prepare()); ?>
+<!--        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php//echo $rechercheForm->get('intervenant')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php //echo $this->formSelect($rechercheForm->get('intervenant')->setAttribute('class','form-control')); ?></div>
+        </div>-->
+        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('intervenant')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php echo $this->formSearchAndSelect($rechercheForm->get('intervenant')); ?></div>
+        </div>
+        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('statut-interv')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php echo $this->formRadio($rechercheForm->get('statut-interv')); ?></div>
+        </div>
+        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('structure-ens')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('structure-ens')->setAttribute('class','form-control')); ?></div>
+        </div>
+        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('etape')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('etape')->setAttribute('class','form-control')); ?></div>
+        </div>
+        <div class="row">
+          <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('element-pedagogique')->getLabel() ?></label></div>
+          <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('element-pedagogique')->setAttribute('class','form-control')); ?></div>
+        </div>
+        <div class="row">
+          <div class="col-md-4">&nbsp;</div>
+          <div class="col-md-8"><?php echo $this->formRow($rechercheForm->get('submit')); ?></div>
+        </div>
+        <?php
+        echo $this->formHidden( $rechercheForm->get('action'));
+        echo $this->form()->closeTag();
+        echo $this->formErrors( $rechercheForm );
+        ?>
+    </div>
 </div>
-<div class="row">
-  <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('structure-ens')->getLabel() ?></label></div>
-  <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('structure-ens')->setAttribute('class','form-control')); ?></div>
-</div>
-<div class="row">
-  <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('etape')->getLabel() ?></label></div>
-  <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('etape')->setAttribute('class','form-control')); ?></div>
-</div>
-<div class="row">
-  <div class="col-md-4"><label class="control-label"><?php echo $rechercheForm->get('element-pedagogique')->getLabel() ?></label></div>
-  <div class="col-md-8"><?php echo $this->formSelect($rechercheForm->get('element-pedagogique')->setAttribute('class','form-control')); ?></div>
-</div>
-<div class="row">
-  <div class="col-md-4">&nbsp;</div>
-  <div class="col-md-8"><?php echo $this->formRow($rechercheForm->get('submit')); ?></div>
-</div>
-<?php
-
-echo $this->formHidden( $rechercheForm->get('action'));
-echo $this->form()->closeTag();
-echo $this->formErrors( $rechercheForm );
 
-?>
-</div>
 <?php endif; ?>
\ No newline at end of file
diff --git a/module/Application/view/application/service/index.phtml b/module/Application/view/application/service/index.phtml
index 0fc0280fe6a69e2c95988b73c296283f3beda386..05a67123c1529e59df3e5dc8a0ccb8579b2b8a66 100644
--- a/module/Application/view/application/service/index.phtml
+++ b/module/Application/view/application/service/index.phtml
@@ -1,37 +1,52 @@
-<div class="jumbotron">
-    <h1>Services <small>Année universitaire <?php echo $annee; ?></small></h1>
-</div>
+<h1 class="page-header">Services <small>Année universitaire <?php echo $annee; ?></small></h1>
 
 <style>
-    .modal-dialog { width: 60%; }
-
-    div.row{
+    .modal-dialog { 
+        width: 70%;
+    }
+    div.row {
         padding:1px;
     }
 </style>
 
+<?php if ($role instanceof \Application\Acl\DbRole): ?>
+<?php echo $this->messenger()->setMessages(array('warning' => "Sont visibles ici les référentiels et les services prévisionnels des intervenants affectés "
+        . "ou enseignant dans votre structure de responsabilité ({$role->getStructure()}) ou l'une de ses sous-structures.")); ?>
+<?php endif; ?>
+
 <?php if (! $role instanceof \Application\Acl\IntervenantRole): ?>
 <div id="filtres" data-url="<?php echo $this->url('service/default', array('action'=>'filtres')) ?>">
-<?php echo $this->filtresListe; ?>
+    <?php echo $this->filtresListe; ?>
 </div>
 <?php endif; ?>
+
 <?php if ('afficher' === $action ): ?>
-<h2>Services référentiels</h2>
-<?php echo $this->servicesRefListe; ?>
 
+<?php if (!$role instanceof \Application\Acl\IntervenantExterieurRole): ?>
+<h2>Référentiel
+    <?php 
+    $urlSaisir = $this->url('service-ref/default', array('action' => 'saisir'));
+    ?>
+    <a class="ajax-modal services-ref btn btn-default pull-right" data-event="service-ref-add-message" href="<?php echo $urlSaisir ?>" title="Ajouter un service référentiel"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>
+</h2>
+<?php echo $this->servicesRefListe; ?>
 <hr />
+<?php endif; ?>
 
-<h2>Services prévisionnels</h2>
+<h2>Service prévisionnel 
+    <?php
+    $url = $this->url('service/default', array('action' => 'saisie'));
+    ?>
+    <a class="ajax-modal services btn btn-default pull-right" data-event="service-add-message" href="<?php echo $url ?>" title="Ajouter un service"><span class="glyphicon glyphicon-plus"></span> Saisir un nouveau service</a>
+</h2>
 
 <?php if ($role instanceof \Application\Acl\IntervenantRole): ?>
-<div class="alert alert-info">
-    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
-    Les heures à saisir ci-dessous sont les heures réellement effectuées et non les heures payables.
-</div>
+<?php echo $this->messenger()->setMessage("Les heures à saisir ci-dessous sont les heures réellement effectuées et non les heures payables."); ?>
 <?php endif; ?>
 
 <?php echo $this->serviceListe( $services )->render(); ?>
 
 <?php else:?>
 <?php echo $this->messenger()->setMessage('Cliquez sur "Afficher" pour afficher les services.', \UnicaenApp\View\Helper\Messenger::INFO); ?>
+
 <?php endif; ?>
\ No newline at end of file
diff --git a/module/Application/view/application/volume-horaire/saisie.phtml b/module/Application/view/application/volume-horaire/saisie.phtml
index 7c058b8c4b758de10379cc6befe89721c8650695..066d892507f4dd5f8538846423fecabc10518067 100644
--- a/module/Application/view/application/volume-horaire/saisie.phtml
+++ b/module/Application/view/application/volume-horaire/saisie.phtml
@@ -4,7 +4,9 @@ $form->prepare();
 
 echo $this->form()->openTag($form);
 echo $this->formControlGroup( $form->get('heures') );
-echo $this->formControlGroup( $form->get('motifNonPaiement') );
+if ($form->has('motifNonPaiement')) {
+    echo $this->formControlGroup( $form->get('motifNonPaiement') );
+}
 echo '<hr /><div style="text-align:right">';
 echo $this->formRow($form->get('submit'));
 echo ' ';
diff --git a/public/css/app.css b/public/css/app.css
index 75dc5b726ebef34103ff5e98f1e2aac74da89c65..837caadae4da4f550f921c873dcd3a1f9fc861d3 100755
--- a/public/css/app.css
+++ b/public/css/app.css
@@ -32,6 +32,10 @@ span.element-rech.periode {
  * Autre
  */
 
+h1.page-header { 
+    margin-top: 10px;
+}
+
 /* Autocomplete jQuery */
 .ui-autocomplete {
     max-height: 300px;
diff --git a/public/js/app.js b/public/js/app.js
index dc743be65e24075b71f61bd51c72db8bfbdaaefc..91dd70ad5ff9c945ed02f35fc4753e87e7103f0b 100755
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -187,39 +187,6 @@ VolumeHoraire.init = function(){
 }
 
 
-/***************************************************************************************************************************************************
-    Etapes
-/***************************************************************************************************************************************************/
-
-$("body").on("etape-after-suppression", function(event, data) {
-    window.history.back();
-});
-
-
-/***************************************************************************************************************************************************
-    Divers
-/***************************************************************************************************************************************************/
-
-/* Initialisation des popovers ajax de liens "a" (utilisés dans les services) */
-$(document).ready(function() {
-/*
-    $("*[data-po-href]").popover({
-        html: true,
-        trigger: 'hover',
-        content: function(e){
-            return $.ajax({
-                type: "GET",
-                url: $($(this).context).data('po-href'),
-                async: false
-            }).responseText;
-        },
-        delay: {show:1000, hide:100}
-    });
-*/
-});
-
-
-
 
 /*************** Propre à l'affichage des services référentiels ***************/
 
@@ -258,4 +225,136 @@ ServiceReferentiel.init = function( voirLigneUrl )
         console.log(event.a.data('id'));
         ServiceReferentiel.get(event.a.data('id')).onAfterDelete();
     });
+}
+
+
+/***************************************************************************************************************************************************
+    Offre de formation
+/***************************************************************************************************************************************************/
+
+//$("body").on("etape-after-suppression", function(event, data) {
+//    window.history.back();
+//});
+
+function Etape( id ) {
+
+    this.id = id;
+
+    this.onAfterAdd = function(){
+//        $.get( Etape.voirLigneUrl + "/" + this.id, function( data ) {
+//            $("#etape-" + this.id + "-ligne").refresh();
+//            $('#etapes > tbody:last').append(data);
+//        });
+        window.location.replace(window.location.toString() + "&etape=" + this.id);
+    }
+
+    this.onAfterModify = function(){
+//        $( "#etape-"+this.id+"-ligne" ).refresh( {details:details} );
+        window.location.replace(window.location.toString() + "&etape=" + this.id);
+    }
+
+    this.onAfterDelete = function(){
+//        $( "#etape-"+this.id+"-ligne" ).fadeOut().remove();
+        window.location.reload();
+    }
+}
+
+Etape.get = function( id )
+{
+    if (null == Etape.services) Etape.services = new Array();
+    if (null == Etape.services[id]) Etape.services[id] = new Etape(id);
+    return Etape.services[id];
+}
+
+Etape.init = function( voirLigneUrl )
+{
+    Etape.voirLigneUrl = voirLigneUrl;
+    
+    $("body").on("event-of-etape-ajouter", function(event, data) {
+        var id = null;
+        event.div.modal('hide'); // ferme la fenêtre modale
+        for (i in data){
+            if (data[i].name === 'id') {
+                id = data[i].value;
+                break;
+            }
+        }
+        if (id) {
+            Etape.get(id).onAfterAdd();
+        }
+    });
+    
+    $("body").on("event-of-etape-modifier", function(event, data) {
+        var id = null;
+        event.div.modal('hide'); // ferme la fenêtre modale
+        for (i in data){
+            if (data[i].name === 'id'){
+                id = data[i].value;
+                break;
+            }
+        }
+        if (id) {
+            Etape.get(id).onAfterModify();
+        }
+    });
+    
+    $("body").on("event-of-etape-supprimer", function(event, data) {
+        event.div.modal('hide'); // ferme la fenêtre modale
+        Etape.get(event.a.data('id')).onAfterDelete();
+    });
+}
+
+
+function ElementPedagogique( id ) {
+
+    this.id = id;
+
+    this.onAfterAdd = function(){
+//        $.get( Etape.voirLigneUrl + "/" + this.id, function( data ) {
+//            $("#etape-" + this.id + "-ligne").refresh();
+//            $('#etapes > tbody:last').append(data);
+//        });
+            window.location.reload();
+    }
+
+    this.onAfterModify = function(){
+//        $( "#etape-"+this.id+"-ligne" ).refresh( {details:details} );
+            window.location.reload();
+    }
+
+    this.onAfterDelete = function(){
+//        $( "#etape-"+this.id+"-ligne" ).fadeOut().remove();
+            window.location.reload();
+    }
+}
+
+ElementPedagogique.get = function( id )
+{
+    if (null == ElementPedagogique.services) ElementPedagogique.services = new Array();
+    if (null == ElementPedagogique.services[id]) ElementPedagogique.services[id] = new ElementPedagogique(id);
+    return ElementPedagogique.services[id];
+}
+
+ElementPedagogique.init = function( voirLigneUrl )
+{
+    ElementPedagogique.voirLigneUrl = voirLigneUrl;
+
+    $("body").on("event-of-element-ajouter", function(event, data) {
+        console.log(event, data);
+        var id = null;
+        event.div.modal('hide'); // ferme la fenêtre modale
+        ElementPedagogique.get(id).onAfterAdd();
+    });
+    
+    $("body").on("event-of-element-modifier", function(event, data) {
+        console.log(event, data);
+        var id = null;
+        event.div.modal('hide'); // ferme la fenêtre modale
+        ElementPedagogique.get(id).onAfterModify();
+    });
+    
+    $("body").on("event-of-element-supprimer", function(event, data) {
+        event.div.modal('hide'); // ferme la fenêtre modale
+        ElementPedagogique.get(event.a.data('id')).onAfterDelete();
+    });
 }
\ No newline at end of file