merge
authorymh <ymh.work@gmail.com>
Wed, 14 Dec 2011 23:53:37 +0100
changeset 64 caeb4c8b5487
parent 63 774ba82dca59 (diff)
parent 56 3bd02e58fd6b (current diff)
child 65 ba6b8e38d90e
merge
Resources/public/css/wikiTag.css
--- a/.hgignore	Mon Dec 12 14:27:37 2011 +0100
+++ b/.hgignore	Wed Dec 14 23:53:37 2011 +0100
@@ -1,2 +1,3 @@
 syntax: regexp
 ^\.settings$
+.*\.orig$
--- a/Command/QueryWikipediaCommand.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Command/QueryWikipediaCommand.php	Wed Dec 14 23:53:37 2011 +0100
@@ -41,8 +41,9 @@
         $this
             ->setName('wikitag:query-wikipedia')
             ->setDescription('Query wikipedia for tags.')
-            ->addOption("force","f",InputOption::VALUE_NONE, "Force remove tags")
-            ->addOption("all","a",InputOption::VALUE_NONE, "all")
+            ->addOption("force","f",InputOption::VALUE_NONE, "Force processing tags, will ask no confirmation")
+            ->addOption("all","a",InputOption::VALUE_NONE, "Search all tags")
+            ->addOption("null","n",InputOption::VALUE_NONE, "Treat only non processed tags")
             ->addOption("redirection",null,InputOption::VALUE_NONE, "Treat redirections")
             ->addOption("random","r",InputOption::VALUE_NONE, "randomize query on tags")
             ->addOption("site","S",InputOption::VALUE_OPTIONAL, "the url for the wikipedia site", "http://fr.wikipedia.org/w/api.php")
@@ -60,6 +61,7 @@
         $site = $input->getOption('site');
         $limit = intval($input->getOption('limit'));
         $start = intval($input->getOption('start'));
+        $null = $input->getOption('null');
         
         $doctrine = $this->getContainer()->get('doctrine');
         $qb = $doctrine->getEntityManager()->createQueryBuilder();
@@ -72,8 +74,11 @@
             if($redirection) {
                 $qb->where($qb->expr()->andx($qb->expr()->eq("t.urlStatus",Tag::$TAG_URL_STATUS_DICT['redirection']), $qb->expr()->isNull("t.alternativeLabel")));
             }
+            elseif($null) {
+                $qb->where($qb->expr()->isNull("t.urlStatus"));
+            }
             else {
-                $qb->where($qb->expr()->isNull("t.urlStatus"));
+                $qb->where($qb->expr()->orx($qb->expr()->isNull("t.urlStatus"), $qb->expr()->eq("t.urlStatus", Tag::$TAG_URL_STATUS_DICT['null_result'])));
             }
         }
         
--- a/Command/SyncDocumentsCommand.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Command/SyncDocumentsCommand.php	Wed Dec 14 23:53:37 2011 +0100
@@ -152,6 +152,7 @@
     
     private function execute_docs(InputInterface $input, OutputInterface $output)
     {
+        $class = $this->getContainer()->getParameter('wiki_tag.document_class');
         $all = $input->getOption('all');
         $doctrine = $this->getContainer()->get('doctrine');
         $docrep = $doctrine->getRepository('WikiTagBundle:Document');
--- a/Controller/WikiTagController.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Controller/WikiTagController.php	Wed Dec 14 23:53:37 2011 +0100
@@ -16,6 +16,7 @@
 use IRI\Bundle\WikiTagBundle\Entity\DocumentTag;
 use IRI\Bundle\WikiTagBundle\Entity\Tag;
 use IRI\Bundle\WikiTagBundle\Utils\WikiTagUtils;
+use IRI\Bundle\WikiTagBundle\Services\WikiTagServiceException;
 use Pagerfanta\Pagerfanta;
 use Pagerfanta\Adapter\ArrayAdapter;
 use Pagerfanta\Adapter\DoctrineORMAdapter;
@@ -267,52 +268,15 @@
     {
         $id_doc = $this->getRequest()->request->get('wikitag_document_id');
         $tag_label = $this->getRequest()->request->get('value');
-        // We get the DocumentTags
-        $em = $this->getDoctrine()->getEntityManager();
-        $tags = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->findByDocumentExternalId($id_doc);
-        $nb_tags = count($tags);
-        $found = false;
-        $i = 0;
-        while($i<$nb_tags && $found==false){
-            $dt = $tags[$i];
-            if(strtolower($dt->getTag()->getLabel())==strtolower($tag_label)){
-                $found = true;
-            }
-            $i++;
-        }
-        // If the label was found, we sent a bad request
-        if($found==true){
-            //TODO : translation
-            return new Response(json_encode(array('error' => 'duplicate_tag', 'message' => sprintf("Le tag %s existe déjà pour cette fiche.", $tag_label))),400);
-        }
-        // returns array($tag, $revision_id, $created)
-        try {
-            $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label);
+        
+        
+        try
+        {
+            $this->get('wiki_tag.document')->addTag($id_doc, $tag_label);
         }
-        catch (\Exception $e){
-            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400);
-        }
-        
-        $tag = $ar[0];
-        $revision_id = $ar[1];
-        $created = $ar[2];
-        
-        $tags = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->findByDocumentExternalId($id_doc, array('tag'=>$tag->getId()));
-        $nb_tags = count($tags);
-
-        if($created==true || $nb_tags==0){
-            $new_order_ar = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->getMaxOrder($id_doc);
-            // The result is a double array. And reset(reset($newOrderAr)) is not allowed. And a string is returned.
-            $a1 = reset($new_order_ar);
-            $new_order = intval(reset($a1)) + 1;
-            $new_DT = new DocumentTag();
-            $new_DT->setDocument($this->getDoctrine()->getRepository('WikiTagBundle:Document')->findOneByExternalId($id_doc));
-            $new_DT->setTag($tag);
-            $new_DT->setOriginalOrder($new_order);
-            $new_DT->setTagOrder($new_order);
-            $new_DT->setWikipediaRevisionId($revision_id);
-            $em->persist($new_DT);
-            $em->flush();
+        catch (WikiTagServiceException $e)
+        {
+            return new Response(json_encode(array('error' => $e->getErrorCode(), 'message' => $e->getMessage())),$e->getCode());
         }
 
         return $this->renderDocTags($id_doc, $this->getRequest()->request->get('wikitag_document_profile'));
@@ -329,17 +293,21 @@
         $id_doc = $this->getRequest()->request->get('wikitag_document_id');
         $id_tag = $this->getRequest()->request->get('tag_id');
         $tag = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->find($id_tag);
-        //return new Response(var_dump(array($tag)));
-        // We search if the unsemantized version of the tag already exist.
-        $un_tag = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->findOneBy(array('label'=>$tag->getLabel(), 'urlStatus'=>Tag::$TAG_URL_STATUS_DICT['null_result']));
+ 
         $em = $this->getDoctrine()->getEntityManager();
-        $un_tag_created = FALSE;
+        $query = $em->createQuery("SELECT t FROM WikiTagBundle:Tag t WHERE t.label = :label AND (t.urlStatus = :status_null OR t.urlStatus = :status_unsemantized)");
+        $query->setParameters(array("label"=>$tag->getLabel(),"status_null"=>Tag::$TAG_URL_STATUS_DICT['null_result'],"status_unsemantized"=>Tag::$TAG_URL_STATUS_DICT['unsemantized']));
+        $un_tag = null;
+        $un_tags = $query->getResult();
+        if(count($un_tags)>0) {
+            $un_tag = $un_tags[0];
+        }
+        $un_tag_created = false;
         if(!$un_tag){
             // Create another tag almost identical, without the W info
             $un_tag = new Tag();
             $un_tag->setLabel($tag->getLabel());
             $un_tag->setOriginalLabel($tag->getOriginalLabel());
-            $un_tag->setUrlStatus(Tag::$TAG_URL_STATUS_DICT['null_result']);
             $un_tag->setWikipediaUrl(null);
             $un_tag->setWikipediaPageId(null);
             $un_tag->setAlternativeWikipediaUrl(null);
@@ -349,9 +317,18 @@
             $un_tag->setCategory($tag->getCategory());
             $un_tag->setAlias($tag->getAlias());
             $un_tag->setPopularity($tag->getPopularity());
-            $em->persist($un_tag);
+            $un_tag->setUrlStatus(Tag::$TAG_URL_STATUS_DICT['unsemantized']);
             $un_tag_created = true;
+            $em->persist($un_tag);
         }
+        elseif($un_tag->getUrlStatus()==Tag::$TAG_URL_STATUS_DICT['null_result'])
+        {
+            $un_tag->setUrlStatus(Tag::$TAG_URL_STATUS_DICT['unsemantized']);
+            $un_tag_created = true;
+            $em->persist($un_tag);
+        }
+        
+        
         
         if($id_doc && $id_doc!=""){
             // We associate the unsemantized tag to the DocumentTag and save datas
@@ -362,13 +339,9 @@
         }
         else{
             // Here we are in the context of tag list.
-            if($un_tag_created==TRUE){
+            if($un_tag_created==true){
                 $em->flush();
-                $num_page = $this->getRequest()->request->get('num_page');
-                $nb_by_page = $this->getRequest()->request->get('nb_by_page');
-                $sort = $this->getRequest()->request->get('sort');
-                $searched = $this->getRequest()->request->get('searched');
-                return $this->renderAllTags($num_page, $nb_by_page, $sort, $searched);
+                return $this->renderAllTags();
             }
             else{
                 // The unsemantized version of the tag already exist, so we send an error.
@@ -404,11 +377,7 @@
             return $this->renderDocTags($id_doc, $this->getRequest()->request->get('wikitag_document_profile'));
         }
         else{
-            $num_page = $this->getRequest()->request->get('num_page');
-            $nb_by_page = $this->getRequest()->request->get('nb_by_page');
-            $sort = $this->getRequest()->request->get('sort');
-            $searched = $this->getRequest()->request->get('searched');
-            return $this->renderAllTags($num_page, $nb_by_page, $sort, $searched);
+            return $this->renderAllTags();
         }
     }
 
@@ -448,11 +417,7 @@
         }
         else{
             // In case we changed the alias from the tag list.
-            $num_page = $this->getRequest()->request->get('num_page');
-            $nb_by_page = $this->getRequest()->request->get('nb_by_page');
-            $sort = $this->getRequest()->request->get('sort');
-            $searched = $this->getRequest()->request->get('searched');
-            return $this->renderAllTags($num_page, $nb_by_page, $sort, $searched);
+            return $this->renderAllTags();
         }
     }
     
@@ -539,18 +504,14 @@
             $this->updateTagWithNewLabel($moved_tag, $tag_label);
         }
         catch (\Exception $e){
-            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400);
+            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),500);
         }
         // We render the tag list.
-        $num_page = $this->getRequest()->request->get('num_page');
-        $nb_by_page = $this->getRequest()->request->get('nb_by_page');
-        $sort = $this->getRequest()->request->get('sort');
-        $searched = $this->getRequest()->request->get('searched');
-        return $this->renderAllTags($num_page, $nb_by_page, $sort, $searched);
+        return $this->renderAllTags();
     }
-    
+        
+        
     /**
-     *
      * Resemantize the tag with its original label. Kind of undo if we changed the tag's label.
      *
      */
@@ -563,16 +524,33 @@
             $this->updateTagWithNewLabel($moved_tag, $moved_tag->getOriginalLabel());
         }
         catch (\Exception $e){
-            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),400);
+            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),500);
         }
         
         // We render the tag list.
-        $num_page = $this->getRequest()->request->get('num_page');
-        $nb_by_page = $this->getRequest()->request->get('nb_by_page');
-        $sort = $this->getRequest()->request->get('sort');
-        $searched = $this->getRequest()->request->get('searched');
-        return $this->renderAllTags($num_page, $nb_by_page, $sort, $searched);
+        return $this->renderAllTags();
     }
+    
+    /**
+     * Redo a Wikipedia search
+     * @return \Symfony\Component\HttpFoundation\Response|\Symfony\Bundle\FrameworkBundle\Controller\Response
+     */
+    public function relaunchWpSearchAction()
+    {
+        $id_tag = $this->getRequest()->request->get('tag_id');
+        $tag = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->findOneBy(array('id' => $id_tag));
+        // We update the tag label and its wikipedia info with the original label.
+        try {
+            $this->updateTagWithNewLabel($tag, $tag->getLabel());
+        }
+        catch (\Exception $e){
+            return new Response(json_encode(array('error' => 'wikipedia_request_failed', 'message' => $e->getMessage())),500);
+        }
+    
+        // We render the tag list.
+        return $this->renderAllTags();
+    }
+    
 
 
     /**
@@ -581,21 +559,14 @@
     private function updateTagWithNewLabel($tag, $label)
     {
         if($tag!=null && $label!=null){
-            if($label!=$tag->getLabel()){
-                // We get the Wikipedia informations for the sent label
-                $tag_label_normalized = WikiTagUtils::normalizeTag($label);
-                try {
-                    $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized);
-                }
-                catch (\Exception $e){
-                    throw new \Exception($e->getMessage());
-                }
-                $tag->setWikipediaInfo($wp_response);
-                // Save datas.
-                $em = $this->getDoctrine()->getEntityManager();
-                $em->persist($tag);
-                $em->flush();
-            }
+            // We get the Wikipedia informations for the sent label
+            $tag_label_normalized = WikiTagUtils::normalizeTag($label);
+            $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized);
+            $tag->setWikipediaInfo($wp_response);
+            // Save datas.
+            $em = $this->getDoctrine()->getEntityManager();
+            $em->persist($tag);
+            $em->flush();
         }
     }
 
@@ -603,9 +574,20 @@
     /**
      * Generic render partial template for tag list
      */
-    public function renderAllTags($num_page=NULL, $nb_by_page=NULL, $sort=NULL, $searched=NULL)
+    public function renderAllTags($num_page=null, $nb_by_page=null, $sort=null, $searched=null)
     {
-        
+        if(is_null($num_page)) {
+            $num_page = $this->getRequest()->request->get('num_page');
+        }
+        if(is_null($nb_by_page)) {
+            $nb_by_page = $this->getRequest()->request->get('nb_by_page');
+        }
+        if(is_null($sort)) {
+            $sort = $this->getRequest()->request->get('sort');
+        }
+        if(is_null($searched)) {
+            $searched = $this->getRequest()->request->get('searched');
+        }
         //We get the needed datas in an array($tags, $num_page, $nb_by_page, $searched, $sort, $reverse_sort, $pagerfanta);
         $ar = $this->getAllTags($num_page, $nb_by_page, $sort, $searched);
         $tags = $ar[0];
@@ -620,7 +602,6 @@
         	'reverse_sort' => $reverse_sort, 'route_for_documents_by_tag' => $this->container->getParameter("wiki_tag.route_for_documents_by_tag")));
     }
 
-
     /**
      * Generic to get all tags with the context (pagination number, nb by page, searched string, sort)
      */
--- a/DependencyInjection/Configuration.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/DependencyInjection/Configuration.php	Wed Dec 14 23:53:37 2011 +0100
@@ -25,6 +25,7 @@
         $rootNode
             ->children()
                 ->scalarNode('route_for_documents_by_tag')->defaultNull()->end()
+                ->booleanNode('ignore_wikipedia_error')->defaultFalse()->end()
                 ->scalarNode('document_class')->isRequired()->end()
                 ->scalarNode('document_id_column')->defaultValue('id')->end()
             ->end()
--- a/DependencyInjection/WikiTagExtension.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/DependencyInjection/WikiTagExtension.php	Wed Dec 14 23:53:37 2011 +0100
@@ -29,6 +29,12 @@
         {
             $container->setParameter("wiki_tag.document_class", $config['document_class']);
         }
+
+        if(isset($config['ignore_wikipedia_error']))
+        {
+            $container->setParameter("wiki_tag.ignore_wikipedia_error", $config['ignore_wikipedia_error']);
+        }
+        
         
         if(isset($config['document_id_column']))
         {
--- a/Entity/DocumentRepository.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Entity/DocumentRepository.php	Wed Dec 14 23:53:37 2011 +0100
@@ -243,7 +243,7 @@
     public function getTagsStr($document)
     {
         $em = $this->getEntityManager();
-        $query = $em->createQuery("SELECT t.label FROM WikiTagBundle:DocumentTag dt JOIN dt.tag t WHERE dt.document = :docid");
+        $query = $em->createQuery("SELECT t.label FROM WikiTagBundle:DocumentTag dt JOIN dt.tag t WHERE dt.document = :docid ORDER BY dt.tagOrder");
         $query = $query->setParameter("docid", $document);
         $result = $query->getScalarResult();
         $tagstr = array();
@@ -253,6 +253,7 @@
         return $tagstr;
     }
     
+    
     /**
      * Update a wikitag document tags string.
      * @param DocumentInterface $document the wikitag document
@@ -365,5 +366,28 @@
         
         return $res;
     }
+    
+    /**
+     * Copy the tahg lst from one document instance to another
+     * @param IRI\Bundle\WikitagBundle\Model\DocumentInterface $src_doc
+     * @param IRI\Bundle\WikitagBundle\Model\DocumentInterface $tgt_doc
+     */
+    public function copyTags($src_doc, $tgt_doc)
+    {
+        //remove the previous tags
+        foreach ($tgt_doc->getTags() as $doctag) {
+            $this->getEntityManager()->remove($doctag);
+        }
+        
+        // add the new ones
+        foreach ($src_doc->getTags() as $doctag) {
+            $new_doctag = clone $doctag;
+            $new_doctag->setDocument($tgt_doc);
+            $this->getEntityManager()->persist($new_doctag);
+        }
+        
+        $tgt_doc->setManualOrder(false);
+        $this->getEntityManager()->persist($tgt_doc);
+    }
         
 }
\ No newline at end of file
--- a/Entity/DocumentTagRepository.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Entity/DocumentTagRepository.php	Wed Dec 14 23:53:37 2011 +0100
@@ -95,5 +95,5 @@
             return null;
         }
     }
-    
+
 }
\ No newline at end of file
--- a/Entity/TagRepository.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Entity/TagRepository.php	Wed Dec 14 23:53:37 2011 +0100
@@ -34,6 +34,7 @@
         $qb->select('t.label');
         $qb->from('WikiTagBundle:Tag','t');
         $qb->where($qb->expr()->orx(
+            $qb->expr()->like('t.label',$qb->expr()->literal(addcslashes(mysql_real_escape_string($seed),"%_")."%")),
             $qb->expr()->like('t.label',$qb->expr()->literal("%".addcslashes(mysql_real_escape_string($seed),"%_"))),
             $qb->expr()->like('t.label',$qb->expr()->literal("% ".addcslashes(mysql_real_escape_string($seed),"%_")."%"))
         ));
@@ -58,9 +59,11 @@
         $tags = $this->findBy(array('normalizedLabel' => $tag_label_normalized));
         $tag = null;
         foreach ($tags as $t) {
-            if($tag==null || $t->getUrlStatus()!=Tag::$TAG_URL_STATUS_DICT['null_result']) {
+            if($tag==null
+            || $tag->getUrlStatus() === Tag::$TAG_URL_STATUS_DICT['unsemantized']
+            || ($tag->getUrlStatus() === Tag::$TAG_URL_STATUS_DICT['null_result'] && $t->getUrlStatus() !== Tag::$TAG_URL_STATUS_DICT['unsemantized'])) {
                 $tag = $t;
-                if($t->getUrlStatus()!=Tag::$TAG_URL_STATUS_DICT['null_result']) {
+                if($tag->getUrlStatus()!=Tag::$TAG_URL_STATUS_DICT['unsemantized'] && $tag->getUrlStatus()!=Tag::$TAG_URL_STATUS_DICT['null_result']) {
                     break;
                 }
             }
@@ -75,45 +78,13 @@
         }
         else {
             $created = false;
-            $match_exists = false;
-            // Even if a tag with the normalised label exists, IF this tag is not wikipedia semantised,
-            // we search if a wikipedia semantised version exists in the base
-            foreach ($tags as $t) {
-                if($t->getUrlStatus()==Tag::$TAG_URL_STATUS_DICT['match']) {
-                    $tag = $t;
-                    $match_exists = true;
-                    break;
-                }
-            }
-            if($match_exists==false) {
-                try {
-                    $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized);
-                }
-                catch (\Exception $e){
-                    throw new \Exception($e->getMessage());
-                }
-                $status = $wp_response['status'];
-                if($status==Tag::$TAG_URL_STATUS_DICT['match']) {
-                    $tag = new Tag();
-                    $tag->setLabel($tag_label_normalized);
-                    $tag->setOriginalLabel($tag_label);
-                    $tag->setNormalizedLabel($tag_label_normalized);
-                    $created = true;
-                    $wp_request_done = true;
-                }
-            }
         }
     
-        // We request Wikipedia if the tag is created
-        if($created==true) {
+        // We request Wikipedia if the tag is created or if this is a null result
+        if($created==true || $tag->getUrlStatus()===Tag::$TAG_URL_STATUS_DICT['null_result']) {
             
             if($wp_request_done==false) {
-                try {
-                    $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized);
-                }
-                catch (\Exception $e){
-                    throw new \Exception($e->getMessage());
-                }
+                $wp_response = WikiTagUtils::getWikipediaInfo($tag_label_normalized);
             }
             
             $tag->setWikipediaInfo($wp_response);
@@ -126,13 +97,8 @@
             $wikipedia_revision_id = $wp_response['revision_id'];
     
         }
-        else if($tag!=null && $tag->getWikipediaPageId()!=null) {
-            try {
-                $wp_response = WikiTagUtils::getWikipediaInfo(null, $tag->getWikipediaPageId());
-            }
-            catch (\Exception $e){
-                throw new \Exception($e->getMessage());
-            }
+        elseif($tag!=null && $tag->getWikipediaPageId()!=null) {
+            $wp_response = WikiTagUtils::getWikipediaInfo(null, $tag->getWikipediaPageId());
             $wikipedia_revision_id = $wp_response['revision_id'];
         }
         else {
@@ -142,5 +108,4 @@
         return array($tag, $wikipedia_revision_id, $created);//, $wpReponse);
     }
     
-    
-}
\ No newline at end of file
+}
--- a/Listener/DocumentListener.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Listener/DocumentListener.php	Wed Dec 14 23:53:37 2011 +0100
@@ -19,6 +19,7 @@
 use Doctrine\Common\EventSubscriber;
 use Doctrine\ORM\Events;
 use Doctrine\ORM\Event\LifecycleEventArgs;
+use Doctrine\ORM\Event\OnFlushEventArgs;
 use Doctrine\ORM\Event\PreUpdateEventArgs;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -59,6 +60,7 @@
         Events::postUpdate,
         Events::preRemove,
         Events::postRemove,
+        Events::onFlush,
         );
     }
     
@@ -70,6 +72,11 @@
         $event_dispatcher->dispatch(WikiTagEvents::onTagChanged, $event);
     }
     
+    public function onFlush(OnFlushEventArgs $eventArgs)
+    {
+        
+    }
+    
     public function postPersist(LifecycleEventArgs $args)
     {
         $logger = $this->container->get('logger');
@@ -117,7 +124,7 @@
         $class = $this->getContainer()->getParameter("wiki_tag.document_class");
         if (is_a($entity, $class))
         {
-            $logger->debug("treating document : " . $entity->getTitle());
+            $logger->debug("treating document : " . $entity->getId());
             $this->container->get('doctrine')->getRepository("WikiTagBundle:Document")->writeDocument($entity, $this->getContainer()->getParameter("wiki_tag.document_id_column"), $this->getContainer()->getParameter("wiki_tag.fields"));
         }
         elseif (is_a($entity, "IRI\Bundle\WikiTagBundle\Model\DocumentTagInterface"))
--- a/Model/Document.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/Document.php	Wed Dec 14 23:53:37 2011 +0100
@@ -20,14 +20,13 @@
     */
     protected $id;
     
-    
     /**
      * @var boolean $manualOrder
      */
     protected $manualOrder = false;
     
     /**
-     * @var string $externalId
+     * @var mixed $externalId
      */
     protected $externalId;
     
@@ -40,6 +39,11 @@
      * @var string tagsStr
      */
     protected $tagsStr;
+        
+   /**
+    * @var DateTime
+    */
+    protected $createdAt;
     
     
     /**
@@ -74,7 +78,7 @@
     }
     
     /**
-     * TODO: (non-PHPdoc)
+     * (non-PHPdoc)
      * @see IRI\Bundle\WikiTagBundle\Model.BaseDocumentInterface::setExternalId()
      */
     function setExternalId($externalId)
@@ -83,7 +87,7 @@
     }
     
     /**
-     * TODO: (non-PHPdoc)
+     * (non-PHPdoc)
      * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::getExternalId()
      */
     function getExternalId()
@@ -92,7 +96,7 @@
     }
     
     /**
-     * TODO: (non-PHPdoc)
+     * (non-PHPdoc)
      * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::getTags()
      */
     function getTags()
@@ -101,23 +105,42 @@
     }
     
     /**
-     * TODO: (non-PHPdoc)
+     * (non-PHPdoc)
      * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::setTagsStr()
      */
     function setTagsStr($tagsStr)
     {
         $this->tagsStr = $tagsStr;
     }
-
-    /**
-    * TODO: (non-PHPdoc)
-    * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::getTagsStr()
-    */
+    
+   	/**
+     * (non-PHPdoc)
+     * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::getTagsStr()
+     */
     function getTagsStr()
     {
         return $this->tagsStr;
     }
     
+    /**
+     * (non-PHPdoc)
+     * @see IRI\Bundle\WikiTagBundle\Model.DocumentInterface::getCreatedAt()
+     */
+    function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+    
+    /**
+    *
+    * construct the class
+    */
+    function __construct()
+    {
+        $this->createdAt = new \DateTime("now", new \DateTimeZone('UTC'));
+    }
+    
+    
     public function __toString()
     {
         return print_r(Debug::export($this, 3),true);
--- a/Model/DocumentInterface.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/DocumentInterface.php	Wed Dec 14 23:53:37 2011 +0100
@@ -51,6 +51,13 @@
     function getManualOrder();
     
     /**
+     * Get the list of tags
+     *
+     * @return array of IRI\Bundle\WikiTagBundle\Model\DocumentTagInterface
+     */
+    function getTags();
+    
+    /**
      * Get tagsStr
      *
      * @return string
@@ -63,5 +70,11 @@
      * @param $tagsStr
      */
     function setTagsStr($tagsStr);
-        
+    
+   /**
+    * return the utc time when this object has been created
+    */
+    function getCreatedAt();
+    
+            
 }
--- a/Model/DocumentTag.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/DocumentTag.php	Wed Dec 14 23:53:37 2011 +0100
@@ -48,6 +48,21 @@
     protected $document;
     
     
+   /**
+    * @var DateTime
+    */
+    protected $createdAt;
+    
+    /**
+    *
+    * construct the class
+    */
+    function __construct()
+    {
+        $this->createdAt = new \DateTime("now", new \DateTimeZone('UTC'));
+    }
+    
+    
     /**
      * Get id
      *
@@ -178,6 +193,16 @@
         return $this->document;
     }
     
+   /**
+    * (non-PHPdoc)
+    * @see IRI\Bundle\WikiTagBundle\Model.DocumentTagInterface::getCreatedAt()
+    */
+    function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
+    
+    
     /**
     * Get wikipedia_version_permalink
     *
@@ -189,4 +214,6 @@
         return $WIKIPEDIA_VERSION_PERMALINK_TEMPLATE.$this->wikipediaRevisionId;
     }
     
+    
+    
 }
\ No newline at end of file
--- a/Model/DocumentTagInterface.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/DocumentTagInterface.php	Wed Dec 14 23:53:37 2011 +0100
@@ -102,5 +102,12 @@
      * @return object
      */
     function getDocument();
+    
+   /**
+    * return the utc time when this object has been created
+    */
+    function getCreatedAt();
+    
+    
 
 }
\ No newline at end of file
--- a/Model/Tag.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/Tag.php	Wed Dec 14 23:53:37 2011 +0100
@@ -15,7 +15,7 @@
 
 abstract class Tag implements TagInterface {
     
-    public static $TAG_URL_STATUS_DICT = array('null_result'=>0,'redirection'=>1,'homonyme'=>2,'match'=>3);
+    public static $TAG_URL_STATUS_DICT = array('null_result'=>0,'redirection'=>1,'homonyme'=>2,'match'=>3, 'unsemantized'=>4);
      
     /**
      * @var integer $id
@@ -56,8 +56,7 @@
     * @var string $alternativeWikipediaUrl
     */
     protected $alternativeWikipediaUrl;
-        
-    
+            
     /**
      * @var bigint $wikipediaPageId
      */
@@ -92,9 +91,23 @@
      * @var ArrayCollection $documents
      */
     protected $documents;
+    
+    /**
+     * @var DateTime
+     */
+    protected $createdAt;
 
 
     /**
+     *
+     * construct the class
+     */
+    function __construct()
+    {
+        $this->createdAt = new \DateTime("now", new \DateTimeZone('UTC'));
+    }
+    
+    /**
      * Get id
      *
      * @return integer
@@ -317,6 +330,10 @@
     */
     public function getUrlStatusText()
     {
+        if(is_null($this->getUrlStatus()))
+        {
+            return null;
+        }
         switch ($this->getUrlStatus()) {
             case 0:
                 return "null_result";
@@ -326,6 +343,10 @@
                 return "homonyme";
             case 3:
                 return "match";
+            case 4:
+                return "unsemantized";
+            default:
+                return "";
         }
     }
     
@@ -389,6 +410,15 @@
     {
         return $this->category;
     }
+    
+    /**
+     * (non-PHPdoc)
+     * @see IRI\Bundle\WikiTagBundle\Model.TagInterface::getCreatedAt()
+     */
+    function getCreatedAt()
+    {
+        return $this->createdAt;
+    }
 
     /**
      * Get Documents
--- a/Model/TagInterface.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Model/TagInterface.php	Wed Dec 14 23:53:37 2011 +0100
@@ -218,6 +218,11 @@
     function getCategory();
     
     /**
+     * return the utc time when this tag has been created
+     */
+    function getCreatedAt();
+    
+    /**
      * Get Documents
      *
      * @return ArrayCollection
@@ -236,5 +241,6 @@
      * @param $wikipedia_info
      */
     function setWikipediaInfo($wikipedia_info);
+        
     
 }
\ No newline at end of file
--- a/Resources/config/doctrine/Document.orm.yml	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/config/doctrine/Document.orm.yml	Wed Dec 14 23:53:37 2011 +0100
@@ -14,9 +14,13 @@
       type: boolean
       column: manual_order
     tagsStr:
-        type: text
-        column: tags_str
-        nullable: true
+      type: text
+      column: tags_str
+      nullable: true
+    createdAt:
+      type: datetime
+      column: created_at
+      nullable: false
   oneToMany:
     tags:
       targetEntity: DocumentTag
--- a/Resources/config/doctrine/DocumentTag.orm.yml	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/config/doctrine/DocumentTag.orm.yml	Wed Dec 14 23:53:37 2011 +0100
@@ -21,6 +21,10 @@
       type: bigint
       column: wikipedia_revision_id
       nullable: true
+    createdAt:
+      type: datetime
+      column: created_at
+      nullable: false      
   manyToOne:
     tag:
       targetEntity: Tag
--- a/Resources/config/doctrine/Tag.orm.yml	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/config/doctrine/Tag.orm.yml	Wed Dec 14 23:53:37 2011 +0100
@@ -57,6 +57,11 @@
       nullable: true
     popularity:
       type: integer
+    createdAt:
+      type: datetime
+      column: created_at
+      nullable: false
+      
   manyToOne:
     category:
       targetEntity: Category
--- a/Resources/config/routing.yml	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/config/routing.yml	Wed Dec 14 23:53:37 2011 +0100
@@ -19,6 +19,9 @@
 wikitag_modify_tag_in_list:
     pattern:  /wtmtl
     defaults: { _controller: WikiTagBundle:WikiTag:modifyTag }
+wikitag_relaunch_wp_search:
+    pattern:  /wtrws
+    defaults: { _controller: WikiTagBundle:WikiTag:relaunchWpSearch }
 wikitag_reset_wp_info:
     pattern:  /wtrwi
     defaults: { _controller: WikiTagBundle:WikiTag:resetWpInfo }
--- a/Resources/config/services.yml	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/config/services.yml	Wed Dec 14 23:53:37 2011 +0100
@@ -3,7 +3,8 @@
     wiki_tag.document_listener.class: IRI\Bundle\WikiTagBundle\Listener\DocumentListener
     wiki_tag.wiki_tag_document_listener.class: IRI\Bundle\WikiTagBundle\Listener\WikiTagDocumentListener
     wiki_tag.shema_utils.class: IRI\Bundle\WikiTagBundle\Utils\SchemaUtils
-    wiki_tag.search_class: IRI\Bundle\WikiTagBundle\Search\Search
+    wiki_tag.search_service_class: IRI\Bundle\WikiTagBundle\Services\SearchService
+    wiki_tag.document_service_class: IRI\Bundle\WikiTagBundle\Services\DocumentService
 
 
 services:
@@ -25,9 +26,14 @@
         arguments: [@service_container]
         
     wiki_tag.search:
-        class: %wiki_tag.search_class%
+        class: %wiki_tag.search_service_class%
         arguments: [@service_container]
 
+    wiki_tag.document:
+        class: %wiki_tag.document_service_class%
+        arguments: [@service_container]
+
+
 #    wiki_tag.example:
 #        class: %wiki_tag.example.class%
 #        arguments: [@service_id, "plain_value", %parameter%]
--- a/Resources/public/css/wikiTag.css	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/public/css/wikiTag.css	Wed Dec 14 23:53:37 2011 +0100
@@ -81,10 +81,21 @@
 .wikitag_updown_td span {
     display: none;
 }
+.wikitag_indicator {
+	cursor: progress;
+	background-image: url('../images/indicator.gif');
+}
+
 .wikitag_remove_wp_link {
 	cursor: pointer;
     background-image: url('../images/red_cross.png');
 }
+
+.wikitag_relaunch_wp_search {
+	cursor : pointer;
+	background-image:  url('../images/arrow_refresh.png');
+}
+
 .wikitag_remove_tag_from_list {
     cursor: pointer;
     background-image: url('../images/tag_remove.png');
Binary file Resources/public/images/arrow_refresh.png has changed
--- a/Resources/public/js/wikiTag.js	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/public/js/wikiTag.js	Wed Dec 14 23:53:37 2011 +0100
@@ -4,17 +4,27 @@
     // Tag simple operations : activate/unactivate wp link, reset wp info, remove wp link, remove tag from list
     $(".wikitag_reset_wp_info").click(function(e){
         if(confirm("Confirmez-vous le rétablissement du label original de ce tag ?")){
-            wikitag_update_tag(this);
+        	var id_tag = $(this).html();
+        	var btn = this;
+        	$(this).html("<img src='"+static_url+"/images/indicator.gif'>");
+            wikitag_update_tag(this, reset_wp_info_url, id_tag, function(){ $(btn).html(id_tag); });
+        }
+    });
+    $(".wikitag_relaunch_wp_search").click(function(e){
+        if(confirm("Confirmez-vous la relance de la recherche Wikipédia pour le tag \"" + $(this).attr('alt') + "\" ?")){
+        	$(this).toggleClass("wikitag_relaunch_wp_search wikitag_indicator");
+        	var btn = this;
+            wikitag_update_tag(this, relaunch_wp_search_url, $(this).attr('id'), function() { $(btn).toggleClass("wikitag_relaunch_wp_search wikitag_indicator");});
         }
     });
     $(".wikitag_remove_wp_link").click(function(e){
         if(confirm("Confirmez-vous le suppression du lien Wikipédia pour le tag \"" + $(this).attr('alt') + "\" ?")){
-            wikitag_update_tag(this);
+            wikitag_update_tag(this, remove_wp_link_url, $(this).attr('id'));
         }
     });
     $(".wikitag_remove_tag_from_list").click(function(){
         if(confirm("Confirmez-vous la suppression du tag \"" + $(this).attr('alt') + "\" de la liste courante ?")){
-            wikitag_update_tag(this);
+            wikitag_update_tag(this,remove_tag_from_list_url,$(this).attr('id'));
         }
     });
     
@@ -316,23 +326,9 @@
     });
 }
 
-function wikitag_update_tag(btn)
+function wikitag_update_tag(btn, url, id_tag, error_callback)
 {
     new_checked = false;
-    if ($(btn).is(".wikitag_remove_tag_from_list")) {
-        var url = remove_tag_from_list_url;
-        var id_tag = $(btn).attr('id');
-    }
-    else if ($(btn).is(".wikitag_reset_wp_info")) {
-        var url = reset_wp_info_url;
-        var id_tag = $(btn).html();
-        $(btn).html("<img src='"+static_url+"/images/indicator.gif'>");
-    }
-    else if ($(btn).is(".wikitag_remove_wp_link")) {
-        var url = remove_wp_link_url;
-        var id_tag = $(btn).attr('id');
-    }
-    
     // 2 cases : 
     // - ordered tag for one datasheet : $('#wikitag_document_id') is not null
     // - all tags list : $('#wikitag_document_id') is null and $('#num_page') and $('#nb_by_page') are not null
@@ -358,7 +354,7 @@
 		error: function(jqXHR, textStatus, errorThrown) {
 			resp = $.parseJSON(jqXHR.responseText);
 			alert(resp.message);
-			$(btn).html(id_tag);
+			error_callback && error_callback();
 		}
     });
 }
--- a/Resources/views/WikiTag/TagListTable.html.twig	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/views/WikiTag/TagListTable.html.twig	Wed Dec 14 23:53:37 2011 +0100
@@ -16,10 +16,10 @@
         {% endif %}
         </th>
         <th>original_label</th>
-        <th class="text_centered">Lien W</th>
-        <th class="text_centered">Lien D</th>
+        <th class="wikitag_text_centered">Lien W</th>
+        <th class="wikitag_text_centered">Lien D</th>
         <th>Catégorie</th>
-        <th class="large_25 text_centered">Supprimer<br/>le lien W</th>
+        <th class="wikitag_large_25 wikitag_text_centered">Supprimer le lien W</th>
         <th>Alias</th>
         <th>
         {% if sort != "nba" and sort != "nbd" %}
@@ -69,8 +69,14 @@
             &nbsp;
             {% endif %}
         </td>
-        <td class="wikitag_category" id="{{tag.id}}">{% if tag.category %}{{ tag.category.label }}{% endif %}</td>
-        <td class="wikitag_text_centered wikitag_td_icon wikitag_remove_wp_link" id="{{tag.id}}" alt="{{tag.label}}" ></td>
+        <td class="wikitag_category" id="{{tag.id}}">{% if tag.category %}{{ tag.category.label }}{% endif %}</td>        
+        {% if not tag.urlstatus or tag.urlstatus == 0 %}
+        <td class="wikitag_text_centered wikitag_td_icon wikitag_relaunch_wp_search" id="{{tag.id}}" alt="{{tag.label}}" ></td>
+        {% elseif   tag.urlstatus == 4 %}
+        <td class="wikitag_text_centered wikitag_td_icon"></td>
+        {% else %}        
+        <td class="wikitag_text_centered wikitag_td_icon wikitag_remove_wp_link" id="{{tag.id}}" alt="{{tag.label}}" ></td>        
+        {% endif %}            
         <td class="wikitag_alias" id="{{tag.id}}" >{% if tag.alias %}{{tag.alias}}{% endif %}</td>
         <td class="wikitag_text_centered">
         {% if nb_docs > 0 %}
@@ -82,4 +88,4 @@
         <td class="wikitag_text_centered">{{tag.popularity}}</td></tr>
     {% endfor %}
     </table>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- a/Resources/views/WikiTag/javascript.html.twig	Mon Dec 12 14:27:37 2011 +0100
+++ b/Resources/views/WikiTag/javascript.html.twig	Wed Dec 14 23:53:37 2011 +0100
@@ -22,6 +22,7 @@
     var modify_tag_url = "{{ url('wikitag_modify_documenttag') }}";
     {% endif %}
     var reset_wp_info_url = "{{ url('wikitag_reset_wp_info') }}";
+    var relaunch_wp_search_url = "{{ url('wikitag_relaunch_wp_search') }}";
     var reorder_tag_datasheet_url = "{{ url('wikitag_reorder_tag_document') }}";
     var add_tag_url = "{{ url('wikitag_add_tag') }}";
     var remove_wp_link_url = "{{ url('wikitag_remove_wp_link') }}";
--- a/Search/Search.php	Mon Dec 12 14:27:37 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-<?php
-/*
- * This file is part of the WikiTagBundle package.
- *
- * (c) IRI <http://www.iri.centrepompidou.fr/>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-namespace IRI\Bundle\WikiTagBundle\Search;
- 
-use Symfony\Component\DependencyInjection\ContainerAware;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-class Search extends ContainerAware
-{
-    /**
-     * Get the container associated with this service.
-     * @return ContainerInterface
-     */
-    public function getContainer()
-    {
-        return $this->container;
-    }
-    
-    /**
-     * Public constructor with container as parameter for contruct injection.
-     * @param ContainerInterface $container
-     */
-    public function __construct(ContainerInterface $container)
-    {
-        $this->setContainer($container);
-    }
-    
-    private $doctrine;
-    
-    public function getDoctrine()
-    {
-        if(is_null($this->doctrine))
-        {
-            $this->doctrine = $this->getContainer()->get('doctrine');
-        }
-        return $this->doctrine;
-    }
-    /**
-     *
-     * Enter description here ...
-     * @param string $value
-     * @param array $conditions
-     * @param array $fields
-     */
-    public function search($value, array $conditions, array $fields=null)
-    {
-        if(is_null($fields))
-        {
-            $fields = $this->getContainer()->getParameter("wiki_tag.fields");
-        }
-        $doctrine = $this->getContainer()->get('doctrine');
-        $res = $doctrine->getRepository('WikiTagBundle:Document');
-        $fieldquery = array();
-        foreach ($fields as $fieldname => $fielddef) {
-            if(isset($fielddef['weight']))
-            {
-                $weight = $fielddef['weight'];
-            }
-            else
-            {
-                $weight = 1.0;
-            }
-            $fieldquery[] = array("columns"=>$fieldname, "value"=>$value, "weight"=>$weight);
-        }
-        
-        $score_res = $res->search($fieldquery, $conditions);
-        
-        return $score_res;
-    }
-    
-    /**
-     * Service to reorder the tags using their notes in the index search
-     * @param IRI\Bundle\WikiTagBundle\Model\DocumentInterface $document
-     */
-    public function reorderTagsForDocument($document)
-    {
-        $doctrine = $this->getContainer()->get('doctrine');
-        
-        $tags_score = array();
-        
-        foreach($document->getTags() as $tag)
-        {
-            $label = $tag->getTag()->getLabel();
-            
-            $score_res = $this->search($label, array("id"=>$document->getId()));
-            
-            if(count($score_res)>0)
-            {
-                $score = floatval($score_res[0]['score']);
-            }
-            else
-            {
-                $score = 0.0;
-            }
-            $tags_score[] = array($score,$tag);
-        }
-        // sort tags based on score
-        $i=1;
-        usort($tags_score, function($a, $b) {
-            return $a[0]<$b[0]?1:-1;
-        });
-
-        foreach($tags_score as $item)
-        {
-            $tag = $item[1];
-            $tag->setTagOrder($i++);
-            $tag->setIndexNote($item[0]);
-            $doctrine->getEntityManager()->persist($tag);
-        }
-        
-    }
-    
-    public function getTagCloud($max_tags)
-    {
-        $rep = $this->getDoctrine()->getRepository('WikiTagBundle:Tag');
-        return $rep->getTagCloud($max_tags);
-    }
-    
-    public function completion($seed)
-    {
-        $rep = $this->getDoctrine()->getRepository('WikiTagBundle:Tag');
-        
-        $res = array();
-        foreach ($rep->getCompletion($seed) as $value) {
-            $res[] = $value['label'];
-        }
-        
-        return $res;
-        
-    }
-    
-        
-}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Services/DocumentService.php	Wed Dec 14 23:53:37 2011 +0100
@@ -0,0 +1,169 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace IRI\Bundle\WikiTagBundle\Services;
+ 
+use IRI\Bundle\WikiTagBundle\Entity\DocumentTag;
+use Symfony\Component\DependencyInjection\ContainerAware;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use IRI\Bundle\WikiTagBundle\Utils\WikiTagUtils;
+
+class DocumentService extends ContainerAware
+{
+    /**
+     * Get the container associated with this service.
+     * @return ContainerInterface
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+    
+    /**
+     * Public constructor with container as parameter for contruct injection.
+     * @param ContainerInterface $container
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->setContainer($container);
+    }
+    
+    private $doctrine;
+    
+    public function getDoctrine()
+    {
+        if(is_null($this->doctrine))
+        {
+            $this->doctrine = $this->getContainer()->get('doctrine');
+        }
+        return $this->doctrine;
+    }
+    
+
+    /**
+     * Copy the list of tags of one document to another.
+     * The ids are the ids of the "host" document.
+	 *
+     * @param mixed $id_doc_src the source document id
+     * @param mixed $id_doc_tgt the target document id
+     */
+    public function copyTags($id_doc_src, $id_doc_tgt)
+    {
+        
+        $doctrine = $this->getDoctrine();
+        $em = $doctrine->getEntityManager();
+        
+        $doc_rep = $em->getRepository("WikiTagBundle:Document");
+        
+        $src_doc = $doc_rep->findOneByExternalId($id_doc_src);
+        if(is_null($src_doc))
+        {
+            throw new Exception("cloneTags: no source doc");
+        }
+        
+        $tgt_doc = $doc_rep->findOneByExternalId($id_doc_tgt);
+        if(is_null($tgt_doc))
+        {
+            throw new Exception("cloneTags: no target doc");
+        }
+        
+        
+        $doc_rep->copyTags($src_doc, $tgt_doc);
+        
+        $em->flush();
+        
+    }
+    
+    /**
+     *
+     * Add a new tag to a "host" document.
+     * If the label already exists, an exception is raised.
+     *
+     * @param mixed $id_doc
+     * @param string|array $tag_label : the label of the new tag
+     */
+    public function addTags($id_doc, $tag_labels)
+    {
+        // We get the DocumentTags
+        $em = $this->getDoctrine()->getEntityManager();
+        
+        $need_flush = false;
+        
+        
+        if(!is_array($tag_labels)) {
+            $tag_labels = array($tag_labels);
+        }
+        
+        foreach ($tag_labels as $tag_label) {
+        
+            $normalized_tag_label = WikiTagUtils::normalizeTag($tag_label);
+            
+            $query = $em->createQuery("SELECT COUNT(dt.id) FROM WikiTagBundle:DocumentTag dt JOIN dt.tag t WHERE dt.document = :id_doc AND t.normalizedLabel = :label");
+            $query->setParameters(array("id_doc"=>$id_doc, "label"=>$normalized_tag_label));
+            
+            $nb_tags = $query->getSingleScalarResult();
+            
+            // If the label was found, we sent a bad request
+            if($nb_tags > 0){
+                throw new WikiTagServiceException(sprintf("Le tag %s existe déjà pour cette fiche.", $tag_label), 400, null, "duplicate_tag");
+            }
+            // returns array($tag, $revision_id, $created)
+            try {
+                $ar = $this->getDoctrine()->getRepository('WikiTagBundle:Tag')->getOrCreateTag($tag_label);
+            }
+            catch (\Exception $e){
+                throw new WikiTagServiceException($e->getMessage(), 500 , $e, "wikipedia_request_failed");
+            }
+            
+            $tag = $ar[0];
+            $revision_id = $ar[1];
+            $created = $ar[2];
+            
+            $tags = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->findByDocumentExternalId($id_doc, array('tag'=>$tag->getId()));
+            $nb_tags = count($tags);
+            
+            if($created==true || $nb_tags==0){
+                $new_order_ar = $this->getDoctrine()->getRepository('WikiTagBundle:DocumentTag')->getMaxOrder($id_doc);
+                // The result is a double array. And reset(reset($newOrderAr)) is not allowed. And a string is returned.
+                $a1 = reset($new_order_ar);
+                $new_order = intval(reset($a1)) + 1;
+                $new_DT = new DocumentTag();
+                $new_DT->setDocument($this->getDoctrine()->getRepository('WikiTagBundle:Document')->findOneByExternalId($id_doc));
+                $new_DT->setTag($tag);
+                $new_DT->setOriginalOrder($new_order);
+                $new_DT->setTagOrder($new_order);
+                $new_DT->setWikipediaRevisionId($revision_id);
+                $em->persist($new_DT);
+                $need_flush = true;
+                
+            }
+        }
+        
+        if($need_flush) {
+            $em->flush();
+        }
+        
+        
+    }
+    
+    public function getTagLabels($id_doc)
+    {
+        $rep = $this->getDoctrine()->getRepository('WikiTagBundle:Document');
+        $doc = $rep->findOneByExternalId($id_doc);
+        
+        if(is_null($doc)) {
+            throw new WikiTagServiceException("Unknown document id");
+        }
+        
+        return $rep->getTagsStr($doc);
+
+    }
+    
+        
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Services/SearchService.php	Wed Dec 14 23:53:37 2011 +0100
@@ -0,0 +1,140 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace IRI\Bundle\WikiTagBundle\Services;
+ 
+use Symfony\Component\DependencyInjection\ContainerAware;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class SearchService extends ContainerAware
+{
+    /**
+     * Get the container associated with this service.
+     * @return ContainerInterface
+     */
+    public function getContainer()
+    {
+        return $this->container;
+    }
+    
+    /**
+     * Public constructor with container as parameter for contruct injection.
+     * @param ContainerInterface $container
+     */
+    public function __construct(ContainerInterface $container)
+    {
+        $this->setContainer($container);
+    }
+    
+    private $doctrine;
+    
+    public function getDoctrine()
+    {
+        if(is_null($this->doctrine))
+        {
+            $this->doctrine = $this->getContainer()->get('doctrine');
+        }
+        return $this->doctrine;
+    }
+    /**
+     *
+     * Enter description here ...
+     * @param string $value
+     * @param array $conditions
+     * @param array $fields
+     */
+    public function search($value, array $conditions, array $fields=null)
+    {
+        if(is_null($fields))
+        {
+            $fields = $this->getContainer()->getParameter("wiki_tag.fields");
+        }
+        $doctrine = $this->getContainer()->get('doctrine');
+        $res = $doctrine->getRepository('WikiTagBundle:Document');
+        $fieldquery = array();
+        foreach ($fields as $fieldname => $fielddef) {
+            if(isset($fielddef['weight']))
+            {
+                $weight = $fielddef['weight'];
+            }
+            else
+            {
+                $weight = 1.0;
+            }
+            $fieldquery[] = array("columns"=>$fieldname, "value"=>$value, "weight"=>$weight);
+        }
+        
+        $score_res = $res->search($fieldquery, $conditions);
+        
+        return $score_res;
+    }
+    
+    /**
+     * Service to reorder the tags using their notes in the index search
+     * @param IRI\Bundle\WikiTagBundle\Model\DocumentInterface $document
+     */
+    public function reorderTagsForDocument($document)
+    {
+        $doctrine = $this->getContainer()->get('doctrine');
+        
+        $tags_score = array();
+        
+        foreach($document->getTags() as $tag)
+        {
+            $label = $tag->getTag()->getLabel();
+            
+            $score_res = $this->search($label, array("id"=>$document->getId()));
+            
+            if(count($score_res)>0)
+            {
+                $score = floatval($score_res[0]['score']);
+            }
+            else
+            {
+                $score = 0.0;
+            }
+            $tags_score[] = array($score,$tag);
+        }
+        // sort tags based on score
+        $i=1;
+        usort($tags_score, function($a, $b) {
+            return $a[0]<$b[0]?1:-1;
+        });
+
+        foreach($tags_score as $item)
+        {
+            $tag = $item[1];
+            $tag->setTagOrder($i++);
+            $tag->setIndexNote($item[0]);
+            $doctrine->getEntityManager()->persist($tag);
+        }
+        
+    }
+    
+    public function getTagCloud($max_tags)
+    {
+        $rep = $this->getDoctrine()->getRepository('WikiTagBundle:Tag');
+        return $rep->getTagCloud($max_tags);
+    }
+    
+    public function completion($seed)
+    {
+        $rep = $this->getDoctrine()->getRepository('WikiTagBundle:Tag');
+        
+        $res = array();
+        foreach ($rep->getCompletion($seed) as $value) {
+            $res[] = $value['label'];
+        }
+        
+        return $res;
+        
+    }
+    
+        
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Services/WikiTagServiceException.php	Wed Dec 14 23:53:37 2011 +0100
@@ -0,0 +1,30 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Services;
+
+class WikiTagServiceException extends \Exception
+{
+    /**
+     * The error code
+     * @var string
+     */
+    protected $error_code;
+    
+    public function __construct ($message = "", $code = 0, $previous = null, $error_code = "")
+    {
+        parent::__construct($message, $code, $previous);
+        $this->error_code = $error_code;
+    }
+    
+    public function getErrorCode() {
+        return $this->error_code;
+    }
+}
\ No newline at end of file
--- a/Tests/Search/SearchServiceTest.php	Mon Dec 12 14:27:37 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-<?php
-/*
- * This file is part of the WikiTagBundle package.
- *
- * (c) IRI <http://www.iri.centrepompidou.fr/>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace IRI\Bundle\WikiTagBundle\Tests\Services;
-
-require_once(__DIR__ . "/../../../../../../../app/AppKernel.php");
-
-class SearchServiceTest extends \PHPUnit_Framework_TestCase
-{
-
-    protected $_container;
-    
-    public function __construct()
-    {
-        $kernel = new \AppKernel("test", true);
-        $kernel->boot();
-        $this->_container = $kernel->getContainer();
-        parent::__construct();
-    }
-    
-    protected function get($service)
-    {
-        return $this->_container->get($service);
-    }
-    
-    
-    public function testTagCloud()
-    {
-        
-        $search_service = $this->get("wiki_tag.search");
-        
-        $result = $search_service->getTagCloud(30);
-        
-        $this->assertNotNull($result, "tag cloud should not be null");
-        $this->assertLessThanOrEqual(30, count($result));
-    }
-
-    
-    public function testCompletion()
-    {
-    
-        $search_service = $this->get("wiki_tag.search");
-    
-        $result = $search_service->completion("fra");
-    
-        $this->assertNotNull($result, "tag cloud should not be null");
-        $this->assertGreaterThanOrEqual(1, count($result));
-    }
-    
-    
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Tests/Services/DocumentServiceTest.php	Wed Dec 14 23:53:37 2011 +0100
@@ -0,0 +1,99 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Tests\Services;
+
+require_once(__DIR__ . "/../../../../../../../app/AppKernel.php");
+
+class DocumentServiceTest extends \PHPUnit_Framework_TestCase
+{
+
+    protected $_container;
+    protected $_doctrine;
+    protected $_application;
+    protected $_kernel;
+    
+    public function __construct()
+    {
+        $this->_kernel = new \AppKernel("test", true);
+        $this->_kernel->boot();
+        $this->_container = $this->_kernel->getContainer();
+        parent::__construct();
+    }
+    
+    protected function get($service)
+    {
+        return $this->_container->get($service);
+    }
+    
+    protected function getDoctrine()
+    {
+        if(is_null($this->_doctrine))
+        {
+            $this->_doctrine = $this->get('doctrine');
+        }
+        return $this->_doctrine;
+    }
+    
+    
+    public function testGetTagLabels()
+    {
+        $doc_service = $this->get("wiki_tag.document");
+        $doc1 = $this->getDoctrine()->getRepository("CompanyBaseBundle:Document")->findOneByTitle("Title 1");
+        
+        $tags = $doc_service->getTagLabels($doc1->getId());
+        
+        $this->assertEquals(4,count($tags));
+        
+        $this->assertEquals(array("Tag1","Tag2","Tag3","Tag4"),$tags);
+        
+    }
+    
+    public function testCopyTags()
+    {
+    
+        $doc_service = $this->get("wiki_tag.document");
+        
+        $doc1 = $this->getDoctrine()->getRepository("CompanyBaseBundle:Document")->findOneByTitle("Title 1");
+
+        $this->assertEquals(4,count($doc_service->getTagLabels($doc1->getId())));
+                
+        $doc2 = $this->getDoctrine()->getRepository("CompanyBaseBundle:Document")->findOneByTitle("Title 4");
+        
+        $this->assertEquals(0,count($doc_service->getTagLabels($doc2->getId())));
+        
+        $doc_service->copyTags($doc1->getId(), $doc2->getId());
+
+        $this->assertEquals(4,count($doc_service->getTagLabels($doc2->getId())));
+        $this->assertEquals(array("Tag1","Tag2","Tag3","Tag4"),$doc_service->getTagLabels($doc2->getId()));
+        
+    }
+    
+    public function setUp()
+    {
+        $this->_application = new \Symfony\Bundle\FrameworkBundle\Console\Application($this->_kernel);
+        $this->_application->setAutoExit(false);
+        $this->runConsole("doctrine:schema:drop", array("--force" => true));
+        $this->runConsole("wikitag:schema:create");
+        $this->runConsole("cache:warmup");
+        $this->runConsole("doctrine:fixtures:load", array("--fixtures" => __DIR__ . "/../../../../../../../src/Company/BaseBundle/DataFixtures"));
+    }
+    
+    protected function runConsole($command, Array $options = array())
+    {
+        
+        $options["-e"] = "test";
+        $options["-q"] = null;
+        $options = array_merge($options, array('command' => $command));
+        return $this->_application->run(new \Symfony\Component\Console\Input\ArrayInput($options));
+    }
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Tests/Services/SearchServiceTest.php	Wed Dec 14 23:53:37 2011 +0100
@@ -0,0 +1,59 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Tests\Services;
+
+require_once(__DIR__ . "/../../../../../../../app/AppKernel.php");
+
+class SearchServiceTest extends \PHPUnit_Framework_TestCase
+{
+
+    protected $_container;
+    
+    public function __construct()
+    {
+        $kernel = new \AppKernel("test", true);
+        $kernel->boot();
+        $this->_container = $kernel->getContainer();
+        parent::__construct();
+    }
+    
+    protected function get($service)
+    {
+        return $this->_container->get($service);
+    }
+    
+    
+    public function testTagCloud()
+    {
+        
+        $search_service = $this->get("wiki_tag.search");
+        
+        $result = $search_service->getTagCloud(30);
+        
+        $this->assertNotNull($result, "tag cloud should not be null");
+        $this->assertLessThanOrEqual(30, count($result));
+    }
+
+    
+    public function testCompletion()
+    {
+    
+        $search_service = $this->get("wiki_tag.search");
+    
+        $result = $search_service->completion("tag");
+    
+        $this->assertNotNull($result, "tag cloud should not be null");
+        $this->assertEquals(4, count($result));
+    }
+    
+    
+}
+
--- a/Utils/SchemaUtils.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Utils/SchemaUtils.php	Wed Dec 14 23:53:37 2011 +0100
@@ -90,7 +90,6 @@
     public function filter_foreign_key(array $sqls)
     {
         $res_sqls = array();
-        //TODO : take the column and table names from the schema
         foreach ($sqls as $sql) {
             if(!preg_match("/ADD CONSTRAINT .+ FOREIGN KEY \(.*\) REFERENCES wikitag_document\(id\)/i", $sql))
             {
--- a/Utils/WikiTagUtils.php	Mon Dec 12 14:27:37 2011 +0100
+++ b/Utils/WikiTagUtils.php	Wed Dec 14 23:53:37 2011 +0100
@@ -49,6 +49,15 @@
      */
     public static function getWikipediaInfo($tag_label_normalized, $page_id=null)
     {
+        // get ignore_wikipedia_error parameter
+        $ignore_wikipedia_error = false;
+        
+        if(key_exists('kernel', $GLOBALS)) {
+            $kernel = $GLOBALS['kernel'];
+            $ignore_wikipedia_error = $kernel->getContainer()->getParameter('wiki_tag.ignore_wikipedia_error');
+        }
+        
+        
         $params = array('action'=>'query', 'prop'=>'info|categories|langlinks', 'inprop'=>'url', 'lllimit'=>'500', 'cllimit'=>'500', 'rvprop'=>'ids', 'format'=>'json');
         if($tag_label_normalized!=null){
             $params['titles'] = urlencode($tag_label_normalized);
@@ -63,9 +72,15 @@
         try {
             $ar = WikiTagUtils::requestWikipedia($params);
         }
-        catch (\Exception $e){
-            throw new \Exception($e->getMessage());
+        catch(\Exception $e) {
+            if($ignore_wikipedia_error) {
+                return WikiTagUtils::returnNullResult(null);
+            }
+            else {
+                throw $e;
+            }
         }
+
         $res = $ar[0];
         $original_response = $res;
         $pages = $ar[1];
@@ -103,7 +118,18 @@
         if($status==Tag::$TAG_URL_STATUS_DICT["redirection"])
         {
             $params['redirects'] = "true";
-            $ar = WikiTagUtils::requestWikipedia($params);
+            try {
+                $ar = WikiTagUtils::requestWikipedia($params);
+            }
+            catch(\Exception $e) {
+                if($ignore_wikipedia_error) {
+                    return WikiTagUtils::returnNullResult(null);
+                }
+                else {
+                    throw $e;
+                }
+            }
+            
             $res = $ar[0];
             $pages = $ar[1];
             #we know that we have at least one answer
@@ -148,7 +174,7 @@
         	'dbpedia_uri'=>$dbpedia_uri,
         	'revision_id'=>$revision_id,
         	'response'=>$original_response);
-        //return $url." <br/>RES =  ".$res/*." <br/>DUMP =  ".var_dump($pages)*/." <br/>COUNT =  ".count($pages)." <br/>page =  ".var_dump($page);
+        
         return $wp_response;
     }
     
@@ -176,6 +202,7 @@
         $ch = curl_init();
         curl_setopt($ch, CURLOPT_URL, $url);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        //TODO: change
         curl_setopt($ch, CURLOPT_USERAGENT, 'http://www.iri.centrepompidou.fr');
         curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
         $res = curl_exec($ch);
@@ -184,7 +211,7 @@
         curl_close($ch);
         
         if ($curl_errno > 0) {
-            throw new \Exception("Wikipedia request failed. cURLError #$curl_errno: $curl_error\n");
+            throw new \Exception("Wikipedia request failed. cURLError #$curl_errno: $curl_error\n", $curl_errno, null);
         }
         
         $val = json_decode($res, true);