diff --git a/VERSIONS_WIP/33379 b/VERSIONS_WIP/33379
new file mode 100644
index 0000000000000000000000000000000000000000..34bc1e9001d84f06fb203b506ff60b574be8fa33
--- /dev/null
+++ b/VERSIONS_WIP/33379
@@ -0,0 +1 @@
+ - ticket #33379 : Intégration des demandes d'assistance
\ No newline at end of file
diff --git a/application/modules/admin/controllers/BibController.php b/application/modules/admin/controllers/BibController.php
index b3a56d3ed894b1c743980fc201d4a52b0153ab4d..b34b17c88e2e029b888f495fc626cc9d8feb9427 100644
--- a/application/modules/admin/controllers/BibController.php
+++ b/application/modules/admin/controllers/BibController.php
@@ -35,8 +35,7 @@ class Admin_BibController extends ZendAfi_Controller_Action {
 
             'actions' => ['edit' => ['title' => $this->_("Modifier une bibliothèque"),
                                      'add' => ['title' => $this->_("Ajouter une bibliothèque")]]],
-            'after_edit' => function($formation) { $this->_redirect('/admin/bib');}
-    ];
+            'form_class_name' => 'ZendAfi_Form_Admin_Library'];
   }
 
 
@@ -60,14 +59,6 @@ class Admin_BibController extends ZendAfi_Controller_Action {
   }
 
 
-  protected function _getForm($model) {
-    $this
-      ->_definitions
-      ->setFormClassName( 'ZendAfi_Form_Admin_Library');
-    $form = parent::_getForm($model);
-    return $form;
-  }
-
   protected function _doAfterSave($model) {
     $model->receiveFile('photo');
   }
diff --git a/application/modules/admin/controllers/RedmineController.php b/application/modules/admin/controllers/RedmineController.php
index 6cf1d3c823d690f715db1b8c92a589a10788b471..64e04914912bd60df35609ea0b1a836110cbf82b 100644
--- a/application/modules/admin/controllers/RedmineController.php
+++ b/application/modules/admin/controllers/RedmineController.php
@@ -23,76 +23,166 @@
 class Admin_RedmineController extends ZendAfi_Controller_Action {
 
   public function indexAction() {
-    $this->view->titre = $this->_('Redmine');
-    $this->view->user = Class_Users::getIdentity();
-    $this->fetchLibrariesDatas($this->view->user->getRedmineLibraries());
+    $this->view->titre = $this->_('Assistance');
+    $this->view->user = $user = Class_Users::getIdentity();
+
+    if (!$libraries = $user->getRedmineLibraries())
+      return;
+
+    $this->view->libraries = $this->_librariesOptions($libraries);
+
+    if (!$id_bib = (int)$this->_getParam('id_bib', 0)) {
+      $this->view->library = $this->fetchLibraryDatas($libraries[0]);
+      return;
+    }
+
+    if (!$library = $this->_selectLibraryIn($id_bib, $libraries))
+      return;
+
+    $this->view->library = $this->fetchLibraryDatas($library);
   }
 
 
-  protected function fetchLibrariesDatas($libraries) {
-    $this->view->libraries = [];
+  protected function _librariesOptions($libraries) {
+    $options = [];
+    foreach($libraries as $library)
+      $options[$library->getId()] = $library->getLabel();
 
-    $renderIssues = function($library_id, $data) {
-      return $this->view->libraries[$library_id]['issues'] = Class_WebService_redmine::extractIssues($data);
-    };
+    return $options;
+  }
 
-    $renderUser = function($library_id, $data) {
-      return $this->view->libraries[$library_id]['user'] = Class_WebService_redmine::extractConnectedUser($data, Class_Users::getIdentity()->getRedmineLibrary());
-    };
 
-    $renderProject = function($library_id, $data) {
-      return $this->view->libraries[$library_id ]['project'] = Class_WebService_redmine::extractProject($data);
-    };
+  protected function _selectLibraryIn($id, $possibles) {
+    foreach($possibles as $possible)
+      if ($possible->getId() == $id)
+        return $possible;
+  }
 
-    foreach($libraries as $library) {
-      $this->withResponseDo($library,
-                            Class_WebService_Redmine::getCurrentUser($library),
-                            $renderUser);
 
-      $this->withResponseDo($library,
-                            Class_WebService_Redmine::getIssuesForLib($library),
-                            $renderIssues);
+  protected function fetchLibraryDatas($library) {
+    $entity = (new Class_Entity())
+      ->setLibrary($library)
+      ->setIssues([])
+      ->setUser(null);
 
-      $this->withResponseDo($library,
-                            Class_WebService_Redmine::getProject($library),
-                            $renderProject);
+    $service = new Class_WebService_Redmine($library);
+    if (!$service->isValid())
+      return $entity;
 
-      $this->view->libraries[$library->getId()]['instance'] = $library;
-    }
+    $entity->setIssues($service->getIssues())
+           ->setClosedIssues($service->getClosedIssues())
+           ->setUser($service->getUser());
+
+    return $entity;
   }
 
 
   public function testAction() {
-    $renderSuccess = function($library_id, $data) {
-      if($connected = Class_WebService_Redmine::extractConnectedUser($data, Class_Bib::find($library_id)))
-        return $this->view->getHelper('Redmine_AccountStatus')->successfullConnection($connected);
-      return $this->view->getHelper('Redmine_AccountStatus')->connectionFail();
-    };
-
     $library = Class_Bib::find($this->_getParam('id_bib', 0));
-    return $this->withResponseDoInPopup($library,
-                                        Class_WebService_Redmine::getCurrentUser($library),
-                                        $renderSuccess,
-                                        $this->_('Test de l\'API Redmine'));
-  }
+    $title = $this->_('Test de la configuration de l\'API d\'assistance');
+    $service = new Class_WebService_Redmine($library);
+
+    if (!$service->isValid()) {
+      return $this->_helper->json(['title' => $title,
+                                   'content' => $service->validate()]);
+    }
 
+    $helper = $this->view->getHelper('Redmine_AccountStatus');
+    $message = ($connected = $service->getUser())
+      ? $helper->successfullConnection($connected)
+      : $helper->connectionFail();
 
-  protected function withResponseDoInPopup($library, $data, $call_back, $title) {
     return $this->_helper->json(['title' => $title,
-                                 'content' => $this->withResponseDo($library, $data, $call_back)]);
+                                 'content' => $message]);
+  }
+
+
+  public function addAction() {
+    $this->view->titre = $this->_('Nouvelle demande');
+
+    $library = Class_Bib::find($this->_getParam('id_lib', 0));
+
+    $service = new Class_WebService_Redmine($library);
+    if (null !== $message = $service->validate()) {
+      $this->_helper->notify($message);
+      $this->_redirectToIndex();
+      return;
+    }
+
+    $issue = (new Class_WebService_Redmine_Issue())
+      ->setService($service);
+
+    $this->view->form = $form = ZendAfi_Form_Redmine_Issue::newWithIssue($issue, $service);
+    if ($this->_request->isPost()
+        && $form->isValid($this->_request->getPost())) {
+      $issue->updateAttributes($form->getValues());
+
+      if ($service->createIssue($issue)) {
+        $this->_helper->notify($this->_('Demande enregistrée'));
+        $this->_redirectToIndex();
+        return;
+      }
+
+      $this->_helper->notify($this->_('Erreur lors de l\'enregistrement'));
+    }
+  }
+
+
+  public function deleteAction() {
+    $this->_redirectToIndex();
+  }
+
+
+  public function editIssueAction() {
+    $ticket_number = $this->_getParam('id', 0);
+    $this->view->titre = $this->_('Modifier la demande #%s', $ticket_number);
+
+    $library = Class_Bib::find($this->_getParam('id_lib', 0));
+
+    $service = new Class_WebService_Redmine($library);
+    if (null !== $message = $service->validate()) {
+      $this->_helper->notify($message);
+      $this->_redirectToIndex();
+      return;
+    }
+
+    $issue = $service->getIssue($ticket_number);
+    if (!$issue->getid()) {
+      $this->_redirectToIndex();
+      return;
+    }
+
+    $this->view->issue = $issue;
+    $this->view->form = $form = ZendAfi_Form_Redmine_Issue::newWithIssue($issue, $service);
+
+    if ($this->_request->isPost()
+        && $form->isValid($this->_request->getPost())) {
+      $issue->updateAttributes($form->getValues());
+      $service->updateIssue($issue);
+
+      $this->_helper->notify($this->_('Demande #%s enregistrée', $issue->getid()));
+      $this->_redirectToIndex();
+    }
   }
 
 
-  protected function withResponseDo($library, $data, $call_back) {
-    $message = $this->view->getHelper('Redmine_AccountStatus')->connectionFail();
+  protected function withResponseDo($data, $call_back) {
+    $error_message = $this->view->getHelper('Redmine_AccountStatus')->connectionFail();
 
     if(false === $data)
-      return $message;
+      return $error_message;
+
+    if(isset($data['error_message']))
+      return $data['error_message']
+        ? $data['error_message']
+        : $error_message;
 
-    if(isset($data['error_message']) && '' != $data['error_message'])
-      return $data['error_message'];
+    return call_user_func($call_back, $data);
+  }
 
-    return call_user_func($call_back, $library->getId(), $data);
+
+  protected function withResponseDoInPopup($data, $call_back, $title) {
+    return $this->_helper->json(['title' => $title,
+                                 'content' => $this->withResponseDo($data, $call_back)]);
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/application/modules/admin/controllers/UsersController.php b/application/modules/admin/controllers/UsersController.php
index 8a9d6c73ca7e0aeec37b4f53d516102b7cce2732..276bd86a5d667ab320ff354b21bb9a5e330b1783 100644
--- a/application/modules/admin/controllers/UsersController.php
+++ b/application/modules/admin/controllers/UsersController.php
@@ -26,14 +26,13 @@ class Admin_UsersController extends ZendAfi_Controller_Action {
                         'name' => 'user',
                         'order' => 'id'],
 
-            'messages' => ['successful_save' => $this->_('Utilisateur "%s" sauvegardé'),
+            'messages' => ['successful_save' => $this->_('L\'utilisateur "%s" a été sauvegardé'),
                            'successful_add' => $this->_('L\'utilisateur "%s" a été ajouté'),
-                           'successful_delete' => $this->_('L\'utilisateur "%s" a été supprimée')],
+                           'successful_delete' => $this->_('L\'utilisateur "%s" a été supprimé')],
 
-            'actions' => ['add' => ['title' => $this->_("Ajouter un utilisateur")],
-                          'edit' => ['title' => $this->_("Modifier le utilisateur: %s")],
-                          'delete' => ['title' => $this->_("Supprimer le utilisateur: %s")]
-            ],
+            'actions' => ['add' => ['title' => $this->_('Ajouter un utilisateur')],
+                          'edit' => ['title' => $this->_('Modifier l\'utilisateur: %s')],
+                          'delete' => ['title' => $this->_('Supprimer l\'utilisateur: %s')]],
 
             'form_class_name' => 'ZendAfi_Form_Admin_User'];
   }
@@ -101,23 +100,14 @@ class Admin_UsersController extends ZendAfi_Controller_Action {
 
 
   protected function _setupFormAndSave($model) {
-    if ($this->_request->isPost()) {
-      $role_level=intval($this->_getParam('role_level'));
-      if ($role_level > 4) $this->_setParam('id_site',0);
-      if ($role_level < 2) {
-        $this->_setParam('idabon',0);
-        $this->_setParam('ordreabon',0);
-      }
+    if ($this->_request->isPost())
       $model->updateSIGBOnSave();
-    }
+
     try {
       return parent::_setupFormAndSave($model);
-
     } catch (Exception $e) {
       $this->_helper->notify($e->getMessage());
-
     }
-
   }
 
 
diff --git a/application/modules/admin/views/scripts/redmine/add.phtml b/application/modules/admin/views/scripts/redmine/add.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..ac7363359759fdc81ec27a1c761cdf8d81e268d8
--- /dev/null
+++ b/application/modules/admin/views/scripts/redmine/add.phtml
@@ -0,0 +1,2 @@
+<?php
+echo $this->renderForm($this->form);
diff --git a/application/modules/admin/views/scripts/redmine/edit-issue.phtml b/application/modules/admin/views/scripts/redmine/edit-issue.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..970664867c8a6c99a93ae75aa30030548fdd6baa
--- /dev/null
+++ b/application/modules/admin/views/scripts/redmine/edit-issue.phtml
@@ -0,0 +1,33 @@
+<div class="form">
+  <fieldset>
+    <table>
+      <tr>
+        <td></td>
+        <td class="gauche" style="font-weight:bold;font-size:130%"><?php echo $this->escape($this->issue->getsubject()); ?></td>
+      </tr>
+    </table>
+  </fieldset>
+  <fieldset>
+    <legend><?php echo $this->_('Description');?></legend>
+    <table>
+      <tr>
+        <td></td>
+        <td class="gauche"><pre style="font-family:inherit;font-size:130%;white-space:normal;">
+          <?php echo nl2br($this->issue->getdescription()); ?><pre>
+        </td>
+      </tr>
+    </table>
+  </fieldset>
+  <fieldset>
+    <legend><?php echo $this->_('Historique');?></legend>
+    <table>
+      <tr>
+        <td></td>
+        <td class="gauche"><?php echo $this->redmine_IssueJournal($this->issue); ?></td>
+      </tr>
+    </table>
+  </fieldset>
+</div>
+<?php
+
+echo $this->renderForm($this->form);
diff --git a/application/modules/admin/views/scripts/redmine/index.phtml b/application/modules/admin/views/scripts/redmine/index.phtml
index a10b599032a596956a6a3b45de01378373a78413..f76fdd0994864e45ca2aa3f7992e910d4d6159d3 100644
--- a/application/modules/admin/views/scripts/redmine/index.phtml
+++ b/application/modules/admin/views/scripts/redmine/index.phtml
@@ -1,11 +1,9 @@
 <?php
-$html = '';
-foreach($this->libraries as $library_id => $data)
-  $html .= $this->tag('li', $this->Redmine_Header($this->user,
-                                                  isset($data['instance']) ? $data['instance'] : null,
-                                                  isset($data['user']) ? $data['user'] : false,
-                                                  isset($data['project']) ? $data['project'] : '') .
-$this->Redmine_Issues(isset($data['issues']) ? $data['issues'] : []));
 
-echo $this->tag('ul', $html);
-?>
+if (!$this->libraries) {
+  echo $this->getHelper('Redmine_Header')->shouldSelectLib($this->user);
+  return;
+}
+
+if ($this->library)
+  echo $this->redmine_Library($this->library, $this->user, $this->libraries);
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 03d4db0b71c9e2c7db7d6ef6c2e2c5466c755d0d..c8c4627f5b4a2c4c4d770955cb4a5c963e7e3fdc 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -80,8 +80,6 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
 
 
   public function findAllByController($controller) {
-
-
     return isset($this->knownVars()[$controller])
       ? self::filterByUserRole(array_map(
                   function($clef) {
@@ -291,6 +289,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
        'users' => ['NDAYS_EXPIRY_NOTICE' => Class_AdminVar_Meta::newDefault($this->_('Prévenir l\'utilisateur xx jour(s) avant l\'expiration de son abonnement (par défaut 30 jours).'), ['value' => 30]),
                    'DISABLE_SUGGESTIONS' => Class_AdminVar_Meta::newOnOff($this->_('Désactivation des suggestions d\'achats'))->bePrivate()],
        'redmine' => ['REDMINE_SERVER_URL' => Class_Adminvar_Meta::newDefault($this->_('Url du serveur redmine'))->bePrivate(),
+                     'REDMINE_PROXY_URL' => Class_Adminvar_Meta::newDefault($this->_('Url du proxy Redmine'))->bePrivate()
                      'REDMINE_PROJECT_ID' => Class_Adminvar_Meta::newDefault($this->_('Identifiant du project Redmine'))->bePrivate()],
        'lesocial' => ['LESOCIAL_URL' => Class_Adminvar_Meta::newDefault($this->_('Url du connecteur Le Social'))->bePrivate(),
                       'LESOCIAL_ID' => Class_Adminvar_Meta::newDefault($this->_('Id du connecteur Le Social'))->bePrivate()]
@@ -445,7 +444,9 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
    * @return bool
    */
   public function isRedmineEnabled() {
-    return ('' != Class_AdminVar::get('REDMINE_SERVER_URL'));
+    return ('' != Class_AdminVar::get('REDMINE_SERVER_URL'))
+      && ('' != Class_AdminVar::get('REDMINE_PROJECT_ID'))
+      && ('' != Class_AdminVar::get('REDMINE_PROXY_URL'));
   }
 
 
diff --git a/library/Class/Bib.php b/library/Class/Bib.php
index 31aa4ee5e595edf7d4d68ac3a951f8ca4905a403..c7054decd83036532b4cde117be713e5f5c03a6e 100644
--- a/library/Class/Bib.php
+++ b/library/Class/Bib.php
@@ -18,11 +18,8 @@
  * along with BOKEH; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                                      Table Name
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-require_once dirname(__FILE__)."/CompositeBuilder.php";
 
+require_once dirname(__FILE__)."/CompositeBuilder.php";
 
 
 class BibCSite extends Zend_Db_Table_Abstract {
@@ -135,11 +132,10 @@ class BibLoader extends Storm_Model_Loader {
   }
 
 
-  public function findAllWithRedmine($user) {
+  public function findAllWithRedmine() {
     return array_filter(array_unique(array_merge(Class_Bib::findAllBy(['redmine_login not' => '',
                                                                        'redmine_password not' => '']),
-                                                 Class_Bib::findAllBy(['redmine_api_key not' => '']),
-                                                 [$user->getBib()])));
+                                                 Class_Bib::findAllBy(['redmine_api_key not' => '']))));
   }
 
 
@@ -233,10 +229,15 @@ class Class_Bib extends Storm_Model_Abstract {
                                           'libelle' => '',
                                           'id_zone' => 0,
                                           'ville' => '',
+                                          'mail' => '',
+                                          'telephone' => '',
                                           'aff_zone' => '',
                                           'interdire_resa' => 0,
                                           'google_map' => '',
-                                          'photo' => ''];
+                                          'photo' => '',
+                                          'redmine_login' => '',
+                                          'redmine_password' => '',
+                                          'redmine_api_key' => ''];
 
   protected $_translate;
 
@@ -738,5 +739,9 @@ class Class_Bib extends Storm_Model_Abstract {
   public function getPermissionsChildren() {
     return $this->getArticleCategories();
   }
+
+
+  public function getRedmineLoginOrKey() {
+    return ($login = $this->getRedmineLogin()) ? $login : $this->getRedmineApiKey();
+  }
 }
-?>
\ No newline at end of file
diff --git a/library/Class/User/Settings.php b/library/Class/User/Settings.php
index 4d7b043e9a886ad323135d9e49e4bcdb1a9828ef..b5b6416223cba9c8cf849463109091393bcf5e3b 100644
--- a/library/Class/User/Settings.php
+++ b/library/Class/User/Settings.php
@@ -68,13 +68,13 @@ class Class_User_Settings {
 
 
   public function getRedmineLibrary() {
-    if(!isset($this->_user_settings[self::REDMINE_LIBRARY]))
-      return $this->getLocalLib();
+    if(!isset($this->_user_settings[self::REDMINE_LIBRARY]) && !$this->_user_settings[self::REDMINE_LIBRARY])
+      return $this->getUserLib();
     return Class_Bib::find($this->_user_settings[self::REDMINE_LIBRARY]);
   }
 
 
-  protected function getLocalLib() {
+  protected function getUserLib() {
     return $this->_user->getBib();
   }
 
diff --git a/library/Class/Users.php b/library/Class/Users.php
index c519c624f45a2835959b2c06a35b8834ec871b54..85121d6200c67dcd310131f678c7ed82bcdddb74 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -326,7 +326,7 @@ class Class_Users extends Storm_Model_Abstract {
                                   'idabon' => '',
                                   'date_fin' => '',
                                   'naissance' => '',
-                                  'date_debut' => 0,
+                                  'date_debut' => '',
                                   'telephone' => '',
                                   'mail' => '',
                                   'mobile' => '',
@@ -1664,16 +1664,13 @@ class Class_Users extends Storm_Model_Abstract {
 
   public function getRedmineLibraries() {
     if($this->getRoleLevel() >= ZendAfi_Acl_AdminControllerRoles::MODO_PORTAIL)
-      return Class_Bib::findAllWithRedmine($this);
+      return Class_Bib::findAll();
 
     return [$this->getRedmineLibrary()];
   }
 
 
   public function getLibelle() {
-    return $this->getPrenom() . ' ' . $this->getNom();
+    return $this->getNomComplet();
   }
-
-
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/Class/WebService/Redmine.php b/library/Class/WebService/Redmine.php
index cde3d28f344e7da698d452a5259b8f19b5a9b36b..fe2d641b2a415d4d686ca37f674a44175e2a0742 100644
--- a/library/Class/WebService/Redmine.php
+++ b/library/Class/WebService/Redmine.php
@@ -22,120 +22,361 @@
 class Class_WebService_Redmine extends Class_WebService_Abstract {
   use Trait_Translator;
 
-  protected static $_redmine_client;
+  const CUSTOM_PRIORITY_ID = 5;
+  const CUSTOM_MODULE_ID = 37;
+  const CUSTOM_CONTACT_PERSON = 43;
+  const CUSTOM_CUSTOMER = 1;
+  const CUSTOM_QUALIFICATION = 38;
 
-  protected function missingApiRequirement($library) {
-    if($error_message = $this->missingApiRequirementMessage($library))
-      return ['error_message' => $error_message];
-    return null;
+  protected static $_redmine_client, $_admin_client;
+
+  protected
+    $_library,
+    $_custom_fields_cache,
+    $_issue_statuses_cache,
+    $_issue_priorities_cache;
+
+
+  public function __construct($library) {
+    $this->_library = $library;
+  }
+
+
+  public function getLibrary() {
+    return $this->_library;
   }
 
 
-  protected function missingApiRequirementMessage($library) {
-    if(!$library)
+  protected function missingApiRequirement() {
+    return ($error_message = $this->validate())
+      ? ['error_message' => $error_message]
+      : null;
+  }
+
+
+  public function validate() {
+    if (!$this->_library)
       return $this->_('Vous devez fournir un identifiant de bibliothèque en paramètre');
 
-    if(!Class_AdminVar::isRedmineEnabled())
-      return $this->_('Aucun serveur Redmine est renseigné');
+    if (!Class_AdminVar::isRedmineEnabled())
+      return $this->_('Redmine n\'est pas correctement configuré');
 
-    if((!$library->getRedmineLogin() || !$library->getRedminePassword()) && !$library->getRedmineApiKey())
+    if ((!$this->_library->getRedmineLogin() || !$this->_library->getRedminePassword())
+        && !$this->_library->getRedmineApiKey())
       return $this->_('Vous devez renseigner les champs login et password ou le champ clé d\'API');
 
     return null;
   }
 
 
-  protected function getUserApi($library) {
-    return $this->getClient($library)->api('user');
+  public function isValid() {
+    return null === $this->validate();
+  }
+
+
+  protected function getUserApi() {
+    return $this->getClient()->api('user');
+  }
+
+
+  protected function getIssueApi() {
+    return $this->getClient()->api('issue');
   }
 
 
-  protected function getIssueApi($library) {
-    return $this->getClient($library)->api('issue');
+  protected function getIssueStatusApi() {
+    return $this->getClient()->api('issue_status');
   }
 
 
-  protected function getProjectApi($library) {
-    return $this->getClient($library)->api('project');
+  protected function getIssuePrioritiesApi() {
+    return $this->getClient()->api('issue_priority');
   }
 
 
-  public function getClient($library) {
-    if(static::$_redmine_client == null || !isset(static::$_redmine_client))
-      return new Redmine\Client(Class_AdminVar::get('REDMINE_SERVER_URL'), $library->getRedmineLogin() ? $library->getRedmineLogin() : $library->getRedmineApiKey(), $library->getRedminePassword());
+  protected function getProjectApi() {
+    return $this->getClient()->api('project');
+  }
+
+
+  public function getClient() {
+    if (static::$_redmine_client == null || !isset(static::$_redmine_client))
+      return new Redmine\Client(Class_AdminVar::get('REDMINE_SERVER_URL'),
+                                $this->_library->getRedmineLoginOrKey(),
+                                $this->_library->getRedminePassword());
 
     return static::$_redmine_client;
   }
 
 
+  /** @category testing */
   public static function setClient($redmine_client) {
     static::$_redmine_client = $redmine_client;
   }
 
 
-  public static function getCurrentUser($library) {
-    $instance = new self();
 
-    if($requirement = $instance->missingApiRequirement($library))
-      return $requirement;
+  /** @category testing */
+  public static function setAdminClient($client) {
+    static::$_admin_client = $client;
+  }
+
+
+  public function getIssues() {
+    return $this->isValid() ? $this->getIssuesWithParams() : [];
+  }
+
 
-    return $instance->getUserApi($library)->getCurrentUser();
+  public function getClosedIssues() {
+    return $this->isValid()
+      ? $this->getIssuesWithParams(['status_id' => 'closed',
+                                    'offset' => 0,
+                                    'limit' => 5])
+      : [];
   }
 
 
-  public static function getIssuesForLib($library) {
-    $instance = new self();
+  protected function getIssuesWithParams($params=[]) {
+    $params['project_id'] = Class_AdminVar::get('REDMINE_PROJECT_ID');
 
-    if($requirement = $instance->missingApiRequirement($library))
-      return $requirement;
+    $data = $this->getIssueApi()->all($params);
 
-    return $instance->getIssueApi($library)->all(['assigned_to_id' => $library->getRedmineUserId(),
-                                                  'project_id' => Class_AdminVar::get('REDMINE_PROJECT_ID')]);
+    return isset($data['issues'])
+      ? array_map(['Class_WebService_Redmine_Issue', 'newWith'], $data['issues'])
+      : [];
   }
 
 
-  public static function getProject($library) {
-    $instance = new self();
+  public function getUser() {
+    if (!$this->isValid())
+      return false;
 
-    if(!$id_project = Class_AdminVar::get('REDMINE_PROJECT_ID'))
-      return ['error_message' => $instance->_('Aucun identifiant de projet a été renseigné')];
+    $data = $this->getUserApi()->getCurrentUser();
 
-    if($requirement = $instance->missingApiRequirement($library))
-      return $requirement;
+    if (!isset($data['user']))
+      return false;
+
+    if ($this->_library)
+      $this->_library->setRedmineUserId($data['user']['id'])
+                     ->save();
 
-    return $instance->getProjectApi($library)->show($id_project);
+    return $this->_('Compte d\'assistance: %s', $data['user']['login']);
   }
 
 
   public static function extractIssues($data) {
-    $models = [];
-    if(!isset($data['issues']))
-       return $models;
+    return isset($data['issues'])
+      ? array_map(['Class_WebService_Redmine_Issue', 'newWith'], $data['issues'])
+      : [];
+  }
 
-    return array_map(['Class_WebService_Redmine_Issue', 'newWith'], $data['issues']);
+
+  public function createIssue($issue) {
+    $params = $issue->getCreateParams();
+    $params['project_id'] = Class_AdminVar::get('REDMINE_PROJECT_ID');
+    $params['status_id'] = 1;
+    $params['assigned_to_id'] = $this->_library->getRedmineUserId();
+
+    return $this->getIssueApi()->create($params);
   }
 
 
-  public static function extractConnectedUser($data, $library) {
-    if(!isset($data['user']))
-      return false;
 
-    if($library)
-      $library->setRedmineUserId($data['user']['id'])
-              ->setRedmineApiKey($data['user']['api_key'])
-              ->save();
+  public function updateIssue($issue) {
+    return $this->getIssueApi()
+                ->update($issue->getid(), $issue->getUpdateParams());
+  }
+
+
+  public function getIssue($ticket) {
+    if (!$this->isValid() || !$ticket)
+      return new Class_WebService_Redmine_Issue();
+
+    $data = $this->getIssueApi()->show($ticket, ['include' => 'journals']);
+    $issue = Class_WebService_Redmine_Issue::newWith(isset($data['issue']) ? $data['issue'] : []);
+    $issue->setService($this);
+
+    return $issue;
+  }
+
+
+  public function getIssueStatusOptions() {
+    $statuses = [];
+    foreach($this->getIssueStatus() as $status)
+      $statuses[$status['id']] = $status['name'];
+
+    return $statuses;
+  }
+
+
+  public function getIssueStatus() {
+    if (!$this->isValid())
+      return [];
+
+    if (null != $this->_issue_statuses_cache)
+      return $this->_issue_statuses_cache;
+
+    $data = $this->getIssueStatusApi()
+                 ->all(['project_id' => Class_AdminVar::get('REDMINE_PROJECT_ID')]);
+
+    if (!isset($data['issue_statuses']))
+      return [];
+
+    return $this->_issue_statuses_cache = $data['issue_statuses'];
+  }
+
+
+  public function getIssueStatusFor($id) {
+    if (!$this->isValid())
+      return $id;
+
+     $statuses = $this->getIssueStatusOptions();
+     foreach($statuses as $k => $v)
+       if ($k == $id)
+         return $v;
+
+     return $id;
+  }
+
+
+  public function getIssuePriorityFor($id) {
+    if (!$this->isValid())
+      return $id;
+
+    $items = $this->getIssuePriorities();
+    foreach($items as $item)
+      if ($item['id'] == $id)
+        return $item['name'];
 
-    return (new self())->_('Vous êtes connecté(e) en tant que %s %s',
-                           $data['user']['firstname'],
-                           $data['user']['lastname']);
+     return $id;
   }
 
 
-  public static function extractProject($data) {
-    if(!isset($data['project']))
-       return '';
+  public function getIssuePriorities() {
+    if (!$this->isValid())
+      return [];
+
+    if (null != $this->_issue_priorities_cache)
+      return $this->_issue_priorities_cache;
+
+    $data = $this->getIssuePrioritiesApi()
+                 ->all(['project_id' => Class_AdminVar::get('REDMINE_PROJECT_ID')]);
+
+    if (!isset($data['issue_priorities']))
+      return [];
+
+    return $this->_issue_priorities_cache = $data['issue_priorities'];
+  }
+
+
+
+  public function isIssueStatusClosed($id) {
+    foreach($this->getIssueStatus() as $status)
+      if ($status['id'] == $id)
+        return $status['is_closed'];
+
+    return false;
+  }
+
+
+  public function getCustomFieldFor($id) {
+    foreach($this->getCustomFields() as $field)
+      if ($field['id'] == $id)
+        return $field['name'];
+
+    return $id;
+  }
+
+
+  public function getIssueCustomPriorities() {
+    return $this->getCustomOptions(static::CUSTOM_PRIORITY_ID);
+  }
+
+
+  public function getDefaultIssuePriority() {
+    return $this->getCustomFieldDefaultValue(static::CUSTOM_PRIORITY_ID);
+  }
+
+
+  public function getDefaultIssueModule() {
+    return $this->getCustomFieldDefaultValue(static::CUSTOM_MODULE_ID);
+  }
+
+
+  public function getDefaultIssueContact() {
+    return $this->getCustomFieldDefaultValue(static::CUSTOM_CONTACT_PERSON);
+  }
+
+
+  public function getDefaultIssueCustomer() {
+    return $this->getCustomFieldDefaultValue(static::CUSTOM_CUSTOMER);
+  }
+
+
+  public function getDefaultIssueQualification() {
+    return $this->getCustomFieldFirstValue(static::CUSTOM_QUALIFICATION);
+  }
+
+
+  protected function getCustomFieldDefaultValue($id) {
+    foreach($this->getCustomFields() as $field) {
+      if ($id != $field['id'])
+        continue;
+
+      return isset($field['default_value']) ? $field['default_value'] : null;
+    }
+  }
+
+
+  protected function getCustomFieldFirstValue($id) {
+    foreach($this->getCustomFields() as $field) {
+      if ($id != $field['id'])
+        continue;
+
+      foreach ($field['possible_values'] as $possible)
+        return $possible['value'];
+
+      return '';
+    }
+  }
+
+
+  public function getIssueModules() {
+    return $this->getCustomOptions(static::CUSTOM_MODULE_ID);
+  }
+
+
+  protected function getCustomOptions($id) {
+    $options = [];
+    foreach ($this->getCustomFields() as $field) {
+      if ($id != $field['id'])
+        continue;
+
+      if (!isset($field['is_required']) || !$field['is_required'])
+        $options[''] = '';
+
+      foreach ($field['possible_values'] as $possible) {
+        $options[$possible['value']] = $possible['value'];
+      }
+    }
+
+    return $options;
+  }
+
+
+  protected function getCustomFields() {
+    if (!$this->isValid())
+      return [];
+
+    if (null != $this->_custom_fields_cache)
+      return $this->_custom_fields_cache;
+
+    $data = json_decode($this->httpGet(Class_AdminVar::get('REDMINE_PROXY_URL')),true);
+    if (!isset($data['custom_fields']))
+      return [];
+
+    $this->_custom_fields_cache = $data['custom_fields'];
 
-    return $data['project']['name'];
+    return $this->_custom_fields_cache;
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/Class/WebService/Redmine/Issue.php b/library/Class/WebService/Redmine/Issue.php
index 3ba822926e2e92eebbb6b545093dde1112126065..e9b7c191b9e0acfa01d11f4c0cff18174e261712 100644
--- a/library/Class/WebService/Redmine/Issue.php
+++ b/library/Class/WebService/Redmine/Issue.php
@@ -21,10 +21,10 @@
 
 
 class Class_WebService_Redmine_Issue extends Class_Entity {
-
   public static function newWith($data) {
-    $instance = new self();
+    $instance = new static();
     $instance->updateAttributes($data);
+
     return $instance;
   }
 
@@ -32,5 +32,92 @@ class Class_WebService_Redmine_Issue extends Class_Entity {
   public function callGetterByAttributeName($attribute) {
     return parent::_get($attribute);
   }
-}
-?>
\ No newline at end of file
+
+
+  public function getUpdateParams() {
+    $custom_fields = $this->getCustomFieldsParams();
+    if ($this->isClosed() && !$this->getQualification())
+      $custom_fields[] = ['id' => Class_WebService_Redmine::CUSTOM_QUALIFICATION,
+                          'value' => $this->getDefaultQualification()];
+
+    return ['status_id' => $this->_attribs['status_id'],
+            'custom_fields' => $custom_fields,
+            'notes' => $this->_attribs['notes']];
+  }
+
+
+  public function getCreateParams() {
+    return ['subject' => $this->_attribs['subject'],
+            'description' => $this->_attribs['description'],
+            'custom_fields' => $this->getCustomFieldsParams()];
+  }
+
+
+  protected function getCustomFieldsParams() {
+    return [['id' => Class_WebService_Redmine::CUSTOM_PRIORITY_ID,
+             'value' => $this->_attribs['priority']],
+            ['id' => Class_WebService_Redmine::CUSTOM_MODULE_ID,
+             'value' => $this->_attribs['module']],
+            ['id' => Class_WebService_Redmine::CUSTOM_CONTACT_PERSON,
+             'value' => $this->_attribs['contact']],
+    ];
+  }
+
+
+  public function getCustomField($id) {
+    if (!$fields = $this->getcustom_fields())
+      return null;
+
+    foreach($fields as $field)
+      if ($id == $field['id'])
+        return $field['value'];
+
+    return null;
+  }
+
+
+  public function getPriority() {
+    return $this->getCustomField(Class_WebService_Redmine::CUSTOM_PRIORITY_ID);
+  }
+
+
+  public function getModule() {
+    return $this->getCustomField(Class_WebService_Redmine::CUSTOM_MODULE_ID);
+  }
+
+
+  public function getContact() {
+    return $this->getCustomField(Class_WebService_Redmine::CUSTOM_CONTACT_PERSON);
+  }
+
+
+  public function getLibrary() {
+    return $this->getService()->getLibrary();
+  }
+
+
+  public function getQualification() {
+    return $this->getCustomField(Class_WebService_Redmine::CUSTOM_QUALIFICATION);
+  }
+
+
+  public function getDefaultQualification() {
+    return $this->withServiceDo(
+                                function($service) {
+                                  return $service->getDefaultIssueQualification();
+                                });
+  }
+
+
+  public function isClosed() {
+    return $this->withServiceDo(
+                                function($service) {
+                                  return $service->isIssueStatusClosed($this->_attribs['status_id']);
+                                });
+  }
+
+
+  protected function withServiceDo($closure) {
+    return ($service = $this->getService()) ? $closure($service) : null;
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Form.php b/library/ZendAfi/Form.php
index d09a50fbec25ccea383387bccccce762e794174b..698a0d0b532f8ba510a76cd76720d22270d0e9c7 100644
--- a/library/ZendAfi/Form.php
+++ b/library/ZendAfi/Form.php
@@ -182,5 +182,4 @@ class ZendAfi_Form extends Zend_Form {
     return $this->_summary;
   }
 }
-
 ?>
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Admin/Formation.php b/library/ZendAfi/Form/Admin/Formation.php
index 2788ac71424fad20060713738327e678f7953986..82de178deee691cc5ac6a535b22ae490c573e119 100644
--- a/library/ZendAfi/Form/Admin/Formation.php
+++ b/library/ZendAfi/Form/Admin/Formation.php
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
  * along with BOKEH; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
 
@@ -27,7 +27,7 @@ class ZendAfi_Form_Admin_Formation extends ZendAfi_Form {
     $this
       ->setAttrib('id', 'formationform')
       ->addRequiredTextNamed('libelle')
-      ->setLabel('Libellé *');
+      ->setLabel('Libellé');
 
     $this
       ->addElement('ckeditor',
diff --git a/library/ZendAfi/Form/Admin/Library.php b/library/ZendAfi/Form/Admin/Library.php
index 46b6513fbbcefbd216853eab2f62cc9e132d2aae..978153d59e2ed6b4645a34b73c22388872d168c3 100644
--- a/library/ZendAfi/Form/Admin/Library.php
+++ b/library/ZendAfi/Form/Admin/Library.php
@@ -200,38 +200,29 @@ class ZendAfi_Form_Admin_Library extends ZendAfi_Form {
       ->addElement('text',
                    'redmine_login',
                    ['label' => $this->_('Pseudo'),
-                    'size' => 50])
+                    'size' => 50,
+                    'autocomplete' => 'off'])
 
       ->addElement('password',
                    'redmine_password',
                    ['label' => $this->_('Mot de passe'),
                     'size' => 50,
-                    'onkeypress' => 'if (event.keyCode == 13) {this.form.submit();return false;}',
-                    'renderPassword' => true])
+                    'renderPassword' => true,
+                    'autocomplete' => 'off'])
 
       ->addElement('password',
                    'redmine_api_key',
                    ['label' => $this->_('Clé API'),
                     'size' => 50,
-                    'onkeypress' => 'if (event.keyCode == 13) {this.form.submit();return false;}',
-                    'renderPassword' => true])
-
-      ->addElement('text',
-                   'redmine_status',
-                   ['label' => $this->_('Tester la configuration')])
+                    'renderPassword' => true,
+                    'autocomplete' => 'off'])
 
       ->addDisplayGroup(['redmine_login',
                          'redmine_password',
-                         'redmine_api_key',
-                         'redmine_status'],
+                         'redmine_api_key'],
                         'redmine',
                         ['legend' => $this->_('Configuration du compte Redmine')]);
 
-    $this->getElement('redmine_status')
-         ->addDecorator('Redmine', ['view' => $this->getView(),
-                                    'library' => $this->getView()->bib]);
-
     return $this;
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Admin/SessionFormation.php b/library/ZendAfi/Form/Admin/SessionFormation.php
index bf0dbfa6bbbd6aac2de0660d2a9d4834a0c7d721..7aebd5914a380bf3515f71db3307fa00d166c721 100644
--- a/library/ZendAfi/Form/Admin/SessionFormation.php
+++ b/library/ZendAfi/Form/Admin/SessionFormation.php
@@ -36,7 +36,7 @@ class ZendAfi_Form_Admin_SessionFormation extends ZendAfi_Form {
       ->setAttrib('id', 'sessionForm')
       ->setMethod('post')
       ->addElement('datePicker', 'date_debut', array(
-                                                     'label' => 'Date début *',
+                                                     'label' => 'Date début',
                                                      'size' => 10,
                                                      'required' => true,
                                                      'allowEmpty' => false  ))
@@ -44,19 +44,19 @@ class ZendAfi_Form_Admin_SessionFormation extends ZendAfi_Form {
                                                    'label' => 'Date fin',
                                                    'size' => 10 ))
       ->addElement('datePicker', 'date_limite_inscription', array(
-                                                                  'label' => 'Date limite d\'inscription *',
+                                                                  'label' => 'Date limite d\'inscription',
                                                                   'size'  => 10,
                                                                   'required' => true,
                                                                   'allowEmpty' => false))
       ->addElement('text', 'effectif_min', array(
-                                                 'label' => 'Effectif minimum *',
+                                                 'label' => 'Effectif minimum',
                                                  'size' => 2,
                                                  'required' => true,
                                                  'allowEmpty' => false,
                                                  'validators' => array('int')))
 
       ->addElement('text', 'effectif_max', array(
-                                                 'label' => 'Effectif maximum *',
+                                                 'label' => 'Effectif maximum',
                                                  'size' => 2,
                                                  'required' => true,
                                                  'allowEmpty' => false,
@@ -67,7 +67,7 @@ class ZendAfi_Form_Admin_SessionFormation extends ZendAfi_Form {
                                           'validators' => array('int')))
 
       ->addElement('text', 'horaires', array(
-                                             'label' => 'Horaires *',
+                                             'label' => 'Horaires',
                                              'size' => 50,
                                              'required' => true,
                                              'allowEmpty' => false))
diff --git a/library/ZendAfi/Form/Admin/User.php b/library/ZendAfi/Form/Admin/User.php
index 2c94ff91ae32c4152445862a48d23fc732c107c0..08a4e957a25c1be80fb4dc40c65aa6cd03f8a6be 100644
--- a/library/ZendAfi/Form/Admin/User.php
+++ b/library/ZendAfi/Form/Admin/User.php
@@ -202,27 +202,38 @@ class ZendAfi_Form_Admin_User extends ZendAfi_Form {
     return $this;
   }
 
+
   protected function addSIGB() {
+    $user = $this->getView()->user;
+    $disabled = $user->getRoleLevel() <= ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB
+      ? ['disabled' => 'disabled']
+      : [];
+
     $this
       ->addElement('text',
                    'idabon',
-                   ['label' => $this->_('Numéro'),
-                    'size' => 50])
+                   array_merge(['label' => $this->_('Numéro'),
+                                'size' => 50],
+                               $disabled))
 
       ->addElement('text',
                    'ordreabon',
-                   ['label' => $this->_('Ordre'),
-                    'size' => 50])
+                   array_merge(['label' => $this->_('Ordre'),
+                                'size' => 50],
+                               $disabled))
 
       ->addElement('dateRangePicker',
                    'subscription_range_date',
                    ['label' => $this->_('Validité'),
+
                     'start' => ['name' => 'date_debut',
                                 'allowEmpty' => true,
                                 'dateOnly' => true,
                                 'disabled' => true,
                                 'toggleAllDay' => 'all_day'],
+
                     'end' => ['name' => 'date_fin',
+                              'allowEmpty' => true,
                               'dateOnly' => true,
                               'disabled' => true,
                               'toggleAllDay' => 'all_day']])
@@ -241,13 +252,13 @@ class ZendAfi_Form_Admin_User extends ZendAfi_Form {
       return $this;
 
     $user = $this->getView()->user;
-    $user_library = $user->getRedmineLibrary();
-    $value = $user_library ? $user_library->getId() : null;
-    $options = ['0' => $this->_('Aucune')];
 
-    foreach(Class_Bib::findAllWithRedmine($user) as $library)
-      $options[$library->getId()] = $library->getLibelle();
+    if($user->getRoleLevel() <= ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB)
+      return $this;
 
+    $user_library = $user->getRedmineLibrary();
+    $value = $user_library ? $user_library->getId() : 0;
+    $options = $this->_getLibraries();
     $this
       ->addElement('select',
                    'redmine_library',
@@ -262,7 +273,7 @@ class ZendAfi_Form_Admin_User extends ZendAfi_Form {
       ->addDisplayGroup(['redmine_library',
                          'redmine_status'],
                         'redmine',
-                        ['legend' => $this->_('Accès à la forge')]);
+                        ['legend' => $this->_('Compte d\'accès à l\'assistance')]);
 
     $this->getElement('redmine_status')
          ->addDecorator('Redmine', ['view' => $this->getView(),
@@ -270,5 +281,11 @@ class ZendAfi_Form_Admin_User extends ZendAfi_Form {
 
     return $this;
   }
+
+
+  protected function _getLibraries() {
+    return array_merge(['0' => $this->_('Aucune')],
+                       Class_Bib::findAllLabels());
+  }
 }
 ?>
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Admin/UserGroup.php b/library/ZendAfi/Form/Admin/UserGroup.php
index c62e231e73b8925fe1852b345892c62116b8bf87..aff54cf68f85d8045b9152b5524eced9e19ad5d8 100644
--- a/library/ZendAfi/Form/Admin/UserGroup.php
+++ b/library/ZendAfi/Form/Admin/UserGroup.php
@@ -27,7 +27,7 @@ class ZendAfi_Form_Admin_UserGroup extends ZendAfi_Form {
     $this
       ->setAttrib('id', 'usergroupform')
       ->addRequiredTextNamed('libelle')
-      ->setLabel('Libellé *');
+      ->setLabel('Libellé');
 
     $this
       ->addElement('radio',
diff --git a/library/ZendAfi/Form/Album.php b/library/ZendAfi/Form/Album.php
index 6c2a2432b3f7fd807e417267503817cb69ffce5e..6e83ade7dc51a1c1fdc47acbf9f1e5dbd67f0631 100644
--- a/library/ZendAfi/Form/Album.php
+++ b/library/ZendAfi/Form/Album.php
@@ -133,7 +133,7 @@ class ZendAfi_Form_Album extends ZendAfi_Form {
     $this
       ->setAttrib('id', 'album')
       ->setAttrib('enctype', self::ENCTYPE_MULTIPART)
-      ->addElement('text', 'titre', ['label'      => $this->_('Titre *'),
+      ->addElement('text', 'titre', ['label'      => $this->_('Titre'),
                                      'style'        => 'width:440px;',
                                      'size'       => 75,
                                      'required'   => true,
diff --git a/library/ZendAfi/Form/Decorator/Redmine.php b/library/ZendAfi/Form/Decorator/Redmine.php
index 91e48e7a535f27c702b1972ab3c8c4a6d47cbe01..8db3e0f31a93342bba1f8d2db0651cae7444589c 100644
--- a/library/ZendAfi/Form/Decorator/Redmine.php
+++ b/library/ZendAfi/Form/Decorator/Redmine.php
@@ -22,7 +22,11 @@
 class ZendAfi_Form_Decorator_Redmine extends Zend_Form_Decorator_Abstract {
   public function render($content) {
     $view = $this->getOption('view');
-    return $content . $view->Redmine_AccountStatus($this->getOption('library'));
+    $library = $this->getOption('library');
+    $content = $content .
+      $view->Redmine_AccountStatus($library) .
+      $view->getHelper('Redmine_Header')->anchorEditLibrary($library);
+    return $content;
   }
 }
 ?>
\ No newline at end of file
diff --git a/library/ZendAfi/Form/FRBR/Link.php b/library/ZendAfi/Form/FRBR/Link.php
index 69f1b294e9fb4c92fae9b3e3980586b9d3482938..6115dfaace873ac06e32c6cfcee7c900328fd024 100644
--- a/library/ZendAfi/Form/FRBR/Link.php
+++ b/library/ZendAfi/Form/FRBR/Link.php
@@ -26,9 +26,9 @@ class ZendAfi_Form_FRBR_Link extends ZendAfi_Form {
       ->setAttrib('id', 'frbr_link')
       ->setAttrib('class', 'zend_form')
 
-      ->addElement('frbrType', 'type_id', ['label' => $this->_('Type').' *'])
-      ->addElement('text', 'source', ['label' => $this->_('URL Objet A') . ' *', 'size' => 80])
-      ->addElement('text', 'target', ['label' => $this->_('URL Objet B') . ' *', 'size' => 80])
+      ->addElement('frbrType', 'type_id', ['label' => $this->_('Type'), 'required' => true])
+      ->addElement('text', 'source', ['label' => $this->_('URL Objet A') ,'required' => true, 'size' => 80])
+      ->addElement('text', 'target', ['label' => $this->_('URL Objet B') ,'required' => true, 'size' => 80])
 
       ->addDisplayGroup(['type_id', 'source', 'target'], 'link', ['legend' => '']);
   }
diff --git a/library/ZendAfi/Form/FRBR/LinkType.php b/library/ZendAfi/Form/FRBR/LinkType.php
index fa6462416851f8ee2034eba7ca787e3593274eb7..5ac3cb83f930be94ec499eae3cbc66b0391a02f2 100644
--- a/library/ZendAfi/Form/FRBR/LinkType.php
+++ b/library/ZendAfi/Form/FRBR/LinkType.php
@@ -26,11 +26,11 @@ class ZendAfi_Form_FRBR_LinkType extends ZendAfi_Form {
       ->setAttrib('id', 'frbr_linktype')
       ->setAttrib('class', 'zend_form')
 
-      ->addElement('text', 'libelle', ['label' => $this->_('Nom').' *', 'size' => 80])
-      ->addElement('text', 'from_source', ['label' => $this->_('Libellé de l\'objet A vers l\'objet B') . ' *',
-                                           'size' => 80])
-      ->addElement('text', 'from_target', ['label' => $this->_('Libellé de l\'objet B vers l\'objet A') . ' *',
-                                           'size' => 80])
+      ->addElement('text', 'libelle', ['label' => $this->_('Nom'), 'size' => 80, 'required' => true])
+      ->addElement('text', 'from_source', ['label' => $this->_('Libellé de l\'objet A vers l\'objet B') ,
+                                           'size' => 80, 'required' => true])
+      ->addElement('text', 'from_target', ['label' => $this->_('Libellé de l\'objet B vers l\'objet A') ,
+                                           'size' => 80, 'required' => true])
 
       ->addDisplayGroup(['libelle', 'from_source', 'from_target'], 'linktype', ['legend' => '']);
   }
diff --git a/library/ZendAfi/Form/Login.php b/library/ZendAfi/Form/Login.php
index 00eacdf86efc6803906a4a0bf42ffe4df7d1cc99..00a80e7bb2d5f006fb3efc9e90b87f05af3e5873 100644
--- a/library/ZendAfi/Form/Login.php
+++ b/library/ZendAfi/Form/Login.php
@@ -54,8 +54,7 @@ class ZendAfi_Form_Login extends ZendAfi_Form {
                        'placeholder' => $this->_data['mot_de_passe_exemple'],
                        'required' => true,
                        'allowEmpty' => false,
-                       'size' => 15,
-                       'onkeypress' => 'if (event.keyCode == 13) {this.form.submit();return false;}'])
+                       'size' => 15])
 
          ->addElement('submit',
                       'login',
diff --git a/library/ZendAfi/Form/Redmine/Issue.php b/library/ZendAfi/Form/Redmine/Issue.php
new file mode 100644
index 0000000000000000000000000000000000000000..d431b4140c5c5f93f37cb2142bd7e65898dbd8db
--- /dev/null
+++ b/library/ZendAfi/Form/Redmine/Issue.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class ZendAfi_Form_Redmine_Issue extends ZendAfi_Form {
+  protected
+    $_issue,
+    $_status = [], $_priorities = [], $_modules = [],
+    $_default_priority, $_default_module, $_default_contact, $_default_customer;
+
+
+  public static function newWithIssue($issue, $service) {
+    return (new static())
+      ->initIssue($issue)
+      ->initMultiOptions($service)
+      ->initInputs();
+  }
+
+
+  protected function initIssue($issue) {
+    $this->_issue = $issue;
+    return $this;
+  }
+
+
+  protected function initMultiOptions($service) {
+    $this->_status = $service->getIssueStatusOptions();
+
+    $this->_priorities = $service->getIssueCustomPriorities();
+    $this->_modules = $service->getIssueModules();
+    $this->_default_priority = $service->getDefaultIssuePriority();
+    $this->_default_module = $service->getDefaultIssueModule();
+    $this->_default_contact = $service->getDefaultIssueContact();
+    $this->_default_customer = $service->getDefaultIssueCustomer();
+
+    return $this;
+  }
+
+
+  protected function initInputs() {
+    if (!$this->_issue->getid()) {
+      $this
+        ->addElement('text',
+                     'subject',
+                     ['label' => $this->_('Sujet'),
+                      'size' => 70,
+                      'required' => true,
+                      'allowEmpty' => false])
+
+        ->addElement('textarea',
+                     'description',
+                     ['label' => $this->_('Description'),
+                      'rows' => 10,
+                      'cols' => 70,
+                      'required' => true,
+                      'allowEmpty' => false,
+                      'value' => $this->getDefaultDescription()])
+
+        ->addDisplayGroup(['subject', 'description'],
+                          'common',
+                          ['legend' => ''])
+        ;
+    }
+
+
+    if ($this->_issue->getid()) {
+      $this
+        ->addElement('textarea', 'notes', ['rows' => 5, 'cols' => 100])
+        ->addDisplayGroup(['notes'], 'default', ['legend' => $this->_('Nouvelle note')]);
+    }
+
+
+    $this->addStatus()
+
+         ->addElement('text',
+                      'customer',
+                      ['label' => $this->_('Client'),
+                       'size' => 70,
+                       'value' => $this->_issue->getCustomer() ? $this->_issue->getCustomer() : $this->getDefaultCustomer()])
+
+         ->addElement('select',
+                      'priority',
+                      ['label' => $this->_('Priorité client'),
+                       'required' => true,
+                       'allowEmpty' => false,
+                       'multioptions' => $this->_priorities,
+                       'value' => $this->_issue->getPriority() ? $this->_issue->getPriority() : $this->_default_priority])
+
+         ->addElement('select',
+                      'module',
+                      ['label' => $this->_('Module Portail'),
+                       'multioptions' => $this->_modules,
+                       'value' => $this->_issue->getModule() ? $this->_issue->getModule() : $this->_default_module])
+
+         ->addElement('textarea',
+                      'contact',
+                      ['label' => $this->_('Personne à contacter'),
+                       'rows' => 2, 'cols' => 70,
+                       'value' => $this->_issue->getContact() ? $this->_issue->getContact() : $this->getDefaultContact()])
+
+
+         ->addDisplayGroup(['customer', 'status_id', 'priority', 'module', 'contact'],
+                           'properties',
+                           ['legend' => $this->_('Propriétés')]);
+
+    return $this;
+  }
+
+
+  protected function getDefaultContact() {
+    if ($this->_issue->getid())
+      return '';
+
+    if ($by_user = $this->getDefaultUserContact())
+      return $by_user;
+
+    return ($by_lib = $this->getDefaultLibraryContact())
+      ? $by_lib : $this->_default_contact;
+  }
+
+
+  protected function getDefaultUserContact() {
+    if (!$user = Class_Users::getIdentity())
+      return;
+
+    $datas = [$user->getNom(),
+              $user->getPrenom(),
+              $user->getMail(),
+              $user->hasTelephone() ? $user->getTelephone() : $user->getMobile()];
+
+    foreach($datas as $data)
+      if ($data)
+        return implode(' / ', array_filter($datas));
+  }
+
+
+  protected function getDefaultLibraryContact() {
+    if (!$library = $this->_issue->getLibrary())
+      return;
+
+    if ($library->hasMail() || $library->hasTelephone())
+        return implode(' / ', array_filter([$library->getLibelle(),
+                                            $library->getMail(),
+                                            $library->getTelephone()]));
+  }
+
+
+  protected function getDefaultCustomer() {
+    if ($this->_issue->getid())
+      return '';
+
+    return ($library = $this->_issue->getLibrary()) && ($label = $library->getLibelle())
+      ? $label : $this->_default_customer;
+  }
+
+
+  protected function addStatus() {
+    if (!$current = $this->_issue->getstatus()['id'])
+      return $this;
+
+    $possibles = [];
+
+    /** !! from id status to id status (AFI, specifics) */
+    $workflow = [1 => [5],
+                 14 => [1],
+                 15 => [],
+                 4 => [2, 5],
+                 19 => [],
+                 2 => [1, 5],
+                 13 => [],
+                 22 => [],
+                 7 => [5],
+                 18 => [],
+                 16 => [],
+                 3 => [5],
+                 17 => [],
+                 11 => [4, 5],
+                 8 => [5],
+                 12 => [],
+                 5 => [1],
+                 6 => [1, 5]];
+
+    foreach($this->_status as $k => $v) {
+      if ($k == $current
+          || (array_key_exists($current, $workflow) && in_array($k, $workflow[$current])))
+        $possibles[$k] = $v;
+    }
+
+    $this->addElement('select',
+                      'status_id',
+                      ['label' => $this->_('Status'),
+                       'multioptions' => $possibles,
+                       'value' => $current]);
+
+    return $this;
+  }
+
+
+  protected function getDefaultDescription() {
+    $datas = [$this->_('Url : ') . Class_Url::rootUrl() . Class_Url::baseUrl(),
+              $this->_('Version : ') . BOKEH_RELEASE_NUMBER . ' (' . BOKEH_VERSION . ')',
+              $this->_('Navigateur : ') . Zend_Controller_Front::getInstance()->getRequest()->getHeader('User-Agent'),
+              $this->_('Base de données : ') . Zend_Db_Table::getDefaultAdapter()->getConfig()['dbname']];
+
+    return implode("\n", $datas);
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php
index 5ba47b992d815333373b055020d70c331287018f..02fb23204d7a4f6e706441f1aba6dc1064adc7b9 100644
--- a/library/ZendAfi/View/Helper/Admin/HelpLink.php
+++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php
@@ -75,7 +75,9 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki {
                                   'accueil' => 'Configurer_une_page',
                                   'menusindex' => 'Configurer_un_menu',
                                   'proprietes' => 'Réglage_de_l\'affichage_des_exemplaires'],
-     'redmine'                => ['index' => 'Visualisation_des_tickets_Redmine'],
+     'redmine'                => ['index' => 'Intégration_des_demandes_d\'assistance',
+                                  'add' => 'Intégration_des_demandes_d\'assistance#Ajout_d.27une_demande',
+                                  'edit-issue' => 'Intégration_des_demandes_d\'assistance#Modification_d.27une_demande'],
      'opds'                   => ['index' => 'Importer_des_livres_numériques'],
      'bib'                    => ['index' => 'Modifier_une_bibliothèque'],
      'index'                  => ['index' => 'Bokeh']
diff --git a/library/ZendAfi/View/Helper/Admin/MenuHorizontalAdmin.php b/library/ZendAfi/View/Helper/Admin/MenuHorizontalAdmin.php
index 1a5a4aa898d1b903285d341b92a5261381d4f466..38e03c7bff1076d7683ddd9dea707470af85a023 100644
--- a/library/ZendAfi/View/Helper/Admin/MenuHorizontalAdmin.php
+++ b/library/ZendAfi/View/Helper/Admin/MenuHorizontalAdmin.php
@@ -26,7 +26,7 @@ class ZendAfi_View_Helper_Admin_MenuHorizontalAdmin extends ZendAfi_View_Helper_
   public function menuHorizontalAdmin() {
     $redmine = Class_AdminVar::isRedmineEnabled()
       ? ['icon' => 'redmine_16.png',
-         'label' => $this->_('Redmine'),
+         'label' => $this->_('Assistance'),
          'url' => $this->view->url(['module' => 'admin',
                                     'controller' => 'redmine',
                                     'action' => 'index'], null, true)]
diff --git a/library/ZendAfi/View/Helper/Redmine/AccountStatus.php b/library/ZendAfi/View/Helper/Redmine/AccountStatus.php
index e6a1b1099b395785dfd06106bb291d2bc6bf8e6c..fac1f8d77b1521c0839459a470fbf1bff622bce0 100644
--- a/library/ZendAfi/View/Helper/Redmine/AccountStatus.php
+++ b/library/ZendAfi/View/Helper/Redmine/AccountStatus.php
@@ -29,7 +29,7 @@ class ZendAfi_View_Helper_Redmine_AccountStatus extends ZendAfi_View_Helper_Base
     Class_ScriptLoader::getInstance()
       ->addOpacScript('redmine')
       ->addJQueryReady('$("a.redmine_status").autoRefresh();')
-      ->addJQueryReady('var html = $("a.redmine_status"); var input = $("#redmine_status"); input.parent().append(html); input.remove()');
+      ->addJQueryReady('var html = $("a.redmine_status, a.library_redmine_account"); var input = $("#redmine_status"); input.parent().append(html); input.remove()');
 
     $lib_id = $library ? $library->getId() : 0;
 
diff --git a/library/ZendAfi/View/Helper/Redmine/Header.php b/library/ZendAfi/View/Helper/Redmine/Header.php
index a12ba3facee0b0d3a6712d700fddc286373fbce7..0800522d6ebaef207b94b756d09224fda677dbbd 100644
--- a/library/ZendAfi/View/Helper/Redmine/Header.php
+++ b/library/ZendAfi/View/Helper/Redmine/Header.php
@@ -21,58 +21,98 @@
 
 
 class ZendAfi_View_Helper_Redmine_Header extends ZendAfi_View_Helper_BaseHelper {
+  protected $_user, $_library, $_user_info, $_others;
+
+  public function Redmine_Header($user, $library, $user_info, $others=[]) {
+    $this->_user = $user;
+    $this->_library = $library;
+    $this->_user_info = $user_info;
+    $this->_others = $others;
 
-  public function Redmine_Header($user, $library, $user_info = false, $project_info) {
     if (!Class_AdminVar::isRedmineEnabled() || !$user)
       return '';
 
-    if(!$user_info)
-      $this->view->getHelper('Redmine_AccountStatus')->connectionFail();
+    if ((!$user->getRedmineLibrary()) && (!$library))
+      return $this->shouldSelectLib();
+
+    $add_button = $user_info
+      ? $this->view->bouton('id=add_issue',
+                            'picto=add.gif',
+                            'texte='.$this->_('Nouvelle demande'),
+                            'url=' . $this->view->url(['action' => 'add',
+                                                       'id' => null,
+                                                       'id_lib' => $library->getId()]),
+                            'largeur=250px;')
+      : '';
+
+    return $this->view->tag('p', $this->connectedThrough())
+      . $add_button;
+  }
+
+
+  public function shouldSelectLib($user=null) {
+    return $this->view
+      ->tag('p',
+            $this->anchorEditUser($this->_('Vous devez sélectionner une bibliothèque'), $user));
+  }
 
-    if((!$user->getRedmineLibrary()) && (!$library))
-      return $this->shouldSelectLib($user);
 
-    return $this->view->tag('p', $this->projectInfo($project_info) .
-                            BR .
-                            $this->connectedThrough($user, $library, $user_info));
+  protected function connectedThrough() {
+    return $this
+      ->_tag('div', $this->renderLibraryLabel() .  $this->anchorEditLibrary($this->_library, $this->_user_info),
+             ['class' => 'form']);
   }
 
 
-  protected function shouldSelectLib($user) {
-    return $this->view->tag('p', $this->anchorEditUser($user, $this->_('Vous devez sélectionner une bibliothèque')));
+  protected function renderLibraryLabel() {
+    return ($this->_others && 1 < count($this->_others))
+      ? $this->renderLibrariesSelector()
+      : $this->anchorEditUser($this->_library->getLabel());
   }
 
 
-  protected function connectedThrough($user, $library, $user_info) {
-    return
-      $this->anchorEditLibrary($library, $user_info) .
-      $this->_(' au travers de la bibliothèque ') .
-      $this->anchorEditUser($user, $library->getLibelle());
+  protected function renderLibrariesSelector() {
+    $location = $this->view->url(['module' => 'admin',
+                                  'controller' => 'redmine'], null, true);
+
+    return $this->_tag('strong', $this->_('Bibliothèque : '))
+      . $this->view->formSelect('library',
+                                $this->_library ? $this->_library->getId() : null,
+                                ['onchange' => "location='" . $location . "/index/id_bib/' + this.value "],
+                                $this->_others);
   }
 
 
-  protected function anchorEdituser($user, $library_label) {
+  protected function anchorEdituser($label, $user=null) {
+    $user = $user ? $user : $this->_user;
+
     return $this->view->tagAnchor($this->view->url(['module' => 'admin',
                                                     'controller' => 'users',
                                                     'action' => 'edit',
                                                     'id' => $user->getId()], null ,true) . '#redmine',
-                                  $library_label,
+                                  $label,
                                   ['title' => $this->_('sélectionner une autre bibliothèque')]);
   }
 
 
-  protected function anchorEditLibrary($library, $user_info) {
+  public function anchorEditLibrary($library, $user_info = '') {
+    if(!$library)
+      return '';
+
+    $title = $this->_('Modifier le compte d\'assistance');
+
     return $this->view->tagAnchor($this->view->url(['module' => 'admin',
                                                     'controller' => 'bib',
                                                     'action' => 'edit',
                                                     'id' => $library->getId()], null ,true) . '#redmine',
-                                  $user_info ? $user_info : $this->_('Aucun compte'),
-                                  ['title' => $this->_('modifier le compte redmine')]);
+                                  $user_info ? $user_info : $title,
+                                  ['class' => 'library_redmine_account',
+                                   'title' => $title]);
   }
 
 
-  protected function projectInfo($name) {
-    return $this->_('Projet sélectionné : ') . $this->anchorEditProject($name);
+  public function projectInfo($name) {
+    return $this->_('Demandes filtrées par le projet : ') . $this->anchorEditProject($name);
   }
 
 
@@ -85,5 +125,4 @@ class ZendAfi_View_Helper_Redmine_Header extends ZendAfi_View_Helper_BaseHelper
                                   ['title' => $this->_('sélectionner un autre projet'),
                                    'data-popup' => 'true']);
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/Redmine/IssueJournal.php b/library/ZendAfi/View/Helper/Redmine/IssueJournal.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b1a65b6fcf9c67033a3f1ca646f8b3a8dc50c44
--- /dev/null
+++ b/library/ZendAfi/View/Helper/Redmine/IssueJournal.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class ZendAfi_View_Helper_Redmine_IssueJournal extends ZendAfi_View_Helper_BaseHelper {
+  protected $_issue;
+
+  public function Redmine_IssueJournal($issue) {
+    if (!$issue || (!$events = $issue->getjournals()))
+      return $this->_('Aucun historique');
+
+    Class_ScriptLoader::getInstance()
+      ->addInlineStyle('
+.models td { padding:5px; vertical-align:top;}
+.models td pre, .models td ul { font-family:inherit; margin:0; }
+');
+
+    $this->_issue = $issue;
+
+    return $this->_tag('table',
+                       $this->renderHeader() . $this->renderBody($events),
+                       ['class' => 'models']);
+  }
+
+
+  protected function renderHeader() {
+    return $this->_tag('thead',
+                       $this->_tag('tr',
+                                   $this->_tag('th', $this->_('Créé le'))
+                                   . $this->_tag('th', $this->_('Acteur'))
+                                   . $this->_tag('th', $this->_('Note'))
+                                   . $this->_tag('th', $this->_('Détails'))));
+  }
+
+
+  protected function renderBody($events) {
+    $html = '';
+    foreach($events as $event)
+      $html .= $this->render($event);
+
+    return $this->_tag('tbody', $html);
+  }
+
+
+  protected function render($event) {
+    return $this->_tag('tr',
+                       $this->renderDate($event)
+                       . $this->renderAuthor($event)
+                       . $this->renderNote($event['notes'])
+                       . $this->renderDetails($event['details'])
+    );
+  }
+
+
+  protected function renderDate($event) {
+    return $this->_tag('td', strftime($this->_('%x à %X'), strtotime($event['created_on'])));
+  }
+
+
+  protected function renderAuthor($event) {
+    return $this->_tag('td', $event['user']['name']);
+  }
+
+
+  protected function renderDetails($details) {
+    $html = '';
+    foreach($details as $detail)
+      $html .= $this->renderDetail($detail);
+
+    return $this->_tag('td', $this->_tag('ul', $html));
+  }
+
+
+  protected function renderNote($note) {
+    return $this->_tag('td', $note ? nl2br($note) : '');
+  }
+
+
+  protected function renderDetail($detail) {
+    if ('relation' == $detail['property'])
+      return $this->_tag('li', $this->labelFromCode($detail['name'],
+                                                    isset($detail['old_value']) ? $detail['old_value'] : null,
+                                                    isset($detail['new_value']) ? $detail['new_value'] : null));
+
+    if ('attr' == $detail['property'])
+      return $this->_tag('li', $this->labelFromCode($detail['name'], $detail['old_value'], $detail['new_value']));
+
+    if ('cf' == $detail['property'])
+      return $this->_tag('li', $this->getChangeLabel($this->customFrom($detail['name']),
+                                                     $detail['old_value'], $detail['new_value']));
+
+    return '';
+  }
+
+
+  protected function labelFromCode($code, $old_value, $new_value) {
+    $mapping =
+      ['copied_from' => function() use ($old_value, $new_value) {
+         return $this->getRelationLabel($this->_('Copié depuis'), $old_value, $new_value);
+       },
+
+       'copied_to' => function() use ($old_value, $new_value) {
+         return $this->getRelationLabel($this->_('Copié vers'), $old_value, $new_value);
+       },
+
+       'blocks' => function() use ($old_value, $new_value) {
+         return $this->getRelationLabel($this->_('Bloque'), $old_value, $new_value);
+       },
+
+       'status_id' => function() use ($old_value, $new_value) {
+         return $this->getChangeLabel($this->_('Statut'),
+                                      $this->statusFrom($old_value), $this->statusFrom($new_value));
+       },
+
+       'priority_id' => function() use ($old_value, $new_value) {
+         return $this->getChangeLabel($this->_('Priorité'),
+                                      $this->priorityFrom($old_value), $this->priorityFrom($new_value));
+       },
+
+       'done_ratio' => function() use ($old_value, $new_value) {
+         return $this->getChangeLabel($this->_('% réalisé'), $old_value, $new_value);
+       }];
+
+    return array_key_exists($code, $mapping)
+      ? $mapping[$code]()
+      : $this->getChangeLabel($code, $old_value, $new_value);
+  }
+
+
+  protected function statusFrom($id) {
+    if (!$service = $this->_issue->getService())
+      return $id;
+
+    return $service->getIssueStatusFor($id);
+  }
+
+
+  protected function priorityFrom($id) {
+    if (!$service = $this->_issue->getService())
+      return $id;
+
+    return $service->getIssuePriorityFor($id);
+  }
+
+
+  protected function customFrom($id) {
+    if (!$service = $this->_issue->getService())
+      return $this->_('Champ personnalisé %s', $id);
+
+    return $service->getCustomFieldFor($id);
+  }
+
+
+  protected function getChangeLabel($field, $old_value, $new_value) {
+    return (!$old_value)
+      ? $this->_('%s mis à %s', $field, $new_value)
+      : $this->_('%s changé de %s à %s', $field, $old_value, $new_value);
+  }
+
+
+  protected function getRelationLabel($type, $old_value, $new_value) {
+    return $new_value
+      ? $this->_('%s #%s ajouté', $type, $new_value)
+      : $this->_('%s #%s supprimé', $type, $old_value);
+  }
+}
diff --git a/library/ZendAfi/View/Helper/Redmine/Issues.php b/library/ZendAfi/View/Helper/Redmine/Issues.php
index f0648d76f11f59378b36a9c5d5a19f0b0ccb2626..11c9c76b7f9573313c9f8203fd97b86eb4414fe4 100644
--- a/library/ZendAfi/View/Helper/Redmine/Issues.php
+++ b/library/ZendAfi/View/Helper/Redmine/Issues.php
@@ -21,22 +21,49 @@
 
 
 class ZendAfi_View_Helper_Redmine_Issues extends ZendAfi_View_Helper_BaseHelper {
+  public function Redmine_Issues($library) {
+    if (!Class_AdminVar::isRedmineEnabled() || !$library)
+      return '';
+
+    return $this->renderIssues($library, $library->getIssues(), $this->_('Demandes en cours'))
+      . $this->renderIssues($library, $library->getClosedIssues(), $this->_('Demandes fermées'));
+  }
 
-  public function Redmine_Issues($issues) {
-    if (!Class_AdminVar::isRedmineEnabled())
+
+  protected function renderIssues($library, $issues, $label) {
+    if (!$issues)
       return '';
 
-    return $this->view->tagModelTable($issues,
-                                      [$this->_('Numéro de ticket'),
-                                       $this->_('Sujet'),
-                                       $this->_('Description'),
-                                       $this->_('Date de création')],
-                                      ['id',
-                                       'subject',
-                                       'description',
-                                       'created_on'],
-                                      null,
-                                      null);
+    $editAction = function($issue) use($library) {
+      return $this->view->tagAnchor(['module' => 'admin',
+                                     'controller' => 'redmine',
+                                     'action' => 'edit-issue',
+                                     'id_lib' => $library->getLibrary()->getId(),
+                                     'id' => $issue->getid()],
+                                    $this->view->boutonIco('type=edit'));
+    };
+
+    $status_renderer = function($model) {
+      return $model->getstatus()['name'];
+    };
+
+    $date_renderer = function($model) {
+      return strftime('%x', strtotime($model->getcreated_on()));
+    };
+
+    return $this->_tag('h3', $label)
+      . $this->view->tagModelTable($issues,
+                                   [$this->_('Numéro'), $this->_('Sujet'),
+                                    $this->_('Statut'), $this->_('Créée le')],
+
+                                   ['id', 'subject', 'status', 'created_on'],
+
+                                   [$editAction],
+
+                                   'issues-' . $library->getLibrary()->getId(),
+                                   null,
+
+                                   ['status' => $status_renderer,
+                                    'created_on' => $date_renderer]);
   }
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/Redmine/Library.php b/library/ZendAfi/View/Helper/Redmine/Library.php
new file mode 100644
index 0000000000000000000000000000000000000000..7515953d320c601a5999cc4f2b361141fd815138
--- /dev/null
+++ b/library/ZendAfi/View/Helper/Redmine/Library.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class ZendAfi_View_Helper_Redmine_Library extends ZendAfi_View_Helper_BaseHelper {
+  public function redmine_Library($library, $user, $others=[]) {
+    return $this->view->redmine_Header($user, $library->getLibrary(), $library->getUser(), $others)
+      . $this->view->redmine_Issues($library);
+  }
+}
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/RenderForm.php b/library/ZendAfi/View/Helper/RenderForm.php
index d7847952eec5473f009cc6522732b1503dac1c4f..999e83afa077bb557c741f6c163fe74624c3f638 100644
--- a/library/ZendAfi/View/Helper/RenderForm.php
+++ b/library/ZendAfi/View/Helper/RenderForm.php
@@ -78,7 +78,11 @@ class ZendAfi_View_Helper_RenderForm extends ZendAfi_View_Helper_BaseHelper {
 
 
   protected function _decoratorsForTableRendering($element) {
-    $newDecorators = array();
+    $newDecorators = [];
+
+    if ('formText' == $element->helper)
+      $element->onkeypress = 'if (event.keyCode == 13) {this.form.submit();return false;}';
+
     $decorators = $element->getDecorators();
     foreach ($decorators as $name => $decorator) {
       $name = explode('_', $name);
diff --git a/library/storm b/library/storm
index 92ffd36e9323bee39bbbdce09ba8e0b11b0ba0b2..72d925ab2bcd2a97f97f4ec2bb8be6b63ebdf462 160000
--- a/library/storm
+++ b/library/storm
@@ -1 +1 @@
-Subproject commit 92ffd36e9323bee39bbbdce09ba8e0b11b0ba0b2
+Subproject commit 72d925ab2bcd2a97f97f4ec2bb8be6b63ebdf462
diff --git a/public/admin/css/global.css b/public/admin/css/global.css
index 27dcc58ca5c1f5904a5b8921055e3cd536a5e65a..0f1b255c31cdfbf9dc02f9e43691ab3d6d1e707b 100644
--- a/public/admin/css/global.css
+++ b/public/admin/css/global.css
@@ -10,8 +10,6 @@ a, .menuAdmin li {
 }
 
 
-
-
 /* Menu Gauche */
 .menuGaucheAdmin{background-color:#FFFFFF;border:1px solid #B0BEC7;width:95%;border-radius: 5px; -moz-border-radius: 5px; margin-bottom: 10px}
 .menuGaucheAdmin table {border:none; width: 100%; border-radius: 5px; border-collapse: collapse}
@@ -72,6 +70,12 @@ li.selected {font-weight: bold;}
 }
 
 /* Form */
+
+.required:after { 
+    content:" *"; 
+    color: red;
+}
+
 .form  {
     background-color:#F0F0F0;
     border:1px solid #C8C8C8;
@@ -1337,6 +1341,10 @@ div#reader {
 }
 
 
+.library_redmine_account {
+    float: right;
+}
+
 
 .digital_connectors td {
     padding-bottom: 20px;
diff --git a/public/admin/images/picto/redmine_16.png b/public/admin/images/picto/redmine_16.png
index 9d48fcf980b8af5371081cf0da26974b61924b1d..93aabf1a1e9d01c400fc846195bf2cf2ec8aeb10 100644
Binary files a/public/admin/images/picto/redmine_16.png and b/public/admin/images/picto/redmine_16.png differ
diff --git a/tests/application/modules/admin/controllers/BibControllerTest.php b/tests/application/modules/admin/controllers/BibControllerTest.php
index fcfc6451ec5714e797f1cbb448190a61759b5be2..ce023d58c2b35881cd786e1be6f0220d4e652106 100644
--- a/tests/application/modules/admin/controllers/BibControllerTest.php
+++ b/tests/application/modules/admin/controllers/BibControllerTest.php
@@ -1240,11 +1240,15 @@ class BibControllerPermissionsPortalPostActionTest
 abstract class BibControllerWithRedmineAPITestCase extends BibControllerWithAdminBibTestCase {
   public function setUp() {
     parent::setUp();
-    $this->fixture('Class_AdminVar',
-                   ['id' => 'REDMINE_API_KEY', 'valeur' => '123456789']);
 
     $this->fixture('Class_AdminVar',
                    ['id' => 'REDMINE_SERVER_URL', 'valeur' => 'http://redmine-forge.gnu']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROJECT_ID', 'valeur' => '123456789']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROXY_URL', 'valeur' => 'http://monserveur']);
   }
 }
 
@@ -1279,23 +1283,6 @@ class BibControllerWithRemineAPITest extends BibControllerWithRedmineAPITestCase
   public function anchorRedmineShouldBePresent() {
     $this->assertXPath('//form//a[@name="redmine"]');
   }
-
-  /** @test */
-  public function anchorTestApiSettingsShouldBePresent() {
-    $this->assertXPath('//a[contains(@href, "/admin/redmine/test/id_bib/2")][@data-popup="true"][@data-status="/admin/redmine/test/id_bib/2"]', $this->_response->getBody());
-  }
-
-
-  /** @test */
-  public function redmineJSShouldBeLoaded() {
-    $this->assertXPath('//script[contains(@src, "/opac/js/redmine.js")]');
-  }
-
-
-  /** @test */
-  public function redmineAutoRefreshShouldLoaded() {
-    $this->assertXPathContentContains('//script', '$("a.redmine_status").autoRefresh();');
-  }
 }
 
 
diff --git a/tests/application/modules/admin/controllers/RedmineControllerTest.php b/tests/application/modules/admin/controllers/RedmineControllerTest.php
index 092eeb8ef93b0c9cf23b7a9f4376aed72e90cc2c..90dbaece3e04feb39bef9da98196496a92e92845 100644
--- a/tests/application/modules/admin/controllers/RedmineControllerTest.php
+++ b/tests/application/modules/admin/controllers/RedmineControllerTest.php
@@ -30,13 +30,29 @@ abstract class Admin_RedmineControllerTestCase extends Admin_AbstractControllerT
     $this->fixture('Class_AdminVar',
                    ['id' => 'REDMINE_SERVER_URL',
                     'valeur' => 'http://forge.afi-sa.fr']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROJECT_ID',
+                    'valeur' => '1234']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROXY_URL',
+                    'valeur' => 'http://monurl']);
+  }
+
+
+  public function tearDown() {
+    Class_WebService_Redmine::setClient(null);
+    Class_WebService_Redmine::setAdminClient(null);
+    Class_WebService_Redmine::setDefaultHttpClient(null);
+
+    parent::tearDown();
   }
 }
 
 
 
 class Admin_RedmineControllerTestActionWithNoBibTest extends Admin_RedmineControllerTestCase {
-
   public function setUp() {
     parent::setUp();
     $this->dispatch('/admin/redmine/test', true);
@@ -123,27 +139,27 @@ class Admin_RedmineControllerTestActionWithEmptyBibTest extends Admin_RedmineCon
 
 
 abstract class Admin_RedmineControllerWithApiTestCase extends Admin_RedmineControllerWithAnnecyLibraryTestCase {
-
   public function setUp() {
     parent::setUp();
 
-    $redmine_api = Storm_Test_ObjectWrapper::mock();
-    $redmine_api
-      ->whenCalled('all')
-      ->answers(RedmineFixtures::sandreIssues())
+    $redmine_api = $this->mock()
+                        ->whenCalled('all')
+                        ->answers(RedmineFixtures::sandreIssues())
 
-      ->whenCalled('getCurrentUser')
-      ->answers(RedmineFixtures::currentUser())
+                        ->whenCalled('getCurrentUser')
+                        ->answers(RedmineFixtures::currentUser())
 
-      ->whenCalled('show')
-      ->answers(RedmineFixtures::projectHotline());
+                        ->whenCalled('show')
+                        ->answers(RedmineFixtures::projectHotline());
 
-    $redmine_client = Storm_Test_ObjectWrapper::mock();
-    $redmine_client
-      ->whenCalled('api')
-      ->answers($redmine_api);
+    Class_WebService_Redmine::setClient($this->mock()
+                                        ->whenCalled('api')
+                                        ->answers($redmine_api));
 
-    Class_WebService_Redmine::setClient($redmine_client);
+    Class_WebService_Redmine::setDefaultHttpClient($this->mock()
+                                                   ->whenCalled('open_url')->with('http://monurl')
+                                                   ->answers(RedmineFixtures::customFields())
+                                                   ->beStrict());
   }
 
 
@@ -177,6 +193,23 @@ class Admin_RedmineControllerTestActionWithBibAndLoginPasswordSetTest extends Ad
 
 
 
+class Admin_RedmineControllerIndexWithNoAccountSetTest extends Admin_RedmineControllerTestCase {
+
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('admin/redmine', true);
+  }
+
+
+  /** @test */
+  public function linkToSetAccountShouldBePresent() {
+    $this->assertXPathContentContains('//div[@class="modules"]//a[contains(@href,"/users/edit/id")]',
+                                      'Vous devez sélectionner une bibliothèque');
+  }
+}
+
+
+
 class Admin_RedmineControllerIndexTest extends Admin_RedmineControllerWithApiTestCase {
   public function setUp() {
     parent::setUp();
@@ -190,8 +223,57 @@ class Admin_RedmineControllerIndexTest extends Admin_RedmineControllerWithApiTes
 
 
   /** @test */
-  public function sandreIssuesShouldContainsTicket34566() {
-    $this->assertXPathContentContains('//table//tr//td', '34247', $this->_response->getBody());
+  public function numberHeaderShouldBePresent() {
+    $this->assertXPathContentContains('//th', 'Numéro');
+  }
+
+
+  /** @test */
+  public function subjectHeaderShouldBePresent() {
+    $this->assertXPathContentContains('//th', 'Sujet');
+  }
+
+
+  /** @test */
+  public function statusHeaderShouldBePresent() {
+    $this->assertXPathContentContains('//th', 'Statut');
+  }
+
+
+  /** @test */
+  public function dateHeaderShouldBePresent() {
+    $this->assertXPathContentContains('//th', 'Créée le');
+  }
+
+
+  /** @test */
+  public function sandreIssuesShouldContainsTicket34247() {
+    $this->assertXPathContentContains('//table//tr//td', '34247');
+  }
+
+
+  /** @test */
+  public function tix34247SubjectShouldBePresent() {
+    $this->assertXPathContentContains('//td', 'Charte graphique back-office');
+  }
+
+
+  /** @test */
+  public function tix34247StatusShouldBePresent() {
+    $this->assertXPathContentContains('//td', 'En développement');
+  }
+
+
+  /** @test */
+  public function tix34247DateShouldBePresent() {
+    $this->assertXPathContentContains('//td', '04/12/2015');
+  }
+
+
+  /** @test */
+  public function editAnchorIssue34247ShouldBePresent() {
+    $this->assertXPath('//table//tr//td//a[contains(@href, "admin/redmine/edit-issue/id_lib/1/id/34247")]',
+                       $this->_response->getBody());
   }
 
 
@@ -211,12 +293,6 @@ class Admin_RedmineControllerIndexTest extends Admin_RedmineControllerWithApiTes
   public function anchorToEditCurrentUserShouldBePresent() {
     $this->assertXPath('//div[@class="modules"]//a[contains(@href, "admin/users/edit/id/")]');
   }
-
-
-  /** @test */
-  public function projectNameShouldBeHotline() {
-    $this->assertXPathContentContains('//div[@class="modules"]//a[contains(@href, "admin/index/adminvaredit/cle/REDMINE_PROJECT_ID")]', 'Hotline');
-  }
 }
 
 
@@ -249,7 +325,7 @@ class Admin_RedmineControllerIndexActionWithModoBibTest extends Admin_RedmineCon
 
   /** @test */
   public function noAccountHaveBeenSetMessageShouldBePresent() {
-    $this->assertXPathContentContains('//div[@class="modules"]//a[contains(@href, "admin/bib/edit/id/1")]', 'Aucun compte', $this->_response->getBody());
+    $this->assertXPathContentContains('//div[@class="modules"]//a[contains(@href, "admin/bib/edit/id/1")]', 'Modifier le compte d\'assistance', $this->_response->getBody());
   }
 }
 
@@ -268,18 +344,269 @@ class Admin_RedmineControllerWithMultipleAccountTest extends Admin_RedmineContro
                                 'redmine_password' => 'late',
                                 'redmine_user_id' => 12]);
 
+    Class_Users::getIdentity()->setBib(null);
+    Class_Users::getIdentity()->setRedmineLibrary(null);
+
     $this->dispatch('admin/redmine', true);
   }
 
 
   /** @test */
-  public function annecyRedmineTableShouldBePresent() {
-    $this->assertXPathContentContains('//ul//li', 'Mediatheque d\'Annecy', $this->_response->getBody());
+  public function annecyShouldBeSelected() {
+    $this->assertXPathContentContains('//select[@name="library"]//option[@selected="selected"]',
+                                      'Mediatheque d\'Annecy',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function chamberyShouldBeSelectable() {
+    $this->assertXPathContentContains('//select[@name="library"]//option', 'Mediatheque de Chambéry',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function urserRedmineLibShouldBeNull() {
+    $this->assertNull(Class_Users::getIdentity()->getRedmineLibrary());
   }
 
 
   /** @test */
-  public function chamberyRedmineTableShouldBePresent() {
-    $this->assertXPathContentContains('//ul//li', 'Mediatheque de Chambéry', $this->_response->getBody());
+  public function annecyLibraryRedmineUserIdShouldHaveBeenSaved() {
+    $this->assertEquals('123456', Class_Bib::find(1)->getRedmineUserId());
+  }
+}
+
+
+
+abstract class Admin_RedmineControllerFixtureAbstractTest extends Admin_RedmineControllerWithApiTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $redmine_api = $this->mock()
+                        ->whenCalled('show')->with('34247', ['include' => 'journals'])
+                        ->answers(RedmineFixtures::fieldIndexation())
+
+                        ->whenCalled('update')->answers('hfg')
+                        ->whenCalled('all')->answers(RedmineFixtures::supportStatus());
+
+
+    $redmine_api_status = $this->mock()
+                               ->whenCalled('all')->answers(RedmineFixtures::supportStatus());
+
+    $redmine_api_priority = $this->mock()
+                                 ->whenCalled('all')->answers(RedmineFixtures::supportPriority());
+
+    $redmine_client = $this->mock()
+                           ->whenCalled('api')->with('issue')->answers($redmine_api)
+                           ->whenCalled('api')->with('issue_status')->answers($redmine_api_status)
+                           ->beStrict();
+
+    Class_WebService_Redmine::setClient($redmine_client);
+  }
+
+
+  public function tearDown() {
+    Class_WebService_Redmine::setDefaultHttpClient(null);
+    parent::tearDown();
+  }
+}
+
+
+
+
+class Admin_RedmineControllerEditIssue34247Test extends Admin_RedmineControllerFixtureAbstractTest {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('admin/redmine/edit-issue/id_lib/1/id/34247', true);
+  }
+
+  public function datas() {
+    return [
+            ['//ul//li', 'Statut changé de A qualifier à Affecté au dév.'],
+            ['//ul//li', '% réalisé mis à 20'],
+            ['//ul//li', 'Copié depuis #35800 ajouté'],
+            ['//ul//li', 'Copié vers #35831 supprimé'],
+            ['//td','Pris en charge par le developpement'],
+            ['//ul//li', 'Priorité client changé de Normale à Urgente'],
+            ['//td','test-support'],
+            ['//td', '07/01/2016 à 09:30:26'],
+
+    ];
+  }
+
+  public function data_form() {
+    return [ ['//form'],
+            ['//textarea[@id="notes"]']];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider datas
+   */
+  public function historyShouldContains($tag, $text) {
+    $this->assertXPathContentContains($tag, $text, $this->_response->getBody());
+  }
+
+  /**
+   * @test
+   * @dataProvider data_form
+   */
+  public function formShouldContains($tag) {
+    $this->assertXPath($tag, $this->_response->getBody());
+  }
+
+  /** @test */
+  public function tikcetNumberShouldBe34247() {
+    $this->assertXPathContentContains('//h1', '#34247');
+  }
+
+
+  /** @test */
+  public function subjectShouldBePresent() {
+    $this->assertXPathContentContains('//div', 'reindexation des champs');
+  }
+
+
+  /** @test */
+  public function statusShouldBePresent() {
+    $this->assertXPathContentContains('//select[@name="status_id"]//option', 'A qualifier');
+  }
+
+
+  /** @test */
+  public function priorityShouldBePresent() {
+    $this->assertXPathContentContains('//select[@name="priority"]//option', 'Normale');
+  }
+
+
+  /** @test */
+  public function moduleShouldBePresent() {
+    $this->assertXPathContentContains('//select[@name="module"]//option', 'Articles');
+  }
+
+
+  /** @test */
+  public function historyShouldBePresent() {
+    $this->assertXPathContentContains('//legend', 'Historique');
+  }
+}
+
+
+
+class Admin_RedmineControllerPostEditIssue34247Test extends Admin_RedmineControllerWithApiTestCase {
+  protected $_update_params;
+
+  public function setUp() {
+    parent::setUp();
+
+    $redmine_api = $this->mock()
+                        ->whenCalled('show')->with('34247', ['include' => 'journals'])
+                        ->answers(RedmineFixtures::fieldIndexation())
+
+                        ->whenCalled('update')->answers('hfg')
+                        ->whenCalled('all')->answers(RedmineFixtures::supportStatus());
+
+    $redmine_client = $this->mock()
+                           ->whenCalled('api')
+                           ->answers($redmine_api);
+
+    Class_WebService_Redmine::setClient($redmine_client);
+
+
+    $this->postDispatch('admin/redmine/edit-issue/id_lib/1/id/34247',
+                        ['status_id' => '5',
+                         'priority' => 'Normale',
+                         'notes' => 'Change priority',
+                         'module' => '',
+                         'contact' => '']);
+
+    $this->_update_params = $redmine_api->getAttributesForLastCallOn('update')[1];
+  }
+
+
+  protected function assertCustomfieldValue($id, $value) {
+    foreach($this->_update_params['custom_fields'] as $field) {
+      if ($field['id'] == $id) {
+        $this->assertEquals($value, $field['value'],
+                            ' in : ' . json_encode($this->_update_params['custom_fields']));
+        return;
+      }
+    }
+
+    $this->fail('No custom field of id "' . $id . '" in : '
+                . json_encode($this->_update_params['custom_fields']));
+  }
+
+
+  /** @test */
+  public function statusShouldBe5() {
+    $this->assertEquals(5, $this->_update_params['status_id']);
+  }
+
+
+  /** @test */
+  public function priorityShouldBeNormale() {
+    $this->assertCustomfieldValue(Class_WebService_Redmine::CUSTOM_PRIORITY_ID, 'Normale');
+  }
+
+
+  /** @test */
+  public function moduleShouldBeEmpty() {
+    $this->assertCustomfieldValue(Class_WebService_Redmine::CUSTOM_MODULE_ID, '');
+  }
+
+
+  /** @test */
+  public function contactShouldBeEmpty() {
+    $this->assertCustomfieldValue(Class_WebService_Redmine::CUSTOM_CONTACT_PERSON, '');
+  }
+
+
+  /** @test */
+  public function qualificationShouldBeBugLogiciel() {
+    $this->assertCustomfieldValue(Class_WebService_Redmine::CUSTOM_QUALIFICATION, 'Bug logiciel');
+  }
+
+
+  /** @test */
+  public function issue34247ShouldHaveBeenUpdated() {
+    $this->assertFlashMessengerContentContains('Demande #34247 enregistrée');
+  }
+
+
+  /** @test */
+  public function shouldRedirectToReferer() {
+    $this->assertRedirect();
+  }
+}
+
+
+
+class Admin_RedmineControllerAddIssueTest extends Admin_RedmineControllerFixtureAbstractTest{
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('admin/redmine/add/id_lib/1', true);
+  }
+
+
+  public function data_form() {
+    return [ ['//form'],
+            ['//input[@id="subject"]'],
+            ['//select[@id="priority"]//option[@value="Normale"][@selected]'],
+            ['//select[@id="module"]'],
+            ['//textarea[@id="description"]'],
+            ['//textarea[@id="contact"]']];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider data_form
+   */
+  public function formShouldContains($tag) {
+    $this->assertXPath($tag, $this->_response->getBody());
   }
 }
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/UsersControllerTest.php b/tests/application/modules/admin/controllers/UsersControllerTest.php
index d8260cc8ec5ab5cdf49d7fd5ae89273975817b26..0e8afd2dae1cf3d116bcb7fc8b429e0381e06f77 100644
--- a/tests/application/modules/admin/controllers/UsersControllerTest.php
+++ b/tests/application/modules/admin/controllers/UsersControllerTest.php
@@ -122,6 +122,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
     $this->dispatch('/admin/users/edit/id/10', true);
   }
 
+
   /** @test **/
   public function roleLevelShouldBeSIGBSubscriber() {
     $this->assertXpathContentContains('//select[@name="role_level"]//option[@value=2][@selected="selected"]','SIGB', $this->_response->getBody());
@@ -130,7 +131,7 @@ class UsersControllerEditMarcusTest extends UsersControllerWithMarcusTestCase {
 
   /** @test **/
   public function testIdentifiantIsMMiller() {
-    $this->assertXPath("//input[@name='login'][@value='mmiller']");
+    $this->assertXPath("//input[@name='login'][@value='mmiller'][contains(@onkeypress, 'submit()')]");
   }
 
 
@@ -772,8 +773,13 @@ abstract class Admin_UsersControllerEditAdminTestCase extends Admin_AbstractCont
     parent::setUp();
 
     $this->fixture('Class_AdminVar',
-                   ['id' => 'REDMINE_SERVER_URL',
-                    'valeur' => 'http://forge.afi-sa.fr']);
+                   ['id' => 'REDMINE_SERVER_URL', 'valeur' => 'http://redmine-forge.gnu']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROJECT_ID', 'valeur' => '123456789']);
+
+    $this->fixture('Class_AdminVar',
+                   ['id' => 'REDMINE_PROXY_URL', 'valeur' => 'http://monurl']);
 
     $this->fixture('Class_Zone',
                    ['id' => 1,
diff --git a/tests/fixtures/RedmineFixtures.php b/tests/fixtures/RedmineFixtures.php
index f3504c4df9a53425e375b820896bb8aeedbb5bb9..2eb4a4168ebde511161d07afca50c342c87df429 100644
--- a/tests/fixtures/RedmineFixtures.php
+++ b/tests/fixtures/RedmineFixtures.php
@@ -22,17 +22,17 @@
 
 class RedmineFixtures {
   public static function noLibraryGiven() {
-    return '{"title":"Test de l\'API Redmine","content":"Vous devez fournir un identifiant de biblioth\u00e8que en param\u00e8tre"}';
+    return '{"title":"Test de la configuration de l\'API d\'assistance","content":"Vous devez fournir un identifiant de biblioth\u00e8que en param\u00e8tre"}';
   }
 
 
   public static function noServerUrlGiven() {
-    return '{"title":"Test de l\'API Redmine","content":"Aucun serveur Redmine est renseign\u00e9"}';
+    return '{"title":"Test de la configuration de l\'API d\'assistance","content":"Redmine n\'est pas correctement configur\u00e9"}';
   }
 
 
   public static function missingParams() {
-    return '{"title":"Test de l\'API Redmine","content":"Vous devez renseigner les champs login et password ou le champ cl\u00e9 d\'API"}';
+    return '{"title":"Test de la configuration de l\'API d\'assistance","content":"Vous devez renseigner les champs login et password ou le champ cl\u00e9 d\'API"}';
   }
 
 
@@ -51,12 +51,12 @@ class RedmineFixtures {
 
 
   public static function sandreBocoeur() {
-    return '{"title":"Test de l\'API Redmine","content":"<p data-success=\"true\">Vous \u00eates connect\u00e9(e) en tant que Sandre Bocoeur<\/p>"}';
+    return '{"title":"Test de la configuration de l\'API d\'assistance","content":"<p data-success=\"true\">Compte d\'assistance: haveBeenSet<\/p>"}';
   }
 
 
   public static function sandreIssues() {
-    return ['issues' => [ ['id' => 34246,
+    return ['issues' => [['id' => 34246,
                            'project' => ['id' => 56,
                                          'name' => 'Développement Bokeh AFI-OPAC 2.0'],
                            'tracker' => ['id' => 2,
@@ -85,42 +85,83 @@ class RedmineFixtures {
                            'updated_on' => '2015-12-04T09:25:42Z',
                            'story_points' => ''],
 
-                         ['id' => 34247,
-                          'project' => ['id' => 56,
-                                        'name' => 'Développement Bokeh AFI-OPAC 2.0'],
-                          'tracker' => ['id' => 2,
-                                        'name' => 'Développement'],
-                          'status' => ['id' => 7 ,
-                                       'name' => 'En développement'],
-                          'priority' => ['id' => 4,
-                                         'name' => 'Normal'],
-                          'author' => ['id' => 207,
-                                       'name' => 'gloas'],
-                          'assigned_to' => ['id' => 207,
-                                            'name' => 'gloas'],
-                          'subject' => 'Charte graphique back-office',
-                          'description' => 'un ticket redmine',
-                          'done_ratio' => 30,
-                          'custom_fields' => [ ['id' => 37,
-                                                'name' => 'Module Portail',
-                                                'value' => ''],
-                                              ['id' => 5 ,
-                                               'name' => 'Priorité client',
-                                               'value' => 'Normale'],
-                                              ['id' => 11,
-                                               'name' => 'Phase',
-                                               'value' => '']],
-                          'created_on' => '2015-12-04T09:19:11Z',
-                          'updated_on' => '2015-12-04T09:25:42Z',
-                          'story_points' => '']],
+                         static::issue34247()],
             'total_count' => 2,
             'offset' => 0 ,
             'limit' => 25];
   }
 
 
+  public static function issue34247() {
+    return ['id' => 34247,
+            'project' => ['id' => 56,
+                          'name' => 'Développement Bokeh AFI-OPAC 2.0'],
+            'tracker' => ['id' => 2,
+                          'name' => 'Développement'],
+            'status' => ['id' => 7 ,
+                         'name' => 'En développement'],
+            'priority' => ['id' => 4,
+                           'name' => 'Normal'],
+            'author' => ['id' => 207,
+                         'name' => 'gloas'],
+            'assigned_to' => ['id' => 207,
+                              'name' => 'gloas'],
+            'subject' => 'Charte graphique back-office',
+            'description' => 'un ticket redmine',
+            'done_ratio' => 30,
+            'custom_fields' => [ ['id' => 37,
+                                  'name' => 'Module Portail',
+                                  'value' => ''],
+                                ['id' => 5 ,
+                                 'name' => 'Priorité client',
+                                 'value' => 'Normale'],
+                                ['id' => 11,
+                                 'name' => 'Phase',
+                                 'value' => '']],
+            'created_on' => '2015-12-04T09:19:11Z',
+            'updated_on' => '2015-12-04T09:25:42Z',
+            'story_points' => ''];
+  }
+
+
   public static function projectHotline() {
     return ['project' => ['name' => 'Hotline']];
   }
+
+
+  public static function fieldIndexation() {
+    return json_decode('{"issue":{"id":34247,"project":{"id":214,"name":"Support Bokeh AFI-OPAC 2.0"},"tracker":{"id":12,"name":"Demande d\'assistance"},"status":{"id":2,"name":"Affect\u00e9 au d\u00e9v."},"priority":{"id":5,"name":"Haut"},"author":{"id":207,"name":"gloas"},"assigned_to":{"id":207,"name":"gloas"},"subject":"reindexation des champs personnalis\u00e9s ","description":"Url de l\'anomalie : http:\/\/web.afi-sa.net\/miop-test.net\/admin\/custom-fields\/edit\/id\/1\r\n\r\n\u00c9tapes pour parvenir au bug :\r\n* indexer un champs personnalis\u00e9\r\n* les facettes des notices des articles qui ont une valeur de param\u00e9tr\u00e9e pour ce champ personnalis\u00e9 ne sont pas mises \u00e0 jour.\r\n","done_ratio":20,"spent_hours":0,"custom_fields":[{"id":43,"name":"Personne \u00e0 contacter","value":"Nom \/ pr\u00e9nom \/ email \/ t\u00e9l\u00e9phone"},{"id":42,"name":"Pris en charge par","value":""},{"id":37,"name":"Module Portail","value":"Recherche"},{"id":5,"name":"Priorit\u00e9 client","value":"Normale"},{"id":1,"name":"Client","value":""},{"id":46,"name":"Relanc\u00e9 le","value":""}],"created_on":"2015-09-29T10:01:49Z","updated_on":"2015-12-02T13:37:49Z","journals":[{"id":149465,"user":{"id":1198,"name":"test-support"},"notes":"","created_on":"2016-01-07T08:30:26Z","details":[{"property":"relation","name":"copied_from","new_value":"35800"}]},{"id":149517,"user":{"id":92,"name":"other"},"notes":"Pris en charge par le developpement","created_on":"2016-01-07T09:38:13Z","details":[{"property":"attr","name":"status_id","old_value":"1","new_value":"2"},{"property":"attr","name":"done_ratio","old_value":"0","new_value":"20"}]},{"id":149608,"user":{"id":92,"name":"dev"},"notes":"","created_on":"2016-01-07T11:10:00Z","details":[{"property":"relation","name":"copied_to","old_value":"35831"}]},{"id":149610,"user":{"id":92,"name":"dev"},"notes":"","created_on":"2016-01-07T11:10:04Z","details":[{"property":"relation","name":"blocks","new_value":"35831"}]},{"id":149612,"user":{"id":1198,"name":"test-support"},"notes":"","created_on":"2016-01-07T11:12:45Z","details":[{"property":"cf","name":"5","old_value":"Normale","new_value":"Urgente"}]}]}}', true);
+  }
+
+
+  public static function supportStatus() {
+    return json_decode('{"issue_statuses":[{"id":1,"name":"A qualifier","is_default":true},{"id":14,"name":"A planifier"},{"id":15,"name":"Planifi\u00e9"},{"id":4,"name":"Affect\u00e9 hotline"},{"id":19,"name":"Affect\u00e9 syst\u00e8me"},{"id":2,"name":"Affect\u00e9 au d\u00e9v."},{"id":13,"name":"Affect\u00e9 HL H\u00e9bergement"},{"id":22,"name":"Retour au d\u00e9v"},{"id":7,"name":"En d\u00e9veloppement"},{"id":20,"name":"Impl\u00e9ment\u00e9"},{"id":18,"name":"\u00c0 documenter"},{"id":16,"name":"\u00c0 revoir tech"},{"id":3,"name":"R\u00e9alis\u00e9 (\u00e0 tester)"},{"id":17,"name":"\u00c0 int\u00e9grer"},{"id":11,"name":"Question au client"},{"id":8,"name":"Test\u00e9"},{"id":12,"name":"Transf\u00e9r\u00e9 au plan de d\u00e9v","is_closed":true},{"id":5,"name":"Ferm\u00e9","is_closed":true},{"id":6,"name":"Rejet\u00e9","is_closed":true},{"id":21,"name":"A facturer"}]}', true);
+  }
+
+
+  public static function supportPriority() {
+    return json_decode('{"issue_priorities":[{"id":3,"name":"Bas"},{"id":4,"name":"Normal","is_default":true},{"id":5,"name":"Haut"},{"id":6,"name":"Urgent"},{"id":7,"name":"Proc\u00e9dure 48h"}]}', true);
+  }
+
+
+  public static function customFields() {
+    return json_encode(['custom_fields' => [['id' => Class_WebService_Redmine::CUSTOM_PRIORITY_ID,
+                                             'name' => 'Priorité client',
+                                             'is_required' => true,
+                                             'default_value' => 'Normale',
+                                             'possible_values' => [['value' => 'Basse'],
+                                                                   ['value' => 'Normale']]],
+
+                                            ['id' => Class_WebService_Redmine::CUSTOM_MODULE_ID,
+                                             'name' => 'Module Portail',
+                                             'is_required' => false,
+                                             'possible_values' => [['value' => 'Articles'],
+                                                                   ['value' => 'Interface SIGB']]],
+
+                                            ['id' => Class_WebService_Redmine::CUSTOM_QUALIFICATION,
+                                             'name' => 'Qualification',
+                                             'is_required' => false,
+                                             'possible_values' => [['value' => 'Bug logiciel'],
+                                                                   ['value' => 'Hors maintenance']]]]]);
+  }
 }
-?>
\ No newline at end of file
diff --git a/tests/library/Class/UsersTest.php b/tests/library/Class/UsersTest.php
index 6a934e0b05f9091bbe486e8da0332d158a2177bc..0f2da9c95ebf4358510a23687e46632e576bf94a 100644
--- a/tests/library/Class/UsersTest.php
+++ b/tests/library/Class/UsersTest.php
@@ -220,7 +220,7 @@ class UsersSaveTest extends ModelTestCase {
               'idabon' => '1234',
               'date_fin' => '',
               'naissance' => '',
-              'date_debut' => 0,
+              'date_debut' => '',
               'telephone' => '',
               'mail' => '',
               'adresse' => '',
@@ -273,7 +273,7 @@ class UsersSaveTest extends ModelTestCase {
               'date_fin' => '2001-10-23',
               'idabon' => '',
               'naissance' => '',
-              'date_debut' => 0,
+              'date_debut' => '',
               'telephone' => '',
               'adresse' => '',
               'code_postal' => '',