Repository: zestedesavoir/zds-site

Issue:
Erreur 500 lors de la publication d'un billet
J'ai rédigé il y a quelques temps un article expliquant le principe des logiciels libres. Toutefois, manquant d'arguments pour une section "Critique", j'ai décidé de le convertir en billet. J'ai donc créé un billet, y ai importé les images utilisées dans l'article et ai copié le texte, avec juste quelques modifications mineures. Toutefois, au moment de la publication, j'obtiens une erreur 500.

Voici l'article concerné : [https://zestedesavoir.com/contenus/2040/quest-ce-quun-logiciel-libre/](https://zestedesavoir.com/contenus/2040/quest-ce-quun-logiciel-libre/)
Et voici le billet concerné : [https://zestedesavoir.com/contenus/2064/quest-ce-quun-logiciel-libre-1/](https://zestedesavoir.com/contenus/2064/quest-ce-quun-logiciel-libre-1/)


Discussion:
... Avec une erreur des plus étonnantes, soit dit en passant: https://sentry.sandhose.fr/zeste-de-savoir/backend-l7/issues/984/ : 

```
UnicodeError: URL u'https://commons.wikimedia.org/wiki/File:Richard_Stallman_-_F\xeate_de_l%27Humanit\xe9_2014_-_010.jpg' contains non-ASCII characters
```

Je te conseille donc, dans l'absolu, d'uploader cette image sur ZdS. Ça te permettra de débloquer la situation pour toi. Maintenant, ça reste un bug étonnant.
> Je te conseille donc, dans l'absolu, d'uploader cette image sur ZdS. Ça te permettra de débloquer la situation pour toi. Maintenant, ça reste un bug étonnant.

C'est d'autant plus étonnant que c'est déjà fait. Voilà le markdown du billet à cet endroit.

```
Cet étudiant, c'est **Richard Matthew Stallman** (parfois appelé *rms*).

![Portrait de Richard Stallman en 2014. [Source](https://commons.wikimedia.org/wiki/File:Richard_Stallman_-_Fête_de_l%27Humanité_2014_-_010.jpg).](/media/galleries/4410/c1016bf1-a1de-48a1-9ef1-144308e8725d.jpg)

Quelques années plus tard...
```

Ça viendrait de l'URL du coup ?
Oh, c'est encore plus bizarre, ça. On dirait effectivement que ça provient du "ê" de l'URL. Sauf que c'est pas sencé le faire à cet endroit là o_O
il y a probablement un double pb : 

- le markdown initial est mauvais et fait planter le système
- à un moment donné l'utf-8 n'est pas géré.

Par contre là tout de suite, je suis pas sûr que ça vienne de la v24
@artragis : le problème a eu lieu en prod, donc de toute façon, il n'y a rien de spécifique à la v24.
Je viens de modifier l'URL et de publier le billet, mais j'ai téléchargé une copie de l'archive avant. Si ça peut vous être utile pour faire des tests, je la mets en pièce jointe. ;-)
rezemika, peux-tu m'envoyer l'archive par quelque media que ce fût? (mon pseudo @ gmail.com devrait fonctionner)
@artragis : C'est fait, désolé pour le délai.

Si quelqu'un d'autre en a besoin, je la mets ici en pièce-jointe.

[quest-ce-quun-logiciel-libre-1.zip](https://github.com/zestedesavoir/zds-site/files/1220059/quest-ce-quun-logiciel-libre-1.zip)


Resolving patch:
diff --git a/zds/tutorialv2/utils.py b/zds/tutorialv2/utils.py
--- a/zds/tutorialv2/utils.py
+++ b/zds/tutorialv2/utils.py
@@ -12,6 +12,7 @@
 
 import os
 import logging
+from urllib.parse import urlsplit, urlunsplit, quote
 from PIL import Image as ImagePIL
 from django.contrib.auth.models import User
 from django.http import Http404
@@ -289,6 +290,27 @@ def get_target_tagged_tree_for_container(movable_child, root, bias=-1):
     return target_tagged_tree
 
 
+def normalize_unicode_url(unicode_url):
+    """Sometimes you get an URL by a user that just isn't a real URL
+    because it contains unsafe characters like 'β' and so on.  This
+    function can fix some of the problems in a similar way browsers
+    handle data entered by the user:
+
+    .. sourcecode:: python
+
+        normalize_unicode_url(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
+        # 'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
+
+
+    :param charset: The target charset for the URL if the url was
+                    given as unicode string.
+    """
+    scheme, netloc, path, querystring, anchor = urlsplit(unicode_url)
+    path = quote(path, '/%')
+    querystring = quote(querystring, ':&=')
+    return urlunsplit((scheme, netloc, path, querystring, anchor))
+
+
 def retrieve_image(url, directory):
     """For a given image, retrieve it, transform it into PNG (if needed) and store it
 
@@ -328,6 +350,7 @@ def retrieve_image(url, directory):
     try:
         if parsed_url.scheme in ['http', 'https', 'ftp'] \
                 or parsed_url.netloc[:3] == 'www' or parsed_url.path[:3] == 'www':
+            url = normalize_unicode_url(url)
             urlretrieve(url, store_path)  # download online image
         else:  # it's a local image, coming from a gallery
 


Test patch:
diff --git a/zds/tutorialv2/tests/tests_utils.py b/zds/tutorialv2/tests/tests_utils.py
--- a/zds/tutorialv2/tests/tests_utils.py
+++ b/zds/tutorialv2/tests/tests_utils.py
@@ -577,6 +577,17 @@ def test_adjust_char_count(self):
         published = PublishedContent.objects.get(pk=published.pk)
         self.assertEqual(published.char_count, published.get_char_count())
 
+    def test_image_with_non_ascii_chars(self):
+        """seen on #4144"""
+        article = PublishableContentFactory(type='article', author_list=[self.user_author])
+        image_string = '![Portrait de Richard Stallman en 2014. [Source](https://commons.wikimedia.org/wiki/' \
+                       'File:Richard_Stallman_-_Fête_de_l%27Humanité_2014_-_010.jpg).]' \
+                       '(/media/galleries/4410/c1016bf1-a1de-48a1-9ef1-144308e8725d.jpg)'
+        article.sha_draft = article.load_version().repo_update(article.title, image_string, '', update_slug=False)
+        article.save(force_slug_update=False)
+        publish_content(article, article.load_version())
+        self.assertTrue(PublishedContent.objects.filter(content_id=article.pk).exists())
+
     def tearDown(self):
         if os.path.isdir(overridden_zds_app['content']['repo_private_path']):
             shutil.rmtree(overridden_zds_app['content']['repo_private_path'])