diff --git a/VERSIONS b/VERSIONS
index ac2ac22b01e8058718277db761661f981c9e8a40..04cf4d692e367f43e20a7b2c5eea6961bcd4d5dd 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,3 +1,14 @@
+06/04/2017 - v7.9.10
+
+ - ticket #57574 : Articles : correction de l'orthographe des jours de la semaine (tous les lundis, mardis ...)
+ 
+ - ticket #58543 : Charte graphique : Ajout de la possibilité de personnaliser l'icone de localisation présent dans le bloc exemplaire
+ 
+ - ticket #56365 : Newsletters : limite de longueur des titres passée à 255 octets
+ 
+ - ticket #58096 : Avis sur les articles : Modification de la base de données qui dans certains cas empechait la création d'un avis
+
+
 30/03/2017 - v7.9.9
 
  - ticket #58461 : LeKiosk : Maintenance du lien SSO vers la nouvelle plateforme
diff --git a/application/modules/admin/views/scripts/lieu/_lieu.phtml b/application/modules/admin/views/scripts/lieu/_lieu.phtml
deleted file mode 100644
index 90e02c22a9eb1f9f9d3f3d98611c7e1654975287..0000000000000000000000000000000000000000
--- a/application/modules/admin/views/scripts/lieu/_lieu.phtml
+++ /dev/null
@@ -1,14 +0,0 @@
-<tr class="<?php echo $this->item_class ?>">
-  <?php 
-     echo sprintf("<td>%s</td>", $this->mapForLieu($this->lieu, array('size' => '100x100', 'zoom' => '12'))); 
-     echo sprintf("<td>%s</td>", $this->lieu->getLibelle()); 
-     echo sprintf("<td>%s %s</td>", 
-                  $this->tagAnchor(array('action' => 'edit',
-                                         'id' => $this->lieu->getId()),
-                                   $this->boutonIco("type=edit")),
-
-                  $this->tagAnchor(array('action' => 'delete',
-                                         'id' => $this->lieu->getId()),
-                                   $this->boutonIco("type=del")));
-  ?>
-</tr>
\ No newline at end of file
diff --git a/application/modules/admin/views/scripts/lieu/index.phtml b/application/modules/admin/views/scripts/lieu/index.phtml
index f79267359f753170a0ccb59dd4b84a43c99ca1b1..e65b8ddafd073f8a15970acbd0663c1aab8e15b4 100644
--- a/application/modules/admin/views/scripts/lieu/index.phtml
+++ b/application/modules/admin/views/scripts/lieu/index.phtml
@@ -7,22 +7,19 @@ echo $this->button((new Class_Entity())
                        ->setUrl($this->url(['action' => 'update-coordinates']))
                        ->setImage($this->tagImg(Class_Admin_Skin::current()->renderIconUrlOn('buttons',
                                                                                              'generate'))));
-?>
 
-<table class="lieux">
-  <thead>
-    <tr class="soustitre">
-      <td>Carte</td>
-      <td>Libellé</td>
-      <td></td>
-    </tr>
-  </thead>
-  <tbody>
-<?php
-   echo $this->partialCycle('lieu/_lieu.phtml',
-                            'lieu',
-                            $this->lieux,
-                            array('first', 'second'));
+
+echo $this->renderTable(
+  (new Class_TableDescription('lieux'))
+       ->addColumn($this->_('Carte'),
+                   ['callback' => function($model) {
+                                    return $this->mapForLieu($model,
+                                                             ['size' => '100x100',
+                                                              'zoom' => '12']);
+                                  },
+                    'options' => ['data-sorter' => 'false']])
+       ->addColumn($this->_('Libellé'), ['attribute' => 'libelle'])
+       ->addRowAction(['action' => 'edit', 'content' => $this->boutonIco("type=edit")])
+       ->addRowAction(['action' => 'delete', 'content' => $this->boutonIco("type=del")]),
+  $this->lieux);
 ?>
-  </tbody>
-</table>
diff --git a/application/modules/admin/views/scripts/newsletter/index.phtml b/application/modules/admin/views/scripts/newsletter/index.phtml
index 5b3543803b756d9c170b2cf1b52f159402a76680..84c8d933dc3dc54681f4cb475fe57e8fa153312d 100644
--- a/application/modules/admin/views/scripts/newsletter/index.phtml
+++ b/application/modules/admin/views/scripts/newsletter/index.phtml
@@ -2,16 +2,17 @@
 echo $this->Button_New((new Class_Entity())
                         ->setText($this->_('Créer une lettre d\'information')));
 
-echo $this->tagModelTable($this->newsletters,
-                          [$this->_('Titre'), $this->_('Dernière distribution')],
-                          ['titre', 'progress'],
-                          [function($model)
-                           {
-                             return $this->renderPluginsActions($model);
-                           }],
-                          'newsletters',
-                          null,
-                          ['progress' => function($model) { return $this->tagProgressBarForNewsletter($model); }]);
+
+echo $this->renderTable(
+  (new Class_TableDescription('newsletters'))
+              ->addColumn($this->_('Titre'),  ['attribute' => 'titre'])
+              ->addColumn($this->_('Dernière distribution'),
+                          ['callback' => function($model) {
+                                            return $this->tagProgressBarForNewsletter($model);
+                                         },
+                           'options' => ['class' => "dateFormat-ddmmyyyy sorter-shortDate"]])
+              ->addRowAction(function($model) { return $this->renderPluginsActions($model); }),
+  $this->newsletters);
 
 if (isset($this->subview))
   echo $this->tag('div', $this->subview, ['class' => 'subview']);
diff --git a/cosmogramme/sql/patch/patch_322.php b/cosmogramme/sql/patch/patch_322.php
new file mode 100644
index 0000000000000000000000000000000000000000..1feedcb4820cdd107353b41c3fedd96629b75ee0
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_322.php
@@ -0,0 +1,9 @@
+<?php
+$adapter = Zend_Db_Table::getDefaultAdapter();
+try {
+	$adapter->query('select flags from cms_avis limit 1');
+} catch (Exception $e) {
+	$adapter->query('ALTER TABLE cms_avis ADD COLUMN flags tinyint default 0');
+	$adapter->query('ALTER TABLE cms_avis ADD INDEX (flags)');
+}
+?>
diff --git a/cosmogramme/sql/patch/patch_323.php b/cosmogramme/sql/patch/patch_323.php
new file mode 100644
index 0000000000000000000000000000000000000000..537b349832738e5695ed0e24f4a0c676736da622
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_323.php
@@ -0,0 +1,6 @@
+<?php
+try {
+   Zend_Db_Table_Abstract::getDefaultAdapter()
+     ->query('alter table newsletters modify column titre varchar(255) not null');
+} catch(Exception $e) {}
+?>
\ No newline at end of file
diff --git a/library/Class/Profil.php b/library/Class/Profil.php
index 05a50f5e37b3a9c2a73d0b70b497222e64530b46..4d33dfbd5f502c2beea48c2e6f1bc8ab9b2073f8 100644
--- a/library/Class/Profil.php
+++ b/library/Class/Profil.php
@@ -518,6 +518,13 @@ class Class_Profil extends Storm_Model_Abstract {
   }
 
 
+  /** @category testing */
+  public function setDefaultSkin($skin) {
+    $this->_skin = $skin;
+    return $this;
+  }
+
+
   protected function _getSkin() {
     if (null === $this->_skin)
       $this->_skin = Class_Profil_Skin::newFor($this);
diff --git a/library/Class/TableDescription.php b/library/Class/TableDescription.php
new file mode 100644
index 0000000000000000000000000000000000000000..30f105e58c143ddced0167d7a4d9bf22f6fdb360
--- /dev/null
+++ b/library/Class/TableDescription.php
@@ -0,0 +1,322 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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 Class_TableDescription {
+  protected
+    $_columns,
+    $_id;
+
+  public function __construct($id) {
+    $this->_id = $id;
+    $this->_columns = new Class_TableDescription_Columns();
+  }
+
+
+  public function getId() {
+    return $this->_id;
+  }
+
+
+  public function columnsCollect($callback) {
+    return $this->_columns->collect($callback);
+  }
+
+
+  public function addColumn($label, $description) {
+    $description = array_merge(['attribute' => '',
+                                'callback' => null,
+                                'options' => ''],
+                               $description);
+    $this
+      ->_columns
+      ->add($this->newColumn($label, $description)
+                 ->setOptions($description['options']));
+
+    return $this;
+  }
+
+
+  public function newColumn($label, $description) {
+    return $description['callback']
+      ? new Class_TableDescription_ColumnForCallback($label,
+                                                     $description['attribute'],
+                                                     $description['callback'])
+      : new Class_TableDescription_ColumnForAttribute($label,
+                                                      $description['attribute']);
+  }
+
+
+  public function getActionColumn() {
+    if (!isset($this->_actions)) {
+      $this->_actions = (new Class_TableDescription_ColumnForActions())
+        ->setOptions(['data-sorter' => 'false',
+                      'class' => 'actions',
+                      'style' => 'min-width: 80px']);
+      $this->_columns->add($this->_actions);
+    }
+
+    return $this->_actions;
+  }
+
+
+  public function addRowAction($description) {
+    $this->getActionColumn()
+         ->add(Class_TableDescription_ActionAbstract::forDescription($description));
+    return $this;
+  }
+
+  public function numberOfColumns() {
+    return $this->_columns->count();
+  }
+}
+
+
+
+class Class_TableDescription_Columns {
+  protected $_columns;
+
+  public function __construct() {
+    $this->_columns = new Storm_Collection();
+  }
+
+
+  public function collect($callback) {
+    return $this->_columns->collect($callback);
+  }
+
+
+  public function count() {
+    return $this->_columns->count();
+  }
+
+
+  /** @return Class_TableDescription_ColumnAbstract subclass */
+  public function add($column) {
+    $this->_columns->append($column);
+    return $column;
+  }
+
+
+  public function acceptVisitor($visitor) {
+    $this->_columns->eachDo(function($column) use($visitor)
+                            {
+                              $visitor->visitColumn($column);
+                            });
+  }
+}
+
+
+
+abstract class Class_TableDescription_ColumnAbstract {
+  protected
+    $_label,
+    $_options = [];
+
+
+  public function __construct($label) {
+    $this->_label = $label;
+  }
+
+
+  public function getLabel() {
+    return $this->_label;
+  }
+
+
+  public function setOptions($options) {
+    $this->_options = $options;
+    return $this;
+  }
+
+
+  public function getOptions() {
+    return $this->_options;
+  }
+
+
+  abstract public function renderModelOn($model, $canvas);
+}
+
+
+
+
+class Class_TableDescription_ColumnForCallback extends Class_TableDescription_ColumnAbstract {
+  protected
+    $_callback,
+    $_attribute;
+
+  public function __construct($label, $attribute, $callback) {
+    parent::__construct($label);
+    $this->_attribute = $attribute;
+    $this->_callback = $callback;
+  }
+
+
+  public function renderModelOn($model, $canvas) {
+    return $canvas->renderContent(call_user_func($this->_callback,
+                                                 $model,
+                                                 $this->_attribute));
+  }
+}
+
+
+
+class Class_TableDescription_ColumnForAttribute extends Class_TableDescription_ColumnAbstract {
+  protected $_attribute;
+
+  public function __construct($label, $attribute) {
+    parent::__construct($label);
+    $this->_attribute = $attribute;
+  }
+
+
+  public function renderModelOn($model, $canvas) {
+    return $canvas->renderContent($model->callGetterByAttributeName($this->_attribute));
+  }
+}
+
+
+
+
+class Class_TableDescription_ColumnForActions extends Class_TableDescription_ColumnAbstract {
+  use Trait_Translator;
+
+  protected $_actions;
+
+  public function __construct() {
+    parent::__construct($this->_('Actions'));
+    $this->_actions = new Storm_Collection();
+  }
+
+
+  /** @return Class_TableDescription_ActionAbstract */
+  public function add($action) {
+    $this->_actions->append($action);
+    return $action;
+  }
+
+
+  public function renderModelOn($model, $canvas) {
+    $this->_actions->eachDo(function($action) use ($model, $canvas)
+                            {
+                              return $action->renderModelOn($model, $canvas);
+                            });
+  }
+}
+
+
+
+
+abstract class Class_TableDescription_ActionAbstract {
+  public static function forDescription($description) {
+    if (is_a($description, 'Closure'))
+      return new Class_TableDescription_ActionCallback($description);
+
+    $classname = is_a($description['content'], 'Closure')
+      ? 'Class_TableDescription_ActionWithContentCallback'
+      : 'Class_TableDescription_Action';
+
+    return new $classname($description['action'], $description['content']);
+  }
+
+  abstract public function renderModelOn($model, $canvas);
+}
+
+
+
+
+class Class_TableDescription_ActionCallback extends Class_TableDescription_ActionAbstract {
+  protected $_callback;
+
+  public function __construct($callback) {
+    $this->_callback = $callback;
+  }
+
+
+  public function renderModelOn($model, $canvas) {
+    return $canvas->renderContent(call_user_func_array($this->_callback, [$model]));
+  }
+}
+
+
+
+
+class Class_TableDescription_ActionWithContentCallback extends Class_TableDescription_ActionAbstract {
+  protected
+    $_content_callback,
+    $_action;
+
+  public function __construct($action, $content_callback) {
+    $this->_content_callback = $content_callback;
+    $this->_action = $action;
+  }
+
+
+  public function renderModelOn($model, $canvas) {
+    return $canvas->tag('a',
+                        call_user_func($this->_content_callback, $model),
+                        $this->_anchorAttributes($model, $canvas));
+  }
+
+
+  protected function _anchorAttributes($model, $canvas) {
+    $url = Class_Url::assemble(['action' => $this->_action,
+                                'id' => $model->getId()],
+                               null,
+                               false);
+
+    $attribs = ['href' => $url, 'rel' => $this->_action];
+
+    if ($this->_isUrlMatchesCurrent($url))
+      $attribs['class'] = 'selected';
+
+    return $attribs;
+  }
+
+
+  protected function _isUrlMatchesCurrent($url) {
+    return
+      isset($_SERVER['REQUEST_URI'])
+      &&
+      (false !== strpos($_SERVER['REQUEST_URI'], $url));
+  }
+}
+
+
+
+
+class Class_TableDescription_Action extends Class_TableDescription_ActionWithContentCallback {
+  protected
+    $_content;
+
+  public function __construct($action, $content) {
+    $this->_content = $content;
+    parent::__construct($action, [$this, 'getContent']);
+  }
+
+
+  public function getContent() {
+    return $this->_content;
+  }
+}
+
+?>
\ No newline at end of file
diff --git a/library/Class/TableDescription/Models.php b/library/Class/TableDescription/Models.php
new file mode 100644
index 0000000000000000000000000000000000000000..323f1675e5e4292d1cee77c990a379af6c638911
--- /dev/null
+++ b/library/Class/TableDescription/Models.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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 Class_TableDescription_Models {
+  protected
+    $_models,
+    $_groups;
+
+  public function __construct($models) {
+    $this->_models = new Storm_Collection($models);
+    $this->groupBy(null);
+  }
+
+
+  public function groupBy($group_by) {
+    if (!$group_by) {
+      $this->_groups = [new Class_TableDescription_ModelGroup($this->_models)];
+      return $this;
+    }
+
+    $this->_groups = [];
+    $this ->_models->eachDo(function($model) use ($group_by)
+                            {
+                              $this->_placeModelInGroups($model, $group_by);
+                            });
+    return $this;
+  }
+
+
+  public function renderOn($canvas) {
+    array_map(function($group) use ($canvas)
+              {
+                $group->renderOn($canvas);
+              },
+              $this->_groups);
+  }
+
+
+  protected function _placeModelInGroups($model, $group_by) {
+    $group = $model->callGetterByAttributeName($group_by);
+    if (!array_key_exists($group, $this->_groups))
+      $this->_groups[$group] = new Class_TableDescription_ModelGroup_Indexed($group);
+    $this->_groups[$group]->add($model) ;
+  }
+}
+
+
+
+
+class Class_TableDescription_ModelGroup {
+  protected $_models;
+
+  public function __construct($models = null) {
+    $this->_models = $models
+      ? $models
+      : new Storm_Collection();
+  }
+
+
+  public function add($model) {
+    $this->_models->append($model);
+  }
+
+
+  public function renderOn($canvas) {
+    $canvas->renderModels($this->_models->getArrayCopy());
+  }
+}
+
+
+
+
+class Class_TableDescription_ModelGroup_Indexed extends Class_TableDescription_ModelGroup {
+  protected
+    $_key;
+
+  public function __construct($key) {
+    parent::__construct();
+    $this->_key = $key;
+  }
+
+
+  public function renderOn($canvas) {
+    $canvas->renderGroupTitle($this->_key);
+    parent::renderOn($canvas);
+  }
+}
+
+?>
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/Notice/ExemplairesTable.php b/library/ZendAfi/View/Helper/Notice/ExemplairesTable.php
index e79914121d441db56895641c16d6c176eab13d81..03f9281757931c18e186b901c150345309e845e0 100644
--- a/library/ZendAfi/View/Helper/Notice/ExemplairesTable.php
+++ b/library/ZendAfi/View/Helper/Notice/ExemplairesTable.php
@@ -319,13 +319,17 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Localisation extends ZendAfi_View_H
     return $this->_('Situer');
   }
 
+
   public function renderContent($exemplaire) {
-    $html='<td class="localisation" style="text-align:center">';
+    return $this->_tag('td', $this->_getLocalisationLink($exemplaire),
+                       ['class' => 'localisation',
+                        'style' => 'text-align:center']);
+  }
 
-    $id = $exemplaire->getIdBib();
 
-    if ($annexe = Class_CodifAnnexe::findFirstBy(['code' => $exemplaire->getAnnexe()]))
-      $id = $annexe->getIdBib();
+  protected function _getLocalisationLink($exemplaire) {
+    if (0 == $this->getBib($exemplaire)->numberOfLocalisations())
+      return '&nbsp;';
 
     $url = CLass_Url::absolute(['module' => 'opac',
                                 'controller' => 'noticeajax',
@@ -334,18 +338,17 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Localisation extends ZendAfi_View_H
 
     $onclick = 'localisationExemplaire(this, \'' . $url . '\')';
 
-    if ($this->getBib($exemplaire)->numberOfLocalisations()>0)
-      $html.= sprintf('<img src="%s" border="0"  title="%s" style="cursor:pointer" onclick="%s" alt="%s" />',
-                      URL_ADMIN_IMG.'picto/localisation.png',
-                      $this->_('Situer cet exemplaire dans la bibliothèque'),
-                      $onclick,
-                      $this->_('Situer en exemplaire'));
-    else
-      $html.='&nbsp;';
-    $html.='</td>';
-    return $html;
+    return $this->view
+      ->tagImg($this->_getPictoUrl(),
+               ['title' => $this->_('Situer cet exemplaire dans la bibliothèque'),
+                'style' => 'border-style:none;cursor:pointer',
+                'onclick' => $onclick]);
   }
 
+
+  protected function _getPictoUrl() {
+    return Class_Profil::getCurrentProfil()->getUrlImage('localisation.png');
+  }
 }
 
 
@@ -355,20 +358,34 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Plan extends ZendAfi_View_Helper_No
     return $this->_('Plan');
   }
 
+
   public function renderContent($exemplaire) {
-    $html='<td class="gmap" style="text-align:center;">';
+    return $this->_tag('td', $this->_getMapLink($exemplaire),
+                       ['class' => 'gmap',
+                        'style' => 'text-align:center']);
+  }
 
-    if($this->getBib($exemplaire)->getGoogleMap() > "")
-      $html .= sprintf('<a href="%s"><img src="%s" border="0" alt="%s" title="%s" /></a>',
-                       BASE_URL.'/bib/mapview?id_bib='.$exemplaire->getIdBib() .'&amp;retour=notice',
-                       URL_ADMIN_IMG.'picto/map.gif',
-                       $this->_('Afficher la carte'),
-                       $this->_('Afficher la carte'));
-    else $html.='&nbsp;';
-    $html.='</td>';
-    return $html;
+
+  protected function _getMapLink($exemplaire) {
+    if (!$this->getBib($exemplaire)->getGoogleMap())
+      return '&nbsp;';
+
+    return $this->_tag('a', $this->_getPicto(),
+                       ['href' => BASE_URL.'/bib/mapview?id_bib='.$exemplaire->getIdBib() .'&amp;retour=notice']);
+  }
+
+
+  protected function _getPicto() {
+    return $this->view->tagImg($this->_getPictoUrl(),
+                               ['title' => $this->_('Afficher la carte'),
+                                'alt' => $this->_('Afficher la carte'),
+                                'style' => 'border-style:none;']);
   }
 
+
+  protected function _getPictoUrl() {
+    return Class_Profil::getCurrentProfil()->getUrlImage('map.gif');
+  }
 }
 
 
@@ -378,10 +395,10 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Resa extends ZendAfi_View_Helper_No
     return $this->_('Réserver');
   }
 
+
   public function renderContent($exemplaire) {
     return $this->view->Notice_ReservationLink($this->getBib($exemplaire), $exemplaire, '');
   }
-
 }
 
 
@@ -390,10 +407,10 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Consultation extends ZendAfi_View_H
     return $this->_('Consultation sur place');
   }
 
+
   public function renderContent($exemplaire) {
     return $this->view->Notice_ConsultationLink($this->getBib($exemplaire), $exemplaire, '');
   }
-
 }
 
 
@@ -403,22 +420,33 @@ class ZendAfi_View_Helper_Notice_Exemplaires_Oeuvre extends ZendAfi_View_Helper_
     return $this->_('Voir');
   }
 
+
   public function  renderContent($exemplaire) {
+    return $this->_tag('td', $this->_getWorkLink($exemplaire),
+                       ['class' => 'oeuvre',
+                        'style' => 'text-align:center']);
+  }
+
+
+  protected function _getWorkLink($exemplaire) {
+    return $this->_tag('a', $this->_getPicto(),
+                       ['href' => $this->view->url(['controller' => 'recherche',
+                                                    'action' => 'viewnotice',
+                                                    'id' => $exemplaire->getIdNotice()])]);
+  }
 
-    $html='<td class="oeuvre" style="text-align:center;">';
-    $html.= sprintf('<a href="%s"> <img src="%s" border="0" alt="%s" title="%s" /></a>',
-                    $this->view->url(['controller' => 'recherche',
-                                      'action' => 'viewnotice',
-                                      'id' => $exemplaire->getIdNotice()]),
-                    URL_IMG.'bouton/loupe.gif',
-                    $this->_('Afficher la notice'),
-                    $this->_('Afficher la notice')
-    );
-    $html.='</td>';
-    return $html;
 
+  protected function _getPicto() {
+    return $this->view->tagImg($this->_getPictoUrl(),
+                               ['title' => $this->_('Afficher la notice'),
+                                'alt' => $this->_('Afficher la notice'),
+                                'style' => 'border-style:none;']);
   }
 
+
+  protected function _getPictoUrl() {
+    return Class_Profil::getCurrentProfil()->getUrlImage('bouton/loupe.gif');
+  }
 }
 
 
diff --git a/library/ZendAfi/View/Helper/RenderTable.php b/library/ZendAfi/View/Helper/RenderTable.php
new file mode 100644
index 0000000000000000000000000000000000000000..92f431dacf65e6a3bc33afbb6f6cbfb623364b88
--- /dev/null
+++ b/library/ZendAfi/View/Helper/RenderTable.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * Copyright (c) 2012-2017, 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_RenderTable extends ZendAfi_View_Helper_BaseHelper {
+  /**
+   * @param description Class_TableDescription
+   * @param grouped_models Class_TableDescription_Models or Array
+   * @param options Array
+   */
+  public function renderTable($description, $grouped_models, $options = []) {
+    $grouped_models = is_array($grouped_models)
+      ? new Class_TableDescription_Models($grouped_models)
+      : $grouped_models;
+
+    $options = array_merge(['pager' => false], $options);
+    Class_ScriptLoader::getInstance()->loadTableSorter($options['pager']);
+
+    return
+      $this->_tag('table',
+                  $this->_head($description)
+                  .$this->_body($description, $grouped_models),
+                  ['id' => $description->getId(),
+                   'class' => 'models tablesorter'])
+      . $this->_renderPager($options);
+  }
+
+
+  protected function _head($description) {
+    return $this->view->renderTable_Header($description);
+  }
+
+
+  protected function _body($description, $grouped_models) {
+    return $this->view->renderTable_Body($description, $grouped_models);
+  }
+
+
+  protected function _renderPager($options) {
+    if (!$options['pager'])
+      return '';
+
+    $first = $this->_tag('span', '|<<' , ['class' => 'first',
+                                          'style' => 'cursor:pointer']);
+
+    $previous = $this->_tag('span', '<' , ['class' => 'first',
+                                           'style' => 'cursor:pointer']);
+
+    $page_display = $this->_tag('input', '', ['class' => 'pagedisplay']);
+
+    $next = $this->_tag('span', '>' , ['class' => 'next',
+                                       'style' => 'cursor:pointer']);
+
+    $last = $this->_tag('span', '>>|' , ['class' => 'last',
+                                         'style' => 'cursor:pointer']);
+
+    $select = $this->view->formSelect('pagesize',
+                                      '',
+                                      ['class' => 'pagesize'],
+                                      ['10' => 10,
+                                       '20' => 20,
+                                       '30' => 30]);
+    $html = $first .
+      $previous .
+      $page_display.
+      $next .
+      $last .
+      $select;
+
+    $form = $this->_tag('form', $html);
+
+    return $this->_tag('div', $form, ['class' => 'pager model_table_pager']);
+  }
+}
+
+
+
+
+class ZendAfi_View_Helper_RenderTable_Header extends ZendAfi_View_Helper_BaseHelper {
+  public function renderTable_Header($description) {
+    return $this->_tag('thead',
+                       $this->_tag('tr',
+                                   $this->_renderColumns($description)));
+  }
+
+
+  protected function _renderColumns($description) {
+    return implode('',
+                   $description->columnsCollect([$this, '_renderColumn'])
+                                ->getArrayCopy());
+  }
+
+
+  public function _renderColumn($column) {
+    return $this->_tag('th',
+                       $column->getLabel(),
+                       $column->getOptions() ? $column->getOptions() : null);
+  }
+}
+
+
+
+
+class ZendAfi_View_Helper_RenderTable_Body extends ZendAfi_View_Helper_BaseHelper {
+  protected
+    $_description,
+    $_html;
+
+  public function renderTable_Body($description, $grouped_models) {
+    $this->_description = $description;
+
+    $this->_html = '';
+    $grouped_models->renderOn($this);
+    return $this->_tag('tbody',
+                       $this->_html);
+
+  }
+
+
+  public function renderModels($models) {
+    $this->_html .= implode('',
+                            array_map([$this, '_renderModelRaw'],
+                                      $models));
+  }
+
+
+  public function renderGroupTitle($title) {
+     $this->_html .= $this->_tag('tr',
+                                $this->_tag('td',
+                                            $this->view->escape($title),
+                                            ['style' => 'background-color:#888;color:white;font-size:120%;padding:2px 10px;font-weight:bold;',
+                                             'colspan' => $this->_description->numberOfColumns()]));
+  }
+
+
+  protected function _renderModelRaw($model) {
+    return $this->_tag('tr',
+                       implode('',
+                               $this->_renderModelColumns($model)
+                               ->getArrayCopy()));
+  }
+
+  protected function _renderModelColumns($model) {
+    return $this
+      ->_description
+      ->columnsCollect(function($column) use ($model)
+                       {
+                         return $this->view->renderTable_Cell($column, $model);
+                       });
+  }
+}
+
+
+
+class ZendAfi_View_Helper_RenderTable_Cell extends ZendAfi_View_Helper_BaseHelper {
+  protected $_html;
+
+  public function renderTable_Cell($column, $model) {
+    $this->_html = '';
+    $column->renderModelOn($model, $this);
+    return $this->_tag('td',
+                       $this->_html);
+  }
+
+
+  public function renderContent($html) {
+    $this->_html .= $html;
+  }
+
+
+  public function tag($name, $content=null, $attribs=[]) {
+    $this->renderContent($this->_tag($name, $content, $attribs));
+  }
+}
+
+?>
\ No newline at end of file
diff --git a/library/ZendAfi/View/Helper/TagArticleEvent.php b/library/ZendAfi/View/Helper/TagArticleEvent.php
index 3cc5645c6f2d44f6a2d45902f68bde05cd73d77a..ec145a0aaef186ecb45e1a4bce62ed59a0a84f16 100644
--- a/library/ZendAfi/View/Helper/TagArticleEvent.php
+++ b/library/ZendAfi/View/Helper/TagArticleEvent.php
@@ -94,9 +94,9 @@ class ZendAfi_View_Helper_TagArticleEvent extends Zend_View_Helper_HtmlElement {
 
     $textual_days = '';
     foreach($days as $day)
-      $textual_days.= $this->numericDayToTextual($day) . ', ';
+      $textual_days.= $this->numericDayToTextual($day) . $this->view->_('s, ');
 
-    return substr($textual_days, 0, -2) . $this->view->_('s et ') . $last;
+    return substr($textual_days, 0, -2) . $this->view->_(' et ') . $last;
   }
 
 
diff --git a/library/ZendAfi/View/Helper/TagModelTable.php b/library/ZendAfi/View/Helper/TagModelTable.php
index 5402ad9c0dbdc523a6768d039d230ca8a95553e4..34bc2515e9ca5d53b2c8baf638c72eb0ff270a01 100644
--- a/library/ZendAfi/View/Helper/TagModelTable.php
+++ b/library/ZendAfi/View/Helper/TagModelTable.php
@@ -19,12 +19,6 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 class ZendAfi_View_Helper_TagModelTable extends ZendAfi_View_Helper_BaseHelper {
-  /** @var boolean */
-  protected $_hasActions = false;
-  /** @var int */
-  protected $_cols_count = 0;
-
-
   /**
      Exemple:
 
@@ -40,176 +34,27 @@ class ZendAfi_View_Helper_TagModelTable extends ZendAfi_View_Helper_BaseHelper {
                             'relations');
   */
   public function tagModelTable($models, $cols, $attribs, $actions, $id, $group_by = null, $callbacks = [], $pager = false) {
-    Class_ScriptLoader::getInstance()->loadTableSorter($pager);
-
-    $this->_hasActions = 0 < count($actions);
-    $this->_cols_count = count($attribs) + ($this->_hasActions ? 1 : 0);
-
-    $html = $this->_tag('table',
-                       $this->head($cols)
-                       .$this->tbody($models, $attribs, $actions, $group_by, $callbacks),
-                      ['id' => $id,
-                       'class' => 'models tablesorter']);
-
-    return $html . $this->_getPager($pager);
-  }
-
-
-  protected function _getPager($pager) {
-    if(!$pager)
-      return '';
-
-    $first = $this->_tag('span', '|<<' , ['class' => 'first',
-                                               'style' => 'cursor:pointer']);
-
-    $previous = $this->_tag('span', '<' , ['class' => 'first',
-                                                'style' => 'cursor:pointer']);
-
-    $page_display = $this->_tag('input', '', ['class' => 'pagedisplay']);
-
-    $next = $this->_tag('span', '>' , ['class' => 'next',
-                                            'style' => 'cursor:pointer']);
-
-    $last = $this->_tag('span', '>>|' , ['class' => 'last',
-                                              'style' => 'cursor:pointer']);
-
-    $select = $this->view->formSelect('pagesize',
-                                      '',
-                                      ['class' => 'pagesize'],
-                                      ['10' => 10,
-                                       '20' => 20,
-                                       '30' => 30]);
-    $html = $first .
-      $previous .
-      $page_display.
-      $next .
-      $last .
-      $select;
-
-    $form = $this->_tag('form', $html);
-
-    return $this->_tag('div', $form, ['class' => 'pager model_table_pager']);
-  }
-
-
-  public function head($cols) {
-    if (!$cols)
-      return '';
-
-    $cols_html =  '';
-    foreach ($cols as $col)
-      $cols_html .= $this->_tag('th', $col);
-
-    $actions = $this->_hasActions ? $this->_tag('th', $this->view->_('Actions'),
-                                               ['class' => 'actions',
-                                                'data-sorter' => 'false',
-                                                'style' => 'min-width: 80px;']) : '';
-
-    return $this->_tag('thead',
-                      $this->_tag('tr',  $cols_html . $actions));
-  }
-
-
-  public function tbody($models, $attribs, $actions, $group_by, $callbacks) {
-    $rows = '';
-
-    $groups = [];
-    if (null != $group_by) {
-      foreach ($models as $model) {
-        $group = $model->callGetterByAttributeName($group_by);
-        if (!array_key_exists($group, $groups))
-          $groups[$group] = [];
-        $groups[$group][] = $model;
-      }
-    } else {
-      $groups['no_group'] = $models;
+    $description = new Class_TableDescription($id);
+    foreach($cols as $i => $col) {
+      $attribute = $attribs[$i];
+      $args = [
+               'attribute' => $attribute,
+               'callback' => array_key_exists($attribute, $callbacks) ? $callbacks[$attribute] : null
+      ];
+
+      $description->addColumn($col, array_filter($args));
     }
 
-    return $this->_tag('tbody',
-                      $this->renderGroupsAsTableRows($groups, $attribs, $actions, $callbacks));
-  }
+    if (!is_array($actions))
+      $actions = [];
 
-
-  public function renderGroupsAsTableRows($groups, $attribs, $actions, $callbacks) {
-    $rows = '';
-
-    foreach ($groups as $name => $groupModels) {
-      if ('no_group' != $name && '' != $name)
-        $rows .= $this->_tag('tr',
-                            $this->_tag('td',
-                                       $this->view->escape($name),
-                                       ['style' => 'background-color:#888;color:white;font-size:120%;padding:2px 10px;font-weight:bold;',
-                                        'colspan' => $this->_cols_count]));
-
-      $rows .= $this->renderModelsAsTableRows($groupModels, $attribs, $actions, $callbacks);
-    }
-
-    return $rows;
-  }
-
-
-  public function renderModelsAsTableRows($groupModels, $attribs, $actions, $callbacks) {
-    $rows = '';
-    foreach ($groupModels as $model)
-      $rows .= $this->renderModelAsTableRow($model, $attribs, $actions, $callbacks);
-    return $rows;
-  }
-
-
-  public function renderModelAsTableRow($model, $attribs, $actions, $callbacks) {
-    $cols = '';
-
-    $default_callback = function ($model, $attrib) {
-        return $this->view->escape($model->callGetterByAttributeName($attrib));
-    };
-
-    foreach ($attribs as $attrib) {
-      $callback = (array_key_exists($attrib, $callbacks)) ? $callbacks[$attrib] : $default_callback;
-      $cols .= $this->_tag('td', $callback($model, $attrib));
+    foreach($actions as $action) {
+      $description->addRowAction($action);
     }
 
-    $actions = ($this->_hasActions) ?
-      $this->_tag('td', $this->renderModelActions($model, $actions)) : '';
-
-    return $this->_tag('tr', $cols . $actions);
-  }
-
-
-  /**
-   * @param Storm_Model_Abstract $model
-   * @param array of arrays of string / Closure $actions
-   * @return string
-   */
-  public function renderModelActions($model, $actions) {
-    $html = '';
-    foreach ($actions as $action)
-      $html .= $this->renderModelAction($model, $action);
-
-    return $html;
-  }
-
-
-  /**
-   * @param Storm_Model_Abstract $model
-   * @param array of strings/closure or closure
-   * @return string
-   */
-  public function renderModelAction($model, $action) {
-    if (is_a($action, 'Closure'))
-      return $action($model);
-
-    $content = $action['content'];
-    $url = $this->view->url(['action' => $action['action'], 'id' => $model->getId()]);
-    $attribs = ['href' => $url, 'rel' => $action['action']];
-
-    if (isset($_SERVER['REQUEST_URI'])
-        && false !== strpos($_SERVER['REQUEST_URI'], $url))
-      $attribs['class'] = 'selected';
-
-    return $this->view
-      ->tag('a',
-            is_a($content, 'Closure') ? $content($model) : $content,
-            $attribs);
+    return $this->view->renderTable($description,
+                                    (new Class_TableDescription_Models($models))->groupBy($group_by),
+                                    ['pager' => $pager]);
   }
 }
 ?>
diff --git a/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php b/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
index 61aaf0fa303ba9b6c7ec14ebb9d432ef9b9232ae..23fa1a93ff2f131e35f1012a27682ae3a6556939 100644
--- a/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
+++ b/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
@@ -32,7 +32,8 @@ class ZendAfi_View_Helper_TagProgressBarForNewsletter extends ZendAfi_View_Helpe
       '$("#progress_bar_newsletter_'.$newsletter->getId().' span").html(data.status);'.
       'if(!data.total) {'.
       '$("#progress_bar_newsletter_'.$newsletter->getId().'").html(data.status);'.
-      'initializePopups();'.
+        'initializePopups();'.
+        '$(".tablesorter").trigger("update");'.
       'return true;'.
       '}'.
       '$("#progress_bar_newsletter_'.$newsletter->getId().'").progressbar({value: data.done, max: data.total});'.
diff --git a/library/startup.php b/library/startup.php
index 0402716d1928250a71cc57bd0d08c05f1a7385a3..6f29cfc6a9db540284e7f58fcb1beac249d54c0c 100644
--- a/library/startup.php
+++ b/library/startup.php
@@ -83,7 +83,7 @@ class Bokeh_Engine {
 
   function setupConstants() {
     defineConstant('BOKEH_MAJOR_VERSION','7.9');
-    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.9');
+    defineConstant('BOKEH_RELEASE_NUMBER', BOKEH_MAJOR_VERSION . '.10');
 
     defineConstant('BOKEH_REMOTE_FILES', 'http://git.afi-sa.fr/afi/opacce/');
 
diff --git a/public/opac/images/localisation.png b/public/opac/images/localisation.png
new file mode 100644
index 0000000000000000000000000000000000000000..f19475ccd8e6313a7763023af903c875dc93ee8b
Binary files /dev/null and b/public/opac/images/localisation.png differ
diff --git a/public/opac/images/map.gif b/public/opac/images/map.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f7f09a2a09e31d80c4feb2f47de408d89bd828be
Binary files /dev/null and b/public/opac/images/map.gif differ
diff --git a/tests/application/modules/admin/controllers/LieuControllerTest.php b/tests/application/modules/admin/controllers/LieuControllerTest.php
index 6991c1767fd98bb53f8640800fd45df2c5f144b6..15eb8502f273e35d9b75701ed4283c1509a26740 100644
--- a/tests/application/modules/admin/controllers/LieuControllerTest.php
+++ b/tests/application/modules/admin/controllers/LieuControllerTest.php
@@ -57,7 +57,7 @@ abstract class LieuControllerTestCase extends AbstractControllerTestCase {
 class LieuControllerListTest extends LieuControllerTestCase {
   public function setUp() {
     parent::setUp();
-    $this->dispatch('/admin/lieu');
+    $this->dispatch('/admin/lieu', true);
   }
 
 
diff --git a/tests/application/modules/admin/controllers/NewsletterControllerTest.php b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
index 74fe8a9ba4c082c6c9f86c6197ef7d24e7967fa9..01a09fd7340e04cc8a73e4352bb3b857e86cba17 100644
--- a/tests/application/modules/admin/controllers/NewsletterControllerTest.php
+++ b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
@@ -114,6 +114,12 @@ class Admin_NewsletterControllerIndexActionTest extends Admin_NewsletterControll
   }
 
 
+  /** @test */
+  public function publicationDateColumnShouldHaveClassDateFormatDDMMYYYYForTableSorter() {
+    $this->assertXPath('//table//tr//th[2][contains(@class, "dateFormat-ddmmyyyy")]');
+  }
+
+
   /** @test */
   public function configLinkShouldBePresent() {
     $this->assertXPath('//h1//a[contains(@href, "newsletter/config")]');
diff --git a/tests/application/modules/admin/controllers/TypeDocsControllerTest.php b/tests/application/modules/admin/controllers/TypeDocsControllerTest.php
index 74654327cc7ce755f40b4f8338147eea443a0111..d51cfe85c4c1474ac2215048dcecf78fecd4ff44 100644
--- a/tests/application/modules/admin/controllers/TypeDocsControllerTest.php
+++ b/tests/application/modules/admin/controllers/TypeDocsControllerTest.php
@@ -62,7 +62,7 @@ class TypeDocsControllerIndexTest extends AbstractTypeDocsControllerTestCase {
 
   /** @test */
   public function actionColumnShouldHaveSorterFalse() {
-    $this->assertXPath('//thead//th[3][@class="actions"][@data-sorter="false"]');
+    $this->assertXPath('//thead//th[3][@class="actions"][@data-sorter="false"]', $this->_response->getBody());
   }
 
 
diff --git a/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php b/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
index 686b630c532cda4aedd7319657de9cc5ed384a03..6a24a0c5f2732867af4c89ee660870f0de915dc5 100644
--- a/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
+++ b/tests/application/modules/opac/controllers/NoticeAjaxControllerItemsTest.php
@@ -31,6 +31,12 @@ abstract class NoticeAjaxControllerItemsTestCase extends AbstractControllerTestC
                     'comm_sigb' => Class_IntBib::COM_VSMART
                    ]);
 
+    $this->fixture('Class_Bib',
+                   ['id' => 3,
+                    'google_map' => 1,
+                    'localisations' => [$this->fixture('Class_Localisation',
+                                                       ['id' => 43])]]);
+
 
     $itemA = $this->fixture('Class_Exemplaire',
                             ['id' => 12,
@@ -230,4 +236,50 @@ class NoticeAjaxControllerItemsFiltredByProfilAnnexTest extends NoticeAjaxContro
    public function itemBShouldNotBePresent() {
      $this->assertNotXPathContentContains('//td[@class="cote"]', 'B');
    }
+}
+
+
+
+
+class NoticeAjaxControllerItemsCustomIconsTest
+  extends NoticeAjaxControllerItemsTestCase {
+
+  protected function _prepareFixtures() {
+    $profil = Class_Profil::getCurrentProfil();
+    $config = $profil->getCfgNoticeAsArray();
+    $config['exemplaires']['grouper'] = '1';
+    $config['exemplaires']['order_by'] = '9';
+    $config['exemplaires']['order_direction'] = 'DESC';
+    $profil->setCfgNotice($config);
+
+    $profil->setDefaultSkin($this->mock()
+                            ->whenCalled('getPath')->answers('/skins/customized')
+                            ->whenCalled('getTemplatesPath')->answers('/skins/customized/html')
+                            ->whenCalled('getImageUrl')
+                            ->willDo(function($image)
+                                     {
+                                       return '/skins/customized/images/' . $image;
+                                     })
+    );
+  }
+
+
+  public function tearDown() {
+    Class_Profil::getCurrentProfil()->setDefaultSkin(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function locationIconShouldBeCustom() {
+    $this->assertXPath('//img[contains(@src, "/customized/images/localisation.png")]',
+                       $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function mapIconShouldBeCustom() {
+    $this->assertXPath('//img[contains(@src, "/customized/images/map.gif")]',
+                       $this->_response->getBody());
+  }
 }
\ No newline at end of file
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 86ea24b584c9dc3e8e0c19eab0dbfef1843f701d..4e18f6d68391354005608a0cc20f9d04e722cb57 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -179,7 +179,9 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
 
   protected function assertField($table, $column, $expected, $field, $message = '') {
     try {
+      $fields = [];
       foreach($this->query(sprintf('show fields from `%s` where field=\'%s\'', $table, $column))->fetchAll() as $row) {
+        $fields[] = $row;
         if ($expected == $row[$field])
           return true;
       }
@@ -189,8 +191,8 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
 
     $message = $message
       ? $message
-      : sprintf('Failed asserting that TABLE %s contains COLUMN %s with field %s = %s',
-                $table, $column, $field, $expected);
+      : sprintf('Failed asserting that TABLE %s contains COLUMN %s with field %s = %s, %s',
+                $table, $column, $field, $expected, json_encode($fields));
 
     $this->fail($message);
   }
@@ -1322,7 +1324,42 @@ class UpgradeDB_320_Test extends UpgradeDBTestCase {
 
 class UpgradeDB_321_Test extends UpgradeDBTestCase {
   public function prepare() {}
-  
+
   /** @test */
   public function placeholderForLeKioskMigrationPatch() {}
 }
+
+
+
+
+class UpgradeDB_322_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    try {
+      $this->query("ALTER TABLE cms_avis DROP COLUMN flags");
+    } catch (Exception $e) {}
+  }
+
+
+  /** @test */
+  public function flagsShouldBeIndexedTinyInt() {
+    $this->assertFieldType('cms_avis','flags', 'tinyint(4)');
+    $this->assertIndex('cms_avis', 'flags', 'BTREE');
+  }
+}
+
+
+
+
+class UpgradeDB_323_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    try {
+      $this->query('alter table newsletters modify column titre varchar(50) not null');
+    } catch(Exception $e) {}
+  }
+
+
+  /** @test */
+  public function newslettersColumnTitreShouldBeVarchar255() {
+    $this->assertFieldType('newsletters', 'titre', 'varchar(255)');
+  }
+}
diff --git a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
index 99b368030ee5fc6d7fa239971934619c21958f19..3ec772945226e2ead6192a1a2d5ae8fa9bb1cf63 100644
--- a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
+++ b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php
@@ -134,6 +134,17 @@ class TagArticleEventTest extends ViewHelperTestCase {
   }
 
 
+
+    /** @test */
+  public function withMondayThuesdayThursdayAndSaturdayPickShouldAnswersTousLesLundisMardisMercredisEtTousLesSamedi() {
+    $this->article
+      ->setEventsDebut('2011-09-05 08:00')
+      ->setEventsFin('2011-10-05 10:00')
+      ->setPickDay('1,2,4,6');
+    $this->assertTagContains('Les lundis, mardis, jeudis et samedis du 5 septembre au 5 octobre 2011');
+  }
+
+
   /** @test */
   public function withAllDaysPickShouldAnswersTousLesJours() {
     $this->article