diff --git a/VERSIONS_HOTLINE/49621 b/VERSIONS_HOTLINE/49621
new file mode 100644
index 0000000000000000000000000000000000000000..b287a3ae172a4fc6e1209592c9314b894439cc70
--- /dev/null
+++ b/VERSIONS_HOTLINE/49621
@@ -0,0 +1 @@
+ - ticket #49621 : ajout d'un filtre anti-spam dans les formulaires intégrés dans les articles
\ No newline at end of file
diff --git a/application/modules/opac/controllers/FormulaireController.php b/application/modules/opac/controllers/FormulaireController.php
index e6ced064ebbd47db10fa1595e95b3264b12e0f7d..f51591098a2bc006fd99da3a4c74f84348fe9212 100644
--- a/application/modules/opac/controllers/FormulaireController.php
+++ b/application/modules/opac/controllers/FormulaireController.php
@@ -22,8 +22,14 @@ class FormulaireController extends ZendAfi_Controller_Action {
   public function addAction() {
     $article = Class_Article::find($this->_getParam('id_article'));
 
+    $post = $this->_request->getPost();
+    if ($this->_getParam('emailCheck'))
+      return $this->_redirect('/');
+
+    unset($post['emailCheck']);
+
     $formulaire = new Class_Formulaire();
-    $formulaire->setData(serialize($this->_request->getPost()))
+    $formulaire->setData(serialize($post))
                ->setUser(Class_Users::getIdentity())
                ->setArticle($article)
                ->save();
diff --git a/library/Class/Article.php b/library/Class/Article.php
index b145ffd1df70dda96620ff2d25007b5b1d154265..facf8eddf41a0290d565283b7855706372481f32 100644
--- a/library/Class/Article.php
+++ b/library/Class/Article.php
@@ -954,17 +954,30 @@ class Class_Article extends Storm_Model_Abstract {
     if (preg_match('/(<form[^>]+)action='.$quote.'http/i', $contenu))
       return $contenu;
 
-    if (!defined('BASE_URL'))
-      define('BASE_URL', '');
+    $action = Class_Url::assemble(['controller' => 'formulaire',
+                                   'action' => 'add',
+                                   'id_article' => $this->getId()],
+                                  null,
+                                  true);
 
     $replaced_form = preg_replace(['/(<form[^>]+)action='.$quoted_value.'/i',
                                    '/(<form[^>]+)method='.$quoted_value.'/i',
                                    '/(<form *)/i'],
                                   ['$1 ',
                                    '$1 ',
-                                   '$1 action="'.BASE_URL.'/formulaire/add/id_article/'.$this->getId().'" method="POST" '],
+                                   '$1 action="'. $action .'" method="POST" '],
                                   $contenu);
 
+
+    $end_form = '</form>';
+    $antispam_tag = '<input data-spambots="true" name="emailCheck" type="text" />';
+
+    $replaced_form = str_replace($antispam_tag, '', $replaced_form);
+    if (false === strpos($replaced_form, 'name="emailCheck"'))
+      $replaced_form = preg_replace('|' . $end_form .'|i',
+                                    $antispam_tag . $end_form,
+                                    $replaced_form);
+
     $typesubmit = 'type='.$quote.'(?:submit|button)'.$quote;
     $namesubmit = 'name='.$quoted_value;
     $otherattributes = '[^>]+';
diff --git a/library/ZendAfi/View/Helper/CkEditor.php b/library/ZendAfi/View/Helper/CkEditor.php
index b835287b50000015cd065cb3d60c0b3fee7c12ea..395c7f26b28a3116d2ef0e107519ac0a384188f1 100644
--- a/library/ZendAfi/View/Helper/CkEditor.php
+++ b/library/ZendAfi/View/Helper/CkEditor.php
@@ -38,8 +38,9 @@ class ZendAfi_View_Helper_CkEditor extends ZendAfi_View_Helper_BaseHelper
     $config['filebrowserImageUploadUrl'] = CKBASEURL."filemanager/upload/php/upload.php?Type=Image&ServerPath=".USERFILESURL;
     $config['filebrowserFlashUploadUrl'] = CKBASEURL."filemanager/upload/php/upload.php?Type=Flash&ServerPath=".USERFILESURL;
     $config['imagesPath'] = URL_ADMIN_IMG."ckeditor_templates/";
-    $config['templates_files'] = array(URL_ADMIN_JS."ckeditor_templates.js");
-    $config['contentsCss'] = array(URL_CSS."global.css");
+    $config['templates_files'] = [URL_ADMIN_JS."ckeditor_templates.js"];
+    $config['contentsCss'] = [BASE_URL . '/public/opac/css/global.css',
+                              URL_CSS . 'global.css'];
 
     $config['toolbar'] = [
       ['Preview', 'Templates', 'Source','Maximize'],
diff --git a/tests/application/modules/opac/controllers/FormulaireControllerTest.php b/tests/application/modules/opac/controllers/FormulaireControllerTest.php
index b65b5f84ee547b5a62707d79c8248688d22a2500..bbc693973b9fe20c33473161a0050a2ddeb6f8b0 100644
--- a/tests/application/modules/opac/controllers/FormulaireControllerTest.php
+++ b/tests/application/modules/opac/controllers/FormulaireControllerTest.php
@@ -20,6 +20,10 @@
  */
 
 abstract class FormulaireControllerPostActionTestCase extends AbstractControllerTestCase {
+  protected
+    $_storm_default_to_volatile = true;
+
+
   public function setUp() {
     parent::setUp();
 
@@ -39,6 +43,9 @@ abstract class FormulaireControllerPostActionTestCase extends AbstractController
   }
 }
 
+
+
+
 class FormulaireControllerWithEmailPostActionTest extends FormulaireControllerPostActionTestCase {
   protected $_user;
 
@@ -57,15 +64,15 @@ class FormulaireControllerWithEmailPostActionTest extends FormulaireControllerPo
     $this->mock_transport = new MockMailTransport();
     Zend_Mail::setDefaultTransport($this->mock_transport);
 
-
     $this->postDispatch('/formulaire/add/id_article/45',
                         ['nom' => 'Tinguette' ,
-                         'prenom' => 'Quentin' ]
-                        ,true);
+                         'prenom' => 'Quentin' ],
+                        true);
 
     $this->new_formulaire = Class_Formulaire::find(2);
   }
 
+
   /** @test */
   public function postFormulaireShouldReturnEmail() {
     $this->assertXpathContentContains('//div','courriel',true );
@@ -74,7 +81,8 @@ class FormulaireControllerWithEmailPostActionTest extends FormulaireControllerPo
 
   /** @test */
   public function mailContentShouldContainData() {
-    $this->assertContains('nom: Tinguette', quoted_printable_decode($this->mock_transport->getSentMails()[0]->getBodyText()->getContent()));
+    $this->assertContains('nom: Tinguette',
+                          quoted_printable_decode($this->mock_transport->getSentMails()[0]->getBodyText()->getContent()));
   }
 
 
@@ -83,18 +91,20 @@ class FormulaireControllerWithEmailPostActionTest extends FormulaireControllerPo
     $this->assertEquals('sender@example.com', $this->mock_transport->getSentMails()[0]->getFrom());
   }
 
+
   /** @test */
   public function emailToShouldBeRecipient() {
     $this->assertEquals('recipient@example.com', $this->mock_transport->getSentMails()[0]->getRecipients()[0]);
   }
 
+
   /** @test */
   public function emailSubjectShouldBeFormSent() {
     $this->assertEquals('[Bokeh] Envoi d\'un formulaire', $this->mock_transport->getSentMails()[0]->getSubject());
   }
+}
 
 
-}
 
 
 class FormulaireControllerPostActionTest extends FormulaireControllerPostActionTestCase {
@@ -111,12 +121,14 @@ class FormulaireControllerPostActionTest extends FormulaireControllerPostActionT
 
     $this->postDispatch('/formulaire/add/id_article/45',
                         ['nom' => 'Tinguette' ,
-                         'prenom' => 'Quentin' ]
-                        ,true);
+                         'prenom' => 'Quentin',
+                         'emailCheck' => ''],
+                        true);
 
     $this->new_formulaire = Class_Formulaire::find(2);
   }
 
+
   /** @test */
   public function saveFormulaireShouldHaveNomTinguette() {
     $this->assertEquals('Tinguette', $this->new_formulaire->getNom());
@@ -176,22 +188,48 @@ class FormulaireControllerWithoutConnectedUserPostActionTest extends FormulaireC
     parent::setUp();
 
     ZendAfi_Auth::getInstance()->clearIdentity();
-
     $this->postDispatch('/formulaire/add/id_article/45',
                         ['nom' => 'Tinguette' ,
-                         'prenom' => 'Quentin' ]
-                        ,true);
+                         'prenom' => 'Quentin' ],
+                        true);
 
     $this->new_formulaire = Class_Formulaire::find(2);
-
   }
 
+
   /** @test */
   public function saveFormulaireShouldNotHaveAnyUsers() {
     $this->assertEmpty($this->new_formulaire->getUser());
+  }
+}
 
+
+
+
+class FormulaireControllerPostAsBotTest extends FormulaireControllerPostActionTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->postDispatch('/formulaire/add/id_article/45',
+                        ['nom' => 'Tinguette' ,
+                         'prenom' => 'Quentin',
+                         'emailCheck' => 'i am a bot'],
+                        true);
+
+    $this->new_formulaire = Class_Formulaire::find(2);
+  }
+
+
+  /** @test */
+  public function formulaireShouldNotBeCreated() {
+    $this->assertNull($this->new_formulaire);
   }
 
+
+  /** @test */
+  public function answerShouldRedirectToRoot() {
+    $this->assertRedirectTo('/');
+  }
 }
 
 ?>
\ No newline at end of file
diff --git a/tests/library/Class/ArticleFormulaireTest.php b/tests/library/Class/ArticleFormulaireTest.php
index 90f3f26f3084d4015dac154475535d1ce2151624..dec8855d7df02ad190fd6c858c9c232d31fb1f08 100644
--- a/tests/library/Class/ArticleFormulaireTest.php
+++ b/tests/library/Class/ArticleFormulaireTest.php
@@ -16,14 +16,19 @@
  *
  * 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
  */
 
-class ArticleFormulaireInternalTest extends Storm_Test_ModelTestCase {
+class ArticleFormulaireInternalTest extends ModelTestCase {
+  protected
+    $_storm_default_to_volatile = true;
+
   public function setUp() {
     parent::setUp();
-    $this->_article = Class_Article::newInstanceWithId(2,['titre' => 'Contactez-nous !',
-                                        'contenu' => '<FORM id="idform" action=\'form\' method="post" name="form" target="_blank">
+    $this->_article = $this->fixture('Class_Article',
+                                     ['id' => 2,
+                                      'titre' => 'Contactez-nous !',
+                                      'contenu' => '<FORM id="idform" action=\'form\' method="post" name="form" target="_blank">
   <p>   Donnee 1 :<br /><input name="champs texte" type="text" value="champtxt" />
   <input value="champ2"  name=\'champs texte\' type="text"/></p>
   <p> &nbsp;</p>
@@ -33,35 +38,46 @@ class ArticleFormulaireInternalTest extends Storm_Test_ModelTestCase {
 
   </form>
   POST<form method="POST">
-  
+
     <input type="button" value="likebutton" />
+    <input data-spambots="true" name="emailCheck" type="text" />
   </form>
   EMPTY<form>
   </form>
   ']);
   }
 
+
   /** @test */
   public function formIdFormActionShouldBeFormulaireAdd() {
-      $this->assertContains('<FORM  action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" id="idform"     name="form" target="_blank', 
+      $this->assertContains('<FORM  action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" id="idform"     name="form" target="_blank',
                             $this->_article->getContenu());
   }
 
 
+  /** @test */
+  public function eachFormShouldContainsInputEmailCheckForSpamBot() {
+    $xpath = new Storm_Test_XPath();
+    $xpath->assertXPathCount($this->_article->getContenu(),
+                             '//form/input[@name="emailCheck"][@data-spambots="true"]',
+                             3);
+  }
+
+
   /** @test */
   public function formWithMethodPostActionShouldBeFormulaireAdd() {
-      $this->assertContains('POST<form   action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" >', 
+      $this->assertContains('POST<form   action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" >',
                             $this->_article->getContenu());
   }
 
 
   /** @test */
   public function emptyFormActionShouldBeFormulaireAdd() {
-      $this->assertContains('EMPTY<form action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" >', 
+      $this->assertContains('EMPTY<form action="'.BASE_URL.'/formulaire/add/id_article/2" method="POST" >',
                             $this->_article->getContenu());
   }
 
- 
+
   /** @test */
   public function formSubmitButtonShouldHaveNoName() {
     $this->assertContains('<input   value="click !" type="submit"/>',
@@ -99,6 +115,26 @@ class ArticleFormulaireInternalTest extends Storm_Test_ModelTestCase {
 
 
 
+class ArticleFormulaireWithLegacyEmailCheckTest extends ModelTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->_article = Class_Article::newInstanceWithId(2,['titre' => 'Contactez-nous !',
+                                        'contenu' => '
+  <form id="old" >
+  <input name="extenvoi" value="extenvoi" type="submit"/>
+  <input name="emailCheck" />
+  </form>
+  ']);
+  }
+
+
+  /** @test */
+  public function inputDataSpamBotsShouldNotBeAdded() {
+    $this->assertNotContains('data-spambots', $this->_article->getContenu());
+  }
+}
+
+
 
 class ArticleFormulaireExternalTest extends Storm_Test_ModelTestCase {
   public function setUp() {
@@ -111,18 +147,23 @@ class ArticleFormulaireExternalTest extends Storm_Test_ModelTestCase {
   ']);
   }
 
- 
+
   /** @test */
   public function formWithExternalUrlShouldNotChange() {
-      $this->assertContains('<form id="extern" action="http://monserveur/post" >', 
+      $this->assertContains('<form id="extern" action="http://monserveur/post" >',
                             $this->_article->getContenu());
   }
 
+
   /** @test */
   public function formSubmitWithExternalUrlShouldNotChange() {
     $this->assertContains(' <input name="extenvoi" value="extenvoi" type="submit"/>',
                             $this->_article->getContenu());
   }
 
-}
 
+  /** @test */
+  public function inputEmailCheckShouldNotBePresent() {
+    $this->assertNotContains('emailCheck', $this->_article->getContenu());
+  }
+}